@reactvision/react-viro 2.55.0 → 2.57.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/README.md +27 -0
  2. package/android/react_viro/react_viro-release.aar +0 -0
  3. package/android/viro_renderer/viro_renderer-release.aar +0 -0
  4. package/components/AR/ViroARScene.tsx +17 -0
  5. package/components/AR/ViroARSceneNavigator.tsx +33 -0
  6. package/components/Utilities/StreamingAudioManager.ts +73 -0
  7. package/components/Utilities/ViroVersion.ts +1 -1
  8. package/components/ViroCameraTexture.tsx +230 -0
  9. package/components/ViroGameLoop.tsx +74 -0
  10. package/components/ViroGameLoopUtils.ts +52 -0
  11. package/components/ViroObjectDetector.tsx +222 -0
  12. package/components/ViroVirtualButton.tsx +101 -0
  13. package/components/ViroVirtualJoystick.tsx +126 -0
  14. package/components/hooks/useGameLoop.ts +78 -0
  15. package/dist/components/AR/ViroARScene.d.ts +12 -0
  16. package/dist/components/AR/ViroARScene.js +8 -1
  17. package/dist/components/AR/ViroARSceneNavigator.d.ts +30 -0
  18. package/dist/components/Utilities/StreamingAudioManager.d.ts +22 -0
  19. package/dist/components/Utilities/StreamingAudioManager.js +45 -0
  20. package/dist/components/Utilities/ViroVersion.d.ts +1 -1
  21. package/dist/components/Utilities/ViroVersion.js +1 -1
  22. package/dist/components/ViroCameraTexture.d.ts +137 -0
  23. package/dist/components/ViroCameraTexture.js +170 -0
  24. package/dist/components/ViroGameLoop.d.ts +31 -0
  25. package/dist/components/ViroGameLoop.js +31 -0
  26. package/dist/components/ViroGameLoopUtils.d.ts +26 -0
  27. package/dist/components/ViroGameLoopUtils.js +44 -0
  28. package/dist/components/ViroObjectDetector.d.ts +138 -0
  29. package/dist/components/ViroObjectDetector.js +85 -0
  30. package/dist/components/ViroVirtualButton.d.ts +76 -0
  31. package/dist/components/ViroVirtualButton.js +62 -0
  32. package/dist/components/ViroVirtualJoystick.d.ts +85 -0
  33. package/dist/components/ViroVirtualJoystick.js +72 -0
  34. package/dist/components/hooks/useGameLoop.d.ts +34 -0
  35. package/dist/components/hooks/useGameLoop.js +63 -0
  36. package/dist/index.d.ts +11 -1
  37. package/dist/index.js +20 -2
  38. package/dist/plugins/withViro.js +2 -15
  39. package/dist/plugins/withViroAndroid.js +52 -15
  40. package/dist/plugins/withViroIos.js +0 -6
  41. package/index.ts +21 -0
  42. package/ios/ViroReact.podspec +25 -6
  43. package/ios/dist/ViroRenderer/ViroKit.framework/ARCoreCoreMLSemanticsResources.bundle/Info.plist +0 -0
  44. package/ios/dist/ViroRenderer/ViroKit.framework/ARCoreResources.bundle/Info.plist +0 -0
  45. package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROARDepthMesh.h +9 -1
  46. package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROARFrame.h +33 -0
  47. package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROARFrameiOS.h +26 -0
  48. package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROARScene.h +15 -1
  49. package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROARSceneDelegateiOS.h +8 -0
  50. package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROARSession.h +7 -0
  51. package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROARSessioniOS.h +18 -0
  52. package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROARWorldMesh.h +111 -8
  53. package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROAudioPlayer.h +19 -0
  54. package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROAudioPlayerStreamiOS.h +95 -0
  55. package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROCameraTextureiOS.h +17 -1
  56. package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VRODriver.h +20 -0
  57. package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VRODynamicGeometry.h +170 -0
  58. package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VRODynamicMeshNode.h +87 -0
  59. package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROGLTFLoader.h +5 -1
  60. package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROGameLoopListener.h +125 -0
  61. package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROInputState.h +133 -0
  62. package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROMonocularDepthEstimator.h +73 -0
  63. package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROObjectDetector.h +134 -0
  64. package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROPCMRingBuffer.h +143 -0
  65. package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROPencil.h +9 -0
  66. package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VRORenderContext.h +4 -0
  67. package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VRORenderer.h +1 -0
  68. package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROVertexBuffer.h +102 -0
  69. package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROViewAR.h +9 -0
  70. package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROVirtualControllerRegistry.h +106 -0
  71. package/ios/dist/ViroRenderer/ViroKit.framework/Info.plist +0 -0
  72. package/ios/dist/ViroRenderer/ViroKit.framework/Shaders.dat +1 -1
  73. package/ios/dist/ViroRenderer/ViroKit.framework/ViroKit +0 -0
  74. package/ios/dist/ViroRenderer/ViroKit.framework/constant_fsh.glsl +1 -0
  75. package/ios/dist/ViroRenderer/ViroKit.framework/constant_fsh_adreno.glsl +1 -0
  76. package/ios/dist/ViroRenderer/ViroKit.framework/standard_fsh.glsl +1 -0
  77. package/ios/dist/ViroRenderer/ViroKit.framework/standard_vsh.glsl +4 -0
  78. package/ios/dist/ViroRenderer/ViroKit.framework/standard_vsh_adreno.glsl +4 -0
  79. package/ios/dist/ViroRendererVisionOS/ViroKit.podspec +27 -0
  80. package/ios/dist/include/VRTARScene.h +1 -0
  81. package/ios/dist/include/VRTARSceneNavigator.h +3 -0
  82. package/ios/dist/include/VRTCameraTexture.h +62 -0
  83. package/ios/dist/include/VRTCameraTextureManager.h +30 -0
  84. package/ios/dist/include/VRTCameraTextureModule.h +38 -0
  85. package/ios/dist/include/VRTGameLoopView.h +19 -0
  86. package/ios/dist/include/VRTGameLoopViewManager.h +8 -0
  87. package/ios/dist/include/VRTManagedAnimation.h +67 -0
  88. package/ios/dist/include/VRTStreamingAudioModule.h +21 -0
  89. package/ios/dist/include/VRTVirtualButtonView.h +47 -0
  90. package/ios/dist/include/VRTVirtualButtonViewManager.h +21 -0
  91. package/ios/dist/include/VRTVirtualJoystickView.h +59 -0
  92. package/ios/dist/include/VRTVirtualJoystickViewManager.h +26 -0
  93. package/ios/dist/lib/libViroReact.a +0 -0
  94. package/package.json +17 -20
@@ -30,6 +30,9 @@
30
30
  #include <string>
31
31
  #include <chrono>
32
32
  #include <functional>
33
+ #include <map>
34
+ #include <mutex>
35
+ #include <cstdint>
33
36
  #include "VROVector3f.h"
34
37
 
35
38
  class VROARDepthMesh;
@@ -50,16 +53,36 @@ struct VROWorldMeshConfig {
50
53
  float maxDepth = 5.0f; // Maximum depth in meters
51
54
 
52
55
  // Update settings
53
- double updateIntervalMs = 100.0; // Minimum time between mesh updates
56
+ double updateIntervalMs = 500.0; // Minimum time between mesh updates
54
57
  double meshPersistenceMs = 500.0; // Time to keep mesh after depth data lost
55
58
 
56
59
  // Physics properties
57
60
  float friction = 0.5f; // Surface friction coefficient
58
61
  float restitution = 0.3f; // Bounciness (0 = no bounce, 1 = full bounce)
59
62
  std::string collisionTag = "world"; // Tag for collision event identification
63
+ int physicsMaxTriangles = 0; // Stride-decimation triangle cap (0 = no limit). Leaves
64
+ // large gaps — prefer physicsCellSize for collision.
65
+ float physicsCellSize = 0.10f; // Vertex-clustering cell size in meters (0 = disabled).
66
+ // Groups nearby vertices into a single representative,
67
+ // producing a gap-free simplified mesh. Max collision gap =
68
+ // sqrt(3) * cellSize. 0.10m → catches objects >~8cm reliably.
69
+ // Applied before physicsMaxTriangles (if both set).
60
70
 
61
71
  // Visualization
62
- bool debugDrawEnabled = false; // Enable wireframe visualization of mesh
72
+ bool debugDrawEnabled = false; // Enable wireframe visualization of mesh
73
+ bool debugDrawDepthTest = true; // Depth-test wireframe against scene (occluded by real surfaces)
74
+ int debugDrawMaxTriangles = 1000; // Triangle cap for wireframe debug draw
75
+ float debugDrawLineThickness = 0.001f; // Line thickness for wireframe (meters)
76
+ };
77
+
78
+ /**
79
+ * Identifies the data source that produced a world mesh.
80
+ */
81
+ enum class VROWorldMeshSource {
82
+ LiDAR, // ARKit ARMeshAnchor (LiDAR-equipped device)
83
+ Monocular, // Monocular depth estimation (non-LiDAR device)
84
+ Plane, // Triangulated AR plane anchors (fallback)
85
+ Unknown
63
86
  };
64
87
 
65
88
  /**
@@ -74,10 +97,45 @@ struct VROWorldMeshStats {
74
97
  };
75
98
 
76
99
  /**
77
- * Callback type for mesh update notifications.
100
+ * Delivered to every registered subscriber when the world mesh is updated.
101
+ */
102
+ struct VROWorldMeshUpdate {
103
+ std::shared_ptr<VROARDepthMesh> mesh;
104
+ VROWorldMeshStats stats;
105
+ VROWorldMeshSource source = VROWorldMeshSource::Unknown;
106
+ };
107
+
108
+ /**
109
+ * Per-subscriber options controlling mesh decimation.
110
+ * maxTriangles = 0 means no limit (full mesh delivered as-is).
111
+ */
112
+ struct VROWorldMeshSubscriberOptions {
113
+ enum class DecimationStrategy {
114
+ Stride, // Take every N-th triangle (O(1) per triangle, default)
115
+ };
116
+
117
+ int maxTriangles = 0; // 0 = unlimited
118
+ DecimationStrategy strategy = DecimationStrategy::Stride;
119
+ };
120
+
121
+ using VROWorldMeshSubscriberId = uint32_t;
122
+ using VROWorldMeshSubscriberCallback = std::function<void(const VROWorldMeshUpdate&)>;
123
+
124
+ /**
125
+ * Legacy callback — delivers only stats. Kept for back-compat; prefer subscribe().
78
126
  */
79
127
  using VROWorldMeshUpdateCallback = std::function<void(const VROWorldMeshStats&)>;
80
128
 
129
+ /**
130
+ * Custom deleter for btRigidBody.
131
+ * Bullet requires removing the body from the dynamics world before deletion;
132
+ * this deleter encapsulates that invariant so it can't be forgotten.
133
+ */
134
+ struct BulletRigidBodyDeleter {
135
+ std::weak_ptr<VROPhysicsWorld> physicsWorld;
136
+ void operator()(btRigidBody *body) const;
137
+ };
138
+
81
139
  /**
82
140
  * VROARWorldMesh manages the lifecycle of a physics collision mesh generated
83
141
  * from AR depth data. It automatically updates the mesh from incoming AR frames
@@ -144,7 +202,21 @@ public:
144
202
  VROWorldMeshStats getStats() const;
145
203
 
146
204
  /**
147
- * Set a callback to be notified when the mesh is updated.
205
+ * Subscribe to mesh updates. Returns an opaque ID used to unsubscribe.
206
+ * The callback is invoked on the render thread after each successful mesh build.
207
+ * @param callback Receives the full VROWorldMeshUpdate (mesh, stats, source).
208
+ * @param options Per-consumer options (e.g. maxTriangles for W3 decimation).
209
+ */
210
+ VROWorldMeshSubscriberId subscribe(VROWorldMeshSubscriberCallback callback,
211
+ VROWorldMeshSubscriberOptions options = {});
212
+
213
+ /**
214
+ * Unsubscribe a previously registered callback. No-op if id is not found.
215
+ */
216
+ void unsubscribe(VROWorldMeshSubscriberId id);
217
+
218
+ /**
219
+ * @deprecated Prefer subscribe(). Delivers stats only, no mesh data.
148
220
  */
149
221
  void setUpdateCallback(VROWorldMeshUpdateCallback callback) {
150
222
  _updateCallback = callback;
@@ -161,9 +233,10 @@ public:
161
233
  private:
162
234
  std::weak_ptr<VROPhysicsWorld> _physicsWorld;
163
235
 
164
- // Bullet physics components (direct, without VROPhysicsBody wrapper)
165
- btRigidBody* _rigidBody = nullptr;
166
- btDefaultMotionState* _motionState = nullptr;
236
+ // Bullet physics components smart pointers for exception-safe lifecycle.
237
+ // BulletRigidBodyDeleter removes the body from the world before deletion.
238
+ std::unique_ptr<btRigidBody, BulletRigidBodyDeleter> _rigidBody;
239
+ std::unique_ptr<btDefaultMotionState> _motionState;
167
240
  std::shared_ptr<VROPhysicsShape> _physicsShape;
168
241
 
169
242
  // Current mesh data
@@ -172,14 +245,21 @@ private:
172
245
  // Configuration and state
173
246
  VROWorldMeshConfig _config;
174
247
  bool _enabled = false;
248
+ bool _isAddedToPhysicsWorld = false; // guard against re-add on rapid updates
175
249
 
176
250
  // Timing
177
251
  double _lastUpdateTimeMs = 0.0;
178
252
  double _lastDepthTimeMs = 0.0;
179
253
 
180
- // Callback
254
+ // Legacy stats-only callback
181
255
  VROWorldMeshUpdateCallback _updateCallback;
182
256
 
257
+ // Subscriber registry
258
+ std::map<VROWorldMeshSubscriberId,
259
+ std::pair<VROWorldMeshSubscriberCallback, VROWorldMeshSubscriberOptions>> _subscribers;
260
+ VROWorldMeshSubscriberId _nextSubscriberId = 1;
261
+ mutable std::mutex _subscriberMutex;
262
+
183
263
  /**
184
264
  * Apply a new mesh to the physics world.
185
265
  * Creates a new physics shape and rigid body from the mesh.
@@ -210,6 +290,29 @@ private:
210
290
  * Check if the mesh is stale (depth data not received recently).
211
291
  */
212
292
  bool isMeshStale() const;
293
+
294
+ /**
295
+ * Fire the legacy _updateCallback and all registered subscribers.
296
+ * Called on the render thread after a mesh is successfully applied.
297
+ */
298
+ void notifySubscribers(std::shared_ptr<VROARDepthMesh> mesh);
299
+
300
+ /**
301
+ * Return a decimated copy of mesh with at most maxTriangles triangles,
302
+ * using Stride strategy (every N-th triangle). Vertices are copied as-is;
303
+ * only the index buffer is thinned. Returns mesh unchanged if already within budget.
304
+ */
305
+ static std::shared_ptr<VROARDepthMesh> decimateMesh(
306
+ std::shared_ptr<VROARDepthMesh> mesh, int maxTriangles);
307
+
308
+ /**
309
+ * Vertex-clustering simplification. Groups all vertices within cellSize metres
310
+ * into a single representative, rebuilds triangles, drops degenerate ones.
311
+ * Produces a gap-free mesh: max gap = sqrt(3)*cellSize.
312
+ * 0.10m cells → catches objects wider than ~8cm reliably.
313
+ */
314
+ static std::shared_ptr<VROARDepthMesh> clusterMesh(
315
+ std::shared_ptr<VROARDepthMesh> mesh, float cellSize);
213
316
  };
214
317
 
215
318
  #endif /* VROARWorldMesh_h */
@@ -51,6 +51,25 @@ public:
51
51
  virtual void setMuted(bool muted) = 0;
52
52
  virtual void seekToTime(float seconds) = 0;
53
53
 
54
+ /*
55
+ Streaming PCM API. Subclasses that support runtime PCM feeding override
56
+ these; the default no-ops preserve all existing non-streaming subclasses.
57
+
58
+ beginStreaming: configure the player for streaming mode (instead of
59
+ loading a file/blob). Must be called before play(). sampleRate is in
60
+ Hz; channels is 1 (mono) or 2 (stereo).
61
+
62
+ pushSamples: feed interleaved float PCM samples in the range [-1, 1].
63
+ Thread-safe — may be called from any thread (sim loop, TTS callback,
64
+ etc.). Returns the number of samples accepted; if the ring buffer is
65
+ full the excess is dropped.
66
+
67
+ isStreaming: true once beginStreaming has been called successfully.
68
+ */
69
+ virtual void beginStreaming(int sampleRate, int channels) {}
70
+ virtual size_t pushSamples(const float *data, size_t count) { return 0; }
71
+ virtual bool isStreaming() const { return false; }
72
+
54
73
  protected:
55
74
  std::shared_ptr<VROSoundDelegateInternal> _delegate;
56
75
  };
@@ -0,0 +1,95 @@
1
+ //
2
+ // VROAudioPlayerStreamiOS.h
3
+ // ViroKit
4
+ //
5
+ // Copyright © 2026 ReactVision. All rights reserved.
6
+ //
7
+ // Permission is hereby granted, free of charge, to any person obtaining
8
+ // a copy of this software and associated documentation files (the
9
+ // "Software"), to deal in the Software without restriction, including
10
+ // without limitation the rights to use, copy, modify, merge, publish,
11
+ // distribute, sublicense, and/or sell copies of the Software, and to
12
+ // permit persons to whom the Software is furnished to do so, subject to
13
+ // the following conditions:
14
+ //
15
+ // The above copyright notice and this permission notice shall be included
16
+ // in all copies or substantial portions of the Software.
17
+
18
+ #ifndef VROAudioPlayerStreamiOS_h
19
+ #define VROAudioPlayerStreamiOS_h
20
+
21
+ #include "VROAudioPlayer.h"
22
+ #include "VROPCMRingBuffer.h"
23
+ #include <memory>
24
+ #include <atomic>
25
+
26
+ #ifdef __OBJC__
27
+ @class AVAudioEngine;
28
+ @class AVAudioSourceNode;
29
+ @class AVAudioFormat;
30
+ #else
31
+ // Forward declarations for non-ObjC translation units (Android, WASM).
32
+ typedef void AVAudioEngine;
33
+ typedef void AVAudioSourceNode;
34
+ typedef void AVAudioFormat;
35
+ #endif
36
+
37
+ /*
38
+ Streaming PCM audio player for iOS using AVAudioEngine + AVAudioSourceNode.
39
+
40
+ Usage:
41
+ auto player = std::make_shared<VROAudioPlayerStreamiOS>();
42
+ player->beginStreaming(32000, 2); // stereo @ 32 kHz
43
+ player->setVolume(1.0f);
44
+ player->play();
45
+ // from any thread:
46
+ player->pushSamples(floatBuffer, frameCount * channels);
47
+
48
+ The AVAudioSourceNode render block drains the VROPCMRingBuffer on the
49
+ real-time audio thread (no locks). If the ring is empty, silence is output.
50
+ Spatialisation (position / attenuation) is NOT applied here — this player
51
+ is for non-spatial streaming audio. Spatial streaming is tracked separately.
52
+ */
53
+ class VROAudioPlayerStreamiOS : public VROAudioPlayer {
54
+
55
+ public:
56
+
57
+ VROAudioPlayerStreamiOS();
58
+ virtual ~VROAudioPlayerStreamiOS();
59
+
60
+ // VROAudioPlayer overrides
61
+ void setup() override;
62
+ void setLoop(bool loop) override {} // streaming is continuous; loop is a no-op
63
+ void play() override;
64
+ void pause() override;
65
+ void setVolume(float volume) override;
66
+ void setMuted(bool muted) override;
67
+ void seekToTime(float seconds) override {} // not meaningful for live PCM
68
+
69
+ // Streaming API
70
+ void beginStreaming(int sampleRate, int channels) override;
71
+ size_t pushSamples(const float *data, size_t count) override;
72
+ bool isStreaming() const override { return _streaming; }
73
+
74
+ private:
75
+
76
+ void teardown();
77
+
78
+ #ifdef __OBJC__
79
+ AVAudioEngine *_engine = nil;
80
+ AVAudioSourceNode *_sourceNode = nil;
81
+ AVAudioFormat *_format = nil;
82
+ #else
83
+ void *_engine = nullptr;
84
+ void *_sourceNode = nullptr;
85
+ void *_format = nullptr;
86
+ #endif
87
+
88
+ std::shared_ptr<VROPCMRingBuffer> _ring;
89
+ std::atomic<bool> _playing { false };
90
+ std::atomic<bool> _muted { false };
91
+ std::atomic<float> _volume { 1.0f };
92
+ bool _streaming = false;
93
+ };
94
+
95
+ #endif /* VROAudioPlayerStreamiOS_h */
@@ -62,7 +62,23 @@ public:
62
62
  texture.
63
63
  */
64
64
  std::vector<float> getCameraIntrinsics() const;
65
-
65
+
66
+ /*
67
+ Expose the underlying capture controller so that callers (e.g. VRTCameraTexture)
68
+ can invoke photo / video capture directly.
69
+ */
70
+ std::shared_ptr<VROAVCaptureController> getCaptureController() const {
71
+ return _controller;
72
+ }
73
+
74
+ // Convenience wrappers so callers don't need to import VROAVCaptureController.h.
75
+ void setUpdateListener(std::function<void(CMSampleBufferRef, std::vector<float>)> listener);
76
+ void capturePhoto(NSString *outputPath,
77
+ std::function<void(bool, NSString *, NSString *)> onCaptured);
78
+ void startRecording(NSString *outputPath,
79
+ std::function<void(bool, NSString *, NSString *)> onStarted);
80
+ void stopRecording(std::function<void(bool, NSString *, NSString *)> onStopped);
81
+
66
82
  private:
67
83
 
68
84
  /*
@@ -30,6 +30,7 @@
30
30
  #include <vector>
31
31
  #include "VRODefines.h"
32
32
  #include "VROSoundData.h"
33
+ #include "VROVertexBuffer.h"
33
34
 
34
35
  class VROGeometry;
35
36
  class VROMaterial;
@@ -278,6 +279,16 @@ public:
278
279
  virtual std::shared_ptr<VRORenderTarget> newRenderTarget(VRORenderTargetType type, int numAttachments, int numImages,
279
280
  bool enableMipmaps, bool needsDepthStencil) = 0;
280
281
  virtual std::shared_ptr<VROVertexBuffer> newVertexBuffer(std::shared_ptr<VROData> data) = 0;
282
+ /*
283
+ Create a vertex buffer with an explicit usage hint. Subclasses that don't
284
+ override this default get a Static buffer; subclasses that do override gain
285
+ mutable buffers via VROVertexBuffer::updateData(). Used by VRODynamicGeometry
286
+ and any other path that needs per-frame buffer updates.
287
+ */
288
+ virtual std::shared_ptr<VROVertexBuffer> newVertexBuffer(std::shared_ptr<VROData> data,
289
+ VROVertexBufferUsage usage) {
290
+ return newVertexBuffer(data);
291
+ }
281
292
  virtual std::shared_ptr<VRORenderTarget> getDisplay() = 0;
282
293
  virtual std::shared_ptr<VROImagePostProcess> newImagePostProcess(std::shared_ptr<VROShaderProgram> shader) = 0;
283
294
  virtual std::shared_ptr<VROVideoTextureCache> newVideoTextureCache() = 0;
@@ -285,6 +296,15 @@ public:
285
296
  virtual std::shared_ptr<VROSound> newSound(std::string resource, VROResourceType resourceType, VROSoundType type) = 0;
286
297
  virtual std::shared_ptr<VROAudioPlayer> newAudioPlayer(std::shared_ptr<VROSoundData> data) = 0;
287
298
  virtual std::shared_ptr<VROAudioPlayer> newAudioPlayer(std::string path, bool isLocal) = 0;
299
+ /*
300
+ Create an audio player pre-configured for streaming PCM. The returned
301
+ player's beginStreaming(sampleRate, channels) must be called before play().
302
+ Default implementation falls back to a no-op stub so subclasses that
303
+ haven't yet implemented streaming still compile.
304
+ */
305
+ virtual std::shared_ptr<VROAudioPlayer> newStreamingAudioPlayer() {
306
+ return newAudioPlayer(std::string(""), true); // stub — subclasses override
307
+ }
288
308
  virtual std::shared_ptr<VROTypefaceCollection> newTypefaceCollection(std::string typefaces, int size, VROFontStyle style, VROFontWeight weight) = 0;
289
309
  virtual void setSoundRoom(float sizeX, float sizeY, float sizeZ, std::string wallMaterial,
290
310
  std::string ceilingMaterial, std::string floorMaterial) = 0;
@@ -0,0 +1,170 @@
1
+ //
2
+ // VRODynamicGeometry.h
3
+ // ViroKit
4
+ //
5
+ // Copyright © 2026 ReactVision. All rights reserved.
6
+ //
7
+ // Permission is hereby granted, free of charge, to any person obtaining
8
+ // a copy of this software and associated documentation files (the
9
+ // "Software"), to deal in the Software without restriction, including
10
+ // without limitation the rights to use, copy, modify, merge, publish,
11
+ // distribute, sublicense, and/or sell copies of the Software, and to
12
+ // permit persons to whom the Software is furnished to do so, subject to
13
+ // the following conditions:
14
+ //
15
+ // The above copyright notice and this permission notice shall be included
16
+ // in all copies or substantial portions of the Software.
17
+
18
+ #ifndef VRODynamicGeometry_h
19
+ #define VRODynamicGeometry_h
20
+
21
+ #include <memory>
22
+ #include <stdint.h>
23
+ #include "VROGeometry.h"
24
+
25
+ class VROVertexBuffer;
26
+ class VRODriver;
27
+
28
+ /*
29
+ A geometry whose vertex buffers can be replaced in place every frame without
30
+ recreating the geometry, its sources/elements, or its underlying GPU substrate.
31
+
32
+ Use this when the data driving the mesh changes at render-loop frequency:
33
+ - Procedural mesh (marching cubes, voxels)
34
+ - External engines that emit vertex soup per tick (libsm64, soft-body sims)
35
+ - CPU skinning of formats ViroCore doesn't parse natively
36
+ - Visualisations of changing scientific data
37
+
38
+ The first iteration supports unindexed triangle soup only. The vertex buffers
39
+ are allocated once at construction with the supplied capacity. Subsequent
40
+ calls to setBuffers() that stay within capacity reuse the GPU buffers without
41
+ reallocation. Exceeding the capacity triggers a one-time reallocation and a
42
+ warning; size the initial capacity to the upper bound your producer will emit.
43
+
44
+ Attribute availability is declared at construction. Buffers passed to
45
+ setBuffers() for absent attributes must be null; mismatches are logged.
46
+
47
+ Typical use:
48
+
49
+ auto geom = std::make_shared<VRODynamicGeometry>(
50
+ driver,
51
+ maxVertices, // upper bound the producer will emit
52
+ VRODynamicGeometryFeatures::All // positions + normals + uvs + colours
53
+ );
54
+ someNode->setGeometry(geom);
55
+ someNode->getGeometry()->setMaterials({myMaterial});
56
+
57
+ // each frame:
58
+ geom->setBuffers(positions, normals, uvs, colours, triangleCount);
59
+ */
60
+
61
+ enum class VRODynamicGeometryFeatures : uint32_t {
62
+ None = 0,
63
+ Positions = 1 << 0, // always implicitly required
64
+ Normals = 1 << 1,
65
+ UVs = 1 << 2,
66
+ Colors = 1 << 3,
67
+ PosNorm = Positions | Normals,
68
+ PosNormUV = Positions | Normals | UVs,
69
+ All = Positions | Normals | UVs | Colors,
70
+ };
71
+
72
+ inline VRODynamicGeometryFeatures operator|(VRODynamicGeometryFeatures a,
73
+ VRODynamicGeometryFeatures b) {
74
+ return static_cast<VRODynamicGeometryFeatures>(
75
+ static_cast<uint32_t>(a) | static_cast<uint32_t>(b));
76
+ }
77
+ inline bool hasFeature(VRODynamicGeometryFeatures set,
78
+ VRODynamicGeometryFeatures probe) {
79
+ return (static_cast<uint32_t>(set) & static_cast<uint32_t>(probe)) != 0;
80
+ }
81
+
82
+ class VRODynamicGeometry : public VROGeometry {
83
+
84
+ public:
85
+
86
+ /*
87
+ Construct a dynamic geometry sized to hold up to maxVertices and configured
88
+ for the given attribute set. Allocates and hydrates one VBO per declared
89
+ attribute up front. Positions are always allocated.
90
+ */
91
+ VRODynamicGeometry(std::shared_ptr<VRODriver> driver,
92
+ int maxVertices,
93
+ VRODynamicGeometryFeatures features =
94
+ VRODynamicGeometryFeatures::PosNormUV);
95
+
96
+ virtual ~VRODynamicGeometry();
97
+
98
+ /*
99
+ Replace the geometry's vertex data and triangle count.
100
+
101
+ - positions: required, vertexCount * 3 floats
102
+ - normals: required iff Normals feature was declared at construction,
103
+ else must be null
104
+ - uvs: required iff UVs feature was declared, vertexCount * 2 floats
105
+ - colors: required iff Colors feature was declared, vertexCount * 4
106
+ RGBA8 bytes (0..255)
107
+ - triangleCount: number of triangles to draw; vertexCount = triangleCount * 3
108
+
109
+ vertexCount may not exceed maxVertices declared at construction without
110
+ triggering a one-time VBO reallocation (logged warning).
111
+
112
+ Safe to call from the render thread. Thread-safety with any other thread
113
+ calling setBuffers concurrently is the caller's responsibility.
114
+ */
115
+ void setBuffers(const float *positions,
116
+ const float *normals,
117
+ const float *uvs,
118
+ const uint8_t *colors,
119
+ int triangleCount);
120
+
121
+ /*
122
+ Maximum vertex capacity declared at construction (or grown by a previous
123
+ overflowing setBuffers call).
124
+ */
125
+ int getMaxVertices() const { return _maxVertices; }
126
+
127
+ /*
128
+ Features (attributes) this dynamic geometry was constructed with.
129
+ */
130
+ VRODynamicGeometryFeatures getFeatures() const { return _features; }
131
+
132
+ private:
133
+
134
+ std::weak_ptr<VRODriver> _driver;
135
+ VRODynamicGeometryFeatures _features;
136
+ int _maxVertices;
137
+ int _currentTriangleCount;
138
+
139
+ std::shared_ptr<VROVertexBuffer> _positionBuffer;
140
+ std::shared_ptr<VROVertexBuffer> _normalBuffer;
141
+ std::shared_ptr<VROVertexBuffer> _uvBuffer;
142
+ std::shared_ptr<VROVertexBuffer> _colorBuffer;
143
+
144
+ std::shared_ptr<VROGeometryElement> _triangleSoupElement;
145
+
146
+ /*
147
+ Allocate the per-attribute VBOs and wire up the parent VROGeometry's
148
+ sources/elements once. Called from the constructor.
149
+ */
150
+ void initBuffers();
151
+
152
+ /*
153
+ Helper: replace a VertexBuffer's data and forward to its updateData hook.
154
+ */
155
+ void updateBuffer(const std::shared_ptr<VROVertexBuffer> &buffer,
156
+ const void *data,
157
+ size_t byteSize);
158
+
159
+ /*
160
+ Like updateBuffer but pads the VROData to totalBytes with zeros so
161
+ VROGeometrySource's declared vertexCount stays consistent with the buffer
162
+ capacity (avoids VROByteBuffer over-read during bounding-box computation).
163
+ */
164
+ void updateBufferPadded(const std::shared_ptr<VROVertexBuffer> &buffer,
165
+ const void *activeData, size_t activeBytes,
166
+ size_t totalBytes);
167
+
168
+ };
169
+
170
+ #endif /* VRODynamicGeometry_h */
@@ -0,0 +1,87 @@
1
+ //
2
+ // VRODynamicMeshNode.h
3
+ // ViroKit
4
+ //
5
+ // Copyright © 2026 ReactVision. All rights reserved.
6
+ //
7
+ // Permission is hereby granted, free of charge, to any person obtaining
8
+ // a copy of this software and associated documentation files (the
9
+ // "Software"), to deal in the Software without restriction, including
10
+ // without limitation the rights to use, copy, modify, merge, publish,
11
+ // distribute, sublicense, and/or sell copies of the Software, and to
12
+ // permit persons to whom the Software is furnished to do so, subject to
13
+ // the following conditions:
14
+ //
15
+ // The above copyright notice and this permission notice shall be included
16
+ // in all copies or substantial portions of the Software.
17
+
18
+ #ifndef VRODynamicMeshNode_h
19
+ #define VRODynamicMeshNode_h
20
+
21
+ #include <memory>
22
+ #include <vector>
23
+ #include "VRONode.h"
24
+ #include "VRODynamicGeometry.h"
25
+ #include "VROVector3f.h"
26
+ #include "VROVector2f.h"
27
+ #include "VROVector4f.h"
28
+
29
+ class VRODriver;
30
+
31
+ /*
32
+ A VRONode that owns a VRODynamicGeometry and exposes a high-level typed
33
+ setBuffers() API, matching the output of simulation adapters (e.g. libsm64).
34
+
35
+ Typical use:
36
+
37
+ auto node = std::make_shared<VRODynamicMeshNode>();
38
+ scene->getRootNode()->addChildNode(node);
39
+ node->init(driver, maxVertices, VRODynamicGeometryFeatures::All);
40
+
41
+ // each frame, from a VROFrameListener:
42
+ node->setBuffers(positions, normals, uvs, colors, vertexCount);
43
+
44
+ init() must be called once with a valid driver (e.g. from sceneWillAppear)
45
+ before the first setBuffers() call. vertexCount = triangleCount * 3.
46
+
47
+ scratch buffers for float packing are owned by the node and reused every
48
+ frame to avoid per-frame heap allocation.
49
+ */
50
+ class VRODynamicMeshNode : public VRONode {
51
+ public:
52
+ VRODynamicMeshNode() = default;
53
+ ~VRODynamicMeshNode() override = default;
54
+
55
+ /*
56
+ Allocate the underlying VRODynamicGeometry sized to hold up to maxVertices.
57
+ Must be called once from the render thread (e.g. sceneWillAppear) before
58
+ any setBuffers() call.
59
+ */
60
+ void init(std::shared_ptr<VRODriver> driver,
61
+ int maxVertices,
62
+ VRODynamicGeometryFeatures features = VRODynamicGeometryFeatures::All);
63
+
64
+ /*
65
+ Replace the mesh data for the current frame. vertexCount = triangleCount * 3.
66
+ colors components must be in [0, 1]; they are converted to uint8 RGBA internally.
67
+ Safe to call from the render thread at frame rate.
68
+ */
69
+ void setBuffers(const std::vector<VROVector3f> &positions,
70
+ const std::vector<VROVector3f> &normals,
71
+ const std::vector<VROVector2f> &uvs,
72
+ const std::vector<VROVector4f> &colors,
73
+ int vertexCount);
74
+
75
+ std::shared_ptr<VRODynamicGeometry> getDynamicGeometry() const { return _dynGeom; }
76
+
77
+ private:
78
+ std::shared_ptr<VRODynamicGeometry> _dynGeom;
79
+
80
+ // Scratch buffers reused every frame to avoid heap churn
81
+ std::vector<float> _scratchPos;
82
+ std::vector<float> _scratchNorm;
83
+ std::vector<float> _scratchUV;
84
+ std::vector<uint8_t> _scratchColor;
85
+ };
86
+
87
+ #endif /* VRODynamicMeshNode_h */
@@ -188,7 +188,11 @@ private:
188
188
  static bool processAnimations(const tinygltf::Model &gModel);
189
189
  static bool processKeyFrameAnimations(const tinygltf::Model &gModel,
190
190
  std::map<int, std::map<int, std::vector<int>>> &gltfAnimatedNodes);
191
- static void flattenSkeletalKeyframeAnimations(
191
+ // Resample all of a skin's animation channels onto a single common time-grid and merge each
192
+ // joint's per-property channels into one index-aligned VROKeyframeAnimation. Replaces the old
193
+ // drop-mismatched-channels flatten (VIRO-5741) so mixed STEP/LINEAR, multi-time-grid glTF
194
+ // animations (e.g. Blender exports) play fully instead of freezing.
195
+ static void resampleSkeletalChannelsToCommonGrid(
192
196
  std::map<int, std::pair<int, std::vector<int>>> &skeletalAnimToNodeSkinPair,
193
197
  int skinIndex);
194
198
  static std::shared_ptr<VROKeyframeAnimation> convertChannelToKeyFrameAnimation(