Skip to content

Commit c8e3334

Browse files
v2.52.0 - Shader System & Memory Improvements (#434)
* chore: fix ios crash, add depth-based ar hit * fix: hitResultId not available issue * chore: made non-lidar to fallback to mono, simplify integration and bug fixes * feat: integrate depth changes from virocore * feat: include latest virocore * chore: implement latest virocore * fix: remove debug shader from production code * feat: integrate ios shader modifiers for testing * feat: add full ios shader support * fix: apply shader overrides down the node tree * feat: se implementan los shader overrides para soporte en tiempo real * fix: add throttle mechanism for performance * fix: share materials across instances * fix: remove debug logs * feat: implement Eduardo's changes on shaders * feat: implement Eduardo's standardization and fix shader propagation * feat: add shader propagation (fix timing) based on eduardo implementation * fix: clear cloned materials array to prevent overflow * fix: fix vrx asset loading and load Eduardo ios crashes fies * fix: add shaderOverrides prop and ensure textures handling correctly * fix: memory leaks on ios fixed for good * chore: remove logs * fix: preserve materials with animations * fix: copy lightning from shaders * fix:Fix TypeScript type (#433) Change gravity type from number to 3D vector Co-authored-by: benmotanoti <benmotanoti@gmail.com> * Update CHANGELOG.md * fix: api key not inserting * chore: version bump * chore: add back bodymesh model --------- Co-authored-by: Benito Mota <benmotanoti@gmail.com>
1 parent b00807c commit c8e3334

81 files changed

Lines changed: 10490 additions & 173 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎CHANGELOG.md‎

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,49 @@
11
# CHANGELOG
22

3+
## v2.52.0 - 08 February 2026
4+
5+
### Added
6+
- **Full Shader Support**: Complete implementation of shader modifiers for iOS and Android platforms
7+
- **Shader Overrides**: New `shaderOverrides` prop for real-time shader customization across both platforms
8+
- **Shader Propagation**: Intelligent shader propagation system down the node tree with proper timing
9+
- **Fragment Output Standardization**: Unified fragment shader outputs across platforms
10+
- **Texture Handling in Shaders**: Full texture support in shader overrides
11+
- **Animated Shader Support**: Uniform binding for animated shaders
12+
- **Material Animation Preservation**: Materials with animations are preserved during shader application
13+
- **Lighting Properties in Shaders**: Lighting properties are properly copied and maintained in custom shaders
14+
- **Material Sharing**: Materials now shared efficiently across instances for improved performance
15+
- **Depth-based AR Hit Testing**: Enhanced AR hit test functionality using depth information
16+
- **Monocular Depth Fallback**: Non-LiDAR devices now automatically fallback to monocular depth estimation
17+
- **Throttle Mechanism**: Added performance throttle to prevent system overload
18+
19+
### Fixed
20+
- **iOS Memory Leaks** (Critical): Completely eliminated memory leaks on iOS platform (both viro and virocore)
21+
- **Portal Crashes** (Critical): Fixed crashes when unmounting portals on iOS
22+
- **Material Overflow**: Fixed cloned materials array overflow that caused crashes
23+
- **VRX Asset Loading**: Resolved VRX asset loading issues
24+
- **Monocular Depth Alignment**: Fixed monocular depth frame alignment issues
25+
- **Double Anchor Reference**: Fixed issue with creating duplicate anchor references
26+
- **Gravity Type Crash**: Fixed Android crash caused by incorrect gravity type definition (now correctly typed as 3D vector `[number, number, number]`)
27+
- **hitResultId**: Fixed hitResultId availability issue in AR hit tests
28+
- **Thread Locks**: Refactored implementation to avoid thread locks
29+
30+
### Changed
31+
- **Depth Integration**: Improved depth integration with simplified logic
32+
33+
### Removed
34+
- Debug logs and visualizations from production code
35+
- Spam/noisy logging
36+
37+
### ViroCore Integration
38+
This release includes 21 commits from virocore focused on:
39+
- Native shader support and modifiers
40+
- Memory management improvements
41+
- Depth processing enhancements
42+
43+
## v2.51.0 - 01 February 2026
44+
45+
Previous release baseline for v2.52.0 changes.
46+
347
## v2.43.6 - 08 October 2025
448

549
- **Fixed pod install issues**: Adjusted ViroKit requirements
10.6 KB
Binary file not shown.

‎android/viro_bridge/src/main/java/com/viromedia/bridge/component/node/VRTNode.java‎

Lines changed: 481 additions & 1 deletion
Large diffs are not rendered by default.

‎android/viro_bridge/src/main/java/com/viromedia/bridge/component/node/control/VRT3DObject.java‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,17 @@ public void onObject3DLoaded(Object3D object, Object3D.Type type) {
205205
setMaterials(mMaterials);
206206
}
207207

208+
// Apply shader overrides if specified (preserves textures)
209+
Log.d(TAG, "VRT3DObject loaded, mShaderOverrides: " +
210+
(mShaderOverrides != null ? mShaderOverrides.size() + " overrides" : "null"));
211+
if (mShaderOverrides != null && !mShaderOverrides.isEmpty()) {
212+
Log.d(TAG, "Applying shader overrides recursively: " + mShaderOverrides);
213+
vrt3DObject.applyShaderOverridesRecursive(true);
214+
Log.d(TAG, "Shader overrides applied");
215+
} else {
216+
Log.d(TAG, "No shader overrides to apply");
217+
}
218+
208219
// Set the bitmasks recursively now that the tree is loaded
209220
object.setLightReceivingBitMask(mLightReceivingBitMask);
210221
object.setShadowCastingBitMask(mShadowCastingBitMask);

‎android/viro_bridge/src/main/java/com/viromedia/bridge/component/node/control/VRT3DObjectManager.java‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,23 @@ public void setMorphTargets(VRT3DObject object3d, @Nullable ReadableArray target
104104
object3d.setMorphTargets(targetList);
105105
}
106106

107+
@ReactProp(name = "shaderOverrides")
108+
public void setShaderOverrides(VRT3DObject object3d, @Nullable ReadableArray shaderOverrides) {
109+
if (shaderOverrides == null || shaderOverrides.size() <= 0) {
110+
object3d.setShaderOverrides(null);
111+
return;
112+
}
113+
114+
List<String> shaderOverridesList = new ArrayList<>();
115+
for (int i = 0; i < shaderOverrides.size(); i++) {
116+
String materialName = shaderOverrides.getString(i);
117+
if (materialName != null && !materialName.isEmpty()) {
118+
shaderOverridesList.add(materialName);
119+
}
120+
}
121+
object3d.setShaderOverrides(shaderOverridesList);
122+
}
123+
107124
@ReactProp(name = "type")
108125
public void setType(VRT3DObject object3d, @Nullable String type) {
109126
object3d.setType(type);

‎android/viro_bridge/src/main/java/com/viromedia/bridge/module/MaterialManager.java‎

Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import android.net.Uri;
2828
import android.util.Log;
2929

30+
import com.facebook.react.bridge.Dynamic;
3031
import com.facebook.react.bridge.ReactApplicationContext;
3132
import com.facebook.react.bridge.ReactContextBaseJavaModule;
3233
import com.facebook.react.bridge.ReactMethod;
@@ -162,6 +163,64 @@ public void deleteMaterials(ReadableArray materials) {
162163
}
163164
}
164165

166+
@ReactMethod
167+
public void updateShaderUniform(String materialName, String uniformName, String uniformType, Dynamic value) {
168+
Material material = getMaterial(materialName);
169+
if (material == null) {
170+
Log.w("VRTMaterialManager", "Cannot update shader uniform: material '" + materialName + "' not found");
171+
return;
172+
}
173+
174+
Object uniformValue = null;
175+
176+
if ("float".equalsIgnoreCase(uniformType)) {
177+
float floatValue = (float) value.asDouble();
178+
material.setShaderUniform(uniformName, floatValue);
179+
uniformValue = floatValue;
180+
} else if ("vec3".equalsIgnoreCase(uniformType)) {
181+
ReadableArray arr = value.asArray();
182+
if (arr != null && arr.size() >= 3) {
183+
float x = (float) arr.getDouble(0);
184+
float y = (float) arr.getDouble(1);
185+
float z = (float) arr.getDouble(2);
186+
material.setShaderUniform(uniformName, x, y, z);
187+
uniformValue = new float[]{x, y, z};
188+
}
189+
} else if ("vec4".equalsIgnoreCase(uniformType)) {
190+
ReadableArray arr = value.asArray();
191+
if (arr != null && arr.size() >= 4) {
192+
float x = (float) arr.getDouble(0);
193+
float y = (float) arr.getDouble(1);
194+
float z = (float) arr.getDouble(2);
195+
float w = (float) arr.getDouble(3);
196+
material.setShaderUniform(uniformName, x, y, z, w);
197+
uniformValue = new float[]{x, y, z, w};
198+
}
199+
} else if ("mat4".equalsIgnoreCase(uniformType)) {
200+
ReadableArray arr = value.asArray();
201+
if (arr != null && arr.size() >= 16) {
202+
float[] matrix = new float[16];
203+
for (int i = 0; i < 16; i++) {
204+
matrix[i] = (float) arr.getDouble(i);
205+
}
206+
material.setShaderUniform(uniformName, matrix);
207+
uniformValue = matrix;
208+
}
209+
}
210+
211+
// Propagate uniform updates to:
212+
// 1. Shader override clones (for shaderOverrides on 3D models)
213+
// 2. All geometries using this material directly (for materials prop)
214+
if (uniformValue != null) {
215+
com.viromedia.bridge.component.node.VRTNode.updateShaderOverrideUniform(
216+
materialName, uniformName, uniformType, uniformValue);
217+
}
218+
219+
// Refresh all nodes using this material directly (materials={["name"]} prop)
220+
// This re-applies the material with updated uniforms to the geometry
221+
com.viromedia.bridge.component.node.VRTNode.refreshNodesUsingMaterial(materialName);
222+
}
223+
165224
private void loadMaterials(ReadableMap newMaterials) {
166225
ReadableMapKeySetIterator iter = newMaterials.keySetIterator();
167226
while (iter.hasNextKey()) {
@@ -336,6 +395,12 @@ else if(materialPropertyName.equalsIgnoreCase("chromaKeyFilteringColor")) {
336395
parsePBRProperties(PBRProperties.ROUGHNESS, nativeMaterial, materialMap);
337396
parsePBRProperties(PBRProperties.AMBIENT_OCCLUSION_TEXTURE, nativeMaterial, materialMap);
338397

398+
// Parse shader modifiers
399+
parseShaderModifiers(nativeMaterial, materialMap);
400+
401+
// Parse material/shader uniforms
402+
parseShaderUniforms(nativeMaterial, materialMap);
403+
339404
// We don't need to hold a Java texture reference after assigning the texture to the material.
340405
// Make an exception for the videoTexture as we use the nativeref to play,pause, loop the video.
341406
if (diffuseTexture != null && videoTexture == null) {
@@ -383,6 +448,157 @@ private void parsePBRProperties(PBRProperties property, Material material, Reada
383448
}
384449
}
385450

451+
private void parseShaderModifiers(Material material, ReadableMap materialMap) {
452+
if (!materialMap.hasKey("shaderModifiers")) {
453+
Log.d("VRTMaterialManager", "No shaderModifiers key in material map");
454+
return;
455+
}
456+
457+
ReadableMap modifiers = materialMap.getMap("shaderModifiers");
458+
if (modifiers == null) {
459+
Log.d("VRTMaterialManager", "shaderModifiers map is null");
460+
return;
461+
}
462+
463+
Log.d("VRTMaterialManager", "Parsing shader modifiers for material");
464+
465+
ReadableMapKeySetIterator iter = modifiers.keySetIterator();
466+
while (iter.hasNextKey()) {
467+
String entryPointName = iter.nextKey();
468+
ReadableType type = modifiers.getType(entryPointName);
469+
470+
Log.d("VRTMaterialManager", "Processing entry point: " + entryPointName + ", type: " + type);
471+
472+
String modifierCode = null;
473+
474+
// Handle both string and dictionary formats
475+
if (type == ReadableType.String) {
476+
modifierCode = modifiers.getString(entryPointName);
477+
Log.d("VRTMaterialManager", "String format, code length: " + (modifierCode != null ? modifierCode.length() : 0));
478+
} else if (type == ReadableType.Map) {
479+
ReadableMap modifierDict = modifiers.getMap(entryPointName);
480+
String uniforms = modifierDict.hasKey("uniforms") ? modifierDict.getString("uniforms") : null;
481+
String body = modifierDict.hasKey("body") ? modifierDict.getString("body") : null;
482+
483+
if (uniforms != null && uniforms.length() > 0) {
484+
modifierCode = uniforms + "\n" + (body != null ? body : "");
485+
} else {
486+
modifierCode = body;
487+
}
488+
489+
if (modifierCode == null) {
490+
Log.e("VRTMaterialManager", "Shader modifier dictionary must contain 'body' or 'uniforms' key");
491+
continue;
492+
}
493+
Log.d("VRTMaterialManager", "Map format, code length: " + modifierCode.length());
494+
} else {
495+
Log.e("VRTMaterialManager", "Shader modifier must be string or dictionary with 'body' key");
496+
continue;
497+
}
498+
499+
if (modifierCode != null && modifierCode.length() > 0) {
500+
Log.d("VRTMaterialManager", "Calling material.addShaderModifier for entry point: " + entryPointName);
501+
material.addShaderModifier(entryPointName, modifierCode);
502+
Log.d("VRTMaterialManager", "Successfully added shader modifier");
503+
}
504+
}
505+
}
506+
507+
private void parseShaderUniforms(Material material, ReadableMap materialMap) {
508+
// Check for both "materialUniforms" and "shaderUniforms" keys
509+
String[] uniformKeys = {"materialUniforms", "shaderUniforms"};
510+
511+
for (String uniformKey : uniformKeys) {
512+
if (!materialMap.hasKey(uniformKey)) {
513+
continue;
514+
}
515+
516+
ReadableType type = materialMap.getType(uniformKey);
517+
518+
// Support array format: [{name, type, value}, ...]
519+
if (type == ReadableType.Array) {
520+
ReadableArray uniforms = materialMap.getArray(uniformKey);
521+
for (int i = 0; i < uniforms.size(); i++) {
522+
if (uniforms.getType(i) != ReadableType.Map) {
523+
continue;
524+
}
525+
526+
ReadableMap uniform = uniforms.getMap(i);
527+
if (!uniform.hasKey("name") || !uniform.hasKey("type")) {
528+
continue;
529+
}
530+
531+
String name = uniform.getString("name");
532+
String uniformType = uniform.getString("type");
533+
setShaderUniform(material, name, uniformType, uniform);
534+
}
535+
}
536+
// Support dictionary format: {uniformName: {type, value}}
537+
else if (type == ReadableType.Map) {
538+
ReadableMap uniforms = materialMap.getMap(uniformKey);
539+
ReadableMapKeySetIterator iter = uniforms.keySetIterator();
540+
541+
while (iter.hasNextKey()) {
542+
String name = iter.nextKey();
543+
if (uniforms.getType(name) != ReadableType.Map) {
544+
continue;
545+
}
546+
547+
ReadableMap uniform = uniforms.getMap(name);
548+
if (!uniform.hasKey("type")) {
549+
continue;
550+
}
551+
552+
String uniformType = uniform.getString("type");
553+
setShaderUniform(material, name, uniformType, uniform);
554+
}
555+
}
556+
}
557+
}
558+
559+
private void setShaderUniform(Material material, String name, String type, ReadableMap uniformData) {
560+
if (!uniformData.hasKey("value")) {
561+
return;
562+
}
563+
564+
Log.d("VRTMaterialManager", "Setting shader uniform: " + name + " of type: " + type);
565+
566+
if ("float".equalsIgnoreCase(type)) {
567+
float value = (float) uniformData.getDouble("value");
568+
material.setShaderUniform(name, value);
569+
Log.d("VRTMaterialManager", "Set float uniform: " + name + " = " + value);
570+
} else if ("vec3".equalsIgnoreCase(type)) {
571+
ReadableArray arr = uniformData.getArray("value");
572+
if (arr != null && arr.size() >= 3) {
573+
float x = (float) arr.getDouble(0);
574+
float y = (float) arr.getDouble(1);
575+
float z = (float) arr.getDouble(2);
576+
material.setShaderUniform(name, x, y, z);
577+
Log.d("VRTMaterialManager", "Set vec3 uniform: " + name);
578+
}
579+
} else if ("vec4".equalsIgnoreCase(type)) {
580+
ReadableArray arr = uniformData.getArray("value");
581+
if (arr != null && arr.size() >= 4) {
582+
float x = (float) arr.getDouble(0);
583+
float y = (float) arr.getDouble(1);
584+
float z = (float) arr.getDouble(2);
585+
float w = (float) arr.getDouble(3);
586+
material.setShaderUniform(name, x, y, z, w);
587+
Log.d("VRTMaterialManager", "Set vec4 uniform: " + name);
588+
}
589+
} else if ("mat4".equalsIgnoreCase(type)) {
590+
ReadableArray arr = uniformData.getArray("value");
591+
if (arr != null && arr.size() >= 16) {
592+
float[] matrix = new float[16];
593+
for (int i = 0; i < 16; i++) {
594+
matrix[i] = (float) arr.getDouble(i);
595+
}
596+
material.setShaderUniform(name, matrix);
597+
Log.d("VRTMaterialManager", "Set mat4 uniform: " + name);
598+
}
599+
}
600+
}
601+
386602
private Texture parseTexture(Image image, boolean sRGB, boolean mipmap,
387603
String name, ReadableMap materialMap) {
388604
Texture nativeTexture = new Texture(image, sRGB, mipmap);
35.2 KB
Binary file not shown.

‎components/AR/ViroCommonProps.ts‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ import {
1515
ViroScale,
1616
ViroSource,
1717
} from "../Types/ViroUtils";
18+
import {
19+
ViroShaderModifiers,
20+
ViroShaderUniform,
21+
} from "../Material/ViroMaterials";
1822
import { NativeSyntheticEvent, ViewProps } from "react-native";
1923

2024
export type ViroCommonProps = ViewProps & {
@@ -98,6 +102,10 @@ export type ViroObjectProps = {
98102
lightReceivingBitMask?: number;
99103
shadowCastingBitMask?: number;
100104

105+
shaderModifiers?: ViroShaderModifiers;
106+
shaderOverrides?: string[];
107+
materialUniforms?: { [key: string]: any } | ViroShaderUniform[];
108+
101109
onTransformUpdate?: (position: Viro3DPoint) => void;
102110

103111
/**

0 commit comments

Comments
 (0)