Created
February 13, 2018 14:35
-
-
Save bnjm/96e037129a08d93e3359c2861335869e to your computer and use it in GitHub Desktop.
Loading a 3d model in React Native Arkit
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// @flow | |
import { ARKit } from 'react-native-arkit' | |
import { Text, View } from 'react-native' | |
import { compose, lifecycle, onlyUpdateForKeys } from 'recompose' | |
import { unzip } from 'react-native-zip-archive' | |
import RNFetchBlob from 'react-native-fetch-blob' | |
import React from 'react' | |
import type { Point3D } from '../../../types' | |
const getModelPath = productResourceId => | |
`${RNFetchBlob.fs.dirs.CacheDir}/models/${productResourceId}` | |
const getModelFile = productResourceId => | |
`${getModelPath(productResourceId)}/${productResourceId}/model.dae` | |
const getModelUrl = productResourceId => | |
`https://homestory-furniture.s3.amazonaws.com/${productResourceId}.zip` | |
async function loadModel(productResourceId) { | |
console.log('loadModel', productResourceId) | |
try { | |
const path = getModelPath(productResourceId) | |
const modelFile = getModelFile(productResourceId) | |
const exists = await RNFetchBlob.fs.exists(modelFile) | |
if (exists) { | |
this.setState({ isLoading: false }) | |
} else { | |
this.setState({ isLoading: true }) | |
const result = await RNFetchBlob.config({ | |
fileCache: true, | |
}) | |
.fetch('GET', getModelUrl(productResourceId)) | |
.progress((received, total) => { | |
console.log('progress', received, total, received / total) | |
}) | |
console.log('The file saved to ', result.path()) | |
const unzipPath = await unzip(result.path(), path) | |
console.log(`unzip completed at ${unzipPath}`) | |
this.setState({ isLoading: false }) | |
} | |
} catch (error) { | |
this.setState({ isLoading: true, error }) | |
} | |
} | |
// position is always position on the floor | |
const getBoxPosition = ({ | |
position, | |
product, | |
}: { | |
position: Point3D, | |
product: any, | |
}) => ({ | |
x: position.x, | |
y: position.y + (product.boundingBox ? product.boundingBox.height / 2 : 0), | |
z: position.z, | |
}) | |
// swap length and width because of AR.Box / 3d.io discrepancy | |
const getBoxDimensions = ({ width = 1, length = 1, height = 1 } = {}) => ({ | |
length: width, | |
width: length, | |
height, | |
}) | |
const withModelDownload = compose( | |
lifecycle({ | |
componentDidMount() { | |
loadModel.call(this, this.props.product.productResourceId) | |
}, | |
componentWillReceiveProps(newProps) { | |
if ( | |
newProps.product && | |
newProps.product.productResourceId !== | |
this.props.product.productResourceId | |
) { | |
loadModel.call(this, newProps.product.productResourceId) | |
} | |
}, | |
}), | |
) | |
const enhance = compose( | |
onlyUpdateForKeys(['position', 'eulerAngles']), | |
withModelDownload, | |
) | |
export default enhance( | |
({ id, position, eulerAngles, isLoading, product, debug = false }) => | |
(<ARKit.Group> | |
{debug && | |
<ARKit.Sprite position={position}> | |
<View | |
style={{ | |
padding: 5, | |
backgroundColor: 'white', | |
width: 200, | |
left: -50, | |
borderRadius: 4, | |
opacity: 0.8, | |
}} | |
> | |
<Text | |
style={{ | |
backgroundColor: 'transparent', | |
}} | |
> | |
{id} | |
{'\n'} | |
{product.productDisplayName} | |
{'\n'} | |
{JSON.stringify({ position, eulerAngles }, null, 2)} | |
</Text> | |
</View> | |
</ARKit.Sprite>} | |
<ARKit.Box | |
id={id} | |
material={{ | |
diffuse: `rgba(120, 120, 120, ${isLoading ? 0.7 : 0.3})`, | |
}} | |
transition={{ | |
duration: 0.1, | |
}} | |
eulerAngles={{ | |
x: eulerAngles && eulerAngles.x, | |
y: eulerAngles && eulerAngles.y + Math.PI / 2, | |
z: eulerAngles && eulerAngles.z, | |
}} | |
position={getBoxPosition({ product, position })} | |
shape={{ | |
...getBoxDimensions(product.boundingBox), | |
chamfer: 0.02, | |
}} | |
/> | |
{!isLoading && | |
<ARKit.Model | |
position={position} | |
transition={{ | |
duration: 0.1, | |
}} | |
eulerAngles={eulerAngles} | |
id={`${id}_model`} | |
model={{ | |
scale: 1, | |
file: getModelFile(product.productResourceId), | |
}} | |
/* | |
material={{ | |
shaders: { | |
[ARKit.ShaderModifierEntryPoint.Geometry]: ` | |
float Amplitude = 0.04; | |
float Frequency = 5.0; | |
vec2 nrm = _geometry.position.xz; | |
float len = length(nrm)+0.0001; // for robustness | |
nrm /= len; | |
float a = len + Amplitude*sin(Frequency * _geometry.position.y + u_time * 10.0); | |
_geometry.position.xz = nrm * a; | |
`, | |
[ARKit.ShaderModifierEntryPoint.Fragment]: ` | |
_output.color.rgba = vec4((sin(u_time*5)), (sin(u_time*4)),(sin(u_time*3)),0)*0.2+_output.color.rgba; | |
`, | |
}, | |
}} | |
*/ | |
/>} | |
</ARKit.Group>), | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment