@nayan-ui/engine 1.0.0-beta.5 → 1.0.0-beta.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.
Files changed (77) hide show
  1. package/README.md +95 -4
  2. package/lib/module/assets/ProceduralCache.js +5 -0
  3. package/lib/module/assets/loft.js +15 -63
  4. package/lib/module/assets/procedural.js +500 -0
  5. package/lib/module/assets/shape.js +147 -245
  6. package/lib/module/assets/texture.js +65 -0
  7. package/lib/module/drawing/DrawingDocument.js +1 -1
  8. package/lib/module/index.js +4 -1
  9. package/lib/module/media/audio-worklet.js +27 -0
  10. package/lib/module/media/audio.js +20 -0
  11. package/lib/module/media/input.web.js +107 -0
  12. package/lib/module/media/microphone.js +89 -0
  13. package/lib/module/media/speech.js +80 -0
  14. package/lib/module/media/speechPlatform.js +12 -0
  15. package/lib/module/media/speechPlatform.web.js +16 -0
  16. package/lib/module/native/BrowserEngine.js +122 -0
  17. package/lib/module/native/NativeNayanEngine.web.js +2 -0
  18. package/lib/module/render/GameView.js +45 -13
  19. package/lib/module/render/quality.js +151 -17
  20. package/lib/module/world/World.js +63 -0
  21. package/lib/typescript/src/assets/ProceduralCache.d.ts +2 -0
  22. package/lib/typescript/src/assets/loft.d.ts +3 -1
  23. package/lib/typescript/src/assets/procedural.d.ts +394 -0
  24. package/lib/typescript/src/assets/shape.d.ts +13 -7
  25. package/lib/typescript/src/assets/texture.d.ts +63 -0
  26. package/lib/typescript/src/index.d.ts +8 -2
  27. package/lib/typescript/src/media/audio.d.ts +13 -0
  28. package/lib/typescript/src/media/input.web.d.ts +4 -0
  29. package/lib/typescript/src/media/microphone.d.ts +51 -0
  30. package/lib/typescript/src/media/speech.d.ts +38 -0
  31. package/lib/typescript/src/media/speechPlatform.d.ts +5 -0
  32. package/lib/typescript/src/media/speechPlatform.web.d.ts +5 -0
  33. package/lib/typescript/src/native/EngineBridge.d.ts +7 -1
  34. package/lib/typescript/src/native/spec/NativeNayanEngine.d.ts +72 -0
  35. package/lib/typescript/src/render/GameView.d.ts +30 -3
  36. package/lib/typescript/src/render/quality.d.ts +54 -2
  37. package/lib/typescript/src/types.d.ts +9 -2
  38. package/lib/typescript/src/world/World.d.ts +16 -0
  39. package/lib/typescript/src/world/types.d.ts +8 -0
  40. package/lib/web/audio-worklet.js +27 -0
  41. package/lib/web/engine_core_bg.wasm +0 -0
  42. package/lib/web/index.js +1181 -422
  43. package/lib/web/types/src/assets/ProceduralCache.d.ts +2 -0
  44. package/lib/web/types/src/assets/loft.d.ts +3 -1
  45. package/lib/web/types/src/assets/procedural.d.ts +393 -0
  46. package/lib/web/types/src/assets/shape.d.ts +13 -7
  47. package/lib/web/types/src/assets/texture.d.ts +63 -0
  48. package/lib/web/types/src/index.d.ts +8 -2
  49. package/lib/web/types/src/media/audio.d.ts +13 -0
  50. package/lib/web/types/src/media/input.d.ts +3 -0
  51. package/lib/web/types/src/media/microphone.d.ts +50 -0
  52. package/lib/web/types/src/media/speech.d.ts +37 -0
  53. package/lib/web/types/src/media/speechPlatform.d.ts +4 -0
  54. package/lib/web/types/src/native/EngineBridge.d.ts +7 -1
  55. package/lib/web/types/src/native/spec/NativeNayanEngine.d.ts +72 -0
  56. package/lib/web/types/src/render/GameView.d.ts +30 -3
  57. package/lib/web/types/src/render/quality.d.ts +54 -2
  58. package/lib/web/types/src/types.d.ts +9 -2
  59. package/lib/web/types/src/world/World.d.ts +16 -0
  60. package/lib/web/types/src/world/types.d.ts +8 -0
  61. package/native/android/src/main/AndroidManifest.xml +10 -0
  62. package/native/android/src/main/java/com/nayanui/engine/EngineMicrophone.java +60 -0
  63. package/native/android/src/main/java/com/nayanui/engine/EngineSpeech.java +28 -0
  64. package/native/android/src/main/java/com/nayanui/engine/EngineSurfaceView.java +1 -0
  65. package/native/android/src/main/java/com/nayanui/engine/NayanEnginePackage.java +2 -0
  66. package/native/core/build/EngineCore.xcframework/ios-arm64/libengine_core.a +0 -0
  67. package/native/core/build/EngineCore.xcframework/ios-arm64-simulator/libengine_core.a +0 -0
  68. package/native/core/build/android/arm64-v8a/libengine_core.a +0 -0
  69. package/native/core/build/android/armeabi-v7a/libengine_core.a +0 -0
  70. package/native/core/build/android/x86/libengine_core.a +0 -0
  71. package/native/core/build/android/x86_64/libengine_core.a +0 -0
  72. package/native/core/include/engine_core.h +63 -1
  73. package/native/cpp/NayanEngineModule.cpp +296 -0
  74. package/native/cpp/NayanEngineModule.h +40 -0
  75. package/native/cpp/generated/NayanEngineSpecJSI.h +397 -0
  76. package/package.json +1 -1
  77. package/src/native/spec/NativeNayanEngine.ts +77 -0
package/lib/web/index.js CHANGED
@@ -53,6 +53,12 @@ function restore_asset_record(name, bytes) {
53
53
  const ret = wasm.restore_asset_record(ptr0, len0, ptr1, len1);
54
54
  return ret !== 0;
55
55
  }
56
+ function speech_request(command) {
57
+ const ptr0 = passStringToWasm0(command, wasm.__wbindgen_export, wasm.__wbindgen_export2);
58
+ const len0 = WASM_VECTOR_LEN;
59
+ const ret = wasm.speech_request(ptr0, len0);
60
+ return takeObject(ret);
61
+ }
56
62
  function __wbg_get_imports() {
57
63
  const import0 = {
58
64
  __proto__: null,
@@ -238,6 +244,9 @@ function __wbg_get_imports() {
238
244
  return addHeapObject(ret);
239
245
  }, arguments);
240
246
  },
247
+ __wbg_cancel_fa9c526b1bfea1fa: function(arg0) {
248
+ getObject(arg0).cancel();
249
+ },
241
250
  __wbg_clearBufferfv_05f7fc7252e623eb: function(arg0, arg1, arg2, arg3, arg4) {
242
251
  getObject(arg0).clearBufferfv(arg1 >>> 0, arg2, getArrayF32FromWasm0(arg3, arg4));
243
252
  },
@@ -762,6 +771,10 @@ function __wbg_get_imports() {
762
771
  const ret = getObject(arg0).getUniformLocation(getObject(arg1), getStringFromWasm0(arg2, arg3));
763
772
  return isLikeNone(ret) ? 0 : addHeapObject(ret);
764
773
  },
774
+ __wbg_getVoices_a94654c567778a75: function(arg0) {
775
+ const ret = getObject(arg0).getVoices();
776
+ return addHeapObject(ret);
777
+ },
765
778
  __wbg_get_31af05bd4842a84f: function() {
766
779
  return handleError(function(arg0, arg1) {
767
780
  const ret = Reflect.get(getObject(arg0), getObject(arg1));
@@ -862,6 +875,13 @@ function __wbg_get_imports() {
862
875
  getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
863
876
  getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
864
877
  },
878
+ __wbg_lang_124ac9807c8fd7c7: function(arg0, arg1) {
879
+ const ret = getObject(arg1).lang;
880
+ const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_export, wasm.__wbindgen_export2);
881
+ const len1 = WASM_VECTOR_LEN;
882
+ getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
883
+ getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
884
+ },
865
885
  __wbg_length_7f3c00c40364105e: function(arg0) {
866
886
  const ret = getObject(arg0).length;
867
887
  return ret;
@@ -1025,6 +1045,13 @@ function __wbg_get_imports() {
1025
1045
  const ret = getObject(arg0).minUniformBufferOffsetAlignment;
1026
1046
  return ret;
1027
1047
  },
1048
+ __wbg_name_73552c55bcab8bef: function(arg0, arg1) {
1049
+ const ret = getObject(arg1).name;
1050
+ const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_export, wasm.__wbindgen_export2);
1051
+ const len1 = WASM_VECTOR_LEN;
1052
+ getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
1053
+ getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
1054
+ },
1028
1055
  __wbg_navigator_2156486643462a87: function(arg0) {
1029
1056
  const ret = getObject(arg0).navigator;
1030
1057
  return addHeapObject(ret);
@@ -1037,6 +1064,24 @@ function __wbg_get_imports() {
1037
1064
  const ret = new Uint8Array(getObject(arg0));
1038
1065
  return addHeapObject(ret);
1039
1066
  },
1067
+ __wbg_new_5502aad30c185fc8: function(arg0, arg1) {
1068
+ try {
1069
+ var state0 = { a: arg0, b: arg1 };
1070
+ var cb0 = (arg0, arg1) => {
1071
+ const a = state0.a;
1072
+ state0.a = 0;
1073
+ try {
1074
+ return __wasm_bindgen_func_elem_10645(a, state0.b, arg0, arg1);
1075
+ } finally {
1076
+ state0.a = a;
1077
+ }
1078
+ };
1079
+ const ret = new Promise(cb0);
1080
+ return addHeapObject(ret);
1081
+ } finally {
1082
+ state0.a = 0;
1083
+ }
1084
+ },
1040
1085
  __wbg_new_617a8cdb8bb1130e: function() {
1041
1086
  const ret = new Object;
1042
1087
  return addHeapObject(ret);
@@ -1066,7 +1111,7 @@ function __wbg_get_imports() {
1066
1111
  const a = state0.a;
1067
1112
  state0.a = 0;
1068
1113
  try {
1069
- return __wasm_bindgen_func_elem_9779(a, state0.b, arg0, arg1);
1114
+ return __wasm_bindgen_func_elem_10645(a, state0.b, arg0, arg1);
1070
1115
  } finally {
1071
1116
  state0.a = a;
1072
1117
  }
@@ -1081,6 +1126,12 @@ function __wbg_get_imports() {
1081
1126
  const ret = new Uint8Array(arg0 >>> 0);
1082
1127
  return addHeapObject(ret);
1083
1128
  },
1129
+ __wbg_new_with_text_de120aa95bd08db1: function() {
1130
+ return handleError(function(arg0, arg1) {
1131
+ const ret = new SpeechSynthesisUtterance(getStringFromWasm0(arg0, arg1));
1132
+ return addHeapObject(ret);
1133
+ }, arguments);
1134
+ },
1084
1135
  __wbg_now_aa4ccb83129e9e55: function() {
1085
1136
  const ret = Date.now();
1086
1137
  return ret;
@@ -1093,6 +1144,13 @@ function __wbg_get_imports() {
1093
1144
  const ret = getObject(arg0).onSubmittedWorkDone();
1094
1145
  return addHeapObject(ret);
1095
1146
  },
1147
+ __wbg_pause_b7da1d13ff23f1f3: function(arg0) {
1148
+ getObject(arg0).pause();
1149
+ },
1150
+ __wbg_pending_decfa19ee64ef69d: function(arg0) {
1151
+ const ret = getObject(arg0).pending;
1152
+ return ret;
1153
+ },
1096
1154
  __wbg_pixelStorei_af18e636b3e7b725: function(arg0, arg1, arg2) {
1097
1155
  getObject(arg0).pixelStorei(arg1 >>> 0, arg2);
1098
1156
  },
@@ -1177,6 +1235,9 @@ function __wbg_get_imports() {
1177
1235
  const ret = Promise.resolve(getObject(arg0));
1178
1236
  return addHeapObject(ret);
1179
1237
  },
1238
+ __wbg_resume_8132df443cbde759: function(arg0) {
1239
+ getObject(arg0).resume();
1240
+ },
1180
1241
  __wbg_samplerParameterf_2d62cb907392e2d6: function(arg0, arg1, arg2, arg3) {
1181
1242
  getObject(arg0).samplerParameterf(getObject(arg1), arg2 >>> 0, arg3);
1182
1243
  },
@@ -1206,6 +1267,12 @@ function __wbg_get_imports() {
1206
1267
  __wbg_setPipeline_b6f981027e02cd16: function(arg0, arg1) {
1207
1268
  getObject(arg0).setPipeline(getObject(arg1));
1208
1269
  },
1270
+ __wbg_setTimeout_db7bbc18a17e152a: function() {
1271
+ return handleError(function(arg0, arg1, arg2) {
1272
+ const ret = getObject(arg0).setTimeout(getObject(arg1), arg2);
1273
+ return ret;
1274
+ }, arguments);
1275
+ },
1209
1276
  __wbg_setVertexBuffer_6db3b60e99280744: function(arg0, arg1, arg2, arg3) {
1210
1277
  getObject(arg0).setVertexBuffer(arg1 >>> 0, getObject(arg2), arg3);
1211
1278
  },
@@ -1470,6 +1537,9 @@ function __wbg_get_imports() {
1470
1537
  __wbg_set_label_ee172cd5f6a96961: function(arg0, arg1, arg2) {
1471
1538
  getObject(arg0).label = getStringFromWasm0(arg1, arg2);
1472
1539
  },
1540
+ __wbg_set_lang_fdb29fe7a7f6b538: function(arg0, arg1, arg2) {
1541
+ getObject(arg0).lang = getStringFromWasm0(arg1, arg2);
1542
+ },
1473
1543
  __wbg_set_layout_454e3a091b390cd4: function(arg0, arg1) {
1474
1544
  getObject(arg0).layout = getObject(arg1);
1475
1545
  },
@@ -1554,6 +1624,9 @@ function __wbg_get_imports() {
1554
1624
  __wbg_set_pass_op_3cf10feb3d76ab97: function(arg0, arg1) {
1555
1625
  getObject(arg0).passOp = __wbindgen_enum_GpuStencilOperation[arg1];
1556
1626
  },
1627
+ __wbg_set_pitch_793c4498ea6e6817: function(arg0, arg1) {
1628
+ getObject(arg0).pitch = arg1;
1629
+ },
1557
1630
  __wbg_set_power_preference_b42d00a8facfbade: function(arg0, arg1) {
1558
1631
  getObject(arg0).powerPreference = __wbindgen_enum_GpuPowerPreference[arg1];
1559
1632
  },
@@ -1566,6 +1639,9 @@ function __wbg_get_imports() {
1566
1639
  __wbg_set_r_6ece4d74af63364f: function(arg0, arg1) {
1567
1640
  getObject(arg0).r = arg1;
1568
1641
  },
1642
+ __wbg_set_rate_caba1c2a5e041669: function(arg0, arg1) {
1643
+ getObject(arg0).rate = arg1;
1644
+ },
1569
1645
  __wbg_set_required_features_bbab71414c45e621: function(arg0, arg1, arg2) {
1570
1646
  getObject(arg0).requiredFeatures = getArrayJsValueViewFromWasm0(arg1, arg2);
1571
1647
  },
@@ -1710,6 +1786,12 @@ function __wbg_get_imports() {
1710
1786
  __wbg_set_visibility_d8a6821789538c25: function(arg0, arg1) {
1711
1787
  getObject(arg0).visibility = arg1 >>> 0;
1712
1788
  },
1789
+ __wbg_set_voice_3e1a3ef98a8d5ecd: function(arg0, arg1) {
1790
+ getObject(arg0).voice = getObject(arg1);
1791
+ },
1792
+ __wbg_set_volume_d84050ffb0189f47: function(arg0, arg1) {
1793
+ getObject(arg0).volume = arg1;
1794
+ },
1713
1795
  __wbg_set_width_0ca908d167690992: function(arg0, arg1) {
1714
1796
  getObject(arg0).width = arg1 >>> 0;
1715
1797
  },
@@ -1737,6 +1819,19 @@ function __wbg_get_imports() {
1737
1819
  __wbg_shaderSource_c8812639b47d87b9: function(arg0, arg1, arg2, arg3) {
1738
1820
  getObject(arg0).shaderSource(getObject(arg1), getStringFromWasm0(arg2, arg3));
1739
1821
  },
1822
+ __wbg_speak_0522b6116a03fd31: function(arg0, arg1) {
1823
+ getObject(arg0).speak(getObject(arg1));
1824
+ },
1825
+ __wbg_speaking_bbf159df39686677: function(arg0) {
1826
+ const ret = getObject(arg0).speaking;
1827
+ return ret;
1828
+ },
1829
+ __wbg_speechSynthesis_9178be485e9e2ef1: function() {
1830
+ return handleError(function(arg0) {
1831
+ const ret = getObject(arg0).speechSynthesis;
1832
+ return addHeapObject(ret);
1833
+ }, arguments);
1834
+ },
1740
1835
  __wbg_static_accessor_GLOBAL_266715b9d96ba635: function() {
1741
1836
  const ret = typeof global === "undefined" ? null : global;
1742
1837
  return isLikeNone(ret) ? 0 : addHeapObject(ret);
@@ -2054,6 +2149,13 @@ function __wbg_get_imports() {
2054
2149
  __wbg_viewport_e35d16c542d2ce70: function(arg0, arg1, arg2, arg3, arg4) {
2055
2150
  getObject(arg0).viewport(arg1, arg2, arg3, arg4);
2056
2151
  },
2152
+ __wbg_voiceURI_85f8d4604a52970f: function(arg0, arg1) {
2153
+ const ret = getObject(arg1).voiceURI;
2154
+ const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_export, wasm.__wbindgen_export2);
2155
+ const len1 = WASM_VECTOR_LEN;
2156
+ getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
2157
+ getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
2158
+ },
2057
2159
  __wbg_width_3d0dce3d9892e35e: function(arg0) {
2058
2160
  const ret = getObject(arg0).width;
2059
2161
  return ret;
@@ -2069,23 +2171,23 @@ function __wbg_get_imports() {
2069
2171
  }, arguments);
2070
2172
  },
2071
2173
  __wbindgen_generic_0000000000000001: function(arg0, arg1) {
2072
- const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_9777);
2174
+ const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_10643);
2073
2175
  return addHeapObject(ret);
2074
2176
  },
2075
2177
  __wbindgen_generic_0000000000000002: function(arg0, arg1) {
2076
- const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_2414);
2178
+ const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_3241);
2077
2179
  return addHeapObject(ret);
2078
2180
  },
2079
2181
  __wbindgen_generic_0000000000000003: function(arg0, arg1) {
2080
- const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_2415);
2182
+ const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_3242);
2081
2183
  return addHeapObject(ret);
2082
2184
  },
2083
2185
  __wbindgen_generic_0000000000000004: function(arg0, arg1) {
2084
- const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_2414_9);
2186
+ const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_3241_10);
2085
2187
  return addHeapObject(ret);
2086
2188
  },
2087
2189
  __wbindgen_generic_0000000000000005: function(arg0, arg1) {
2088
- const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_2414_10);
2190
+ const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_3241_11);
2089
2191
  return addHeapObject(ret);
2090
2192
  },
2091
2193
  __wbindgen_generic_0000000000000006: function(arg0) {
@@ -2138,16 +2240,16 @@ function __wbg_get_imports() {
2138
2240
  "./snippets/engine-core-64626f8edd8232bb/inline0.js": exports_inline0
2139
2241
  };
2140
2242
  }
2141
- function __wasm_bindgen_func_elem_2415(arg0, arg1, arg2) {
2142
- wasm.__wasm_bindgen_func_elem_2415(arg0, arg1, addHeapObject(arg2));
2243
+ function __wasm_bindgen_func_elem_3242(arg0, arg1, arg2) {
2244
+ wasm.__wasm_bindgen_func_elem_3242(arg0, arg1, addHeapObject(arg2));
2143
2245
  }
2144
- function __wasm_bindgen_func_elem_9779(arg0, arg1, arg2, arg3) {
2145
- wasm.__wasm_bindgen_func_elem_9779(arg0, arg1, addHeapObject(arg2), addHeapObject(arg3));
2246
+ function __wasm_bindgen_func_elem_10645(arg0, arg1, arg2, arg3) {
2247
+ wasm.__wasm_bindgen_func_elem_10645(arg0, arg1, addHeapObject(arg2), addHeapObject(arg3));
2146
2248
  }
2147
- function __wasm_bindgen_func_elem_9777(arg0, arg1, arg2) {
2249
+ function __wasm_bindgen_func_elem_10643(arg0, arg1, arg2) {
2148
2250
  try {
2149
2251
  const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
2150
- wasm.__wasm_bindgen_func_elem_9777(retptr, arg0, arg1, addHeapObject(arg2));
2252
+ wasm.__wasm_bindgen_func_elem_10643(retptr, arg0, arg1, addHeapObject(arg2));
2151
2253
  var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
2152
2254
  var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
2153
2255
  if (r1) {
@@ -2157,10 +2259,10 @@ function __wasm_bindgen_func_elem_9777(arg0, arg1, arg2) {
2157
2259
  wasm.__wbindgen_add_to_stack_pointer(16);
2158
2260
  }
2159
2261
  }
2160
- function __wasm_bindgen_func_elem_2414(arg0, arg1, arg2) {
2262
+ function __wasm_bindgen_func_elem_3241(arg0, arg1, arg2) {
2161
2263
  try {
2162
2264
  const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
2163
- wasm.__wasm_bindgen_func_elem_2414(retptr, arg0, arg1, addHeapObject(arg2));
2265
+ wasm.__wasm_bindgen_func_elem_3241(retptr, arg0, arg1, addHeapObject(arg2));
2164
2266
  var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
2165
2267
  var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
2166
2268
  if (r1) {
@@ -2170,10 +2272,10 @@ function __wasm_bindgen_func_elem_2414(arg0, arg1, arg2) {
2170
2272
  wasm.__wbindgen_add_to_stack_pointer(16);
2171
2273
  }
2172
2274
  }
2173
- function __wasm_bindgen_func_elem_2414_9(arg0, arg1, arg2) {
2275
+ function __wasm_bindgen_func_elem_3241_10(arg0, arg1, arg2) {
2174
2276
  try {
2175
2277
  const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
2176
- wasm.__wasm_bindgen_func_elem_2414_9(retptr, arg0, arg1, addHeapObject(arg2));
2278
+ wasm.__wasm_bindgen_func_elem_3241_10(retptr, arg0, arg1, addHeapObject(arg2));
2177
2279
  var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
2178
2280
  var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
2179
2281
  if (r1) {
@@ -2183,10 +2285,10 @@ function __wasm_bindgen_func_elem_2414_9(arg0, arg1, arg2) {
2183
2285
  wasm.__wbindgen_add_to_stack_pointer(16);
2184
2286
  }
2185
2287
  }
2186
- function __wasm_bindgen_func_elem_2414_10(arg0, arg1, arg2) {
2288
+ function __wasm_bindgen_func_elem_3241_11(arg0, arg1, arg2) {
2187
2289
  try {
2188
2290
  const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
2189
- wasm.__wasm_bindgen_func_elem_2414_10(retptr, arg0, arg1, addHeapObject(arg2));
2291
+ wasm.__wasm_bindgen_func_elem_3241_11(retptr, arg0, arg1, addHeapObject(arg2));
2190
2292
  var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
2191
2293
  var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
2192
2294
  if (r1) {
@@ -2723,6 +2825,99 @@ class IndexedDbCache {
2723
2825
  }
2724
2826
  }
2725
2827
 
2828
+ // src/media/input.web.ts
2829
+ var BLOCK = 2048;
2830
+ var MIC_FAILED = -1;
2831
+ function installMicrophoneInput(core, bridge) {
2832
+ if (typeof navigator === "undefined" || !navigator.mediaDevices?.getUserMedia)
2833
+ return;
2834
+ let permission = 0;
2835
+ let stream = null;
2836
+ let context = null;
2837
+ let failed = false;
2838
+ let session = 0;
2839
+ const startCore = bridge.micStart;
2840
+ const stopCore = bridge.micStop;
2841
+ const stateCore = bridge.micState;
2842
+ const allocate = core.engine_web_alloc;
2843
+ const push = core.engine_audio_mic_push;
2844
+ let pointer = 0;
2845
+ navigator.permissions?.query({ name: "microphone" }).then((status) => {
2846
+ const read = () => {
2847
+ permission = status.state === "granted" ? 1 : status.state === "denied" ? -1 : 0;
2848
+ };
2849
+ read();
2850
+ status.onchange = read;
2851
+ }).catch(() => {});
2852
+ const open = async () => {
2853
+ stream ??= await navigator.mediaDevices.getUserMedia({
2854
+ audio: { echoCancellation: true, noiseSuppression: true, autoGainControl: true, channelCount: 1 }
2855
+ });
2856
+ permission = 1;
2857
+ return stream;
2858
+ };
2859
+ const close = () => {
2860
+ stream?.getTracks().forEach((track) => track.stop());
2861
+ stream = null;
2862
+ context?.close().catch(() => {});
2863
+ context = null;
2864
+ };
2865
+ bridge.micPermission = () => permission;
2866
+ bridge.micRequestPermission = async () => {
2867
+ try {
2868
+ await open();
2869
+ return true;
2870
+ } catch {
2871
+ permission = -1;
2872
+ return false;
2873
+ }
2874
+ };
2875
+ bridge.micStart = (maxSeconds) => {
2876
+ session++;
2877
+ const current = session;
2878
+ close();
2879
+ failed = false;
2880
+ if (!startCore(maxSeconds))
2881
+ return false;
2882
+ (async () => {
2883
+ const input = await open();
2884
+ const audio = new AudioContext;
2885
+ context = audio;
2886
+ await audio.audioWorklet.addModule(new URL("./audio-worklet.js", import.meta.url));
2887
+ if (current !== session)
2888
+ return audio.close();
2889
+ const node = new AudioWorkletNode(audio, "nayan-microphone", { numberOfInputs: 1, numberOfOutputs: 1 });
2890
+ const muted = audio.createGain();
2891
+ muted.gain.value = 0;
2892
+ audio.createMediaStreamSource(input).connect(node).connect(muted).connect(audio.destination);
2893
+ pointer ||= allocate(BLOCK * 4);
2894
+ node.port.onmessage = ({ data }) => {
2895
+ if (current !== session)
2896
+ return;
2897
+ new Float32Array(core.memory.buffer, pointer, data.length).set(data);
2898
+ push(pointer, data.length, audio.sampleRate);
2899
+ };
2900
+ await audio.resume();
2901
+ })().catch(() => {
2902
+ if (current !== session)
2903
+ return;
2904
+ failed = true;
2905
+ if (permission === 0)
2906
+ permission = -1;
2907
+ stopCore();
2908
+ close();
2909
+ });
2910
+ return true;
2911
+ };
2912
+ bridge.micStop = () => {
2913
+ session++;
2914
+ failed = false;
2915
+ stopCore();
2916
+ close();
2917
+ };
2918
+ bridge.micState = () => failed ? MIC_FAILED : stateCore();
2919
+ }
2920
+
2726
2921
  // src/media/output.web.ts
2727
2922
  function installAudioOutput(core) {
2728
2923
  if (typeof window === "undefined")
@@ -2940,6 +3135,16 @@ function createBrowserEngine(core) {
2940
3135
  end++;
2941
3136
  return decoder.decode(b.subarray(pointer, end));
2942
3137
  };
3138
+ const text = (index, value) => {
3139
+ const b = encoder.encode(value);
3140
+ return [put(index, b), b.length];
3141
+ };
3142
+ const readDoubles = (read) => {
3143
+ const count = read(0, 0);
3144
+ const out = zeroed(2, Math.max(8, count * 8));
3145
+ read(out, count);
3146
+ return copy(out, count * 8);
3147
+ };
2943
3148
  const geometry = (handle) => {
2944
3149
  try {
2945
3150
  const error = cString(fn("geometry_error", handle));
@@ -3173,6 +3378,18 @@ function createBrowserEngine(core) {
3173
3378
  const [x, y, z] = dimensions("model_size", mesh, 3, true);
3174
3379
  return [x, y, z];
3175
3380
  },
3381
+ bakeKept(key) {
3382
+ const [k, length] = text(0, key);
3383
+ return fn("bake_kept", k, length) !== 0;
3384
+ },
3385
+ modelBake(mesh, spec) {
3386
+ const [s, length] = text(0, spec);
3387
+ const out = zeroed(1, 16);
3388
+ if (!fn("model_bake", mesh, s, length, out))
3389
+ return [];
3390
+ const [texture, x, y, extent] = new Float32Array(u8().slice(out, out + 16).buffer);
3391
+ return [texture, x, y, extent];
3392
+ },
3176
3393
  modelLoadAsync(value, center, fit) {
3177
3394
  return settle(() => engine.modelLoad(value, center, fit));
3178
3395
  },
@@ -3287,6 +3504,14 @@ function createBrowserEngine(core) {
3287
3504
  const buckets = source(4, ranges);
3288
3505
  ex("render_overlay")(surface, m, c, r, capacity, buckets, ranges.byteLength / 4, count, width, height);
3289
3506
  },
3507
+ renderBackdrop(surface, matrices, colors, regions, ranges, count) {
3508
+ const capacity = Math.min(buf(matrices).byteLength / MATRIX_BYTES, buf(colors).byteLength / VEC4_BYTES, buf(regions).byteLength / VEC4_BYTES);
3509
+ const m = source(1, matrices);
3510
+ const c = source(2, colors);
3511
+ const r = source(3, regions);
3512
+ const buckets = source(4, ranges);
3513
+ ex("render_backdrop")(surface, m, c, r, capacity, buckets, ranges.byteLength / 4, count);
3514
+ },
3290
3515
  renderError: () => string("render_error"),
3291
3516
  renderBackend: () => string("render_backend"),
3292
3517
  drawingCreate: (width, height, ratio) => fn("drawing_create", width, height, ratio),
@@ -3327,10 +3552,100 @@ function createBrowserEngine(core) {
3327
3552
  fn("audio_set_muted", +muted);
3328
3553
  },
3329
3554
  audioIsRunning: () => fn("audio_is_running") !== 0,
3555
+ audioRelease(sound) {
3556
+ fn("audio_release", sound);
3557
+ },
3558
+ audioDuration: (sound) => fn("audio_duration", sound),
3559
+ audioLevels(sound, perSecond) {
3560
+ const count = fn("audio_levels", sound, perSecond, 0, 0);
3561
+ if (!count)
3562
+ return new ArrayBuffer(0);
3563
+ const p = zeroed(0, count * 4);
3564
+ fn("audio_levels", sound, perSecond, p, count);
3565
+ return copy(p, count * 4);
3566
+ },
3567
+ micPermission: () => 0,
3568
+ micRequestPermission: () => Promise.resolve(false),
3569
+ micStart: (maxSeconds) => fn("audio_mic_start", maxSeconds) !== 0,
3570
+ micStop() {
3571
+ fn("audio_mic_stop");
3572
+ },
3573
+ micState: () => fn("audio_mic_state"),
3574
+ micLevel: () => fn("audio_mic_level"),
3575
+ micHeld: () => fn("audio_mic_held"),
3576
+ micTake: (seconds, skipEnd) => fn("audio_mic_take", seconds, skipEnd),
3330
3577
  hapticsSupported: () => fn("haptics_supported") !== 0,
3331
3578
  hapticsPlay(taps, throttle) {
3332
3579
  const p = put(0, new Uint8Array(new Float32Array(taps).buffer));
3333
3580
  fn("haptics_play", p, taps.length / 3, +throttle);
3581
+ },
3582
+ worldgenBuild: (recipe, key, prepareTerrain) => {
3583
+ const [r, rl] = text(0, recipe);
3584
+ const [k, kl] = text(1, key);
3585
+ return fn("worldgen_build", r, rl, k, kl, +prepareTerrain) >>> 0;
3586
+ },
3587
+ worldgenBuildAsync: async (recipe, key, prepareTerrain) => engine.worldgenBuild(recipe, key, prepareTerrain),
3588
+ worldgenError: () => string("worldgen_error"),
3589
+ worldgenKept(key) {
3590
+ const [k, kl] = text(0, key);
3591
+ return fn("worldgen_kept", k, kl) !== 0;
3592
+ },
3593
+ worldgenFree(world) {
3594
+ fn("worldgen_free", world);
3595
+ },
3596
+ worldgenInfo: (world) => string("worldgen_info", [world]),
3597
+ worldgenLayer(world, name) {
3598
+ const [n, nl] = text(1, name);
3599
+ return readDoubles((out, capacity) => fn("worldgen_layer", world, n, nl, out, capacity));
3600
+ },
3601
+ worldgenReachable: (world) => readDoubles((out, capacity) => fn("worldgen_reachable", world, out, capacity)),
3602
+ worldgenOverview(world) {
3603
+ const count = fn("worldgen_overview", world, 0, 0);
3604
+ const out = zeroed(0, Math.max(1, count));
3605
+ fn("worldgen_overview", world, out, count);
3606
+ return copy(out, count);
3607
+ },
3608
+ worldgenPick: (world, run) => fn("worldgen_pick", world, run) !== 0,
3609
+ worldgenTerrain: (world, material) => geometry(fn("worldgen_terrain", world, material)),
3610
+ worldgenMesh(recipe) {
3611
+ const [r, rl] = text(0, recipe);
3612
+ return geometry(fn("worldgen_mesh", r, rl));
3613
+ },
3614
+ worldgenPaint(vertices, recipe) {
3615
+ const [v] = data(vertices);
3616
+ const [r, rl] = text(1, recipe);
3617
+ const ok = fn("worldgen_paint", v, bytes(vertices).length / 44, r, rl) !== 0;
3618
+ if (ok)
3619
+ bytes(vertices).set(u8().subarray(v, v + vertices.byteLength));
3620
+ return ok;
3621
+ },
3622
+ worldgenGround: (world, x, z) => fn("worldgen_ground", world, x, z),
3623
+ worldgenSurface: (world, x, z) => fn("worldgen_surface", world, x, z),
3624
+ worldgenPlatform: (world, x, z) => fn("worldgen_platform", world, x, z),
3625
+ worldgenWalkable: (world, x, z, radius) => fn("worldgen_walkable", world, x, z, radius) !== 0,
3626
+ worldgenClearance: (world, x, z, radius) => fn("worldgen_clearance", world, x, z, radius),
3627
+ worldgenSolidClearance: (world, x, z, radius, bottom, top) => fn("worldgen_solid_clearance", world, x, z, radius, bottom, top),
3628
+ worldgenFieldIndex(world, name) {
3629
+ const [n, nl] = text(0, name);
3630
+ return fn("worldgen_field_index", world, n, nl);
3631
+ },
3632
+ worldgenField: (world, field, x, z) => fn("worldgen_field", world, field, x, z),
3633
+ worldgenTilt(world, x, z, yaw) {
3634
+ const out = zeroed(0, 32);
3635
+ fn("worldgen_tilt", world, x, z, yaw, out);
3636
+ const at = out >> 3;
3637
+ const q = f64();
3638
+ return [q[at], q[at + 1], q[at + 2], q[at + 3]];
3639
+ },
3640
+ worldgenSight: (world, ax, az, bx, bz, radius, step, rise) => fn("worldgen_sight", world, ax, az, bx, bz, radius, step, rise) !== 0,
3641
+ worldgenMove(world, x, z, dx, dz, radius, bodies) {
3642
+ const list = bytes(bodies);
3643
+ const b = put(1, list);
3644
+ const p = zeroed(0, 16);
3645
+ f64().set([x, z], p >> 3);
3646
+ fn("worldgen_move", world, p, dx, dz, radius, b, Math.floor(list.byteLength / 24));
3647
+ const q = f64();
3648
+ return [q[p >> 3], q[(p >> 3) + 1]];
3334
3649
  }
3335
3650
  };
3336
3651
  return engine;
@@ -3351,6 +3666,7 @@ function prepareEngine() {
3351
3666
  } catch {}
3352
3667
  const ready = createBrowserEngine(core);
3353
3668
  ready.audioIsRunning = installAudioOutput(core);
3669
+ installMicrophoneInput(core, ready);
3354
3670
  Object.assign(browser, ready);
3355
3671
  })().catch((error) => {
3356
3672
  pending = undefined;
@@ -3619,50 +3935,6 @@ var SHAPE_MESH = {
3619
3935
 
3620
3936
  // src/render/meshes.ts
3621
3937
  var VERTEX_FLOATS = 11;
3622
-
3623
- class Builder {
3624
- vertices = [];
3625
- indices = [];
3626
- vertex(p, n, u, v) {
3627
- this.vertices.push(p[0], p[1], p[2], n[0], n[1], n[2], 1, 1, 1, u, v);
3628
- return this.vertices.length / VERTEX_FLOATS - 1;
3629
- }
3630
- quad(a, b, c, d) {
3631
- this.indices.push(a, b, c, a, c, d);
3632
- }
3633
- grid(rows, cols, at) {
3634
- const first = this.vertices.length / VERTEX_FLOATS;
3635
- for (let i = 0;i <= rows; i++) {
3636
- for (let j = 0;j <= cols; j++) {
3637
- const [p, n] = at(j / cols, i / rows);
3638
- this.vertex(p, n, j / cols, i / rows);
3639
- }
3640
- }
3641
- for (let i = 0;i < rows; i++) {
3642
- for (let j = 0;j < cols; j++) {
3643
- const a = first + i * (cols + 1) + j;
3644
- const b = a + cols + 1;
3645
- this.indices.push(a, b, a + 1, a + 1, b, b + 1);
3646
- }
3647
- }
3648
- }
3649
- disc(y, radius, facing, segments) {
3650
- const center = this.vertex([0, y, 0], [0, facing, 0], 0.5, 0.5);
3651
- const first = center + 1;
3652
- for (let j = 0;j <= segments; j++) {
3653
- const t = j / segments * TAU;
3654
- const x = Math.cos(t);
3655
- const z = Math.sin(t);
3656
- this.vertex([x * radius, y, z * radius], [0, facing, 0], 0.5 + x / 2, 0.5 + z / 2);
3657
- }
3658
- for (let j = 0;j < segments; j++) {
3659
- if (facing === 1)
3660
- this.indices.push(center, first + j + 1, first + j);
3661
- else
3662
- this.indices.push(center, first + j, first + j + 1);
3663
- }
3664
- }
3665
- }
3666
3938
  var TAU = Math.PI * 2;
3667
3939
  var registered = new Map;
3668
3940
  var idLimit = 16;
@@ -3906,36 +4178,6 @@ function layoutText(font, text, align, options = {}) {
3906
4178
  return placed;
3907
4179
  }
3908
4180
 
3909
- // src/assets/texture.ts
3910
- var loader3 = new AssetLoader("loadTexture");
3911
- function loadTexture(source) {
3912
- return loader3.load(source, (native, bytes) => {
3913
- const finish = (id) => {
3914
- if (id < 0)
3915
- throw new Error(native.loadError() || "not a PNG or JPEG image");
3916
- const [width, height] = native.textureSize(id);
3917
- return { id, width, height };
3918
- };
3919
- return typeof native.textureLoadAsync === "function" ? native.textureLoadAsync(bytes).then(finish) : finish(native.textureLoad(bytes));
3920
- });
3921
- }
3922
- var variants = new Map;
3923
- function texturedMesh(mesh, texture) {
3924
- const key = `${mesh}:${texture.id}`;
3925
- let id = variants.get(key);
3926
- if (id === undefined) {
3927
- const base = meshInfo(mesh);
3928
- if (!base)
3929
- throw new Error("texture: this mesh has nothing to draw");
3930
- id = NativeNayanEngine_web_default.meshAlias(mesh);
3931
- if (id < 0)
3932
- throw new Error(`texture: ${NativeNayanEngine_web_default.loadError()}`);
3933
- registerCoreMesh(id, { texture: texture.id, shine: base.shine }, () => base.geometry);
3934
- variants.set(key, id);
3935
- }
3936
- return id;
3937
- }
3938
-
3939
4181
  // src/world/color.ts
3940
4182
  var HEX = /^#([0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$/i;
3941
4183
  var RGB = /^rgba?\(\s*([\d.]+)\s*,\s*([\d.]+)\s*,\s*([\d.]+)\s*(?:,\s*([\d.]+%?)\s*)?\)$/i;
@@ -4000,6 +4242,77 @@ function resolveColors(colors) {
4000
4242
  return colors.map(resolveColor);
4001
4243
  }
4002
4244
 
4245
+ // src/assets/texture.ts
4246
+ var loader3 = new AssetLoader("loadTexture");
4247
+ function loadTexture(source) {
4248
+ return loader3.load(source, (native, bytes) => {
4249
+ const finish = (id) => {
4250
+ if (id < 0)
4251
+ throw new Error(native.loadError() || "not a PNG or JPEG image");
4252
+ const [width, height] = native.textureSize(id);
4253
+ return { id, width, height };
4254
+ };
4255
+ return typeof native.textureLoadAsync === "function" ? native.textureLoadAsync(bytes).then(finish) : finish(native.textureLoad(bytes));
4256
+ });
4257
+ }
4258
+ var variants = new Map;
4259
+ function texturedMesh(mesh, texture) {
4260
+ const key = `${mesh}:${texture.id}`;
4261
+ let id = variants.get(key);
4262
+ if (id === undefined) {
4263
+ const base = meshInfo(mesh);
4264
+ if (!base)
4265
+ throw new Error("texture: this mesh has nothing to draw");
4266
+ id = NativeNayanEngine_web_default.meshAlias(mesh);
4267
+ if (id < 0)
4268
+ throw new Error(`texture: ${NativeNayanEngine_web_default.loadError()}`);
4269
+ registerCoreMesh(id, { texture: texture.id, shine: base.shine }, () => base.geometry);
4270
+ variants.set(key, id);
4271
+ }
4272
+ return id;
4273
+ }
4274
+ function bakeModel(model, options) {
4275
+ const mesh = model == null ? -1 : typeof model === "number" ? model : model.id;
4276
+ const native = NativeNayanEngine_web_default;
4277
+ if (typeof native.modelBake !== "function")
4278
+ throw new Error("bakeModel: this app's native core predates baked models: rebuild it");
4279
+ const { light } = options;
4280
+ const lightColor = resolveColor(light.color ?? [1, 1, 1]);
4281
+ const sky = resolveColor(options.background ?? [0, 0, 0]);
4282
+ const spec = {
4283
+ size: Math.max(1, Math.min(1024, Math.round(options.size))),
4284
+ supersample: options.supersample ?? 4,
4285
+ light: light.direction,
4286
+ ambient: light.ambient,
4287
+ lightColor: [lightColor[0], lightColor[1], lightColor[2]],
4288
+ sky: [sky[0], sky[1], sky[2]],
4289
+ shine: options.shine ?? meshInfo(mesh)?.shine ?? 0,
4290
+ padding: options.padding ?? 0.04,
4291
+ parts: options.parts?.map((part) => {
4292
+ const id = typeof part.model === "number" ? part.model : part.model.id;
4293
+ const scale = part.scale ?? 1;
4294
+ const color = resolveColor(part.color ?? [1, 1, 1]);
4295
+ return {
4296
+ mesh: id,
4297
+ position: part.position ?? [0, 0, 0],
4298
+ scale: typeof scale === "number" ? [scale, scale, scale] : scale,
4299
+ color: [color[0], color[1], color[2]],
4300
+ shine: part.shine ?? meshInfo(id)?.shine ?? 0
4301
+ };
4302
+ }),
4303
+ frame: options.frame && [options.frame.center[0], options.frame.center[1], options.frame.extent],
4304
+ key: options.key
4305
+ };
4306
+ const baked = native.modelBake(Math.max(0, mesh), JSON.stringify(spec));
4307
+ if (baked.length !== 4)
4308
+ throw new Error(`bakeModel: ${native.loadError() || "failed"}`);
4309
+ const [texture, x, y, extent] = baked;
4310
+ return { texture: { id: texture, width: spec.size, height: spec.size }, center: [x, y], extent };
4311
+ }
4312
+ function isBakeKept(key) {
4313
+ return typeof NativeNayanEngine_web_default?.bakeKept === "function" && NativeNayanEngine_web_default.bakeKept(key);
4314
+ }
4315
+
4003
4316
  // src/world/desc.ts
4004
4317
  var DESC_LEN = 64;
4005
4318
  var FLAG = {
@@ -4463,6 +4776,9 @@ class World {
4463
4776
  boundsValue = null;
4464
4777
  listenerValue = null;
4465
4778
  updates = 0;
4779
+ backdropValue = null;
4780
+ stills = new Map;
4781
+ stillChanged = false;
4466
4782
  constructor(capacity) {
4467
4783
  this.capacity = capacity;
4468
4784
  if (!NativeNayanEngine_web_default) {
@@ -4491,6 +4807,8 @@ class World {
4491
4807
  return this.disposed ? 0 : this.native.count(this.id);
4492
4808
  }
4493
4809
  spawn(options = {}) {
4810
+ if (options.still)
4811
+ return this.spawnStill(options);
4494
4812
  if (options.parent != null && options.physics != null) {
4495
4813
  throw new Error("World.spawn: attached entities can't have physics (put it on the parent)");
4496
4814
  }
@@ -4523,9 +4841,35 @@ class World {
4523
4841
  }
4524
4842
  return e;
4525
4843
  }
4844
+ get backdrop() {
4845
+ return this.backdropValue;
4846
+ }
4847
+ backdropLayer(capacity = 64) {
4848
+ if (this.disposed)
4849
+ throw new Error("World: used after dispose()");
4850
+ return this.backdropValue ??= new World(capacity);
4851
+ }
4852
+ spawnStill({ still: _, ...options }) {
4853
+ if (options.parent != null || options.physics != null || options.text !== undefined) {
4854
+ throw new Error("World.spawn: a still entity can't be attached or have physics or text");
4855
+ }
4856
+ const layer = this.backdropLayer();
4857
+ const entity = layer.spawn({ pickable: false, ...options });
4858
+ const standIn = this.spawn({ mesh: "none", pickable: false });
4859
+ this.stills.set(standIn, entity);
4860
+ this.stillChanged = true;
4861
+ return standIn;
4862
+ }
4526
4863
  set(e, options) {
4527
4864
  if (this.disposed)
4528
4865
  return false;
4866
+ const still = this.stills.get(e);
4867
+ if (still !== undefined) {
4868
+ if (options.parent != null || options.physics != null)
4869
+ throw new Error("World.set: a still entity can't be attached or have physics");
4870
+ this.stillChanged = true;
4871
+ return this.backdropValue.set(still, options);
4872
+ }
4529
4873
  const text = this.texts.get(e);
4530
4874
  encode(this.desc, options, text ? undefined : this.resolveMesh(options, "set"));
4531
4875
  const ok = this.native.setDesc(this.id, e, this.desc.buffer);
@@ -4573,6 +4917,12 @@ class World {
4573
4917
  despawn(e) {
4574
4918
  if (this.disposed)
4575
4919
  return false;
4920
+ const still = this.stills.get(e);
4921
+ if (still !== undefined) {
4922
+ this.stills.delete(e);
4923
+ this.backdropValue.despawn(still);
4924
+ this.stillChanged = true;
4925
+ }
4576
4926
  const existed = this.native.despawn(this.id, e);
4577
4927
  this.texts.delete(e);
4578
4928
  this.resolveFinished();
@@ -4949,6 +5299,10 @@ class World {
4949
5299
  this.runFallbackTimers(dt);
4950
5300
  if (!this.disposed)
4951
5301
  this.scoreboardValue?.dispatch();
5302
+ if (this.stillChanged && this.backdropValue && !this.disposed) {
5303
+ this.stillChanged = false;
5304
+ this.backdropValue.update(0);
5305
+ }
4952
5306
  }
4953
5307
  forEachCollision(fn) {
4954
5308
  if (this.disposed)
@@ -4973,6 +5327,8 @@ class World {
4973
5327
  this.pending.clear();
4974
5328
  this.timers.clear();
4975
5329
  this.fallbackTimers = [];
5330
+ this.stills.clear();
5331
+ this.backdropValue?.dispose();
4976
5332
  NativeNayanEngine_web_default.destroyWorld(this.id);
4977
5333
  }
4978
5334
  }
@@ -5915,14 +6271,27 @@ function Surface({ surfaceId, style, onSize, onError, touch, onPrimaryAction })
5915
6271
  }
5916
6272
 
5917
6273
  // src/render/quality.ts
5918
- var QUALITIES = ["high", "medium", "low", "lowest"];
5919
6274
  var QUALITY_LEVELS = {
5920
- high: { maxPixelRatio: 3, samples: 4, shadowSize: 2048, softShadows: true },
5921
- medium: { maxPixelRatio: 3, samples: 4, shadowSize: 1024, softShadows: true },
5922
- low: { maxPixelRatio: 3, samples: 1, shadowSize: 1024, softShadows: false },
5923
- lowest: { maxPixelRatio: 2, samples: 1, shadowSize: 512, softShadows: false }
6275
+ high: { maxPixelRatio: 3, samples: 4, shadows: true, shadowSize: 2048, softShadows: true },
6276
+ medium: { maxPixelRatio: 3, samples: 4, shadows: true, shadowSize: 2048, softShadows: false },
6277
+ low: { maxPixelRatio: 3, samples: 1, shadows: false, shadowSize: 2048, softShadows: true },
6278
+ lowest: { maxPixelRatio: 2, samples: 1, fxaa: true, shadows: false, shadowSize: 2048, softShadows: true, maxPixels: 800000 }
5924
6279
  };
5925
- var SLOW_FRAME_MS = 20;
6280
+ var AUTO_STEPS = [
6281
+ { quality: "high" },
6282
+ { quality: "high", maxPixels: 1500000 },
6283
+ { quality: "high", maxPixels: 1100000 },
6284
+ { quality: "medium", maxPixels: 1100000 },
6285
+ { quality: "low", maxPixels: 1100000 },
6286
+ { quality: "lowest" }
6287
+ ];
6288
+ function stepLevel(step) {
6289
+ const level = QUALITY_LEVELS[step.quality];
6290
+ if (!step.maxPixels || level.maxPixels && level.maxPixels <= step.maxPixels)
6291
+ return level;
6292
+ return { ...level, maxPixels: step.maxPixels };
6293
+ }
6294
+ var SLOW_FRAME_MS = 18.5;
5926
6295
  var VERY_SLOW_FRAME_MS = 30;
5927
6296
  var FAST_FRAME_MS = 17.4;
5928
6297
  var RENDER_SHARE = 0.6;
@@ -5931,7 +6300,42 @@ var SETTLE_MS = 1500;
5931
6300
  var SLOW_SECONDS = 3;
5932
6301
  var VERY_SLOW_SECONDS = 1;
5933
6302
  var FAST_SECONDS = 10;
6303
+ var HEADROOM_RENDER_MS = 10;
6304
+ var HEADROOM_SECONDS = 2;
5934
6305
  var RELAPSE_MS = 20000;
6306
+ function levelPixelRatio(level, screen, width, height) {
6307
+ const ratio = Math.min(screen, level.maxPixelRatio);
6308
+ const area = width * height;
6309
+ if (!level.maxPixels || area <= 0)
6310
+ return ratio;
6311
+ return Math.min(ratio, Math.sqrt(level.maxPixels / area));
6312
+ }
6313
+ var storeKey = (key) => `quality-step:${key}`;
6314
+ var settled = new Map;
6315
+ function settledQuality(key, store) {
6316
+ if (!key)
6317
+ return;
6318
+ const known = settled.get(key);
6319
+ if (known !== undefined || typeof store?.cacheRead !== "function")
6320
+ return known;
6321
+ try {
6322
+ const bytes = new Uint8Array(store.cacheRead(storeKey(key)));
6323
+ const step = bytes.length === 1 && bytes[0] < AUTO_STEPS.length ? bytes[0] : undefined;
6324
+ if (step !== undefined)
6325
+ settled.set(key, step);
6326
+ return step;
6327
+ } catch {
6328
+ return;
6329
+ }
6330
+ }
6331
+ function rememberQuality(key, step, store) {
6332
+ if (!key || settledQuality(key, store) === step)
6333
+ return;
6334
+ settled.set(key, step);
6335
+ try {
6336
+ store?.cacheWrite?.(storeKey(key), new Uint8Array([step]).buffer);
6337
+ } catch {}
6338
+ }
5935
6339
 
5936
6340
  class AutoQuality {
5937
6341
  index = 0;
@@ -5942,8 +6346,19 @@ class AutoQuality {
5942
6346
  slow = 0;
5943
6347
  verySlow = 0;
5944
6348
  fast = 0;
6349
+ headroom = 0;
6350
+ constructor(start = 0) {
6351
+ const index = typeof start === "number" ? Math.floor(start) : AUTO_STEPS.findIndex((s) => s.quality === start);
6352
+ this.index = Math.min(Math.max(0, index), AUTO_STEPS.length - 1);
6353
+ }
6354
+ get step() {
6355
+ return this.index;
6356
+ }
5945
6357
  get quality() {
5946
- return QUALITIES[this.index];
6358
+ return AUTO_STEPS[this.index].quality;
6359
+ }
6360
+ get level() {
6361
+ return stepLevel(AUTO_STEPS[this.index]);
5947
6362
  }
5948
6363
  report(now, frames) {
5949
6364
  if (!this.started)
@@ -5955,24 +6370,35 @@ class AutoQuality {
5955
6370
  this.slow = gpuBound ? this.slow + 1 : 0;
5956
6371
  this.verySlow = gpuBound && frames.frameMs > VERY_SLOW_FRAME_MS ? this.verySlow + 1 : 0;
5957
6372
  this.fast = smooth ? this.fast + 1 : 0;
5958
- if ((this.slow >= SLOW_SECONDS || this.verySlow >= VERY_SLOW_SECONDS) && this.index < QUALITIES.length - 1) {
6373
+ this.headroom = !gpuBound && frames.renderMs <= HEADROOM_RENDER_MS ? this.headroom + 1 : 0;
6374
+ if ((this.slow >= SLOW_SECONDS || this.verySlow >= VERY_SLOW_SECONDS) && this.index < AUTO_STEPS.length - 1) {
5959
6375
  if (now - this.raisedAt < RELAPSE_MS)
5960
6376
  this.ceiling = Math.max(this.ceiling, this.index + 1);
5961
6377
  this.index++;
5962
6378
  return this.moved(now);
5963
6379
  }
5964
- if (this.fast >= FAST_SECONDS && this.index > this.ceiling) {
6380
+ if ((this.fast >= FAST_SECONDS || this.headroom >= HEADROOM_SECONDS) && this.index > this.ceiling) {
5965
6381
  this.index--;
5966
6382
  this.raisedAt = now;
5967
6383
  return this.moved(now);
5968
6384
  }
5969
6385
  return false;
5970
6386
  }
6387
+ hold(now) {
6388
+ if (!this.started)
6389
+ this.started = now;
6390
+ this.changed = now;
6391
+ this.slow = 0;
6392
+ this.verySlow = 0;
6393
+ this.fast = 0;
6394
+ this.headroom = 0;
6395
+ }
5971
6396
  moved(now) {
5972
6397
  this.changed = now;
5973
6398
  this.slow = 0;
5974
6399
  this.verySlow = 0;
5975
6400
  this.fast = 0;
6401
+ this.headroom = 0;
5976
6402
  return true;
5977
6403
  }
5978
6404
  }
@@ -5987,7 +6413,7 @@ function median(values) {
5987
6413
  // src/render/GameView.tsx
5988
6414
  import { jsx as jsx2 } from "react/jsx-runtime";
5989
6415
  var DEFAULT_BACKGROUND = [0.04, 0.05, 0.09];
5990
- var FRAME_LEN = 26;
6416
+ var FRAME_LEN = 29;
5991
6417
  var WHITE3 = [1, 1, 1];
5992
6418
  var nextSurface = 1;
5993
6419
  var defaultCamera = () => ({
@@ -6078,11 +6504,11 @@ function GameView(props) {
6078
6504
  const threaded = native2.hasFrameClock?.() === true && typeof native2.publishFrame === "function" && typeof native2.startFrames === "function";
6079
6505
  let presentedAt = 0;
6080
6506
  if (autoQuality.current?.source !== source)
6081
- autoQuality.current = { source, auto: new AutoQuality };
6507
+ autoQuality.current = { source, auto: new AutoQuality(settledQuality(live.current.props.qualityKey, native)) };
6082
6508
  const auto = autoQuality.current.auto;
6083
6509
  const chosen = () => {
6084
6510
  const setting = live.current.props.quality ?? "auto";
6085
- return setting === "auto" ? auto.quality : setting;
6511
+ return setting === "auto" ? [auto.quality, auto.level] : [setting, QUALITY_LEVELS[setting]];
6086
6512
  };
6087
6513
  const followed = [0, 0, 0];
6088
6514
  const shaken = { eye: [0, 0, 0], target: [0, 0, 0], fov: 1 };
@@ -6111,10 +6537,12 @@ function GameView(props) {
6111
6537
  return Math.min(s.count, end);
6112
6538
  };
6113
6539
  let overlay = null;
6540
+ let backdrop = null;
6114
6541
  let last = 0;
6115
6542
  let frames = 0;
6116
6543
  let fpsStart = 0;
6117
6544
  let updateTime = 0;
6545
+ let publishTime = 0;
6118
6546
  let renderTime = 0;
6119
6547
  const intervals = [];
6120
6548
  const draws = [];
@@ -6143,10 +6571,10 @@ function GameView(props) {
6143
6571
  updateTime += performance.now() - t0;
6144
6572
  follow(camera, source, followed, dt);
6145
6573
  const view = shake(camera, shaken, dt);
6146
- const quality = chosen();
6147
- const level = QUALITY_LEVELS[quality];
6574
+ const [quality, level] = chosen();
6148
6575
  const { width, height } = size.current;
6149
- const ratio = Math.min(pixelRatio(), level.maxPixelRatio);
6576
+ const screenRatio = Math.min(pixelRatio(), level.maxPixelRatio);
6577
+ const ratio = levelPixelRatio(level, pixelRatio(), width, height);
6150
6578
  const f = frameData;
6151
6579
  f.set(view.eye, 0);
6152
6580
  f.set(view.target, 3);
@@ -6154,7 +6582,7 @@ function GameView(props) {
6154
6582
  f[7] = view.ortho ?? 0;
6155
6583
  f.set(light.direction, 8);
6156
6584
  f[11] = light.ambient;
6157
- f[12] = light.shadows ?? true ? 1 : 0;
6585
+ f[12] = (light.shadows ?? true) !== false && level.shadows ? 1 : 0;
6158
6586
  f[13] = light.shadowExtent ?? 30;
6159
6587
  const lightColor = light.color === undefined ? WHITE3 : resolveColor(light.color);
6160
6588
  f[14] = lightColor[0];
@@ -6165,11 +6593,14 @@ function GameView(props) {
6165
6593
  f[18] = sky[1];
6166
6594
  f[19] = sky[2];
6167
6595
  f[20] = environment.fog;
6168
- f[21] = Math.round(width * ratio);
6169
- f[22] = Math.round(height * ratio);
6596
+ f[21] = Math.round(width * screenRatio);
6597
+ f[22] = Math.round(height * screenRatio);
6170
6598
  f[23] = level.samples;
6171
6599
  f[24] = level.shadowSize;
6172
6600
  f[25] = level.softShadows ? 1 : 0;
6601
+ f[26] = screenRatio > 0 ? ratio / screenRatio : 1;
6602
+ f[27] = level.fxaa ? 1 : 0;
6603
+ f[28] = light.shadows === "backdrop" ? 0 : 1;
6173
6604
  const t1 = performance.now();
6174
6605
  if (layer) {
6175
6606
  if (overlay?.source !== layer)
@@ -6179,9 +6610,23 @@ function GameView(props) {
6179
6610
  overlay = null;
6180
6611
  native.renderOverlay(surfaceId, regions.buffer, regions.buffer, regions.buffer, regions.buffer, 0, 0, 0);
6181
6612
  }
6613
+ const backSource = current.backdrop ?? source.backdrop;
6614
+ const back = backSource && !backSource.isDisposed ? backSource : undefined;
6615
+ if (back && typeof native.renderBackdrop === "function") {
6616
+ if (backdrop?.source !== back || backdrop.updates !== (back.updateCount ?? 0)) {
6617
+ if (!back.updateCount && back.update)
6618
+ back.update(0);
6619
+ backdrop = { source: back, updates: back.updateCount ?? 0, regions: wholeRegions(back) };
6620
+ native.renderBackdrop(surfaceId, back.matrices.buffer, back.colors.buffer, backdrop.regions.buffer, back.ranges.buffer, drawn(back));
6621
+ }
6622
+ } else if (backdrop) {
6623
+ backdrop = null;
6624
+ native.renderBackdrop?.(surfaceId, regions.buffer, regions.buffer, regions.buffer, regions.buffer, 0);
6625
+ }
6182
6626
  const render = threaded ? native2.publishFrame : native.renderFrame;
6183
6627
  const rendered = render(surfaceId, f.buffer, source.matrices.buffer, source.colors.buffer, regions.buffer, source.ranges.buffer, drawn(source), viewProj.buffer, source.nativeWorld ?? -1);
6184
6628
  const drawMs = performance.now() - t1;
6629
+ publishTime += drawMs;
6185
6630
  if (!threaded) {
6186
6631
  renderTime += drawMs;
6187
6632
  draws.push(drawMs);
@@ -6207,18 +6652,26 @@ function GameView(props) {
6207
6652
  frameMs = shownFrames > 0 ? Math.max(frameMs, elapsed / shownFrames) : elapsed;
6208
6653
  renderMs = meanRenderMs = nativeMs;
6209
6654
  }
6210
- auto.report(now, { frameMs, renderMs });
6655
+ if (current.adaptQuality ?? true)
6656
+ auto.report(now, { frameMs, renderMs });
6657
+ else
6658
+ auto.hold(now);
6659
+ if ((current.quality ?? "auto") === "auto")
6660
+ rememberQuality(current.qualityKey, auto.step, native);
6211
6661
  intervals.length = 0;
6212
6662
  draws.length = 0;
6213
6663
  current.onStats?.({
6214
6664
  fps,
6215
6665
  updateMs: updateTime / frames,
6666
+ publishMs: publishTime / frames,
6216
6667
  renderMs: meanRenderMs,
6217
6668
  quality,
6669
+ sceneScale: frameData[26],
6218
6670
  backend: native.renderBackend()
6219
6671
  });
6220
6672
  frames = 0;
6221
6673
  updateTime = 0;
6674
+ publishTime = 0;
6222
6675
  renderTime = 0;
6223
6676
  fpsStart = now;
6224
6677
  }
@@ -6346,6 +6799,9 @@ class ProceduralCache {
6346
6799
  this.revision = revision;
6347
6800
  this.storage = storage;
6348
6801
  }
6802
+ storageKey(key) {
6803
+ return `procedural:${this.revision}:${key}`;
6804
+ }
6349
6805
  get stats() {
6350
6806
  return {
6351
6807
  hits: this.hits,
@@ -6556,6 +7012,322 @@ class ProceduralCache {
6556
7012
  return mapValue(header.value, restored);
6557
7013
  }
6558
7014
  }
7015
+ // src/assets/procedural.ts
7016
+ class FieldRef {
7017
+ id;
7018
+ constructor(id) {
7019
+ this.id = id;
7020
+ }
7021
+ }
7022
+
7023
+ class FieldGraph {
7024
+ nodes = [];
7025
+ outputs = {};
7026
+ constants = new Map;
7027
+ inputs = new Map;
7028
+ node(node) {
7029
+ this.nodes.push(node);
7030
+ return new FieldRef(this.nodes.length - 1);
7031
+ }
7032
+ ref(value) {
7033
+ return (value instanceof FieldRef ? value : this.constant(value)).id;
7034
+ }
7035
+ refs(values) {
7036
+ return values.map((v) => this.ref(v));
7037
+ }
7038
+ constant(value) {
7039
+ if (!Number.isFinite(value))
7040
+ throw new Error("FieldGraph: constants must be finite");
7041
+ let ref = this.constants.get(value);
7042
+ if (!ref || Object.is(value, -0) !== Object.is(this.nodes[ref.id]?.value, -0)) {
7043
+ ref = this.node({ op: "const", value });
7044
+ this.constants.set(value, ref);
7045
+ }
7046
+ return ref;
7047
+ }
7048
+ input(name) {
7049
+ let ref = this.inputs.get(name);
7050
+ if (!ref) {
7051
+ ref = this.node({ op: "var", name });
7052
+ this.inputs.set(name, ref);
7053
+ }
7054
+ return ref;
7055
+ }
7056
+ get x() {
7057
+ return this.input("x");
7058
+ }
7059
+ get z() {
7060
+ return this.input("z");
7061
+ }
7062
+ add(a, b) {
7063
+ return this.node({ op: "add", a: this.ref(a), b: this.ref(b) });
7064
+ }
7065
+ sub(a, b) {
7066
+ return this.node({ op: "sub", a: this.ref(a), b: this.ref(b) });
7067
+ }
7068
+ mul(a, b) {
7069
+ return this.node({ op: "mul", a: this.ref(a), b: this.ref(b) });
7070
+ }
7071
+ div(a, b) {
7072
+ return this.node({ op: "div", a: this.ref(a), b: this.ref(b) });
7073
+ }
7074
+ sum(...values) {
7075
+ if (!values.length)
7076
+ return this.constant(0);
7077
+ let total = values[0] instanceof FieldRef ? values[0] : this.constant(values[0]);
7078
+ for (const value of values.slice(1))
7079
+ total = this.add(total, value);
7080
+ return total;
7081
+ }
7082
+ product(...values) {
7083
+ if (!values.length)
7084
+ return this.constant(1);
7085
+ let total = values[0] instanceof FieldRef ? values[0] : this.constant(values[0]);
7086
+ for (const value of values.slice(1))
7087
+ total = this.mul(total, value);
7088
+ return total;
7089
+ }
7090
+ min(...args) {
7091
+ return this.node({ op: "min", args: this.refs(args) });
7092
+ }
7093
+ max(...args) {
7094
+ return this.node({ op: "max", args: this.refs(args) });
7095
+ }
7096
+ neg(a) {
7097
+ return this.node({ op: "neg", a: this.ref(a) });
7098
+ }
7099
+ abs(a) {
7100
+ return this.node({ op: "abs", a: this.ref(a) });
7101
+ }
7102
+ sqrt(a) {
7103
+ return this.node({ op: "sqrt", a: this.ref(a) });
7104
+ }
7105
+ floor(a) {
7106
+ return this.node({ op: "floor", a: this.ref(a) });
7107
+ }
7108
+ square(a) {
7109
+ return this.node({ op: "square", a: this.ref(a) });
7110
+ }
7111
+ sin(a) {
7112
+ return this.node({ op: "sin", a: this.ref(a) });
7113
+ }
7114
+ cos(a) {
7115
+ return this.node({ op: "cos", a: this.ref(a) });
7116
+ }
7117
+ exp(a) {
7118
+ return this.node({ op: "exp", a: this.ref(a) });
7119
+ }
7120
+ pow(a, b) {
7121
+ return this.node({ op: "pow", a: this.ref(a), b: this.ref(b) });
7122
+ }
7123
+ fract(a) {
7124
+ return this.node({ op: "fract", a: this.ref(a) });
7125
+ }
7126
+ noise(x, z, period = 0) {
7127
+ return this.node({ op: "noise", x: this.ref(x), z: this.ref(z), period });
7128
+ }
7129
+ mix(a, b, t) {
7130
+ return this.add(a, this.mul(this.sub(b, a), t));
7131
+ }
7132
+ hypot(...args) {
7133
+ return this.node({ op: "hypot", args: this.refs(args) });
7134
+ }
7135
+ smooth(a) {
7136
+ return this.node({ op: "smooth", a: this.ref(a) });
7137
+ }
7138
+ clamp(a, lo, hi) {
7139
+ return this.node({ op: "clamp", a: this.ref(a), lo: this.ref(lo), hi: this.ref(hi) });
7140
+ }
7141
+ select(test, le, gt) {
7142
+ return this.node({ op: "select", test: this.ref(test), le: this.ref(le), gt: this.ref(gt) });
7143
+ }
7144
+ sines(x, z, base, terms) {
7145
+ return this.node({ op: "sines", x: this.ref(x), z: this.ref(z), base, terms });
7146
+ }
7147
+ bumps(x, z, items, power = 2) {
7148
+ return this.node({ op: "bumps", x: this.ref(x), z: this.ref(z), items, power });
7149
+ }
7150
+ distance(x, z, shapes) {
7151
+ return this.node({ op: "distance", x: this.ref(x), z: this.ref(z), shapes });
7152
+ }
7153
+ falloff(x, z, shapes) {
7154
+ return this.node({ op: "falloff", x: this.ref(x), z: this.ref(z), shapes });
7155
+ }
7156
+ depth(x, z, shapes) {
7157
+ return this.node({ op: "depth", x: this.ref(x), z: this.ref(z), shapes });
7158
+ }
7159
+ output(name, value) {
7160
+ this.outputs[name] = this.ref(value);
7161
+ return name;
7162
+ }
7163
+ toJSON() {
7164
+ return { nodes: this.nodes, outputs: this.outputs };
7165
+ }
7166
+ }
7167
+
7168
+ class FieldFunction {
7169
+ fields;
7170
+ outputs;
7171
+ constructor(fields, outputs) {
7172
+ this.fields = fields;
7173
+ this.outputs = outputs;
7174
+ }
7175
+ toJSON() {
7176
+ return { fields: this.fields, out: Array.from({ length: this.outputs }, (_, i) => `out${i}`) };
7177
+ }
7178
+ }
7179
+ function fieldFunction(names, build) {
7180
+ const f = new FieldGraph;
7181
+ const inputs = Object.fromEntries(names.map((name) => [name, f.input(name)]));
7182
+ const result = build(f, inputs);
7183
+ const out = Array.isArray(result) ? result : [result];
7184
+ out.forEach((value, i) => f.output(`out${i}`, value));
7185
+ return new FieldFunction(f, out.length);
7186
+ }
7187
+ var surfaceColor = (build) => fieldFunction(["u", "v", "x", "y", "z", "nx", "ny", "nz"], build);
7188
+ var faceColor = (build) => fieldFunction(["r", "angle"], build);
7189
+ var sectionColor = (build) => fieldFunction(["r", "y"], build);
7190
+ var surfaceDisplacement = (build) => fieldFunction(["u", "v"], build);
7191
+ var engine = () => {
7192
+ const native = NativeNayanEngine_web_default;
7193
+ if (!native)
7194
+ throw new Error("procedural worlds: the native core is not linked into this build");
7195
+ if (typeof native.worldgenBuild !== "function")
7196
+ throw new Error("procedural worlds: this app's native core predates them: rebuild it");
7197
+ return native;
7198
+ };
7199
+ var recipeText = (recipe) => JSON.stringify(recipe, (_key, value) => value instanceof FieldGraph ? value.toJSON() : value);
7200
+ var geometry = (buffers) => ({
7201
+ vertices: new Float32Array(buffers.vertices),
7202
+ indices: new Uint32Array(buffers.indices)
7203
+ });
7204
+ var spots = (points) => points.map(([x, z]) => ({ x, z }));
7205
+ var bodyBuffers = new Map;
7206
+
7207
+ class ProceduralWorld {
7208
+ handle;
7209
+ spawn;
7210
+ restored;
7211
+ layers;
7212
+ picks;
7213
+ fields = new Map;
7214
+ disposed = false;
7215
+ constructor(handle) {
7216
+ this.handle = handle;
7217
+ const info = JSON.parse(engine().worldgenInfo(handle));
7218
+ this.spawn = { x: info.spawn[0], z: info.spawn[1] };
7219
+ this.restored = info.restored;
7220
+ this.layers = info.layers;
7221
+ this.picks = Object.fromEntries(Object.entries(info.picks).map(([name, points]) => [name, spots(points)]));
7222
+ }
7223
+ get id() {
7224
+ if (this.disposed)
7225
+ throw new Error("ProceduralWorld: used after dispose");
7226
+ return this.handle;
7227
+ }
7228
+ layer(name) {
7229
+ return new Float64Array(engine().worldgenLayer(this.id, name));
7230
+ }
7231
+ reachable() {
7232
+ return new Float64Array(engine().worldgenReachable(this.id));
7233
+ }
7234
+ overview() {
7235
+ return new Uint8Array(engine().worldgenOverview(this.id));
7236
+ }
7237
+ pick(run) {
7238
+ const native = engine();
7239
+ if (!native.worldgenPick(this.id, run))
7240
+ return false;
7241
+ const info = JSON.parse(native.worldgenInfo(this.id));
7242
+ this.picks = Object.fromEntries(Object.entries(info.picks).map(([name, points]) => [name, spots(points)]));
7243
+ return true;
7244
+ }
7245
+ terrain(material) {
7246
+ return geometry(engine().worldgenTerrain(this.id, material));
7247
+ }
7248
+ ground(x, z) {
7249
+ return engine().worldgenGround(this.id, x, z);
7250
+ }
7251
+ surface(x, z) {
7252
+ return engine().worldgenSurface(this.id, x, z);
7253
+ }
7254
+ platformAt(x, z) {
7255
+ return engine().worldgenPlatform(this.id, x, z);
7256
+ }
7257
+ walkable(x, z, radius) {
7258
+ return engine().worldgenWalkable(this.id, x, z, radius);
7259
+ }
7260
+ clearance(x, z, radius) {
7261
+ return engine().worldgenClearance(this.id, x, z, radius);
7262
+ }
7263
+ solidClearance(x, z, radius, bottom, top) {
7264
+ return engine().worldgenSolidClearance(this.id, x, z, radius, bottom, top);
7265
+ }
7266
+ field(name, x, z) {
7267
+ let index = this.fields.get(name);
7268
+ if (index === undefined) {
7269
+ index = engine().worldgenFieldIndex(this.id, name);
7270
+ if (index < 0)
7271
+ throw new Error(`ProceduralWorld: no field named ${name}`);
7272
+ this.fields.set(name, index);
7273
+ }
7274
+ return engine().worldgenField(this.id, index, x, z);
7275
+ }
7276
+ tilt(x, z, yaw = 0) {
7277
+ return engine().worldgenTilt(this.id, x, z, yaw);
7278
+ }
7279
+ canSee(a, b, { radius = 0.25, step = 0.4, rise = Infinity } = {}) {
7280
+ return engine().worldgenSight(this.id, a.x, a.z, b.x, b.z, radius, step, rise === Infinity ? Number.MAX_VALUE : rise);
7281
+ }
7282
+ move(point, dx, dz, radius, bodies = []) {
7283
+ let buffer = bodyBuffers.get(bodies.length);
7284
+ if (!buffer)
7285
+ bodyBuffers.set(bodies.length, buffer = new Float64Array(bodies.length * 3));
7286
+ for (let i = 0;i < bodies.length; i++) {
7287
+ const b = bodies[i];
7288
+ buffer[i * 3] = b.x;
7289
+ buffer[i * 3 + 1] = b.z;
7290
+ buffer[i * 3 + 2] = b.radius;
7291
+ }
7292
+ const [x, z] = engine().worldgenMove(this.id, point.x, point.z, dx, dz, radius, buffer.buffer);
7293
+ point.x = x;
7294
+ point.z = z;
7295
+ }
7296
+ dispose() {
7297
+ if (this.disposed)
7298
+ return;
7299
+ this.disposed = true;
7300
+ engine().worldgenFree(this.handle);
7301
+ }
7302
+ }
7303
+ var opened = (handle) => {
7304
+ if (!handle)
7305
+ throw new Error(`procedural world: ${engine().worldgenError() || "generation failed"}`);
7306
+ return new ProceduralWorld(handle);
7307
+ };
7308
+ function generateWorld(recipe, options = {}) {
7309
+ return opened(engine().worldgenBuild(recipeText(recipe), options.key ?? "", options.prepareTerrain ?? false));
7310
+ }
7311
+ async function generateWorldAsync(recipe, options = {}) {
7312
+ return opened(await engine().worldgenBuildAsync(recipeText(recipe), options.key ?? "", options.prepareTerrain ?? false));
7313
+ }
7314
+ function isWorldKept(key) {
7315
+ return engine().worldgenKept(key);
7316
+ }
7317
+ function generateMesh(recipe) {
7318
+ return geometry(engine().worldgenMesh(recipeText(recipe)));
7319
+ }
7320
+ function paintGeometry(geometry, color) {
7321
+ const v = geometry.vertices;
7322
+ const whole = v.byteOffset === 0 && v.byteLength === v.buffer.byteLength;
7323
+ const buffer = whole ? v.buffer : v.slice().buffer;
7324
+ if (!engine().worldgenPaint(buffer, JSON.stringify(color)))
7325
+ throw new Error(`recolor: ${engine().worldgenError()}`);
7326
+ if (!whole)
7327
+ v.set(new Float32Array(buffer));
7328
+ return geometry;
7329
+ }
7330
+
6559
7331
  // src/assets/shape.ts
6560
7332
  var TAU2 = Math.PI * 2;
6561
7333
  var CURVE_STEPS = 12;
@@ -6874,10 +7646,6 @@ function paint(g, color) {
6874
7646
  }
6875
7647
  return g;
6876
7648
  }
6877
- var fromBuilder = (b) => ({
6878
- vertices: new Float32Array(b.vertices),
6879
- indices: new Uint32Array(b.indices)
6880
- });
6881
7649
  function shapeGeometry(shape, options = {}) {
6882
7650
  const info = meshInfo(SHAPE_MESH[shape]);
6883
7651
  if (!info)
@@ -6907,7 +7675,11 @@ function extrude(path, options = {}) {
6907
7675
  indices[i] = i;
6908
7676
  return paint({ vertices, indices }, options.color);
6909
7677
  }
7678
+ var perPoint = (x) => typeof x === "function" || x instanceof FieldFunction;
7679
+ var asField = (x) => x instanceof FieldFunction ? x : undefined;
6910
7680
  function paintBy(g, color) {
7681
+ if (color instanceof FieldFunction)
7682
+ return paintGeometry(g, color);
6911
7683
  if (typeof color !== "function")
6912
7684
  return paint(g, color);
6913
7685
  const v = g.vertices;
@@ -6927,29 +7699,40 @@ function paintBy(g, color) {
6927
7699
  function recolor(geometry, color) {
6928
7700
  return paintBy(geometry, color);
6929
7701
  }
6930
- function roundFace(center, x, y, normal, radius, sweep, segments, rings, color) {
6931
- const b = new Builder;
6932
- b.grid(rings, segments, (u, v) => {
6933
- const a = u * sweep;
6934
- const r = radius(u) * v;
6935
- const [c, s] = [Math.cos(a) * r, Math.sin(a) * r];
6936
- return [[center[0] + x[0] * c + y[0] * s, center[1] + x[1] * c + y[1] * s, center[2] + x[2] * c + y[2] * s], normal];
6937
- });
6938
- const g = facingOutward(fromBuilder(b));
7702
+ var flat = (color) => color ? [color[0], color[1], color[2]] : undefined;
7703
+ function nativeSweep(part) {
7704
+ const core = native();
7705
+ if (typeof core.worldgenMesh !== "function")
7706
+ throw new Error("shapes: this app's native core predates native lathes and tubes: rebuild it");
7707
+ const built = core.worldgenMesh(JSON.stringify({ parts: [part] }));
7708
+ return { vertices: new Float32Array(built.vertices), indices: new Uint32Array(built.indices) };
7709
+ }
7710
+ function paintRange(g, from, to, color) {
7711
+ const v = g.vertices;
7712
+ for (let i = from * VERTEX_FLOATS;i < to * VERTEX_FLOATS; i += VERTEX_FLOATS) {
7713
+ const c = color({
7714
+ u: v[i + 9],
7715
+ v: v[i + 10],
7716
+ position: [v[i], v[i + 1], v[i + 2]],
7717
+ normal: [v[i + 3], v[i + 4], v[i + 5]]
7718
+ });
7719
+ v[i + 6] = c[0];
7720
+ v[i + 7] = c[1];
7721
+ v[i + 8] = c[2];
7722
+ }
7723
+ }
7724
+ function paintFace(g, from, to, sweep, color) {
6939
7725
  const vs = g.vertices;
6940
- for (let i = 0;i < vs.length; i += VERTEX_FLOATS) {
7726
+ for (let i = from * VERTEX_FLOATS;i < to * VERTEX_FLOATS; i += VERTEX_FLOATS) {
6941
7727
  const a = vs[i + 9] * sweep;
6942
7728
  const r = vs[i + 10];
6943
7729
  vs[i + 9] = 0.5 + Math.cos(a) * r / 2;
6944
7730
  vs[i + 10] = 0.5 + Math.sin(a) * r / 2;
6945
- if (typeof color === "function") {
6946
- const c = color(r, a);
6947
- vs[i + 6] = c[0];
6948
- vs[i + 7] = c[1];
6949
- vs[i + 8] = c[2];
6950
- }
7731
+ const c = color(r, a);
7732
+ vs[i + 6] = c[0];
7733
+ vs[i + 7] = c[1];
7734
+ vs[i + 8] = c[2];
6951
7735
  }
6952
- return typeof color === "function" ? g : paint(g, color);
6953
7736
  }
6954
7737
  var whole = (a) => a.byteOffset === 0 && a.byteLength === a.buffer.byteLength;
6955
7738
  var nativeGeometry3 = (g) => NativeNayanEngine_web_default && typeof NativeNayanEngine_web_default.geometryPlace === "function" && whole(g.vertices) && whole(g.indices) ? NativeNayanEngine_web_default : null;
@@ -7013,70 +7796,52 @@ function lathe(profile, options = {}) {
7013
7796
  const sweep = Math.min(TAU2, Math.max(0.001, options.sweep ?? TAU2));
7014
7797
  const full = sweep >= TAU2 - 0.000001;
7015
7798
  const last = points.length - 1;
7016
- const edge = (i) => {
7017
- const a = points[Math.max(0, i)];
7018
- const b = points[Math.min(last, i + 1)];
7019
- const len = Math.hypot(b[0] - a[0], b[1] - a[1]) || 1;
7020
- return [(a[1] - b[1]) / len, (b[0] - a[0]) / len];
7021
- };
7022
- const normals = points.map(([r], i) => {
7023
- if (r < 0.000001 && (i === 0 || i === last))
7024
- return [0, i === 0 ? 1 : -1];
7025
- const e1 = edge(i - 1);
7026
- const e2 = edge(i);
7027
- const n = i === 0 ? e2 : i === last ? e1 : [e1[0] + e2[0], e1[1] + e2[1]];
7028
- const len = Math.hypot(n[0], n[1]) || 1;
7029
- return [n[0] / len, n[1] / len];
7030
- });
7031
- const b = new Builder;
7032
7799
  const displace = options.displace;
7033
- b.grid(last, segments, (u, v) => {
7034
- const i = Math.round(v * last);
7035
- const a = u * sweep;
7036
- const [r, y] = points[i];
7037
- const [nr, ny] = normals[i];
7038
- const n = [nr * Math.cos(a), ny, -nr * Math.sin(a)];
7039
- const d = displace ? displace(r < 0.000001 || full && u === 1 ? 0 : u, v) : 0;
7040
- return [[r * Math.cos(a) + n[0] * d, y + n[1] * d, -r * Math.sin(a) + n[2] * d], n];
7041
- });
7042
- const parts = [
7043
- {
7044
- geometry: paintBy(displace ? smoothNormals(fromBuilder(b)) : fromBuilder(b), options.color)
7800
+ let offsets;
7801
+ if (typeof displace === "function") {
7802
+ offsets = new Array((last + 1) * (segments + 1));
7803
+ for (let i = 0;i <= last; i++) {
7804
+ const pole = points[i][0] < 0.000001;
7805
+ for (let j = 0;j <= segments; j++) {
7806
+ const u = j / segments;
7807
+ offsets[i * (segments + 1) + j] = displace(pole || full && u === 1 ? 0 : u, i / last);
7808
+ }
7045
7809
  }
7046
- ];
7047
- const capColor = options.capColor ?? (typeof options.color === "function" ? undefined : options.color);
7048
- const rings = Math.max(1, Math.round(options.capRings ?? (typeof capColor === "function" ? 12 : 1)));
7049
- for (const [i, facing] of [
7050
- [0, 1],
7051
- [last, -1]
7052
- ]) {
7053
- const [r, y] = points[i];
7054
- if (r < 0.000001)
7055
- continue;
7056
- parts.push({
7057
- geometry: roundFace([0, y, 0], [1, 0, 0], [0, 0, -1], [0, facing, 0], () => r, sweep, segments, rings, capColor)
7058
- });
7059
7810
  }
7060
- if (full)
7061
- return parts.length > 1 ? merge(parts) : parts[0].geometry;
7062
- const cutColor = options.cutColor ?? (typeof options.color === "function" ? undefined : options.color);
7063
- const falling = points.every((p, i) => i === 0 || p[1] <= points[i - 1][1] + 0.000000001);
7064
- let face;
7065
- if (falling) {
7066
- const g = new Builder;
7067
- const steps = Math.max(1, Math.round(options.cutSteps ?? (typeof cutColor === "function" ? 12 : 1)));
7068
- g.grid(last, steps, (u, v) => {
7069
- const [r, y] = points[Math.round(v * last)];
7070
- return [
7071
- [u * r, y, 0],
7072
- [0, 0, 1]
7073
- ];
7074
- });
7075
- face = facingOutward(fromBuilder(g));
7076
- } else {
7077
- face = extrude(new Path().addPolygon([[0, -points[0][1]], ...points.map(([r, y]) => [r, -y]), [0, -points[last][1]]]), { depth: 0 });
7811
+ const color = options.color;
7812
+ const capColor = options.capColor ?? (perPoint(color) ? undefined : color);
7813
+ const capRings = Math.max(1, Math.round(options.capRings ?? (perPoint(capColor) ? 12 : 1)));
7814
+ const cutColor = options.cutColor ?? (perPoint(color) ? undefined : color);
7815
+ const cutSteps = Math.max(1, Math.round(options.cutSteps ?? (perPoint(cutColor) ? 12 : 1)));
7816
+ const g = nativeSweep({
7817
+ kind: "lathe",
7818
+ points,
7819
+ segments,
7820
+ sweep,
7821
+ displace: offsets,
7822
+ displaceField: asField(displace),
7823
+ color: perPoint(color) ? undefined : flat(color),
7824
+ colorField: asField(color),
7825
+ capColor: perPoint(capColor) ? undefined : flat(capColor),
7826
+ capColorField: asField(capColor),
7827
+ rawCaps: typeof capColor === "function",
7828
+ capRings,
7829
+ cutColor: perPoint(cutColor) ? undefined : flat(cutColor),
7830
+ cutColorField: asField(cutColor),
7831
+ cutSteps
7832
+ });
7833
+ let at = (last + 1) * (segments + 1);
7834
+ if (typeof color === "function")
7835
+ paintRange(g, 0, at, color);
7836
+ for (const i of [0, last]) {
7837
+ if (points[i][0] < 0.000001)
7838
+ continue;
7839
+ const end = at + (capRings + 1) * (segments + 1);
7840
+ if (typeof capColor === "function")
7841
+ paintFace(g, at, end, sweep, capColor);
7842
+ at = end;
7078
7843
  }
7079
- if (typeof cutColor === "function") {
7844
+ if (!full && typeof cutColor === "function") {
7080
7845
  const widest = Math.max(...points.map((p) => p[0]), 0.000001);
7081
7846
  const skinAt = (y) => {
7082
7847
  let best = 0;
@@ -7088,33 +7853,19 @@ function lathe(profile, options = {}) {
7088
7853
  }
7089
7854
  return best || widest;
7090
7855
  };
7091
- const fv = face.vertices;
7092
- for (let i = 0;i < fv.length; i += VERTEX_FLOATS) {
7093
- const c = cutColor(Math.min(1, fv[i] / skinAt(fv[i + 1])), fv[i + 1]);
7094
- fv.set(c.slice(0, 3), i + 6);
7856
+ const v = g.vertices;
7857
+ const half = (v.length / VERTEX_FLOATS - at) / 2;
7858
+ for (let k = 0;k < half; k++) {
7859
+ const i = (at + k) * VERTEX_FLOATS;
7860
+ const c = cutColor(Math.min(1, v[i] / skinAt(v[i + 1])), v[i + 1]);
7861
+ for (const o of [i, i + half * VERTEX_FLOATS]) {
7862
+ v[o + 6] = c[0];
7863
+ v[o + 7] = c[1];
7864
+ v[o + 8] = c[2];
7865
+ }
7095
7866
  }
7096
- } else {
7097
- paint(face, cutColor);
7098
7867
  }
7099
- const cut = (angle, flip) => {
7100
- const g = {
7101
- vertices: face.vertices.slice(),
7102
- indices: face.indices.slice()
7103
- };
7104
- const [c, s] = [Math.cos(angle), Math.sin(angle)];
7105
- const n = angle === 0 ? [0, 0, 1] : [-s, 0, -c];
7106
- for (let v = 0;v < g.vertices.length; v += VERTEX_FLOATS) {
7107
- const r = g.vertices[v];
7108
- g.vertices[v] = r * c;
7109
- g.vertices[v + 2] = -r * s;
7110
- g.vertices.set(n, v + 3);
7111
- }
7112
- if (flip)
7113
- for (let t = 0;t < g.indices.length; t += 3)
7114
- [g.indices[t + 1], g.indices[t + 2]] = [g.indices[t + 2], g.indices[t + 1]];
7115
- return g;
7116
- };
7117
- return merge([...parts, { geometry: cut(0, false) }, { geometry: cut(sweep, true) }]);
7868
+ return g;
7118
7869
  }
7119
7870
  function tube(points, options = {}) {
7120
7871
  if (points.some((p) => p.some((v) => !Number.isFinite(v))))
@@ -7127,133 +7878,43 @@ function tube(points, options = {}) {
7127
7878
  if (points.length < (closed ? 3 : 2))
7128
7879
  throw new Error(`tube: needs at least ${closed ? "three" : "two"} distinct points`);
7129
7880
  const radius = typeof options.radius === "function" ? options.radius : () => options.radius ?? 0.1;
7130
- const shape = options.shape ?? (() => 1);
7881
+ const shape = options.shape;
7131
7882
  const sides = Math.max(3, Math.round(options.sides ?? 12));
7132
7883
  const steps = Math.max(1, Math.round(options.smoothness ?? 8));
7133
- const p = (i) => points[closed ? (i + points.length) % points.length : Math.max(0, Math.min(points.length - 1, i))];
7134
- const curve = [];
7135
- for (let i = 0;i < points.length - (closed ? 0 : 1); i++) {
7136
- const [a, b, c, d] = [p(i - 1), p(i), p(i + 1), p(i + 2)];
7137
- for (let s = 0;s < steps; s++) {
7138
- const t = s / steps;
7139
- const t2 = t * t;
7140
- const t3 = t2 * t;
7141
- const coordinate = (k) => 0.5 * (2 * b[k] + (-a[k] + c[k]) * t + (2 * a[k] - 5 * b[k] + 4 * c[k] - d[k]) * t2 + (-a[k] + 3 * b[k] - 3 * c[k] + d[k]) * t3);
7142
- curve.push([coordinate(0), coordinate(1), coordinate(2)]);
7143
- }
7144
- }
7145
- curve.push(p(closed ? 0 : points.length - 1));
7146
- const sub = (a, b) => [a[0] - b[0], a[1] - b[1], a[2] - b[2]];
7147
- const cross = (a, b) => [a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]];
7148
- const dot = (a, b) => a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
7149
- const norm = (a) => {
7150
- const l = Math.hypot(a[0], a[1], a[2]) || 1;
7151
- return [a[0] / l, a[1] / l, a[2] / l];
7152
- };
7153
- const last = curve.length - 1;
7154
- const tangents = curve.map((_, i) => {
7155
- let delta = sub(curve[closed && i === last ? 1 : Math.min(last, i + 1)], curve[closed && i === 0 ? last - 1 : Math.max(0, i - 1)]);
7156
- if (Math.hypot(...delta) < 0.000000001)
7157
- delta = i > 0 ? sub(curve[i], curve[i - 1]) : sub(curve[1], curve[0]);
7158
- return norm(delta);
7159
- });
7160
- if (closed)
7161
- tangents[last] = tangents[0];
7162
- const t0 = tangents[0];
7163
- let normal = norm(cross(t0, Math.abs(t0[1]) < 0.9 ? [0, 1, 0] : [1, 0, 0]));
7164
- let previous = t0;
7165
- const frames = [];
7166
- for (const t of tangents) {
7167
- const axis = cross(previous, t);
7168
- const cosine = Math.max(-1, Math.min(1, dot(previous, t)));
7169
- if (cosine > -1 + 0.00000001) {
7170
- const turn = cross(axis, normal);
7171
- const second = cross(axis, turn);
7172
- normal = [
7173
- normal[0] + turn[0] + second[0] / (1 + cosine),
7174
- normal[1] + turn[1] + second[1] / (1 + cosine),
7175
- normal[2] + turn[2] + second[2] / (1 + cosine)
7176
- ];
7177
- }
7178
- const projection = dot(normal, t);
7179
- normal = norm(sub(normal, [t[0] * projection, t[1] * projection, t[2] * projection]));
7180
- frames.push([normal, cross(t, normal)]);
7181
- previous = t;
7182
- }
7183
- if (closed) {
7184
- const end = frames[last][0], start = frames[0][0];
7185
- const correction = Math.atan2(dot(t0, cross(end, start)), dot(end, start));
7186
- const lengths = [0];
7187
- for (let i = 1;i <= last; i++)
7188
- lengths.push(lengths[i - 1] + distance(curve[i], curve[i - 1]));
7189
- for (let i = 1;i <= last; i++) {
7190
- const angle = correction * lengths[i] / lengths[last];
7191
- const [n, bn] = frames[i];
7192
- const c = Math.cos(angle), s = Math.sin(angle);
7193
- const corrected = [n[0] * c + bn[0] * s, n[1] * c + bn[1] * s, n[2] * c + bn[2] * s];
7194
- frames[i] = [corrected, cross(tangents[i], corrected)];
7195
- }
7196
- frames[last] = frames[0];
7197
- }
7198
- const b = new Builder;
7199
- const ring = curve.map((c, i) => {
7200
- const r = Math.max(0, radius(closed && i === last ? 0 : i / last));
7201
- const [n, bn] = frames[i];
7202
- const first = b.vertices.length / VERTEX_FLOATS;
7203
- for (let j = 0;j <= sides; j++) {
7204
- const a = (j === sides ? 0 : j / sides) * TAU2;
7205
- const cosine = Math.cos(a), sine = Math.sin(a);
7206
- const dir = [cosine * n[0] + sine * bn[0], cosine * n[1] + sine * bn[1], cosine * n[2] + sine * bn[2]];
7207
- const rj = r * shape(j === sides ? 0 : j / sides);
7208
- b.vertex([c[0] + dir[0] * rj, c[1] + dir[1] * rj, c[2] + dir[2] * rj], dir, j / sides, i / (curve.length - 1));
7209
- }
7210
- return { first, r };
7884
+ const count = (points.length - (closed ? 0 : 1)) * steps + 1;
7885
+ const last = count - 1;
7886
+ const radii = Array.from({ length: count }, (_, i) => Math.max(0, radius(closed && i === last ? 0 : i / last)));
7887
+ const color = options.color;
7888
+ const caps = !closed && (options.caps ?? true);
7889
+ const capColor = options.capColor ?? (perPoint(color) ? undefined : color);
7890
+ const capRings = perPoint(capColor) ? 8 : 1;
7891
+ const g = nativeSweep({
7892
+ kind: "tube",
7893
+ points,
7894
+ closed,
7895
+ sides,
7896
+ steps,
7897
+ radii,
7898
+ shape: shape && Array.from({ length: sides + 1 }, (_, j) => shape(j === sides ? 0 : j / sides)),
7899
+ smooth: !!shape || typeof options.radius === "function",
7900
+ caps,
7901
+ color: perPoint(color) ? undefined : flat(color),
7902
+ colorField: asField(color),
7903
+ capColor: perPoint(capColor) ? undefined : flat(capColor),
7904
+ capColorField: asField(capColor),
7905
+ rawCaps: typeof capColor === "function",
7906
+ capRings
7211
7907
  });
7212
- for (let i = 0;i < ring.length - 1; i++) {
7213
- for (let j = 0;j < sides; j++) {
7214
- const a = ring[i].first + j;
7215
- const c = ring[i + 1].first + j;
7216
- b.indices.push(a, a + 1, c, a + 1, c + 1, c);
7217
- }
7218
- }
7219
- let side = facingOutward(fromBuilder(b));
7220
- if (options.shape || typeof options.radius === "function")
7221
- side = smoothNormals(side);
7222
- const parts = [{ geometry: paintBy(side, options.color) }];
7223
- if (!closed && (options.caps ?? true)) {
7224
- const capColor = options.capColor ?? (typeof options.color === "function" ? undefined : options.color);
7225
- const rings = typeof capColor === "function" ? 8 : 1;
7226
- for (const [i, facing] of [
7227
- [0, -1],
7228
- [ring.length - 1, 1]
7229
- ]) {
7230
- const { r } = ring[i];
7231
- if (r < 0.000001)
7908
+ let at = count * (sides + 1);
7909
+ if (typeof color === "function")
7910
+ paintRange(g, 0, at, color);
7911
+ if (caps && typeof capColor === "function") {
7912
+ for (const i of [0, last]) {
7913
+ if (radii[i] < 0.000001)
7232
7914
  continue;
7233
- const t = tangents[i];
7234
- const [fn, fb] = frames[i];
7235
- parts.push({
7236
- geometry: roundFace(curve[i], fn, fb, [t[0] * facing, t[1] * facing, t[2] * facing], (u) => r * shape(u === 1 ? 0 : u), TAU2, sides, rings, capColor)
7237
- });
7238
- }
7239
- }
7240
- return parts.length > 1 ? merge(parts) : parts[0].geometry;
7241
- }
7242
- function facingOutward(g) {
7243
- const native = nativeGeometry3(g);
7244
- if (native) {
7245
- native.geometryFaceOutward(g.vertices.buffer, g.indices.buffer);
7246
- return g;
7247
- }
7248
- const v = g.vertices;
7249
- const at = (i, k) => v[g.indices[i] * VERTEX_FLOATS + k];
7250
- for (let t = 0;t < g.indices.length; t += 3) {
7251
- const e1 = [0, 1, 2].map((k) => at(t + 1, k) - at(t, k));
7252
- const e2 = [0, 1, 2].map((k) => at(t + 2, k) - at(t, k));
7253
- const face = [e1[1] * e2[2] - e1[2] * e2[1], e1[2] * e2[0] - e1[0] * e2[2], e1[0] * e2[1] - e1[1] * e2[0]];
7254
- const n = [0, 1, 2].map((k) => at(t, k + 3) + at(t + 1, k + 3) + at(t + 2, k + 3));
7255
- if (face[0] * n[0] + face[1] * n[1] + face[2] * n[2] < 0) {
7256
- [g.indices[t + 1], g.indices[t + 2]] = [g.indices[t + 2], g.indices[t + 1]];
7915
+ const end = at + (capRings + 1) * (sides + 1);
7916
+ paintFace(g, at, end, TAU2, capColor);
7917
+ at = end;
7257
7918
  }
7258
7919
  }
7259
7920
  return g;
@@ -7513,42 +8174,6 @@ function deformGeometry(geometry, deform) {
7513
8174
  }
7514
8175
  return smoothNormals({ vertices, indices: geometry.indices.slice() });
7515
8176
  }
7516
- // src/assets/surface.ts
7517
- function parametricSurface(sample, options = {}) {
7518
- const nu = options.uSegments ?? 32, nv = options.vSegments ?? 16;
7519
- if (![nu, nv].every((n) => Number.isInteger(n) && n >= 1 && n <= 1024) || (nu + 1) * (nv + 1) > 1e6)
7520
- throw new Error("parametricSurface: invalid segments or surface exceeds one million vertices");
7521
- const vertices = new Float32Array((nu + 1) * (nv + 1) * 11);
7522
- for (let y = 0;y <= nv; y++)
7523
- for (let x = 0;x <= nu; x++) {
7524
- const u = x / nu, v = y / nv;
7525
- const p = sample(options.wrapU && x === nu ? 0 : u, options.wrapV && y === nv ? 0 : v);
7526
- if (p.some((c) => !Number.isFinite(c)))
7527
- throw new Error("parametricSurface: sample returned a non-finite position");
7528
- vertices.set([p[0], p[1], p[2], 0, 1, 0, 1, 1, 1, u, v], (y * (nu + 1) + x) * 11);
7529
- }
7530
- const indices = [];
7531
- const triangle = (a, b, c) => {
7532
- const v = vertices;
7533
- const oa = a * 11, ob = b * 11, oc = c * 11;
7534
- const x1 = v[ob] - v[oa], y1 = v[ob + 1] - v[oa + 1], z1 = v[ob + 2] - v[oa + 2];
7535
- const x2 = v[oc] - v[oa], y2 = v[oc + 1] - v[oa + 1], z2 = v[oc + 2] - v[oa + 2];
7536
- if (Math.hypot(y1 * z2 - z1 * y2, z1 * x2 - x1 * z2, x1 * y2 - y1 * x2) > 0)
7537
- indices.push(a, b, c);
7538
- };
7539
- for (let y = 0;y < nv; y++)
7540
- for (let x = 0;x < nu; x++) {
7541
- const a = y * (nu + 1) + x, b = a + nu + 1;
7542
- triangle(a, a + 1, b);
7543
- triangle(a + 1, b + 1, b);
7544
- }
7545
- const geometry = smoothNormals({
7546
- vertices,
7547
- indices: new Uint32Array(indices)
7548
- });
7549
- return options.color ? recolor(geometry, typeof options.color === "function" ? options.color : () => options.color) : geometry;
7550
- }
7551
-
7552
8177
  // src/assets/loft.ts
7553
8178
  function loft(sections, options = {}) {
7554
8179
  if (sections.length < 2 || sections.some((s) => ![s.z, s.bottom, s.top, s.width, s.topWidth ?? s.width, s.square ?? 4].every(Number.isFinite) || s.width < 0 || (s.topWidth ?? s.width) < 0 || s.top <= s.bottom || (s.square ?? 4) < 1))
@@ -7560,53 +8185,23 @@ function loft(sections, options = {}) {
7560
8185
  if (!Number.isInteger(n) || n < 6 || n > 512 || !Number.isInteger(steps) || steps < 1 || steps > 64)
7561
8186
  throw new Error("loft: invalid around/smoothness");
7562
8187
  const values = ordered.map((s) => [s.z, s.bottom, s.top, s.width, s.topWidth ?? s.width, s.square ?? 4]);
7563
- const at = (i) => values[Math.max(0, Math.min(values.length - 1, i))];
7564
- const row = (v) => {
7565
- const cursor = v * (values.length - 1), i = Math.min(values.length - 2, Math.floor(cursor)), t = cursor - i;
7566
- const a = at(i - 1), b = at(i), c = at(i + 1), d = at(i + 2);
7567
- const f = b.map((_, k) => 0.5 * (2 * b[k] + (-a[k] + c[k]) * t + (2 * a[k] - 5 * b[k] + 4 * c[k] - d[k]) * t * t + (-a[k] + 3 * b[k] - 3 * c[k] + d[k]) * t * t * t));
7568
- f[2] = Math.max(f[1] + 0.000001, f[2]);
7569
- f[3] = Math.max(0, f[3]);
7570
- f[4] = Math.max(0, f[4]);
7571
- f[5] = Math.max(1, f[5]);
7572
- return f;
7573
- };
7574
- const rowCount = (sections.length - 1) * steps;
7575
- const rows = Array.from({ length: rowCount + 1 }, (_, i) => row(i / rowCount));
7576
- const point = (u, v) => {
7577
- const [z, bottom, top, width, topWidth, square] = rows[Math.round(v * rowCount)], angle = -Math.PI / 2 + u * Math.PI * 2;
7578
- const c = Math.cos(angle), s = Math.sin(angle), exponent = 2 / square;
7579
- const x = Math.sign(c) * Math.abs(c) ** exponent, y = Math.sign(s) * Math.abs(s) ** exponent;
7580
- return [x * (y > 0 ? width + (topWidth - width) * y : width), (bottom + top) / 2 + y * (top - bottom) / 2, z];
7581
- };
7582
- const side = parametricSurface(point, {
7583
- uSegments: n,
7584
- vSegments: rowCount,
7585
- wrapU: true
8188
+ const color = options.color;
8189
+ const g = nativeSweep({
8190
+ kind: "loft",
8191
+ sections: values,
8192
+ around: n,
8193
+ smoothness: steps,
8194
+ color: typeof color === "function" || color instanceof FieldFunction || !color ? undefined : [color[0], color[1], color[2]],
8195
+ colorField: color instanceof FieldFunction ? color : undefined
7586
8196
  });
7587
- const parts = [{ geometry: side }];
7588
- for (const [v, facing] of [
7589
- [0, -1],
7590
- [1, 1]
7591
- ]) {
7592
- const section = rows[v * rowCount], cy = (section[1] + section[2]) / 2;
7593
- const vertices = new Float32Array((n + 1) * 11), indices = [];
7594
- vertices.set([0, cy, section[0], 0, 0, facing, 1, 1, 1, 0.5, 0.5]);
7595
- for (let j = 0;j < n; j++) {
7596
- const p = point(j / n, v), width = Math.max(section[3], section[4], 0.000001);
7597
- vertices.set([p[0], p[1], p[2], 0, 0, facing, 1, 1, 1, 0.5 + p[0] / (2 * width), (p[1] - section[1]) / (section[2] - section[1])], (j + 1) * 11);
7598
- const a = j + 1, b = (j + 1) % n + 1;
7599
- if (facing === 1)
7600
- indices.push(0, a, b);
7601
- else
7602
- indices.push(0, b, a);
7603
- }
7604
- parts.push({ geometry: { vertices, indices: new Uint32Array(indices) } });
7605
- }
7606
- return paint2(merge(parts), options.color);
8197
+ return typeof color === "function" ? paint2(g, color) : g;
7607
8198
  }
7608
8199
  function paint2(geometry, color) {
7609
- return color ? recolor(geometry, typeof color === "function" ? ({ position, normal }) => color(position, normal) : () => color) : geometry;
8200
+ if (!color)
8201
+ return geometry;
8202
+ if (color instanceof FieldFunction)
8203
+ return recolor(geometry, color);
8204
+ return recolor(geometry, typeof color === "function" ? ({ position, normal }) => color(position, normal) : () => color);
7610
8205
  }
7611
8206
  function block(w, h, d, square = 6, color) {
7612
8207
  return loft([
@@ -7643,6 +8238,41 @@ function smoothCurve(points) {
7643
8238
  return (2 * t * t * t - 3 * t * t + 1) * y0 + (t * t * t - 2 * t * t + t) * h * slope(i) + (-2 * t * t * t + 3 * t * t) * y1 + (t * t * t - t * t) * h * slope(i + 1);
7644
8239
  };
7645
8240
  }
8241
+ // src/assets/surface.ts
8242
+ function parametricSurface(sample, options = {}) {
8243
+ const nu = options.uSegments ?? 32, nv = options.vSegments ?? 16;
8244
+ if (![nu, nv].every((n) => Number.isInteger(n) && n >= 1 && n <= 1024) || (nu + 1) * (nv + 1) > 1e6)
8245
+ throw new Error("parametricSurface: invalid segments or surface exceeds one million vertices");
8246
+ const vertices = new Float32Array((nu + 1) * (nv + 1) * 11);
8247
+ for (let y = 0;y <= nv; y++)
8248
+ for (let x = 0;x <= nu; x++) {
8249
+ const u = x / nu, v = y / nv;
8250
+ const p = sample(options.wrapU && x === nu ? 0 : u, options.wrapV && y === nv ? 0 : v);
8251
+ if (p.some((c) => !Number.isFinite(c)))
8252
+ throw new Error("parametricSurface: sample returned a non-finite position");
8253
+ vertices.set([p[0], p[1], p[2], 0, 1, 0, 1, 1, 1, u, v], (y * (nu + 1) + x) * 11);
8254
+ }
8255
+ const indices = [];
8256
+ const triangle = (a, b, c) => {
8257
+ const v = vertices;
8258
+ const oa = a * 11, ob = b * 11, oc = c * 11;
8259
+ const x1 = v[ob] - v[oa], y1 = v[ob + 1] - v[oa + 1], z1 = v[ob + 2] - v[oa + 2];
8260
+ const x2 = v[oc] - v[oa], y2 = v[oc + 1] - v[oa + 1], z2 = v[oc + 2] - v[oa + 2];
8261
+ if (Math.hypot(y1 * z2 - z1 * y2, z1 * x2 - x1 * z2, x1 * y2 - y1 * x2) > 0)
8262
+ indices.push(a, b, c);
8263
+ };
8264
+ for (let y = 0;y < nv; y++)
8265
+ for (let x = 0;x < nu; x++) {
8266
+ const a = y * (nu + 1) + x, b = a + nu + 1;
8267
+ triangle(a, a + 1, b);
8268
+ triangle(a + 1, b + 1, b);
8269
+ }
8270
+ const geometry = smoothNormals({
8271
+ vertices,
8272
+ indices: new Uint32Array(indices)
8273
+ });
8274
+ return options.color ? recolor(geometry, typeof options.color === "function" ? options.color : () => options.color) : geometry;
8275
+ }
7646
8276
  // src/media/audio.ts
7647
8277
  var loader4 = new AssetLoader("audio");
7648
8278
  function loadSound(source) {
@@ -7693,6 +8323,17 @@ var audio = {
7693
8323
  if (voice)
7694
8324
  NativeNayanEngine_web_default?.audioStop(voice);
7695
8325
  },
8326
+ release(sound) {
8327
+ if (sound != null)
8328
+ NativeNayanEngine_web_default?.audioRelease?.(sound);
8329
+ },
8330
+ duration(sound) {
8331
+ return NativeNayanEngine_web_default?.audioDuration?.(sound) ?? 0;
8332
+ },
8333
+ levels(sound, perSecond = 30) {
8334
+ const buffer = NativeNayanEngine_web_default?.audioLevels?.(sound, perSecond);
8335
+ return buffer ? new Float32Array(buffer) : new Float32Array(0);
8336
+ },
7696
8337
  get muted() {
7697
8338
  return muted;
7698
8339
  },
@@ -7717,6 +8358,108 @@ var audio = {
7717
8358
  return NativeNayanEngine_web_default?.audioIsRunning() ?? false;
7718
8359
  }
7719
8360
  };
8361
+ // src/media/speechPlatform.web.ts
8362
+ var speechPlatform = {
8363
+ get supported() {
8364
+ return typeof window !== "undefined" && "speechSynthesis" in window && "SpeechSynthesisUtterance" in window;
8365
+ },
8366
+ async request(command) {
8367
+ await prepareEngine();
8368
+ return speech_request(command);
8369
+ }
8370
+ };
8371
+
8372
+ // src/media/speech.ts
8373
+ async function request(command) {
8374
+ if (!speechPlatform.supported)
8375
+ throw new Error("Speech is unavailable on this platform or engine build");
8376
+ const result = JSON.parse(await speechPlatform.request(JSON.stringify(command)));
8377
+ if (result.error)
8378
+ throw new Error(result.error);
8379
+ return result.value;
8380
+ }
8381
+ var speech = {
8382
+ get supported() {
8383
+ return speechPlatform.supported;
8384
+ },
8385
+ async speak(text, options = {}) {
8386
+ if (typeof text !== "string")
8387
+ throw new TypeError("Speech text must be a string");
8388
+ for (const [key, min, max] of [
8389
+ ["rate", 0.1, 2],
8390
+ ["pitch", 0.5, 2],
8391
+ ["volume", 0, 1]
8392
+ ]) {
8393
+ const value = options[key];
8394
+ if (value !== undefined && (!Number.isFinite(value) || value < min || value > max)) {
8395
+ throw new RangeError(`Speech ${key} must be between ${min} and ${max}`);
8396
+ }
8397
+ }
8398
+ for (const key of ["language", "voice"]) {
8399
+ if (options[key] !== undefined && typeof options[key] !== "string")
8400
+ throw new TypeError(`Speech ${key} must be a string`);
8401
+ }
8402
+ if (!text.trim())
8403
+ return;
8404
+ const { language, voice, rate, pitch, volume } = options;
8405
+ await request({ action: "speak", text, language, voice, rate, pitch, volume });
8406
+ },
8407
+ async stop() {
8408
+ await request({ action: "stop" });
8409
+ },
8410
+ async pause() {
8411
+ await request({ action: "pause" });
8412
+ },
8413
+ async resume() {
8414
+ await request({ action: "resume" });
8415
+ },
8416
+ isSpeakingAsync() {
8417
+ return request({ action: "isSpeaking" });
8418
+ },
8419
+ getAvailableVoicesAsync() {
8420
+ return request({ action: "voices" });
8421
+ }
8422
+ };
8423
+ // src/media/microphone.ts
8424
+ var STATES = { 0: "off", 1: "starting", 2: "listening", [-1]: "failed" };
8425
+ var microphone = {
8426
+ get supported() {
8427
+ return typeof NativeNayanEngine_web_default?.micStart === "function";
8428
+ },
8429
+ get permission() {
8430
+ const value = microphone.supported ? NativeNayanEngine_web_default.micPermission() : -1;
8431
+ return value > 0 ? "granted" : value < 0 ? "denied" : "undetermined";
8432
+ },
8433
+ requestPermission() {
8434
+ return microphone.supported ? NativeNayanEngine_web_default.micRequestPermission() : Promise.resolve(false);
8435
+ },
8436
+ start({ maxSeconds = 10 } = {}) {
8437
+ return microphone.supported && NativeNayanEngine_web_default.micStart(maxSeconds);
8438
+ },
8439
+ stop() {
8440
+ if (microphone.supported)
8441
+ NativeNayanEngine_web_default.micStop();
8442
+ },
8443
+ get state() {
8444
+ return microphone.supported ? STATES[NativeNayanEngine_web_default.micState()] ?? "off" : "off";
8445
+ },
8446
+ get level() {
8447
+ return microphone.supported ? NativeNayanEngine_web_default.micLevel() : 0;
8448
+ },
8449
+ get held() {
8450
+ return microphone.supported ? NativeNayanEngine_web_default.micHeld() : 0;
8451
+ },
8452
+ take(seconds = Infinity, { skipEnd = 0, levelsPerSecond = 30 } = {}) {
8453
+ if (!microphone.supported)
8454
+ return null;
8455
+ const id = NativeNayanEngine_web_default.micTake(Number.isFinite(seconds) ? seconds : 3600, skipEnd);
8456
+ if (id < 0)
8457
+ return null;
8458
+ const sound = id;
8459
+ const buffer = NativeNayanEngine_web_default.audioLevels(sound, levelsPerSecond);
8460
+ return { sound, duration: NativeNayanEngine_web_default.audioDuration(sound), levels: new Float32Array(buffer), levelsPerSecond };
8461
+ }
8462
+ };
7720
8463
  // src/media/haptics.ts
7721
8464
  var PATTERNS = {
7722
8465
  success: [
@@ -7862,7 +8605,7 @@ class DrawingDocument {
7862
8605
  boundTexture = null;
7863
8606
  constructor(width, height, pixelRatio = 2) {
7864
8607
  if (!NativeNayanEngine_web_default || typeof NativeNayanEngine_web_default.drawingCreate !== "function") {
7865
- throw new Error("Drawing requires a native app rebuild with the updated Nayan engine.");
8608
+ throw new Error("Drawing requires a native app rebuild with the updated Nayan Game Engine.");
7866
8609
  }
7867
8610
  NativeNayanEngine_web_default.renderWarmUp?.();
7868
8611
  const id = NativeNayanEngine_web_default.drawingCreate(width, height, pixelRatio);
@@ -8190,16 +8933,21 @@ var DrawingCanvas = memo(function DrawingCanvas({
8190
8933
  export {
8191
8934
  DrawingCanvas,
8192
8935
  DrawingDocument,
8936
+ FieldFunction,
8937
+ FieldGraph,
8938
+ FieldRef,
8193
8939
  GameView,
8194
8940
  Joystick,
8195
8941
  MAX_MESHES,
8196
8942
  Path,
8197
8943
  ProceduralCache,
8944
+ ProceduralWorld,
8198
8945
  Scoreboard,
8199
8946
  Solid,
8200
8947
  World,
8201
8948
  afterNextFrame,
8202
8949
  audio,
8950
+ bakeModel,
8203
8951
  block,
8204
8952
  clearAssetCache,
8205
8953
  createGeometryLods,
@@ -8210,11 +8958,17 @@ export {
8210
8958
  effects,
8211
8959
  ellipsoid,
8212
8960
  extrude,
8961
+ faceColor,
8213
8962
  frontLight,
8963
+ generateMesh,
8964
+ generateWorld,
8965
+ generateWorldAsync,
8214
8966
  getGeometryStats,
8215
8967
  getMeshIdLimit,
8216
8968
  haptics,
8969
+ isBakeKept,
8217
8970
  isEngineAvailable,
8971
+ isWorldKept,
8218
8972
  lathe,
8219
8973
  loadFont,
8220
8974
  loadModel,
@@ -8222,14 +8976,19 @@ export {
8222
8976
  loft,
8223
8977
  measureText,
8224
8978
  merge,
8979
+ microphone,
8225
8980
  optimizeGeometry,
8226
8981
  parametricSurface,
8227
8982
  quat,
8228
8983
  recolor,
8984
+ sectionColor,
8229
8985
  shapeGeometry,
8230
8986
  smoothCurve,
8231
8987
  smoothNormals,
8232
8988
  solidGeometry,
8989
+ speech,
8990
+ surfaceColor,
8991
+ surfaceDisplacement,
8233
8992
  toColor,
8234
8993
  tube,
8235
8994
  useJoystick,
@@ -8237,4 +8996,4 @@ export {
8237
8996
  warmUpEngine
8238
8997
  };
8239
8998
 
8240
- //# debugId=1367AF6CC344167A64756E2164756E21
8999
+ //# debugId=089DF8C707AEEB2364756E2164756E21