269 lines
7.2 KiB
JavaScript
269 lines
7.2 KiB
JavaScript
import React from "react"
|
|
import { StyleSheet, View, TouchableHighlight, Image } from "react-native"
|
|
import {
|
|
ViroARSceneNavigator,
|
|
ViroARScene,
|
|
ViroARPlane,
|
|
ViroAmbientLight,
|
|
ViroDirectionalLight,
|
|
ViroMaterials,
|
|
Viro3DObject,
|
|
ViroSpotLight,
|
|
ViroNode,
|
|
ViroQuad,
|
|
ViroConstants,
|
|
} from "react-viro"
|
|
import autobind from "autobind-decorator"
|
|
import backImage from "./images/back.png"
|
|
import { config } from "../config"
|
|
import "url-search-params-polyfill"
|
|
|
|
const shapes = {
|
|
order: {
|
|
source: require("./models/hardhat_obj.obj"),
|
|
resources: [require("./models/hardhat.mtl")],
|
|
scale: [0.5, 0.5, 0.5],
|
|
},
|
|
complaint: {
|
|
source: require("./models/question_obj.obj"),
|
|
resources: [require("./models/question.mtl")],
|
|
scale: [1, 1, 1],
|
|
},
|
|
inspection: {
|
|
source: require("./models/clipboard_obj.obj"),
|
|
resources: [require("./models/clipboard.mtl")],
|
|
scale: [1, 1, 1],
|
|
},
|
|
}
|
|
|
|
const distance = (vectorA, vectorB) => {
|
|
return Math.sqrt(
|
|
(vectorB[0] - vectorA[0]) * (vectorB[0] - vectorA[0]) +
|
|
(vectorB[1] - vectorA[1]) * (vectorB[1] - vectorA[1]) +
|
|
(vectorB[2] - vectorA[2]) * (vectorB[2] - vectorA[2])
|
|
)
|
|
}
|
|
|
|
class WorkItemSceneAR extends React.Component {
|
|
constructor(props) {
|
|
super(props)
|
|
|
|
this.state = {
|
|
position: [0, 0, 0],
|
|
rotation: [0, 0, 0],
|
|
shouldBillboard: true,
|
|
trackingInitialized: false,
|
|
haveAnchor: false,
|
|
}
|
|
}
|
|
|
|
@autobind
|
|
handleLoadEnd() {
|
|
this.arScene.getCameraOrientationAsync().then((orientation) => {
|
|
return this.arScene
|
|
.performARHitTestWithRay(orientation.forward)
|
|
.then((results) => {
|
|
const { forward, position } = orientation
|
|
// Default position is just one meter in front of the user
|
|
const defaultPosition =
|
|
[forward[0] * 1.0, forward[1] * 1.0, forward[2]] * 1.0
|
|
let hitResultPosition = null
|
|
|
|
// Filter the hit test results based on the position.
|
|
for (var i = 0; i < results.length; i++) {
|
|
let result = results[i]
|
|
|
|
if (result.type == "ExistingPlaneUsingExtent") {
|
|
let distance = Math.sqrt(
|
|
(result.transform.position[0] - position[0]) *
|
|
(result.transform.position[0] - position[0]) +
|
|
(result.transform.position[1] - position[1]) *
|
|
(result.transform.position[1] - position[1]) +
|
|
(result.transform.position[2] - position[2]) *
|
|
(result.transform.position[2] - position[2])
|
|
)
|
|
if (distance > 1 && distance < 15) {
|
|
// If we found a plane a decent distance away, choose it!
|
|
hitResultPosition = result.transform.position
|
|
break
|
|
}
|
|
} else if (result.type == "FeaturePoint" && !hitResultPosition) {
|
|
// If we haven't found a plane and this feature point is within range,
|
|
// then we'll use it as the initial display point
|
|
let d = distance(position, result.transform.position)
|
|
|
|
if (d > 1 && d < 15) {
|
|
hitResultPosition = result.transform.position
|
|
}
|
|
}
|
|
}
|
|
|
|
this.setState({
|
|
position: hitResultPosition || defaultPosition,
|
|
})
|
|
|
|
setTimeout(() => {
|
|
this.updateInitialRotation()
|
|
}, 200)
|
|
})
|
|
.catch((err) => {})
|
|
})
|
|
}
|
|
|
|
@autobind
|
|
updateInitialRotation() {
|
|
this.arNode.getTransformAsync().then((retDict) => {
|
|
let rotation = retDict.rotation
|
|
let absX = Math.abs(rotation[0])
|
|
let absZ = Math.abs(rotation[2])
|
|
|
|
let yRotation = rotation[1]
|
|
|
|
// If the X and Z aren't 0, then adjust the y rotation.
|
|
if (absX > 1 && absZ > 1) {
|
|
yRotation = 180 - yRotation
|
|
}
|
|
|
|
this.setState({
|
|
rotation: [0, yRotation, 0],
|
|
shouldBillboard: false,
|
|
})
|
|
})
|
|
}
|
|
|
|
@autobind
|
|
handleTrackingUpdated(state, reason) {
|
|
if (
|
|
!this.state.trackingInitialized &&
|
|
state === ViroConstants.TRACKING_NORMAL
|
|
) {
|
|
this.setState({ trackingInitialized: true })
|
|
}
|
|
}
|
|
|
|
@autobind
|
|
handleAnchorFound() {
|
|
this.setState({ haveAnchor: true })
|
|
}
|
|
|
|
@autobind
|
|
handleClick(position, source) {
|
|
const { workItemId } = this.props
|
|
|
|
this.props.history.replace(
|
|
`/activity${workItemId ? "?workItemId=" + workItemId : ""}`
|
|
)
|
|
}
|
|
|
|
render() {
|
|
const {
|
|
position,
|
|
scale,
|
|
rotation,
|
|
shouldBillboard,
|
|
trackingInitialized,
|
|
haveAnchor,
|
|
} = this.state
|
|
const shape = shapes[this.props.workItemType]
|
|
|
|
return (
|
|
<ViroARScene
|
|
anchorDetectionTypes="PlanesHorizontal"
|
|
ref={(ref) => (this.arScene = ref)}
|
|
onTrackingUpdated={this.handleTrackingUpdated}
|
|
onAnchorFound={this.handleAnchorFound}>
|
|
<ViroAmbientLight color="#ffffff" intensity={200} />
|
|
<ViroNode
|
|
ref={(ref) => (this.arNode = ref)}
|
|
transformBehaviors={shouldBillboard ? "billboardY" : []}
|
|
position={position}
|
|
rotation={rotation}>
|
|
<ViroSpotLight
|
|
innerAngle={5}
|
|
outerAngle={20}
|
|
direction={[0, -1, 0]}
|
|
position={[0, 10, 0]}
|
|
color="#ffffff"
|
|
castsShadow={true}
|
|
shadowNearZ={0.1}
|
|
shadowFarZ={6}
|
|
shadowOpacity={0.9}
|
|
/>
|
|
{shape &&
|
|
trackingInitialized &&
|
|
haveAnchor && (
|
|
<Viro3DObject
|
|
position={[0, 0, -1]}
|
|
source={shape.source}
|
|
resources={shape.resources}
|
|
scale={shape.scale}
|
|
type="OBJ"
|
|
onLoadEnd={this.handleLoadEnd}
|
|
onClick={this.handleClick}
|
|
/>
|
|
)}
|
|
<ViroQuad
|
|
rotation={[-90, 0, 0]}
|
|
position={[0, -0.01, 0]}
|
|
width={2.5}
|
|
height={2.5}
|
|
arShadowReceiver={true}
|
|
ignoreEventHandling={true}
|
|
/>
|
|
</ViroNode>
|
|
</ViroARScene>
|
|
)
|
|
}
|
|
}
|
|
|
|
export class ARViewer extends React.Component {
|
|
constructor(props) {
|
|
super(props)
|
|
|
|
const { search } = this.props.location
|
|
|
|
if (search) {
|
|
const params = new URLSearchParams(search)
|
|
|
|
this.workItemId = params.get("workItemId")
|
|
this.workItemType = params.get("workItemType")
|
|
}
|
|
}
|
|
|
|
@autobind
|
|
handleBackPress() {
|
|
this.props.history.replace("/")
|
|
}
|
|
|
|
render() {
|
|
return (
|
|
<View style={{ width: "100%", height: "100%" }}>
|
|
<ViroARSceneNavigator
|
|
style={{ width: "100%", height: "100%" }}
|
|
apiKey={config.viroAPIKey}
|
|
initialScene={{
|
|
scene: WorkItemSceneAR,
|
|
passProps: {
|
|
history: this.props.history,
|
|
workItemId: this.workItemId,
|
|
workItemType: this.workItemType,
|
|
},
|
|
}}
|
|
/>
|
|
|
|
<View style={{ position: "absolute", left: 30, right: 0, top: 50 }}>
|
|
<TouchableHighlight
|
|
style={{
|
|
height: 80,
|
|
width: 80,
|
|
}}
|
|
onPress={this.handleBackPress}
|
|
underlayColor={"#00000000"}>
|
|
<Image source={backImage} />
|
|
</TouchableHighlight>
|
|
</View>
|
|
</View>
|
|
)
|
|
}
|
|
}
|