Skip to main content

Plane Detection

Very similar to Mesh Detection, Plane Detection provides access to planes that estimate real-world surfaces.

Each plane can represent a surface with optionally available labels, such as: wall, floor, table, etc.

The underlying system can provide pre-captured data as well as provide real-time reconstruction depending on the underlying system implementation.

Support​

if (app.xr.planeDetection.supported) {
// plane detection is supported
}

app.xr.on('start', () => {
if (app.xr.planeDetection.available) {
// plane detection is available
}
});

Access​

A feature flag needs to be added to the session start:

app.xr.start(camera, pc.XRTYPE_AR, pc.XRSPACE_LOCALFLOOR, {
planeDetection: true
});

Then planes are added/removed asynchronously:

app.xr.planeDetection.on('add', (xrPlane) => {
// a new XrPlane has been added

xrPlane.once('remove', () => {
// an XrPlane has been removed
});
});

Also, the list of XrPlanes is available:

const xrPlanes = app.xr.planeDetection.meshes;
for (let i = 0; i < xrPlanes.length; i++) {
const xrPlane = xrPlanes[i];
}

Mesh​

Each plane is represented as an instance of XrPlane which can be added/removed during an active session. It also has data that can be changed during its lifetime.

You can access the position and rotation (world-space) of an XrPlane:

entity.setPosition(xrPlane.getPosition());
entity.setRotation(xrPlane.getRotation());

Each XrPlane has its points (in local-space) and orientation, that can be used to construct a visual mesh. The list of points represents the vertices of a polygon perimeter.

An xrPlane.orientation provides information as to whether a plane is vertical, horizontal or anything else.

An example below creates a visual mesh for each XrPlane and adds it to the scene root:

app.xr.planeDetection.on('add', (xrPlane) => {
// geometry data
const mesh = new pc.Mesh(app.graphicsDevice);
mesh.clear(true, true); // ensure that mesh is created with dynamic buffers

// create a list of vertices
const vertices = new Float32Array((xrPlane.points.length + 1) * 3);

// first point is always in the origin
vertices[0] = 0;
vertices[1] = 0;
vertices[2] = 0;

// create a list of indices
const indices = new Uint32Array(xrPlane.points.length * 3);

// construct a polygon where each edge is connected to the origin of a mesh
for (let i = 0; i < xrPlane.points.length; i++) {
vertices[i * 3 + 3 + 0] = xrPlane.points[i].x;
vertices[i * 3 + 3 + 1] = xrPlane.points[i].y;
vertices[i * 3 + 3 + 2] = xrPlane.points[i].z;
indices[i * 3 + 0] = 0;
indices[i * 3 + 1] = i + 1;
indices[i * 3 + 2] = ((i + 1) % xrPlane.points.length) + 1;
}

mesh.setPositions(vertices); // set vertices
mesh.setNormals(pc.calculateNormals(vertices, indices)); // calculate normals
mesh.setIndices(indices); // set indices
mesh.update(pc.PRIMITIVE_TRIANGLES); // update buffers

const material = new pc.StandardMaterial();
const meshInstance = new pc.MeshInstance(mesh, material);

const entity = new pc.Entity(xrPlane.label);

// add render component
entity.addComponent('render', {
meshInstances: [ meshInstance ]
});

// add entity to the scene root
app.root.addChild(entity);

// clean up after XrPlane is removed
xrPlane.once('remove', () => {
material.destroy();
mesh.destroy();
entity.destroy();
});
});

Semantic Label​

An XrPlane can represent various real-world objects and a label can help to identify what it represents using its property XrPlane.label.

These labels can be any of: floor, wall, door, window, table, screen, global mesh, other and more. Here is a list of semantic labels, although this list is not definitive and the platform can report anything it feels fit.

Changes​

Depending on the underlying system capabilities, XrPlane attributes can change while an XR session is active. You can subscribe to that event and update a visual mesh accordingly:

xrPlane.on('change', () => {
// points and/or label have been changed
});