NOTE: GIF above looks choppy because it's a GIF, the real thing is smooth, I promise...
Use THREE on Expo! Just npm i -S three expo-three
in your Expo project and import it with import ExpoTHREE from 'expo-three';
.
Given a gl
from an
Expo.GLView
, return a
THREE.WebGLRenderer
that draws into it.
Given an Expo.Asset
,
return a
(Promise
that will resolve with a)
THREE.Texture
backed by that
asset as an image.
Given an arSession
from NativeModules.ExponentGLViewManager.startARSession
,
return a
THREE.PerspectiveCamera
that automatically updates its view and projection matrices to reflect the AR
session camera. width, height
specify the dimensions of the target viewport to
render to and near, far
specify the near and far clipping distances
respectively. The THREE.PerspectiveCamera
returned has its updateMatrixWorld
and updateProjectionMatrix
methods overriden to update to the AR session's
state automatically.
Given an arSession
from NativeModules.ExponentGLViewManager.startARSession
and a
THREE.WebGLRenderer
,
return a THREE.Texture
that
reflects the live video feed of the AR session. Usually this is set as the
.background
property of a
THREE.Scene
to render the video
feed behind the scene's objects.
Given an arSession
from NativeModules.ExponentGLViewManager.startARSession
,
return a (object
) the shape of which looks like:
- ambientIntensity: number
- This value ranges from 0 - 2000. 0 being very dark and 2000 being very bright.
- ambientColorTemperature: number
- This value ranges from 0 - 6500. This value is in kelvins and 6500 is white.
Given an arSession
from NativeModules.ExponentGLViewManager.startARSession
,
return an (array
) of points:
- x
- y
- z
- id
Given an arSession
from NativeModules.ExponentGLViewManager.startARSession
and a bool
, Enable or disable light estimation.
Given an arSession
from NativeModules.ExponentGLViewManager.startARSession
and a bool
, sets the type of planes to detect in the scene.
ExpoTHREE.utils.alignMesh
: A function that requires aTHREE.Mesh
, an optional object containingx
,y
,z
axis values relative to the model.ExpoTHREE.utils.scaleLongestSideToSize
: Given aTHREE.Mesh
and anumber
, this will find the longest side and scale it to the provided model.ExpoTHREE.utils.computeMeshNormals
: Used for smoothing imported geometry, specifically when imported from.obj
models.
A function that will asynchronously load files based on their extension.
-
res
: The file to load -
onProgress
: A callbackFunction
that will return axhr
object -
assetProvider
: A callbackFunction
that is used to request static assets required by the model(assetName: string)
: The asyncFunction
should return a static assetrequire('./texture.*')
or an Expo.AssetExpo.Asset.fromModule(require('./texture.*'))
A list of supported formats can be found here
A function that suppresses EXGL compatibility warnings and logs them instead.
You will need to import the ExpoTHREE.THREE
global instance to use this. By
default this function will be activated on import.
shouldSuppress
: boolean
import { THREE } from 'expo-three';
THREE.suppressExpoWarnings(true);
Used to load in a texture cube or skybox.
assetForDirection
: This function will be called for each of the 6 directions.({ direction })
: A direction string will be passed back looking for the corresponding image. You can send back:static resource
,localUri
,Expo.Asset
,remote image url
directions
: The order that image will be requested in. The default value is:['px', 'nx', 'py', 'ny', 'pz', 'nz']
Example:
const skybox = {
nx: require('./nx.jpg'),
ny: require('./ny.jpg'),
nz: require('./nz.jpg'),
px: require('./px.jpg'),
py: require('./py.jpg'),
pz: require('./pz.jpg')
}
scene.background = await loadCubeTextureAsync({
assetForDirection: ({ direction }) => skybox[direction],
})
This is based on https://threejs.org/docs/#manual/introduction/Creating-a-scene.
In a
new blank Expo project,
run npm i -S three expo-three
to install THREE and ExpoTHREE. Then replace
main.js
with the following:
import Expo from 'expo';
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import * as THREE from 'three';
import ExpoTHREE from 'expo-three';
export default class App extends React.Component {
render() {
// Create an `Expo.GLView` covering the whole screen, tell it to call our
// `_onGLContextCreate` function once it's initialized.
return (
<Expo.GLView
style={{ flex: 1 }}
onContextCreate={this._onGLContextCreate}
/>
);
}
// This is called by the `Expo.GLView` once it's initialized
_onGLContextCreate = async gl => {
// Based on https://threejs.org/docs/#manual/introduction/Creating-a-scene
// In this case we instead use a texture for the material (because textures
// are cool!). All differences from the normal THREE.js example are
// indicated with a `NOTE:` comment.
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
75,
gl.drawingBufferWidth / gl.drawingBufferHeight,
0.1,
1000,
);
// NOTE: How to create an `Expo.GLView`-compatible THREE renderer
const renderer = ExpoTHREE.createRenderer({ gl });
renderer.setSize(gl.drawingBufferWidth, gl.drawingBufferHeight);
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshBasicMaterial({
// NOTE: How to create an Expo-compatible THREE texture
map: await ExpoTHREE.createTextureAsync({
asset: Expo.Asset.fromModule(require('./assets/icons/app-icon.png')),
}),
});
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
camera.position.z = 5;
const render = () => {
requestAnimationFrame(render);
cube.rotation.x += 0.07;
cube.rotation.y += 0.04;
renderer.render(scene, camera);
// NOTE: At the end of each frame, notify `Expo.GLView` with the below
gl.endFrameEXP();
};
render();
};
}