@reactvision/react-viro 2.44.0 → 2.44.2
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.
- package/android/react_viro/react_viro-release.aar +0 -0
- package/android/viro_renderer/viro_renderer-release.aar +0 -0
- package/components/AR/ViroARPlaneSelector.tsx +226 -100
- package/components/Material/ViroMaterials.ts +4 -2
- package/components/Types/ViroEvents.ts +50 -5
- package/components/Utilities/ViroVersion.ts +1 -1
- package/dist/components/AR/ViroARPlaneSelector.d.ts +15 -15
- package/dist/components/AR/ViroARPlaneSelector.js +130 -55
- package/dist/components/Material/ViroMaterials.js +2 -2
- package/dist/components/Types/ViroEvents.d.ts +30 -4
- package/dist/components/Utilities/ViroVersion.d.ts +1 -1
- package/dist/components/Utilities/ViroVersion.js +1 -1
- package/ios/dist/ViroRenderer/ViroKit.framework/ARCoreResources.bundle/Info.plist +0 -0
- package/ios/dist/ViroRenderer/ViroKit.framework/CardboardSDK.bundle/Info.plist +0 -0
- package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROARDeclarativeNode.h +1 -1
- package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROARDeclarativePlane.h +21 -2
- package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROARPlaneAnchor.h +191 -4
- package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROVector2f.h +124 -0
- package/ios/dist/ViroRenderer/ViroKit.framework/Info.plist +0 -0
- package/ios/dist/ViroRenderer/ViroKit.framework/ViroKit +0 -0
- package/ios/dist/ViroRenderer/armv7_arm64/ViroKit.framework/ARCoreResources.bundle/Info.plist +0 -0
- package/ios/dist/ViroRenderer/armv7_arm64/ViroKit.framework/CardboardSDK.bundle/Info.plist +0 -0
- package/ios/dist/ViroRenderer/armv7_arm64/ViroKit.framework/Headers/VROARDeclarativeNode.h +1 -1
- package/ios/dist/ViroRenderer/armv7_arm64/ViroKit.framework/Headers/VROARDeclarativePlane.h +21 -2
- package/ios/dist/ViroRenderer/armv7_arm64/ViroKit.framework/Headers/VROARPlaneAnchor.h +191 -4
- package/ios/dist/ViroRenderer/armv7_arm64/ViroKit.framework/Headers/VROVector2f.h +124 -0
- package/ios/dist/ViroRenderer/armv7_arm64/ViroKit.framework/Info.plist +0 -0
- package/ios/dist/ViroRenderer/armv7_arm64/ViroKit.framework/ViroKit +0 -0
- package/ios/dist/ViroRenderer/x86_64/ViroKit.framework/CardboardSDK.bundle/Info.plist +0 -0
- package/ios/dist/ViroRenderer/x86_64/ViroKit.framework/Info.plist +0 -0
- package/ios/dist/armv7_arm64/libViroReact.a +0 -0
- package/ios/dist/lib/libViroReact.a +0 -0
- package/package.json +3 -3
|
@@ -48,8 +48,8 @@ const React = __importStar(require("react"));
|
|
|
48
48
|
const ViroMaterials_1 = require("../Material/ViroMaterials");
|
|
49
49
|
const ViroNode_1 = require("../ViroNode");
|
|
50
50
|
const ViroQuad_1 = require("../ViroQuad");
|
|
51
|
+
const ViroPolygon_1 = require("../ViroPolygon");
|
|
51
52
|
const ViroARPlane_1 = require("./ViroARPlane");
|
|
52
|
-
var _maxPlanes = 15;
|
|
53
53
|
var _planePrefix = "ViroARPlaneSelector_Plane_";
|
|
54
54
|
/**
|
|
55
55
|
* This component wraps the logic required to enable user selection
|
|
@@ -59,9 +59,8 @@ var _planePrefix = "ViroARPlaneSelector_Plane_";
|
|
|
59
59
|
class ViroARPlaneSelector extends React.Component {
|
|
60
60
|
_component = null;
|
|
61
61
|
state = {
|
|
62
|
-
|
|
63
|
-
foundARPlanes:
|
|
64
|
-
arPlaneSizes: [],
|
|
62
|
+
selectedPlaneId: null,
|
|
63
|
+
foundARPlanes: new Map(),
|
|
65
64
|
};
|
|
66
65
|
render() {
|
|
67
66
|
// Uncomment this line to check for misnamed props
|
|
@@ -69,78 +68,151 @@ class ViroARPlaneSelector extends React.Component {
|
|
|
69
68
|
return <ViroNode_1.ViroNode>{this._getARPlanes()}</ViroNode_1.ViroNode>;
|
|
70
69
|
}
|
|
71
70
|
_getARPlanes() {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
const isSelected = this.state.selectedSurface === i;
|
|
79
|
-
// Get real plane data if available, or use defaults
|
|
80
|
-
const foundARPlane = this.state.foundARPlanes[i];
|
|
81
|
-
const hasPlaneData = !!foundARPlane;
|
|
82
|
-
// Extract plane data or use defaults
|
|
83
|
-
const surfaceWidth = hasPlaneData ? foundARPlane.width || 0.5 : 0.5;
|
|
84
|
-
const surfaceHeight = hasPlaneData ? foundARPlane.height || 0.5 : 0.5;
|
|
85
|
-
const surfacePosition = hasPlaneData
|
|
86
|
-
? foundARPlane.center || [0, 0, 0]
|
|
87
|
-
: [0, 0, 0];
|
|
88
|
-
const anchorId = hasPlaneData
|
|
89
|
-
? foundARPlane.anchorId
|
|
90
|
-
: undefined;
|
|
91
|
-
// Determine visibility based on selection state
|
|
92
|
-
// - In selection mode (selectedSurface === -1): show all planes
|
|
93
|
-
// - In selected mode: only show the selected plane
|
|
94
|
-
const isVisible = this.state.selectedSurface === -1 || isSelected;
|
|
95
|
-
arPlanes.push(<ViroARPlane_1.ViroARPlane key={_planePrefix + i} minWidth={this.props.minWidth || 0.5} minHeight={this.props.minHeight || 0.5} alignment={this.props.alignment} anchorId={anchorId} onAnchorFound={(anchor) => {
|
|
96
|
-
// If we find an anchor, update our plane data
|
|
97
|
-
this._onARPlaneUpdated(i)(anchor);
|
|
98
|
-
}} onAnchorUpdated={this._onARPlaneUpdated(i)}>
|
|
99
|
-
{/* Always render both the quad and children, controlling only visibility */}
|
|
100
|
-
<ViroQuad_1.ViroQuad materials={"ViroARPlaneSelector_Translucent"} onClickState={(clickState, position, source) => this._getOnClickSurface(i, { clickState, position, source })} position={surfacePosition} width={surfaceWidth} height={surfaceHeight} rotation={[-90, 0, 0]} opacity={isSelected ? 0 : isVisible ? 1 : 0}/>
|
|
101
|
-
|
|
102
|
-
{/* Wrap children in a ViroNode to control visibility if children exist */}
|
|
103
|
-
{this.props.children && (<ViroNode_1.ViroNode opacity={isSelected ? 1 : 0}>
|
|
104
|
-
{this.props.children}
|
|
105
|
-
</ViroNode_1.ViroNode>)}
|
|
106
|
-
</ViroARPlane_1.ViroARPlane>);
|
|
71
|
+
const arPlanes = [];
|
|
72
|
+
const detectBothAlignments = this.props.alignment === "Both" || !this.props.alignment;
|
|
73
|
+
// Determine which alignments to detect
|
|
74
|
+
const alignmentsToDetect = [];
|
|
75
|
+
if (detectBothAlignments) {
|
|
76
|
+
alignmentsToDetect.push("Horizontal", "Vertical");
|
|
107
77
|
}
|
|
78
|
+
else if (this.props.alignment) {
|
|
79
|
+
// Type assertion safe here because we know it's not "Both" due to detectBothAlignments check
|
|
80
|
+
alignmentsToDetect.push(this.props.alignment);
|
|
81
|
+
}
|
|
82
|
+
// Create detector ViroARPlane components for each alignment type
|
|
83
|
+
// These don't have anchorId set initially, but will discover and track planes
|
|
84
|
+
// We add visual children based on detected plane data
|
|
85
|
+
const detectorsPerAlignment = 25; // 25 detectors per alignment type
|
|
86
|
+
alignmentsToDetect.forEach((alignment) => {
|
|
87
|
+
for (let i = 0; i < detectorsPerAlignment; i++) {
|
|
88
|
+
const detectorKey = `${_planePrefix}detector_${alignment}_${i}`;
|
|
89
|
+
// Check if this detector has discovered a plane
|
|
90
|
+
// We'll match by checking if any plane in foundARPlanes has this alignment
|
|
91
|
+
// and hasn't been assigned to a previous detector
|
|
92
|
+
// Note: ARCore returns "HorizontalUpward", "HorizontalDownward", etc.
|
|
93
|
+
// so we need to check if alignment starts with the requested type
|
|
94
|
+
const detectedPlanes = Array.from(this.state.foundARPlanes.entries()).filter(([_id, plane]) => {
|
|
95
|
+
if (alignment === "Horizontal") {
|
|
96
|
+
return plane.alignment.includes("Horizontal");
|
|
97
|
+
}
|
|
98
|
+
else if (alignment === "Vertical") {
|
|
99
|
+
return plane.alignment.includes("Vertical");
|
|
100
|
+
}
|
|
101
|
+
return plane.alignment === alignment;
|
|
102
|
+
});
|
|
103
|
+
const planeData = detectedPlanes[i]?.[1];
|
|
104
|
+
const anchorId = detectedPlanes[i]?.[0];
|
|
105
|
+
const hasPlaneData = !!planeData;
|
|
106
|
+
// Extract visual rendering data if plane detected
|
|
107
|
+
let visualElement = null;
|
|
108
|
+
if (hasPlaneData) {
|
|
109
|
+
const isSelected = this.state.selectedPlaneId === anchorId;
|
|
110
|
+
const surfaceWidth = planeData.width || 0.5;
|
|
111
|
+
const surfaceHeight = planeData.height || 0.5;
|
|
112
|
+
const vertices3D = planeData.vertices;
|
|
113
|
+
// Convert 3D vertices to 2D based on plane alignment
|
|
114
|
+
// ViroARPlane provides vertices in the plane's LOCAL coordinate system
|
|
115
|
+
// where the plane is always in the XZ plane. The anchor handles world orientation.
|
|
116
|
+
// Always extract [x, z] since vertices are in the plane's local XZ plane
|
|
117
|
+
const vertices2D = vertices3D && vertices3D.length >= 3
|
|
118
|
+
? vertices3D.map(([x, _y, z]) => [
|
|
119
|
+
x,
|
|
120
|
+
z,
|
|
121
|
+
])
|
|
122
|
+
: undefined;
|
|
123
|
+
// Rotation for ViroPolygon:
|
|
124
|
+
// ViroPolygon renders in XY plane by default, vertices are provided in XZ
|
|
125
|
+
// Need to rotate to map XZ plane to XY rendering plane
|
|
126
|
+
const polygonRotation = [-90, 0, 0];
|
|
127
|
+
const isVisible = this.state.selectedPlaneId === null || isSelected;
|
|
128
|
+
// Use actual plane shapes (ViroPolygon with vertices)
|
|
129
|
+
const forceQuadForAndroid = false; // Now using actual shapes on Android
|
|
130
|
+
const useActualShape = !forceQuadForAndroid &&
|
|
131
|
+
this.props.useActualShape !== false &&
|
|
132
|
+
vertices2D &&
|
|
133
|
+
vertices2D.length >= 3;
|
|
134
|
+
const finalOpacity = isSelected ? 0 : isVisible ? 1 : 0;
|
|
135
|
+
visualElement = useActualShape ? (<ViroPolygon_1.ViroPolygon key={`polygon-${anchorId}`} vertices={vertices2D} holes={[]} materials={["ViroARPlaneSelector_Translucent"]} {...(!this.props.disableClickSelection && {
|
|
136
|
+
onClickState: (clickState, position, source) => this._getOnClickSurface(anchorId, {
|
|
137
|
+
clickState,
|
|
138
|
+
position,
|
|
139
|
+
source,
|
|
140
|
+
}),
|
|
141
|
+
})} position={[0, 0, 0]} rotation={polygonRotation} opacity={finalOpacity}/>) : (<ViroQuad_1.ViroQuad key={`quad-${anchorId}`} materials={["ViroARPlaneSelector_Translucent"]} {...(!this.props.disableClickSelection && {
|
|
142
|
+
onClickState: (clickState, position, source) => this._getOnClickSurface(anchorId, {
|
|
143
|
+
clickState,
|
|
144
|
+
position,
|
|
145
|
+
source,
|
|
146
|
+
}),
|
|
147
|
+
})} position={[0, 0, 0]} width={surfaceWidth} height={surfaceHeight} rotation={polygonRotation} opacity={finalOpacity}/>);
|
|
148
|
+
}
|
|
149
|
+
arPlanes.push(<ViroARPlane_1.ViroARPlane key={detectorKey} minWidth={this.props.minWidth || 0} minHeight={this.props.minHeight || 0} alignment={alignment} anchorId={hasPlaneData ? anchorId : undefined} onAnchorFound={(anchor) => {
|
|
150
|
+
this._onARPlaneUpdated(anchor);
|
|
151
|
+
}} onAnchorUpdated={(anchor) => {
|
|
152
|
+
this._onARPlaneUpdated(anchor);
|
|
153
|
+
}}>
|
|
154
|
+
{visualElement}
|
|
155
|
+
{hasPlaneData && this.props.children && (<ViroNode_1.ViroNode opacity={this.state.selectedPlaneId === anchorId ? 1 : 0}>
|
|
156
|
+
{this.props.children}
|
|
157
|
+
</ViroNode_1.ViroNode>)}
|
|
158
|
+
</ViroARPlane_1.ViroARPlane>);
|
|
159
|
+
}
|
|
160
|
+
});
|
|
108
161
|
return arPlanes;
|
|
109
162
|
}
|
|
110
|
-
_getOnClickSurface = (
|
|
163
|
+
_getOnClickSurface = (anchorId, event) => {
|
|
111
164
|
if (event.clickState < 3) {
|
|
112
165
|
return;
|
|
113
166
|
}
|
|
114
167
|
// Get the plane data before updating state to avoid race conditions
|
|
115
|
-
const selectedPlane = this.state.foundARPlanes
|
|
168
|
+
const selectedPlane = this.state.foundARPlanes.get(anchorId);
|
|
116
169
|
if (!selectedPlane) {
|
|
117
170
|
console.warn("ViroARPlaneSelector: Cannot select plane - plane data not found");
|
|
118
171
|
return;
|
|
119
172
|
}
|
|
120
173
|
// Update state and call callback with the captured data
|
|
121
|
-
this.setState({
|
|
174
|
+
this.setState({ selectedPlaneId: anchorId }, () => {
|
|
122
175
|
this._onPlaneSelected(selectedPlane);
|
|
123
176
|
});
|
|
124
177
|
};
|
|
125
|
-
_onARPlaneUpdated = (
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
178
|
+
_onARPlaneUpdated = (anchor) => {
|
|
179
|
+
if (!anchor.anchorId) {
|
|
180
|
+
console.warn("ViroARPlaneSelector: Anchor missing anchorId");
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
const updateMap = {
|
|
184
|
+
anchorId: anchor.anchorId,
|
|
185
|
+
type: anchor.type || "plane",
|
|
186
|
+
position: anchor.position,
|
|
187
|
+
rotation: anchor.rotation,
|
|
188
|
+
scale: anchor.scale,
|
|
189
|
+
center: anchor.center,
|
|
190
|
+
width: anchor.width,
|
|
191
|
+
height: anchor.height,
|
|
192
|
+
alignment: anchor.alignment,
|
|
193
|
+
classification: anchor.classification,
|
|
194
|
+
vertices: anchor.vertices,
|
|
133
195
|
};
|
|
196
|
+
// Update or add plane in Map
|
|
197
|
+
this.setState((prevState) => {
|
|
198
|
+
const newPlanes = new Map(prevState.foundARPlanes);
|
|
199
|
+
newPlanes.set(anchor.anchorId, updateMap);
|
|
200
|
+
return { foundARPlanes: newPlanes };
|
|
201
|
+
});
|
|
202
|
+
// Call validation callback if provided
|
|
203
|
+
if (this.props.onPlaneDetected) {
|
|
204
|
+
this.props.onPlaneDetected(updateMap);
|
|
205
|
+
}
|
|
134
206
|
};
|
|
135
207
|
_onPlaneSelected = (updateMap) => {
|
|
136
208
|
this.props.onPlaneSelected && this.props.onPlaneSelected(updateMap);
|
|
137
209
|
};
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
210
|
+
/**
|
|
211
|
+
* This function allows the user to reset the surface and select a new plane.
|
|
212
|
+
*/
|
|
141
213
|
reset = () => {
|
|
142
214
|
this.setState({
|
|
143
|
-
|
|
215
|
+
selectedPlaneId: null,
|
|
144
216
|
});
|
|
145
217
|
};
|
|
146
218
|
}
|
|
@@ -148,6 +220,9 @@ exports.ViroARPlaneSelector = ViroARPlaneSelector;
|
|
|
148
220
|
ViroMaterials_1.ViroMaterials.createMaterials({
|
|
149
221
|
ViroARPlaneSelector_Translucent: {
|
|
150
222
|
lightingModel: "Constant",
|
|
151
|
-
diffuseColor: "
|
|
223
|
+
diffuseColor: "rgba(0, 122, 255, 0.5)", // Bright blue with 50% opacity for better visibility
|
|
224
|
+
blendMode: "Alpha",
|
|
225
|
+
cullMode: "None", // Render both sides for better Android compatibility
|
|
226
|
+
writesToDepthBuffer: false,
|
|
152
227
|
},
|
|
153
228
|
});
|
|
@@ -17,7 +17,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
17
17
|
exports.ViroMaterials = void 0;
|
|
18
18
|
const react_native_1 = require("react-native");
|
|
19
19
|
// @ts-ignore
|
|
20
|
-
const AssetRegistry_1 =
|
|
20
|
+
const AssetRegistry_1 = require("react-native/Libraries/Image/AssetRegistry");
|
|
21
21
|
// @ts-ignore
|
|
22
22
|
const resolveAssetSource_1 = __importDefault(require("react-native/Libraries/Image/resolveAssetSource"));
|
|
23
23
|
var MaterialManager = react_native_1.NativeModules.VRTMaterialManager;
|
|
@@ -47,7 +47,7 @@ class ViroMaterials {
|
|
|
47
47
|
else {
|
|
48
48
|
var assetType = "unknown";
|
|
49
49
|
if (typeof material[prop] !== "object") {
|
|
50
|
-
var asset = AssetRegistry_1.
|
|
50
|
+
var asset = (0, AssetRegistry_1.getAssetByID)(material[prop]);
|
|
51
51
|
if (asset) {
|
|
52
52
|
assetType = asset.type;
|
|
53
53
|
}
|
|
@@ -167,6 +167,35 @@ export type ViroAnimatedComponentFinishEvent = {};
|
|
|
167
167
|
/** ===========================================================================
|
|
168
168
|
* Viro AR Anchor Events
|
|
169
169
|
* ============================================================================ */
|
|
170
|
+
/**
|
|
171
|
+
* Classification of detected planes.
|
|
172
|
+
* iOS 12+ provides ML-based classification via ARKit.
|
|
173
|
+
* Android provides basic inference from plane orientation.
|
|
174
|
+
*/
|
|
175
|
+
export type ViroARPlaneClassification = "None" | "Wall" | "Floor" | "Ceiling" | "Table" | "Seat" | "Door" | "Window" | "Unknown";
|
|
176
|
+
/**
|
|
177
|
+
* Alignment of detected planes with respect to gravity.
|
|
178
|
+
*/
|
|
179
|
+
export type ViroARPlaneAlignment = "Horizontal" | "HorizontalUpward" | "HorizontalDownward" | "Vertical";
|
|
180
|
+
/**
|
|
181
|
+
* Represents an AR anchor detected in the real world.
|
|
182
|
+
*/
|
|
183
|
+
export type ViroAnchor = {
|
|
184
|
+
anchorId: string;
|
|
185
|
+
type: "anchor" | "plane" | "image";
|
|
186
|
+
position: [number, number, number];
|
|
187
|
+
rotation: [number, number, number];
|
|
188
|
+
scale: [number, number, number];
|
|
189
|
+
center?: [number, number, number];
|
|
190
|
+
width?: number;
|
|
191
|
+
height?: number;
|
|
192
|
+
alignment?: ViroARPlaneAlignment;
|
|
193
|
+
classification?: ViroARPlaneClassification;
|
|
194
|
+
vertices?: Array<[number, number, number]>;
|
|
195
|
+
trackingMethod?: string;
|
|
196
|
+
};
|
|
197
|
+
export type ViroAnchorFoundMap = ViroAnchor;
|
|
198
|
+
export type ViroAnchorUpdatedMap = ViroAnchor;
|
|
170
199
|
export type ViroARAnchorRemovedEvent = {
|
|
171
200
|
anchor: ViroAnchor;
|
|
172
201
|
};
|
|
@@ -178,13 +207,10 @@ export type ViroARAnchorFoundEvent = {
|
|
|
178
207
|
anchorFoundMap: ViroAnchorFoundMap;
|
|
179
208
|
anchor: ViroAnchor;
|
|
180
209
|
};
|
|
181
|
-
export type ViroAnchor = any;
|
|
182
|
-
export type ViroAnchorFoundMap = any;
|
|
183
|
-
export type ViroAnchorUpdatedMap = any;
|
|
184
210
|
/** ===========================================================================
|
|
185
211
|
* Viro AR Plane Events
|
|
186
212
|
* ============================================================================ */
|
|
187
|
-
export type ViroPlaneUpdatedMap =
|
|
213
|
+
export type ViroPlaneUpdatedMap = ViroAnchor;
|
|
188
214
|
export type ViroPlaneUpdatedEvent = any;
|
|
189
215
|
export type ViroARPlaneSizes = any;
|
|
190
216
|
/** ===========================================================================
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const VIRO_VERSION = "2.44.
|
|
1
|
+
export declare const VIRO_VERSION = "2.44.2";
|
|
Binary file
|
|
Binary file
|
|
@@ -46,14 +46,31 @@ public:
|
|
|
46
46
|
|
|
47
47
|
/*
|
|
48
48
|
Returns whether or not the given VROARAnchor fulfills this plane's requirements.
|
|
49
|
+
|
|
50
|
+
NOTE: This uses hysteresis for CONSTRAINT MATCHING only - it doesn't affect the
|
|
51
|
+
precision of plane data from ARCore/ARKit. Hysteresis prevents nodes from rapidly
|
|
52
|
+
attaching/detaching when plane dimensions fluctuate near the app's minimum requirements.
|
|
53
|
+
|
|
54
|
+
This is application-level logic and does not filter or modify ARCore/ARKit plane data.
|
|
49
55
|
*/
|
|
50
56
|
bool hasRequirementsFulfilled(std::shared_ptr<VROARAnchor> candidate) {
|
|
51
57
|
std::shared_ptr<VROARPlaneAnchor> planeAnchor = std::dynamic_pointer_cast<VROARPlaneAnchor>(candidate->getAnchorForTrackable());
|
|
52
58
|
if (!planeAnchor) {
|
|
53
59
|
return false;
|
|
54
60
|
}
|
|
55
|
-
|
|
56
|
-
if
|
|
61
|
+
|
|
62
|
+
// Apply hysteresis: if the plane is already attached to this anchor,
|
|
63
|
+
// allow it to be 10% smaller before detaching. This prevents flickering
|
|
64
|
+
// when plane dimensions fluctuate near the application's size threshold.
|
|
65
|
+
// This does NOT affect the precision of the plane data itself.
|
|
66
|
+
bool isCurrentlyAttached = (getAnchor() == candidate);
|
|
67
|
+
float hysteresisMargin = isCurrentlyAttached ? 0.90f : 1.0f;
|
|
68
|
+
|
|
69
|
+
float effectiveMinWidth = _minWidth * hysteresisMargin;
|
|
70
|
+
float effectiveMinHeight = _minHeight * hysteresisMargin;
|
|
71
|
+
|
|
72
|
+
if (planeAnchor->getExtent().x < effectiveMinWidth ||
|
|
73
|
+
planeAnchor->getExtent().z < effectiveMinHeight) {
|
|
57
74
|
return false;
|
|
58
75
|
}
|
|
59
76
|
|
|
@@ -98,10 +115,12 @@ public:
|
|
|
98
115
|
&& _alignment != VROARPlaneAlignment::HorizontalDownward) {
|
|
99
116
|
return false;
|
|
100
117
|
}
|
|
118
|
+
break;
|
|
101
119
|
case VROARPlaneAlignment::Vertical:
|
|
102
120
|
if (_alignment != VROARPlaneAlignment::Vertical) {
|
|
103
121
|
return false;
|
|
104
122
|
}
|
|
123
|
+
break;
|
|
105
124
|
default:
|
|
106
125
|
break;
|
|
107
126
|
}
|
|
@@ -29,6 +29,20 @@
|
|
|
29
29
|
|
|
30
30
|
#include "VROARAnchor.h"
|
|
31
31
|
#include "VROVector3f.h"
|
|
32
|
+
#include "VROVector2f.h"
|
|
33
|
+
#include <chrono>
|
|
34
|
+
|
|
35
|
+
/*
|
|
36
|
+
ENABLED: Change detection and update throttling to reduce noise and artifacts.
|
|
37
|
+
This filters out small plane changes and prevents excessive updates, which is
|
|
38
|
+
particularly important for vertical plane detection where ARCore can be noisy.
|
|
39
|
+
|
|
40
|
+
Thresholds:
|
|
41
|
+
- Minimum extent change: 1cm or 5%
|
|
42
|
+
- Minimum center change: 1cm
|
|
43
|
+
- Update throttle: 100ms (10 updates/sec max)
|
|
44
|
+
*/
|
|
45
|
+
#define VRO_PLANE_CHANGE_DETECTION_ENABLED
|
|
32
46
|
|
|
33
47
|
enum class VROARPlaneAlignment {
|
|
34
48
|
Horizontal = 0x1,
|
|
@@ -37,14 +51,33 @@ enum class VROARPlaneAlignment {
|
|
|
37
51
|
Vertical = 0x10,
|
|
38
52
|
};
|
|
39
53
|
|
|
54
|
+
/*
|
|
55
|
+
Classification of detected planes (iOS 12+, ARCore semantic labels).
|
|
56
|
+
Indicates the semantic meaning of a detected plane.
|
|
57
|
+
*/
|
|
58
|
+
enum class VROARPlaneClassification {
|
|
59
|
+
None,
|
|
60
|
+
Wall,
|
|
61
|
+
Floor,
|
|
62
|
+
Ceiling,
|
|
63
|
+
Table,
|
|
64
|
+
Seat,
|
|
65
|
+
Door,
|
|
66
|
+
Window,
|
|
67
|
+
Unknown
|
|
68
|
+
};
|
|
69
|
+
|
|
40
70
|
/*
|
|
41
71
|
Anchor representing a planar surface.
|
|
42
72
|
*/
|
|
43
73
|
class VROARPlaneAnchor : public VROARAnchor {
|
|
44
|
-
|
|
74
|
+
|
|
45
75
|
public:
|
|
46
|
-
|
|
47
|
-
VROARPlaneAnchor()
|
|
76
|
+
|
|
77
|
+
VROARPlaneAnchor() :
|
|
78
|
+
_lastUpdateTime(std::chrono::steady_clock::now()),
|
|
79
|
+
_updateCount(0),
|
|
80
|
+
_significantChangeCount(0) {}
|
|
48
81
|
virtual ~VROARPlaneAnchor() {}
|
|
49
82
|
|
|
50
83
|
/*
|
|
@@ -85,7 +118,140 @@ public:
|
|
|
85
118
|
std::vector<VROVector3f> getBoundaryVertices() {
|
|
86
119
|
return _boundaryVertices;
|
|
87
120
|
}
|
|
88
|
-
|
|
121
|
+
|
|
122
|
+
/*
|
|
123
|
+
Full mesh geometry (iOS 11.3+ only - ARSCNPlaneGeometry equivalent).
|
|
124
|
+
Provides detailed tessellated surface representation beyond just boundary.
|
|
125
|
+
On Android/ARCore, these will be empty as ARCore only provides boundaries.
|
|
126
|
+
*/
|
|
127
|
+
void setMeshVertices(std::vector<VROVector3f> vertices) {
|
|
128
|
+
_meshVertices = std::move(vertices);
|
|
129
|
+
}
|
|
130
|
+
std::vector<VROVector3f> getMeshVertices() const {
|
|
131
|
+
return _meshVertices;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
void setTextureCoordinates(std::vector<VROVector2f> uvs) {
|
|
135
|
+
_textureCoordinates = std::move(uvs);
|
|
136
|
+
}
|
|
137
|
+
std::vector<VROVector2f> getTextureCoordinates() const {
|
|
138
|
+
return _textureCoordinates;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
void setTriangleIndices(std::vector<int> indices) {
|
|
142
|
+
_triangleIndices = std::move(indices);
|
|
143
|
+
}
|
|
144
|
+
std::vector<int> getTriangleIndices() const {
|
|
145
|
+
return _triangleIndices;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/*
|
|
149
|
+
Plane classification (iOS 12+, ARCore semantic labels).
|
|
150
|
+
Indicates what type of surface this plane represents.
|
|
151
|
+
*/
|
|
152
|
+
void setClassification(VROARPlaneClassification classification) {
|
|
153
|
+
_classification = classification;
|
|
154
|
+
}
|
|
155
|
+
VROARPlaneClassification getClassification() const {
|
|
156
|
+
return _classification;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/*
|
|
160
|
+
Change detection and throttling for plane updates.
|
|
161
|
+
*/
|
|
162
|
+
|
|
163
|
+
// Check if plane properties have changed significantly
|
|
164
|
+
bool hasSignificantChanges(VROVector3f newCenter, VROVector3f newExtent,
|
|
165
|
+
VROARPlaneAlignment newAlignment,
|
|
166
|
+
const std::vector<VROVector3f> &newBoundaryVertices) const {
|
|
167
|
+
// Thresholds for detecting significant changes
|
|
168
|
+
static const float EXTENT_THRESHOLD = 0.01f; // 1cm change in dimensions
|
|
169
|
+
static const float CENTER_THRESHOLD = 0.01f; // 1cm change in center
|
|
170
|
+
static const float EXTENT_PERCENT_THRESHOLD = 0.05f; // 5% change in size
|
|
171
|
+
|
|
172
|
+
// Check alignment change
|
|
173
|
+
if (newAlignment != _alignment) {
|
|
174
|
+
return true;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Check extent change (absolute and percentage)
|
|
178
|
+
VROVector3f extentDiff = newExtent - _extent;
|
|
179
|
+
float maxExtentDiff = std::max(std::abs(extentDiff.x), std::abs(extentDiff.z));
|
|
180
|
+
if (maxExtentDiff > EXTENT_THRESHOLD) {
|
|
181
|
+
// Also check percentage change for larger planes
|
|
182
|
+
if (_extent.magnitude() > 0.001f) {
|
|
183
|
+
float percentChange = maxExtentDiff / _extent.magnitude();
|
|
184
|
+
if (percentChange > EXTENT_PERCENT_THRESHOLD) {
|
|
185
|
+
return true;
|
|
186
|
+
}
|
|
187
|
+
} else {
|
|
188
|
+
// For very small planes, any change is significant
|
|
189
|
+
return true;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Check center change
|
|
194
|
+
VROVector3f centerDiff = newCenter - _center;
|
|
195
|
+
if (centerDiff.magnitude() > CENTER_THRESHOLD) {
|
|
196
|
+
return true;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Check boundary vertices count change
|
|
200
|
+
if (newBoundaryVertices.size() != _boundaryVertices.size()) {
|
|
201
|
+
return true;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Check boundary vertices significant changes (sample-based for performance)
|
|
205
|
+
if (!newBoundaryVertices.empty() && !_boundaryVertices.empty()) {
|
|
206
|
+
// Sample a few vertices instead of checking all for performance
|
|
207
|
+
size_t sampleCount = std::min(size_t(4), newBoundaryVertices.size());
|
|
208
|
+
size_t step = newBoundaryVertices.size() / sampleCount;
|
|
209
|
+
if (step < 1) step = 1;
|
|
210
|
+
|
|
211
|
+
for (size_t i = 0; i < newBoundaryVertices.size(); i += step) {
|
|
212
|
+
VROVector3f diff = newBoundaryVertices[i] - _boundaryVertices[i];
|
|
213
|
+
if (diff.magnitude() > CENTER_THRESHOLD) {
|
|
214
|
+
return true;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return false;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Check if update should be throttled
|
|
223
|
+
bool shouldThrottleUpdate() const {
|
|
224
|
+
// Minimum time between updates (milliseconds)
|
|
225
|
+
static const int MIN_UPDATE_INTERVAL_MS = 100; // 10 updates per second max
|
|
226
|
+
|
|
227
|
+
auto now = std::chrono::steady_clock::now();
|
|
228
|
+
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - _lastUpdateTime).count();
|
|
229
|
+
|
|
230
|
+
return elapsed < MIN_UPDATE_INTERVAL_MS;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Mark that an update occurred
|
|
234
|
+
void recordUpdate(bool wasSignificant) {
|
|
235
|
+
_lastUpdateTime = std::chrono::steady_clock::now();
|
|
236
|
+
_updateCount++;
|
|
237
|
+
if (wasSignificant) {
|
|
238
|
+
_significantChangeCount++;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Get update statistics for diagnostics
|
|
243
|
+
uint32_t getUpdateCount() const { return _updateCount; }
|
|
244
|
+
uint32_t getSignificantChangeCount() const { return _significantChangeCount; }
|
|
245
|
+
float getSignificantChangeRatio() const {
|
|
246
|
+
return _updateCount > 0 ? (float)_significantChangeCount / _updateCount : 0.0f;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Get time since last update (milliseconds) - useful for debugging update frequency
|
|
250
|
+
int64_t getTimeSinceLastUpdate() const {
|
|
251
|
+
auto now = std::chrono::steady_clock::now();
|
|
252
|
+
return std::chrono::duration_cast<std::chrono::milliseconds>(now - _lastUpdateTime).count();
|
|
253
|
+
}
|
|
254
|
+
|
|
89
255
|
private:
|
|
90
256
|
|
|
91
257
|
/*
|
|
@@ -108,6 +274,27 @@ private:
|
|
|
108
274
|
A vector of points representing the vertex boundaries of this plane, if any.
|
|
109
275
|
*/
|
|
110
276
|
std::vector<VROVector3f> _boundaryVertices;
|
|
277
|
+
|
|
278
|
+
/*
|
|
279
|
+
Full mesh geometry (iOS 11.3+ only).
|
|
280
|
+
Detailed tessellated mesh from ARSCNPlaneGeometry.
|
|
281
|
+
Empty on Android as ARCore only provides boundary polygon.
|
|
282
|
+
*/
|
|
283
|
+
std::vector<VROVector3f> _meshVertices;
|
|
284
|
+
std::vector<VROVector2f> _textureCoordinates;
|
|
285
|
+
std::vector<int> _triangleIndices;
|
|
286
|
+
|
|
287
|
+
/*
|
|
288
|
+
Plane classification (iOS 12+, ARCore semantic labels).
|
|
289
|
+
*/
|
|
290
|
+
VROARPlaneClassification _classification = VROARPlaneClassification::None;
|
|
291
|
+
|
|
292
|
+
/*
|
|
293
|
+
Update tracking and throttling.
|
|
294
|
+
*/
|
|
295
|
+
std::chrono::steady_clock::time_point _lastUpdateTime;
|
|
296
|
+
uint32_t _updateCount;
|
|
297
|
+
uint32_t _significantChangeCount;
|
|
111
298
|
};
|
|
112
299
|
|
|
113
300
|
#endif /* VROARPlaneAnchor_h */
|