@nayan-ui/engine 1.0.0-beta.3 → 1.0.0-beta.5

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 (71) hide show
  1. package/README.md +229 -113
  2. package/lib/module/assets/AssetLoader.js +1 -1
  3. package/lib/module/assets/ProceduralCache.js +31 -4
  4. package/lib/module/assets/font.js +10 -7
  5. package/lib/module/assets/model.js +41 -19
  6. package/lib/module/assets/storage.shared.js +2 -0
  7. package/lib/module/assets/storage.web.js +1 -0
  8. package/lib/module/assets/texture.js +14 -11
  9. package/lib/module/index.js +4 -1
  10. package/lib/module/media/audio.js +6 -3
  11. package/lib/module/native/BrowserEngine.js +17 -0
  12. package/lib/module/render/GameView.js +86 -18
  13. package/lib/module/render/light.js +24 -0
  14. package/lib/module/render/quality.js +12 -5
  15. package/lib/module/render/useWorld.js +72 -0
  16. package/lib/module/world/Scoreboard.js +21 -3
  17. package/lib/module/world/TransformBatch.js +5 -3
  18. package/lib/module/world/World.js +27 -13
  19. package/lib/module/world/color.js +80 -0
  20. package/lib/module/world/desc.js +6 -5
  21. package/lib/module/world/effects.js +20 -8
  22. package/lib/typescript/src/assets/AssetLoader.d.ts +1 -1
  23. package/lib/typescript/src/assets/ProceduralCache.d.ts +12 -0
  24. package/lib/typescript/src/assets/model.d.ts +10 -0
  25. package/lib/typescript/src/index.d.ts +4 -1
  26. package/lib/typescript/src/native/EngineBridge.d.ts +6 -2
  27. package/lib/typescript/src/native/spec/NativeNayanEngine.d.ts +29 -0
  28. package/lib/typescript/src/render/GameView.d.ts +19 -7
  29. package/lib/typescript/src/render/light.d.ts +12 -0
  30. package/lib/typescript/src/render/quality.d.ts +4 -2
  31. package/lib/typescript/src/render/useWorld.d.ts +20 -0
  32. package/lib/typescript/src/types.d.ts +13 -4
  33. package/lib/typescript/src/world/Scoreboard.d.ts +12 -2
  34. package/lib/typescript/src/world/TransformBatch.d.ts +2 -2
  35. package/lib/typescript/src/world/World.d.ts +10 -2
  36. package/lib/typescript/src/world/color.d.ts +18 -0
  37. package/lib/typescript/src/world/effects.d.ts +13 -12
  38. package/lib/typescript/src/world/types.d.ts +11 -6
  39. package/lib/web/engine_core_bg.wasm +0 -0
  40. package/lib/web/index.js +521 -255
  41. package/lib/web/types/src/assets/AssetLoader.d.ts +1 -1
  42. package/lib/web/types/src/assets/ProceduralCache.d.ts +12 -0
  43. package/lib/web/types/src/assets/model.d.ts +10 -0
  44. package/lib/web/types/src/index.d.ts +4 -1
  45. package/lib/web/types/src/native/EngineBridge.d.ts +6 -2
  46. package/lib/web/types/src/native/spec/NativeNayanEngine.d.ts +29 -0
  47. package/lib/web/types/src/render/GameView.d.ts +19 -7
  48. package/lib/web/types/src/render/light.d.ts +11 -0
  49. package/lib/web/types/src/render/quality.d.ts +4 -2
  50. package/lib/web/types/src/render/useWorld.d.ts +19 -0
  51. package/lib/web/types/src/types.d.ts +13 -4
  52. package/lib/web/types/src/world/Scoreboard.d.ts +12 -2
  53. package/lib/web/types/src/world/TransformBatch.d.ts +2 -2
  54. package/lib/web/types/src/world/World.d.ts +10 -2
  55. package/lib/web/types/src/world/color.d.ts +17 -0
  56. package/lib/web/types/src/world/effects.d.ts +13 -12
  57. package/lib/web/types/src/world/types.d.ts +11 -6
  58. package/native/android/src/main/java/com/nayanui/engine/EngineSurfaceView.java +87 -26
  59. package/native/core/build/EngineCore.xcframework/ios-arm64/libengine_core.a +0 -0
  60. package/native/core/build/EngineCore.xcframework/ios-arm64-simulator/libengine_core.a +0 -0
  61. package/native/core/build/android/arm64-v8a/libengine_core.a +0 -0
  62. package/native/core/build/android/armeabi-v7a/libengine_core.a +0 -0
  63. package/native/core/build/android/x86/libengine_core.a +0 -0
  64. package/native/core/build/android/x86_64/libengine_core.a +0 -0
  65. package/native/core/include/engine_core.h +9 -0
  66. package/native/cpp/NayanEngineModule.cpp +171 -0
  67. package/native/cpp/NayanEngineModule.h +30 -0
  68. package/native/cpp/generated/NayanEngineSpecJSI.h +105 -0
  69. package/native/ios/NayanEngineSurface.mm +30 -1
  70. package/package.json +2 -2
  71. package/src/native/spec/NativeNayanEngine.ts +39 -0
@@ -70,12 +70,14 @@ protected:
70
70
  methodMap_["shapePrepare"] = MethodMetadata {.argCount = 3, .invoker = __shapePrepare};
71
71
  methodMap_["solidPrepare"] = MethodMetadata {.argCount = 2, .invoker = __solidPrepare};
72
72
  methodMap_["modelLoad"] = MethodMetadata {.argCount = 3, .invoker = __modelLoad};
73
+ methodMap_["modelLoadAsync"] = MethodMetadata {.argCount = 3, .invoker = __modelLoadAsync};
73
74
  methodMap_["modelVertices"] = MethodMetadata {.argCount = 1, .invoker = __modelVertices};
74
75
  methodMap_["modelIndices"] = MethodMetadata {.argCount = 1, .invoker = __modelIndices};
75
76
  methodMap_["modelSize"] = MethodMetadata {.argCount = 1, .invoker = __modelSize};
76
77
  methodMap_["modelTexture"] = MethodMetadata {.argCount = 1, .invoker = __modelTexture};
77
78
  methodMap_["meshAlias"] = MethodMetadata {.argCount = 1, .invoker = __meshAlias};
78
79
  methodMap_["meshCreate"] = MethodMetadata {.argCount = 2, .invoker = __meshCreate};
80
+ methodMap_["renderModel"] = MethodMetadata {.argCount = 3, .invoker = __renderModel};
79
81
  methodMap_["pathExtrude"] = MethodMetadata {.argCount = 4, .invoker = __pathExtrude};
80
82
  methodMap_["geometrySmoothNormals"] = MethodMetadata {.argCount = 2, .invoker = __geometrySmoothNormals};
81
83
  methodMap_["geometrySolid"] = MethodMetadata {.argCount = 1, .invoker = __geometrySolid};
@@ -83,9 +85,11 @@ protected:
83
85
  methodMap_["geometryFaceOutward"] = MethodMetadata {.argCount = 2, .invoker = __geometryFaceOutward};
84
86
  methodMap_["geometryPlace"] = MethodMetadata {.argCount = 8, .invoker = __geometryPlace};
85
87
  methodMap_["textureLoad"] = MethodMetadata {.argCount = 1, .invoker = __textureLoad};
88
+ methodMap_["textureLoadAsync"] = MethodMetadata {.argCount = 1, .invoker = __textureLoadAsync};
86
89
  methodMap_["texturePixels"] = MethodMetadata {.argCount = 1, .invoker = __texturePixels};
87
90
  methodMap_["textureSize"] = MethodMetadata {.argCount = 1, .invoker = __textureSize};
88
91
  methodMap_["fontLoad"] = MethodMetadata {.argCount = 4, .invoker = __fontLoad};
92
+ methodMap_["fontLoadAsync"] = MethodMetadata {.argCount = 4, .invoker = __fontLoadAsync};
89
93
  methodMap_["fontGlyphs"] = MethodMetadata {.argCount = 1, .invoker = __fontGlyphs};
90
94
  methodMap_["loadError"] = MethodMetadata {.argCount = 0, .invoker = __loadError};
91
95
  methodMap_["generateBuiltinMeshes"] = MethodMetadata {.argCount = 0, .invoker = __generateBuiltinMeshes};
@@ -94,6 +98,11 @@ protected:
94
98
  methodMap_["renderMeshIndices"] = MethodMetadata {.argCount = 1, .invoker = __renderMeshIndices};
95
99
  methodMap_["renderMesh"] = MethodMetadata {.argCount = 5, .invoker = __renderMesh};
96
100
  methodMap_["renderFrame"] = MethodMetadata {.argCount = 9, .invoker = __renderFrame};
101
+ methodMap_["publishFrame"] = MethodMetadata {.argCount = 9, .invoker = __publishFrame};
102
+ methodMap_["renderStats"] = MethodMetadata {.argCount = 1, .invoker = __renderStats};
103
+ methodMap_["hasFrameClock"] = MethodMetadata {.argCount = 0, .invoker = __hasFrameClock};
104
+ methodMap_["startFrames"] = MethodMetadata {.argCount = 2, .invoker = __startFrames};
105
+ methodMap_["stopFrames"] = MethodMetadata {.argCount = 1, .invoker = __stopFrames};
97
106
  methodMap_["renderOverlay"] = MethodMetadata {.argCount = 8, .invoker = __renderOverlay};
98
107
  methodMap_["renderError"] = MethodMetadata {.argCount = 0, .invoker = __renderError};
99
108
  methodMap_["renderBackend"] = MethodMetadata {.argCount = 0, .invoker = __renderBackend};
@@ -101,6 +110,7 @@ protected:
101
110
  methodMap_["drawingCommand"] = MethodMetadata {.argCount = 2, .invoker = __drawingCommand};
102
111
  methodMap_["drawingDestroy"] = MethodMetadata {.argCount = 1, .invoker = __drawingDestroy};
103
112
  methodMap_["audioLoad"] = MethodMetadata {.argCount = 1, .invoker = __audioLoad};
113
+ methodMap_["audioLoadAsync"] = MethodMetadata {.argCount = 1, .invoker = __audioLoadAsync};
104
114
  methodMap_["audioPlay"] = MethodMetadata {.argCount = 5, .invoker = __audioPlay};
105
115
  methodMap_["audioStop"] = MethodMetadata {.argCount = 1, .invoker = __audioStop};
106
116
  methodMap_["audioSetVolume"] = MethodMetadata {.argCount = 1, .invoker = __audioSetVolume};
@@ -573,6 +583,16 @@ private:
573
583
  count <= 2 ? throw jsi::JSError(rt, "Expected argument in position 2 to be passed") : args[2].asNumber());
574
584
  }
575
585
 
586
+ static jsi::Value __modelLoadAsync(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
587
+ static_assert(
588
+ bridging::getParameterCount(&T::modelLoadAsync) == 4,
589
+ "Expected modelLoadAsync(...) to have 4 parameters");
590
+ return bridging::callFromJs<jsi::Value>(rt, &T::modelLoadAsync, static_cast<NativeNayanEngineCxxSpec*>(&turboModule)->jsInvoker_, static_cast<T*>(&turboModule),
591
+ count <= 0 ? throw jsi::JSError(rt, "Expected argument in position 0 to be passed") : args[0].asObject(rt),
592
+ count <= 1 ? throw jsi::JSError(rt, "Expected argument in position 1 to be passed") : args[1].asBool(),
593
+ count <= 2 ? throw jsi::JSError(rt, "Expected argument in position 2 to be passed") : args[2].asNumber());
594
+ }
595
+
576
596
  static jsi::Value __modelVertices(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
577
597
  static_assert(
578
598
  bridging::getParameterCount(&T::modelVertices) == 2,
@@ -622,6 +642,16 @@ private:
622
642
  count <= 1 ? throw jsi::JSError(rt, "Expected argument in position 1 to be passed") : args[1].asObject(rt));
623
643
  }
624
644
 
645
+ static jsi::Value __renderModel(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
646
+ static_assert(
647
+ bridging::getParameterCount(&T::renderModel) == 4,
648
+ "Expected renderModel(...) to have 4 parameters");
649
+ return bridging::callFromJs<bool>(rt, &T::renderModel, static_cast<NativeNayanEngineCxxSpec*>(&turboModule)->jsInvoker_, static_cast<T*>(&turboModule),
650
+ count <= 0 ? throw jsi::JSError(rt, "Expected argument in position 0 to be passed") : args[0].asNumber(),
651
+ count <= 1 ? throw jsi::JSError(rt, "Expected argument in position 1 to be passed") : args[1].asNumber(),
652
+ count <= 2 ? throw jsi::JSError(rt, "Expected argument in position 2 to be passed") : args[2].asNumber());
653
+ }
654
+
625
655
  static jsi::Value __pathExtrude(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
626
656
  static_assert(
627
657
  bridging::getParameterCount(&T::pathExtrude) == 5,
@@ -694,6 +724,14 @@ private:
694
724
  count <= 0 ? throw jsi::JSError(rt, "Expected argument in position 0 to be passed") : args[0].asObject(rt));
695
725
  }
696
726
 
727
+ static jsi::Value __textureLoadAsync(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
728
+ static_assert(
729
+ bridging::getParameterCount(&T::textureLoadAsync) == 2,
730
+ "Expected textureLoadAsync(...) to have 2 parameters");
731
+ return bridging::callFromJs<jsi::Value>(rt, &T::textureLoadAsync, static_cast<NativeNayanEngineCxxSpec*>(&turboModule)->jsInvoker_, static_cast<T*>(&turboModule),
732
+ count <= 0 ? throw jsi::JSError(rt, "Expected argument in position 0 to be passed") : args[0].asObject(rt));
733
+ }
734
+
697
735
  static jsi::Value __texturePixels(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
698
736
  static_assert(
699
737
  bridging::getParameterCount(&T::texturePixels) == 2,
@@ -721,6 +759,17 @@ private:
721
759
  count <= 3 ? throw jsi::JSError(rt, "Expected argument in position 3 to be passed") : args[3].asNumber());
722
760
  }
723
761
 
762
+ static jsi::Value __fontLoadAsync(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
763
+ static_assert(
764
+ bridging::getParameterCount(&T::fontLoadAsync) == 5,
765
+ "Expected fontLoadAsync(...) to have 5 parameters");
766
+ return bridging::callFromJs<jsi::Value>(rt, &T::fontLoadAsync, static_cast<NativeNayanEngineCxxSpec*>(&turboModule)->jsInvoker_, static_cast<T*>(&turboModule),
767
+ count <= 0 ? throw jsi::JSError(rt, "Expected argument in position 0 to be passed") : args[0].asObject(rt),
768
+ count <= 1 ? throw jsi::JSError(rt, "Expected argument in position 1 to be passed") : args[1].asNumber(),
769
+ count <= 2 ? throw jsi::JSError(rt, "Expected argument in position 2 to be passed") : args[2].asString(rt),
770
+ count <= 3 ? throw jsi::JSError(rt, "Expected argument in position 3 to be passed") : args[3].asNumber());
771
+ }
772
+
724
773
  static jsi::Value __fontGlyphs(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
725
774
  static_assert(
726
775
  bridging::getParameterCount(&T::fontGlyphs) == 2,
@@ -794,6 +843,54 @@ private:
794
843
  count <= 8 ? throw jsi::JSError(rt, "Expected argument in position 8 to be passed") : args[8].asNumber());
795
844
  }
796
845
 
846
+ static jsi::Value __publishFrame(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
847
+ static_assert(
848
+ bridging::getParameterCount(&T::publishFrame) == 10,
849
+ "Expected publishFrame(...) to have 10 parameters");
850
+ return bridging::callFromJs<double>(rt, &T::publishFrame, static_cast<NativeNayanEngineCxxSpec*>(&turboModule)->jsInvoker_, static_cast<T*>(&turboModule),
851
+ count <= 0 ? throw jsi::JSError(rt, "Expected argument in position 0 to be passed") : args[0].asNumber(),
852
+ count <= 1 ? throw jsi::JSError(rt, "Expected argument in position 1 to be passed") : args[1].asObject(rt),
853
+ count <= 2 ? throw jsi::JSError(rt, "Expected argument in position 2 to be passed") : args[2].asObject(rt),
854
+ count <= 3 ? throw jsi::JSError(rt, "Expected argument in position 3 to be passed") : args[3].asObject(rt),
855
+ count <= 4 ? throw jsi::JSError(rt, "Expected argument in position 4 to be passed") : args[4].asObject(rt),
856
+ count <= 5 ? throw jsi::JSError(rt, "Expected argument in position 5 to be passed") : args[5].asObject(rt),
857
+ count <= 6 ? throw jsi::JSError(rt, "Expected argument in position 6 to be passed") : args[6].asNumber(),
858
+ count <= 7 ? throw jsi::JSError(rt, "Expected argument in position 7 to be passed") : args[7].asObject(rt),
859
+ count <= 8 ? throw jsi::JSError(rt, "Expected argument in position 8 to be passed") : args[8].asNumber());
860
+ }
861
+
862
+ static jsi::Value __renderStats(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
863
+ static_assert(
864
+ bridging::getParameterCount(&T::renderStats) == 2,
865
+ "Expected renderStats(...) to have 2 parameters");
866
+ return bridging::callFromJs<jsi::Array>(rt, &T::renderStats, static_cast<NativeNayanEngineCxxSpec*>(&turboModule)->jsInvoker_, static_cast<T*>(&turboModule),
867
+ count <= 0 ? throw jsi::JSError(rt, "Expected argument in position 0 to be passed") : args[0].asNumber());
868
+ }
869
+
870
+ static jsi::Value __hasFrameClock(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* /*args*/, size_t /*count*/) {
871
+ static_assert(
872
+ bridging::getParameterCount(&T::hasFrameClock) == 1,
873
+ "Expected hasFrameClock(...) to have 1 parameters");
874
+ return bridging::callFromJs<bool>(rt, &T::hasFrameClock, static_cast<NativeNayanEngineCxxSpec*>(&turboModule)->jsInvoker_, static_cast<T*>(&turboModule));
875
+ }
876
+
877
+ static jsi::Value __startFrames(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
878
+ static_assert(
879
+ bridging::getParameterCount(&T::startFrames) == 3,
880
+ "Expected startFrames(...) to have 3 parameters");
881
+ bridging::callFromJs<void>(rt, &T::startFrames, static_cast<NativeNayanEngineCxxSpec*>(&turboModule)->jsInvoker_, static_cast<T*>(&turboModule),
882
+ count <= 0 ? throw jsi::JSError(rt, "Expected argument in position 0 to be passed") : args[0].asNumber(),
883
+ count <= 1 ? throw jsi::JSError(rt, "Expected argument in position 1 to be passed") : args[1].asObject(rt).asFunction(rt));return jsi::Value::undefined();
884
+ }
885
+
886
+ static jsi::Value __stopFrames(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
887
+ static_assert(
888
+ bridging::getParameterCount(&T::stopFrames) == 2,
889
+ "Expected stopFrames(...) to have 2 parameters");
890
+ bridging::callFromJs<void>(rt, &T::stopFrames, static_cast<NativeNayanEngineCxxSpec*>(&turboModule)->jsInvoker_, static_cast<T*>(&turboModule),
891
+ count <= 0 ? throw jsi::JSError(rt, "Expected argument in position 0 to be passed") : args[0].asNumber());return jsi::Value::undefined();
892
+ }
893
+
797
894
  static jsi::Value __renderOverlay(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
798
895
  static_assert(
799
896
  bridging::getParameterCount(&T::renderOverlay) == 9,
@@ -858,6 +955,14 @@ private:
858
955
  count <= 0 ? throw jsi::JSError(rt, "Expected argument in position 0 to be passed") : args[0].asObject(rt));
859
956
  }
860
957
 
958
+ static jsi::Value __audioLoadAsync(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
959
+ static_assert(
960
+ bridging::getParameterCount(&T::audioLoadAsync) == 2,
961
+ "Expected audioLoadAsync(...) to have 2 parameters");
962
+ return bridging::callFromJs<jsi::Value>(rt, &T::audioLoadAsync, static_cast<NativeNayanEngineCxxSpec*>(&turboModule)->jsInvoker_, static_cast<T*>(&turboModule),
963
+ count <= 0 ? throw jsi::JSError(rt, "Expected argument in position 0 to be passed") : args[0].asObject(rt));
964
+ }
965
+
861
966
  static jsi::Value __audioPlay(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
862
967
  static_assert(
863
968
  bridging::getParameterCount(&T::audioPlay) == 6,
@@ -1,5 +1,6 @@
1
1
  // The EngineSurface view: a CAMetalLayer the Rust renderer draws into with Metal. JS refers to it by
2
- // `surfaceId`; frames are drawn from the JS thread by NativeNayanEngine.renderFrame.
2
+ // `surfaceId`. A display link asks the game for each frame on the JS thread (`deliverFrame`); the
3
+ // frames it publishes are drawn on the engine's render thread.
3
4
  #import <QuartzCore/CAMetalLayer.h>
4
5
  #import <QuartzCore/CADisplayLink.h>
5
6
  #import <React/RCTViewManager.h>
@@ -7,6 +8,10 @@
7
8
 
8
9
  #include <engine_core.h>
9
10
 
11
+ namespace facebook::react {
12
+ void deliverFrame(int surface, double timeMs); // NayanEngineModule.cpp
13
+ }
14
+
10
15
  @interface NayanEngineSurfaceView : UIView <UIGestureRecognizerDelegate>
11
16
  @property (nonatomic, assign) NSInteger surfaceId;
12
17
  @property (nonatomic, assign) NSInteger drawingId;
@@ -18,6 +23,7 @@
18
23
  @implementation NayanEngineSurfaceView {
19
24
  BOOL _attached;
20
25
  CADisplayLink *_drawingClock;
26
+ CADisplayLink *_clock;
21
27
  UITouch *_drawingTouch;
22
28
  UIPinchGestureRecognizer *_drawingPinch;
23
29
  UIPanGestureRecognizer *_drawingPan;
@@ -47,6 +53,8 @@
47
53
  [self addGestureRecognizer:_drawingPan];
48
54
  [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(stopDrawingClock) name:UIApplicationWillResignActiveNotification object:nil];
49
55
  [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(startDrawingClock) name:UIApplicationDidBecomeActiveNotification object:nil];
56
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(stopClock) name:UIApplicationWillResignActiveNotification object:nil];
57
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(startClock) name:UIApplicationDidBecomeActiveNotification object:nil];
50
58
  self.layer.opaque = YES;
51
59
  }
52
60
  return self;
@@ -74,10 +82,12 @@
74
82
  (int32_t)_surfaceId, (__bridge void *)self.layer, (uint32_t)(size.width * scale), (uint32_t)(size.height * scale)) == 1;
75
83
  if (_drawingId >= 0) engine_drawing_invalidate((int32_t)_drawingId);
76
84
  [self startDrawingClock];
85
+ [self startClock];
77
86
  }
78
87
 
79
88
  - (void)detach
80
89
  {
90
+ [self stopClock];
81
91
  [self stopDrawingClock];
82
92
  if (_attached) {
83
93
  engine_render_detach((int32_t)_surfaceId);
@@ -126,6 +136,25 @@
126
136
  _drawingBackground = value;
127
137
  if (_drawingId >= 0) engine_drawing_invalidate((int32_t)_drawingId);
128
138
  }
139
+ /**
140
+ * The game's display clock: once per refresh while the surface is attached, on screen and the app is
141
+ * active, the engine is asked for its next frame on the JS thread. 60 a second, also on ProMotion
142
+ * screens, so a JS game loop isn't run twice as often.
143
+ */
144
+ - (void)startClock {
145
+ if (_clock != nil || _surfaceId < 0 || self.window == nil || !_attached || UIApplication.sharedApplication.applicationState != UIApplicationStateActive) return;
146
+ _clock = [CADisplayLink displayLinkWithTarget:self selector:@selector(tick:)];
147
+ if (@available(iOS 15.0, *)) _clock.preferredFrameRateRange = CAFrameRateRangeMake(30, 60, 60);
148
+ else _clock.preferredFramesPerSecond = 60;
149
+ [_clock addToRunLoop:NSRunLoop.mainRunLoop forMode:NSRunLoopCommonModes];
150
+ }
151
+ - (void)stopClock {
152
+ [_clock invalidate];
153
+ _clock = nil;
154
+ }
155
+ - (void)tick:(CADisplayLink *)link {
156
+ if (_surfaceId >= 0) facebook::react::deliverFrame((int)_surfaceId, link.timestamp * 1000.0);
157
+ }
129
158
  - (void)startDrawingClock {
130
159
  if (_drawingClock != nil || _drawingId < 0 || self.window == nil || !_attached || UIApplication.sharedApplication.applicationState != UIApplicationStateActive) return;
131
160
  _drawingClock = [CADisplayLink displayLinkWithTarget:self selector:@selector(presentDrawing)];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nayan-ui/engine",
3
- "version": "1.0.0-beta.3",
3
+ "version": "1.0.0-beta.5",
4
4
  "description": "Fast 3D game engine for React and React Native: Rust and wgpu rendering, Rapier physics, animation, particle effects, audio, haptics and drawing.",
5
5
  "keywords": [
6
6
  "3d",
@@ -25,7 +25,7 @@
25
25
  "webgpu",
26
26
  "wgpu"
27
27
  ],
28
- "homepage": "https://www.nayanui.com/engine",
28
+ "homepage": "https://www.nayanui.com/game-engine",
29
29
  "bugs": {
30
30
  "url": "https://github.com/ursnj/nayan-ui/issues"
31
31
  },
@@ -100,6 +100,8 @@ export interface Spec extends TurboModule {
100
100
  shapePrepare(vertices: Object, indices: Object, options: string): number;
101
101
  solidPrepare(recipe: string, options: string): number;
102
102
  modelLoad(data: Object, center: boolean, fit: number): number;
103
+ /** `modelLoad` on a background native thread (the data is copied first): resolves to the mesh id, or -1. */
104
+ modelLoadAsync(data: Object, center: boolean, fit: number): Promise<number>;
103
105
  /** 11 floats per vertex (position, normal, color, uv); aliases native memory. */
104
106
  modelVertices(mesh: number): Object;
105
107
  /** u32 triangle indices; aliases native memory. */
@@ -112,6 +114,11 @@ export interface Spec extends TurboModule {
112
114
  meshAlias(base: number): number;
113
115
  /** Registers geometry (copied): 11 floats per vertex, u32 triangle indices. Returns the mesh id, or -1. */
114
116
  meshCreate(vertices: Object, indices: Object): number;
117
+ /**
118
+ * Hands a core model's own geometry (from `meshCreate`, `modelLoad`, a font) to the renderer, in
119
+ * Rust: nothing is copied through JS. `texture` -1 for none. False if the mesh is unknown.
120
+ */
121
+ renderModel(mesh: number, texture: number, shine: number): boolean;
115
122
  /**
116
123
  * Fills 2D contours ([x, y] f32 pairs, `counts[k]` points per contour) by the non-zero or even-odd
117
124
  * rule and extrudes them `depth` deep. Returns an ArrayBuffer of 11 floats per vertex (unindexed).
@@ -141,12 +148,16 @@ export interface Spec extends TurboModule {
141
148
  ): void;
142
149
  /** Decodes a PNG/JPEG ArrayBuffer. Returns the texture id, or -1. */
143
150
  textureLoad(data: Object): number;
151
+ /** `textureLoad` on a background native thread: resolves to the texture id, or -1. */
152
+ textureLoadAsync(data: Object): Promise<number>;
144
153
  /** RGBA8 pixels, top row first; aliases native memory. */
145
154
  texturePixels(texture: number): Object;
146
155
  /** [width, height]. */
147
156
  textureSize(texture: number): Array<number>;
148
157
  /** Builds extruded glyph meshes for `chars`. Returns the font id, or -1. */
149
158
  fontLoad(data: Object, depth: number, chars: string, weight: number): number;
159
+ /** `fontLoad` (outlines and glyph meshes) on a background native thread: resolves to the font id, or -1. */
160
+ fontLoadAsync(data: Object, depth: number, chars: string, weight: number): Promise<number>;
150
161
  /** [codepoint, mesh (-1 = nothing drawn), advance, ...] followed by the cap height. */
151
162
  fontGlyphs(font: number): Array<number>;
152
163
  /** Why the last model / texture / font / alias load failed ("" after a success). */
@@ -182,6 +193,32 @@ export interface Spec extends TurboModule {
182
193
  viewProj: Object,
183
194
  world: number
184
195
  ): number;
196
+ /**
197
+ * `renderFrame`'s arguments, but the frame is copied and drawn on the native render thread: returns
198
+ * at once, with `viewProj` written. 1 once the surface has presented a frame, 0 before, -1 if the
199
+ * last frame failed (see `renderError`).
200
+ */
201
+ publishFrame(
202
+ surface: number,
203
+ frame: Object,
204
+ matrices: Object,
205
+ colors: Object,
206
+ regions: Object,
207
+ ranges: Object,
208
+ count: number,
209
+ viewProj: Object,
210
+ world: number
211
+ ): number;
212
+ /** [frames presented, smoothed render milliseconds] for a surface drawn by the render thread. */
213
+ renderStats(surface: number): Array<number>;
214
+ /** True where the platform drives frames from its display clock (`startFrames`); else use requestAnimationFrame. */
215
+ hasFrameClock(): boolean;
216
+ /**
217
+ * Calls `onFrame` on the JS thread once per display refresh while the surface is attached and
218
+ * visible, with the frame time in milliseconds. A frame JS hasn't started yet isn't queued again.
219
+ */
220
+ startFrames(surface: number, onFrame: (timeMs: number) => void): void;
221
+ stopFrames(surface: number): void;
185
222
  /**
186
223
  * The screen-space overlay drawn over surface `surface` from its next frame on, copied from a world's
187
224
  * buffers. In points: x right and y up from the view's top-left (on screen y <= 0), z toward the
@@ -210,6 +247,8 @@ export interface Spec extends TurboModule {
210
247
  // Audio (global)
211
248
  /** Decodes a WAV ArrayBuffer. Returns a sound id, or -1. */
212
249
  audioLoad(data: Object): number;
250
+ /** `audioLoad` on a background native thread: resolves to the sound id, or -1. */
251
+ audioLoadAsync(data: Object): Promise<number>;
213
252
  /** Returns a voice id (> 0), or 0 if nothing played. */
214
253
  audioPlay(sound: number, volume: number, pan: number, pitch: number, loop: boolean): number;
215
254
  audioStop(voice: number): void;