@bezlepkin/nativescript-ar
Version:
NativeScript Augmented Reality plugin. ARKit on iOS and (with the help of Sceneform) ARCore on Android.
984 lines (983 loc) • 38.3 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.AR = void 0;
const core_1 = require("@nativescript/core");
const ar_common_1 = require("./ar-common");
const arbox_1 = require("./nodes/ios/arbox");
const argroup_1 = require("./nodes/ios/argroup");
const arimage_1 = require("./nodes/ios/arimage");
const armaterialfactory_1 = require("./nodes/ios/armaterialfactory");
const armodel_1 = require("./nodes/ios/armodel");
const arplane_1 = require("./nodes/ios/arplane");
const arsphere_1 = require("./nodes/ios/arsphere");
const artext_1 = require("./nodes/ios/artext");
const artube_1 = require("./nodes/ios/artube");
const aruiview_1 = require("./nodes/ios/aruiview");
const arvideo_1 = require("./nodes/ios/arvideo");
const main_queue = dispatch_get_current_queue();
const sdkVersion = parseInt(core_1.Device.sdkVersion);
const ARState = {
planes: new Map(),
shapes: new Map(),
};
const addUIView = (options, parentNode, sceneView, renderer) => {
return new Promise((resolve, reject) => {
const view = aruiview_1.ARUIView.create(options, sceneView, renderer);
ARState.shapes.set(view.id, view);
parentNode.addChildNode(view.ios);
resolve(view);
});
};
const addNode = (options, parentNode, renderer) => {
return new Promise((resolve, reject) => {
const group = argroup_1.ARGroup.create(options, renderer);
ARState.shapes.set(group.id, group);
parentNode.addChildNode(group.ios);
resolve(group);
});
};
const addVideo = (options, parentNode, renderer) => {
return new Promise((resolve, reject) => {
const video = arvideo_1.ARVideo.create(options, renderer);
ARState.shapes.set(video.id, video);
parentNode.addChildNode(video.ios);
resolve(video);
});
};
const addImage = (options, parentNode, renderer) => {
return arimage_1.ARImage.create(options, renderer).then(image => {
ARState.shapes.set(image.id, image);
parentNode.addChildNode(image.ios);
return image;
});
};
const addText = (options, parentNode, renderer) => {
return new Promise((resolve, reject) => {
const text = artext_1.ARText.create(options, renderer);
ARState.shapes.set(text.id, text);
parentNode.addChildNode(text.ios);
resolve(text);
});
};
const addBox = (options, parentNode, renderer) => {
return new Promise((resolve, reject) => {
const box = arbox_1.ARBox.create(options, renderer);
ARState.shapes.set(box.id, box);
parentNode.addChildNode(box.ios);
resolve(box);
});
};
const addPlane = (options, parentNode) => {
return new Promise((resolve, reject) => {
const plane = arplane_1.ARPlane.createExternal(options);
ARState.shapes.set(plane.id, plane);
parentNode.addChildNode(plane.ios);
resolve(plane);
});
};
const addModel = (options, parentNode, renderer) => {
return new Promise((resolve, reject) => {
const model = armodel_1.ARModel.create(options, renderer);
setTimeout(() => {
ARState.shapes.set(model.id, model);
});
parentNode.addChildNode(model.ios);
resolve(model);
});
};
const addSphere = (options, parentNode, renderer) => {
return new Promise((resolve, reject) => {
const sphere = arsphere_1.ARSphere.create(options, renderer);
ARState.shapes.set(sphere.id, sphere);
parentNode.addChildNode(sphere.ios);
resolve(sphere);
});
};
const addTube = (options, parentNode, renderer) => {
return new Promise((resolve, reject) => {
const tube = artube_1.ARTube.create(options, renderer);
ARState.shapes.set(tube.id, tube);
parentNode.addChildNode(tube.ios);
resolve(tube);
});
};
class AR extends ar_common_1.AR {
static isSupported() {
try {
return !!ARSCNView && NSProcessInfo.processInfo.environment.objectForKey("SIMULATOR_DEVICE_NAME") === null;
}
catch (ignore) {
return false;
}
}
static isImageTrackingSupported() {
try {
return !!ARImageTrackingConfiguration && ARImageTrackingConfiguration.isSupported;
}
catch (ignore) {
return false;
}
}
static isFaceTrackingSupported() {
try {
return !!ARFaceTrackingConfiguration && ARFaceTrackingConfiguration.isSupported;
}
catch (ignore) {
return false;
}
}
setDebugLevel(to) {
if (!this.sceneView) {
return;
}
if (to === "WORLD_ORIGIN") {
this.sceneView.debugOptions = ARSCNDebugOptionShowWorldOrigin;
}
else if (to === "FEATURE_POINTS") {
this.sceneView.debugOptions = ARSCNDebugOptionShowFeaturePoints;
}
else if (to === "PHYSICS_SHAPES") {
this.sceneView.debugOptions = 1;
}
else {
this.sceneView.debugOptions = 0;
}
}
grabScreenshot() {
return new Promise((resolve, reject) => {
if (this.sceneView) {
resolve(core_1.ImageSource.fromDataSync(this.sceneView.snapshot()));
return;
}
reject("sceneView is not available");
});
}
startRecordingVideo() {
return new Promise((resolve, reject) => {
if (!this.recorder) {
this.recorder = RecordAR.alloc().initWithARSceneKit(this.sceneView);
}
if (this.recorder.status === 1) {
this.recorder.record();
resolve(true);
}
else {
reject();
}
});
}
stopRecordingVideo() {
return new Promise((resolve, reject) => {
this.recorder.stop(nsUrl => resolve(nsUrl.absoluteString));
});
}
toggleStatistics(on) {
if (!this.sceneView) {
return;
}
this.sceneView.showsStatistics = !!on;
}
setPlaneDetection(to) {
if (!this.sceneView) {
return;
}
let arPlaneDetection = 0;
if (to === "HORIZONTAL") {
arPlaneDetection = 1;
}
else if (to === "VERTICAL") {
arPlaneDetection = 2;
}
const config = this.configuration;
config.planeDetection = arPlaneDetection;
if (sdkVersion >= 13) {
try {
config.frameSemantics = 3;
}
catch (ignore) {
}
}
if (this.sceneView.session.configuration) {
this.sceneView.session.runWithConfiguration(config);
}
}
togglePlaneVisibility(on) {
const material = armaterialfactory_1.ARMaterialFactory.getMaterial(this.planeMaterial);
ARState.planes.forEach(plane => {
plane.setMaterial(material, on ? this.planeOpacity : 0);
});
}
getCameraPosition() {
const p = this.sceneView.defaultCameraController.pointOfView.worldPosition;
return { x: p.x, y: p.y, z: p.z };
}
getCameraRotationRad() {
let rot = this.sceneView.defaultCameraController.pointOfView.eulerAngles;
return { x: rot.x, y: rot.y, z: rot.z };
}
getCameraRotation() {
const rot = this.getCameraRotationRad();
const toDeg = (rad) => {
return ((rad * (180.0 / Math.PI)) + 360) % 360;
};
return { x: toDeg(rot.x), y: toDeg(rot.y), z: toDeg(rot.z) };
}
initAR() {
if (!AR.isSupported()) {
console.log("############### AR is not supported on this device.");
return;
}
if (this.trackingMode === "IMAGE") {
if (!AR.isImageTrackingSupported()) {
console.log("############### Image tracking is not supported on this device. It's probably not running iOS 12+.");
return;
}
const imageTrackingConfig = ARImageTrackingConfiguration.new();
if (this.trackingImagesBundle) {
const trackingImages = ARReferenceImage.referenceImagesInGroupNamedBundle(this.trackingImagesBundle, null);
if (!trackingImages) {
console.log("Could not load images from bundle!");
return;
}
imageTrackingConfig.trackingImages = trackingImages;
imageTrackingConfig.maximumNumberOfTrackedImages = Math.min(trackingImages.count, 50);
}
this.configuration = imageTrackingConfig;
}
else if (this.trackingMode === "FACE") {
if (!AR.isFaceTrackingSupported()) {
console.log("############### Face tracking is not supported on this device. A device running iOS 12+ is required, with a front-facing TrueDepth camera.");
return;
}
this.configuration = ARFaceTrackingConfiguration.new();
}
else {
this.configuration = ARWorldTrackingConfiguration.new();
}
this.sceneView = ARSCNView.new();
this.sceneView.delegate = this.delegate = ARSCNViewDelegateImpl.createWithOwnerResultCallbackAndOptions(new WeakRef(this), data => {
}, {});
this.toggleStatistics(this.showStatistics);
this.sceneView.autoenablesDefaultLighting = true;
this.sceneView.automaticallyUpdatesLighting = true;
this.sceneView.scene.rootNode.name = "root";
const scene = SCNScene.new();
this.sceneView.scene = scene;
if (this.trackingMode === "WORLD") {
this.setPlaneDetection(this.planeDetection);
this.addBottomPlane(scene);
}
this.configuration.lightEstimationEnabled = true;
this.sceneView.session.runWithConfiguration(this.configuration);
this.sceneTapHandler = SceneTapHandlerImpl.initWithOwner(new WeakRef(this));
const tapGestureRecognizer = UITapGestureRecognizer.alloc().initWithTargetAction(this.sceneTapHandler, "tap");
tapGestureRecognizer.numberOfTapsRequired = 1;
this.sceneView.addGestureRecognizer(tapGestureRecognizer);
this.sceneLongPressHandler = SceneLongPressHandlerImpl.initWithOwner(new WeakRef(this));
const longPressGestureRecognizer = UILongPressGestureRecognizer.alloc().initWithTargetAction(this.sceneLongPressHandler, "longpress");
longPressGestureRecognizer.minimumPressDuration = 0.5;
this.sceneView.addGestureRecognizer(longPressGestureRecognizer);
this.scenePanHandler = ScenePanHandlerImpl.initWithOwner(new WeakRef(this));
const panGestureRecognizer = UIPanGestureRecognizer.alloc().initWithTargetAction(this.scenePanHandler, "pan");
panGestureRecognizer.minimumNumberOfTouches = 1;
this.sceneView.addGestureRecognizer(panGestureRecognizer);
this.sceneRotationHandler = SceneRotationHandlerImpl.initWithOwner(new WeakRef(this));
const rotationGestureRecognizer = UIRotationGestureRecognizer.alloc().initWithTargetAction(this.sceneRotationHandler, "rotate");
this.sceneView.addGestureRecognizer(rotationGestureRecognizer);
this.scenePinchHandler = ScenePinchHandlerImpl.initWithOwner(new WeakRef(this));
const pinchGestureRecognizer = UIPinchGestureRecognizer.alloc().initWithTargetAction(this.scenePinchHandler, "pinch");
this.sceneView.addGestureRecognizer(pinchGestureRecognizer);
this.sceneView.antialiasingMode = 2;
setTimeout(() => {
this.nativeView.addSubview(this.sceneView);
const eventData = {
eventName: ar_common_1.AR.arLoadedEvent,
object: this,
ios: this.sceneView
};
this.notify(eventData);
});
}
resolveParentNode(options) {
if (options.parentNode && options.parentNode.ios) {
return options.parentNode.ios;
}
return this.sceneView.scene.rootNode;
}
addBottomPlane(scene) {
const bottomPlane = SCNBox.boxWithWidthHeightLengthChamferRadius(1000, 0.5, 1000, 0);
const bottomMaterial = SCNMaterial.new();
bottomMaterial.diffuse.contents = UIColor.colorWithWhiteAlpha(1.0, 0.0);
const materialArray = NSMutableArray.alloc().initWithCapacity(6);
materialArray.addObject(bottomMaterial);
bottomPlane.materials = materialArray;
const bottomNode = SCNNode.nodeWithGeometry(bottomPlane);
bottomNode.position = new ar_common_1.ARPosition(0, -25, 0);
bottomNode.physicsBody = SCNPhysicsBody.bodyWithTypeShape(2, null);
bottomNode.physicsBody.categoryBitMask = 0;
bottomNode.physicsBody.contactTestBitMask = 1;
scene.rootNode.addChildNode(bottomNode);
scene.physicsWorld.contactDelegate = this.physicsWorldContactDelegate = SCNPhysicsContactDelegateImpl.createWithOwner(new WeakRef(this));
}
createNativeView() {
let v = super.createNativeView();
this.initAR();
return v;
}
onLayout(left, top, right, bottom) {
super.onLayout(left, top, right, bottom);
if (this.sceneView) {
this.sceneView.layer.frame = this.ios.layer.bounds;
}
}
sceneLongPressed(recognizer) {
if (recognizer.state !== 1) {
return;
}
const tapPoint = recognizer.locationInView(this.sceneView);
const hitTestResults = this.sceneView.hitTestOptions(tapPoint, {
SCNHitTestBoundingBoxOnlyKey: true,
SCNHitTestFirstFoundOnlyKey: true
});
if (hitTestResults.count === 0) {
return;
}
const hitResult = hitTestResults.firstObject;
const savedModel = this.getTargetARNodeFromSCNNode(hitResult.node, "onLongPress");
if (savedModel) {
savedModel.onLongPress({
x: tapPoint.x,
y: tapPoint.y
});
}
}
getHitTargetWithProperty(position, property) {
const hitTestResults = this.sceneView.hitTestOptions(position, NSDictionary.dictionaryWithDictionary({
SCNHitTestBoundingBoxOnlyKey: false,
SCNHitTestFirstFoundOnlyKey: false
}));
if (hitTestResults.count === 0) {
return undefined;
}
let i = 0;
let savedModel = null;
while (hitTestResults.count > i) {
savedModel = this.getTargetARNodeFromSCNNode(hitTestResults.objectAtIndex(i).node);
if (savedModel && (!!savedModel[property])) {
return savedModel;
}
i++;
}
return undefined;
}
getTargetARNodeFromSCNNode(node, functionName) {
if (!(node && node.name)) {
return undefined;
}
const shape = ARState.shapes.get(node.name);
if (shape && (!functionName || shape[functionName])) {
return shape;
}
return node.parentNode ? this.getTargetARNodeFromSCNNode(node.parentNode, functionName) : undefined;
}
scenePanned(recognizer) {
let state = recognizer.state;
if (state === 5 || state === 4) {
return;
}
let position = recognizer.locationInView(null);
let translation = recognizer.translationInView(null);
if (state === 1) {
this.lastPositionForPanning = position;
const savedModel = this.getHitTargetWithProperty(position, "draggingEnabled");
if (savedModel) {
this.targetNodeForPanning = savedModel;
this.targetNodeInitialPan = this.targetNodeForPanning.getWorldPosition();
}
else {
this.targetNodeForPanning = undefined;
}
}
else if (this.targetNodeForPanning) {
if (state === 2) {
const pixelsPerMeter = 700;
let node = SCNNode.node();
node.position = this.sceneView.defaultCameraController.pointOfView.convertPositionToNode({
x: (translation.x / pixelsPerMeter),
y: -(translation.y / pixelsPerMeter),
z: 0
}, null);
node.rotation = this.sceneView.defaultCameraController.pointOfView.rotation;
let p = node.worldPosition;
let cp = this.sceneView.defaultCameraController.pointOfView.worldPosition;
const pos = this.targetNodeInitialPan;
this.targetNodeForPanning.setWorldPosition({
x: pos.x + p.x - cp.x, y: pos.y + p.y - cp.y, z: pos.z + p.z - cp.z
});
}
else if (state === 3) {
this.targetNodeForPanning = undefined;
}
}
}
sceneRotated(recognizer) {
let state = recognizer.state;
if (state === 5 || state === 4) {
return;
}
let position = recognizer.locationInView(this.sceneView);
if (state === 1) {
const savedModel = this.getHitTargetWithProperty(position, "rotatingEnabled");
if (savedModel && savedModel.ios) {
this.targetNodeForRotating = savedModel.ios;
}
else {
this.targetNodeForRotating = undefined;
}
}
else if (this.targetNodeForRotating) {
if (state === 2) {
const previousAngles = this.targetNodeForRotating.eulerAngles;
this.targetNodeForRotating.eulerAngles = {
x: previousAngles.x,
y: previousAngles.y - recognizer.rotation,
z: previousAngles.z
};
recognizer.rotation = 0;
}
else if (state === 3) {
this.targetNodeForRotating = undefined;
}
}
}
scenePinched(recognizer) {
let state = recognizer.state;
if (state === 5 || state === 4) {
return;
}
let position = recognizer.locationInView(this.sceneView);
if (state === 1) {
const savedModel = this.getHitTargetWithProperty(position, "scalingEnabled");
if (savedModel && savedModel.ios) {
this.targetNodeForScaling = savedModel.ios;
this.targetNodeInitialScale = this.targetNodeForScaling.scale;
}
else {
this.targetNodeForScaling = undefined;
}
}
else if (this.targetNodeForScaling) {
if (state === 2) {
this.targetNodeForScaling.scale = {
x: this.targetNodeInitialScale.x * recognizer.scale,
y: this.targetNodeInitialScale.y * recognizer.scale,
z: this.targetNodeInitialScale.z * recognizer.scale
};
}
else if (state === 3) {
this.targetNodeForScaling = undefined;
}
}
}
sceneTapped(recognizer) {
const sceneView = recognizer.view;
const tapPoint = recognizer.locationInView(sceneView);
const hitTestResults = sceneView.hitTestOptions(tapPoint, null);
if (hitTestResults.count === 0) {
const eventData = {
eventName: ar_common_1.AR.sceneTappedEvent,
object: this,
position: {
x: tapPoint.x,
y: tapPoint.y,
z: 0
}
};
this.notify(eventData);
return;
}
const hitResult = hitTestResults.firstObject;
let node = hitResult.node;
if (node !== undefined) {
let savedModel = this.getTargetARNodeFromSCNNode(node, "onTap");
if (savedModel !== undefined) {
savedModel.onTap({
x: tapPoint.x,
y: tapPoint.y
});
return;
}
}
const planeTapResults = this.sceneView.hitTestTypes(tapPoint, 16);
if (planeTapResults.count > 0) {
const planeHitResult = planeTapResults.firstObject;
const hitResultStr = "" + planeHitResult;
const transformStart = hitResultStr.indexOf("worldTransform=<translation=(") + "worldTransform=<translation=(".length;
const transformStr = hitResultStr.substring(transformStart, hitResultStr.indexOf(")", transformStart));
const transformParts = transformStr.split(" ");
const eventData = {
eventName: ar_common_1.AR.planeTappedEvent,
object: this,
position: {
x: +transformParts[0],
y: +transformParts[1],
z: +transformParts[2]
}
};
this.notify(eventData);
}
}
addUIView(options) {
return addUIView(options, this.resolveParentNode(options), this.sceneView, this.renderer);
}
addNode(options) {
return addNode(options, this.resolveParentNode(options), this.renderer);
}
addVideo(options) {
return addVideo(options, this.resolveParentNode(options), this.renderer);
}
addImage(options) {
return addImage(options, this.resolveParentNode(options), this.renderer);
}
addModel(options) {
return addModel(options, this.resolveParentNode(options), this.renderer);
}
addPlane(options) {
return addPlane(options, this.resolveParentNode(options));
}
addBox(options) {
return addBox(options, this.resolveParentNode(options), this.renderer);
}
addSphere(options) {
return addSphere(options, this.resolveParentNode(options), this.renderer);
}
addText(options) {
return addText(options, this.resolveParentNode(options), this.renderer);
}
addTube(options) {
return addTube(options, this.resolveParentNode(options), this.renderer);
}
trackImage(options) {
let set;
if (this.configuration instanceof ARImageTrackingConfiguration) {
set = NSMutableSet.setWithSet(this.configuration.trackingImages);
this.configuration.trackingImages = set;
}
else if (this.configuration instanceof ARWorldTrackingConfiguration) {
set = NSMutableSet.setWithSet(this.configuration.detectionImages);
this.configuration.detectionImages = set;
}
else {
throw "'trackImage' is only supported with trackingMode: IMAGE";
}
const name = options.name || options.image.split('/').pop().split('.').slice(0, -1).join('.');
let img;
if (options.image.indexOf('://') > 0) {
img = UIImage.imageWithData(NSData.alloc().initWithContentsOfURL(NSURL.URLWithString(options.image)));
}
else {
img = UIImage.imageNamed(options.image);
}
const refImage = ARReferenceImage.alloc().initWithCGImageOrientationPhysicalWidth(img.CGImage, 1, options.width || 1);
refImage.name = name;
set.addObject(refImage);
this.configuration.maximumNumberOfTrackedImages = Math.min(set.count, 50);
this.sceneView.session.runWithConfigurationOptions(this.configuration, 1 | 2);
if (!options.onDetectedImage) {
return;
}
this.on(ar_common_1.AR.trackingImageDetectedEvent, (args) => {
if (args.imageName === name) {
options.onDetectedImage(args);
}
});
}
reset() {
this.configuration.planeDetection = 1;
this.sceneView.session.runWithConfigurationOptions(this.configuration, 1 | 2);
ARState.planes.forEach(plane => plane.remove());
ARState.planes.clear();
ARState.shapes.forEach(node => node.remove());
ARState.shapes.clear();
}
}
exports.AR = AR;
class ScenePinchHandlerImpl extends NSObject {
static initWithOwner(owner) {
let handler = ScenePinchHandlerImpl.new();
handler._owner = owner;
return handler;
}
pinch(args) {
this._owner.get().scenePinched(args);
}
}
ScenePinchHandlerImpl.ObjCExposedMethods = {
"pinch": { returns: interop.types.void, params: [interop.types.id] }
};
class SceneTapHandlerImpl extends NSObject {
static initWithOwner(owner) {
let handler = SceneTapHandlerImpl.new();
handler._owner = owner;
return handler;
}
tap(args) {
this._owner.get().sceneTapped(args);
}
}
SceneTapHandlerImpl.ObjCExposedMethods = {
"tap": { returns: interop.types.void, params: [interop.types.id] }
};
class SceneLongPressHandlerImpl extends NSObject {
static initWithOwner(owner) {
let handler = SceneLongPressHandlerImpl.new();
handler._owner = owner;
return handler;
}
longpress(args) {
this._owner.get().sceneLongPressed(args);
}
}
SceneLongPressHandlerImpl.ObjCExposedMethods = {
"longpress": { returns: interop.types.void, params: [interop.types.id] }
};
class ScenePanHandlerImpl extends NSObject {
static initWithOwner(owner) {
let handler = ScenePanHandlerImpl.new();
handler._owner = owner;
return handler;
}
pan(args) {
this._owner.get().scenePanned(args);
}
}
ScenePanHandlerImpl.ObjCExposedMethods = {
"pan": { returns: interop.types.void, params: [interop.types.id] }
};
class SceneRotationHandlerImpl extends NSObject {
static initWithOwner(owner) {
let handler = SceneRotationHandlerImpl.new();
handler._owner = owner;
return handler;
}
rotate(args) {
this._owner.get().sceneRotated(args);
}
}
SceneRotationHandlerImpl.ObjCExposedMethods = {
"rotate": { returns: interop.types.void, params: [interop.types.id] }
};
class ARSCNViewDelegateImpl extends NSObject {
constructor() {
super(...arguments);
this.currentTrackingState = 2;
this.hasFace = false;
}
static new() {
try {
ARSCNViewDelegateImpl.ObjCProtocols.push(ARSCNViewDelegate);
}
catch (ignore) {
}
return super.new();
}
static createWithOwnerResultCallbackAndOptions(owner, callback, options) {
let delegate = ARSCNViewDelegateImpl.new();
delegate.owner = owner;
delegate.options = options;
delegate.resultCallback = callback;
return delegate;
}
sessionDidFailWithError(session, error) {
console.log(">>> sessionDidFailWithError: " + error);
}
sessionWasInterrupted(session) {
console.log(">>> sessionWasInterrupted: The tracking session has been interrupted. The session will be reset once the interruption has completed");
}
sessionInterruptionEnded(session) {
console.log(">>> sessionInterruptionEnded, calling reset");
this.owner.get().reset();
}
sessionCameraDidChangeTrackingState(session, camera) {
if (this.currentTrackingState === camera.trackingState) {
return;
}
this.currentTrackingState = camera.trackingState;
let trackingState = null, limitedTrackingStateReason = null;
if (camera.trackingState === 0) {
trackingState = "Not available";
}
else if (camera.trackingState === 1) {
trackingState = "Limited";
const reason = camera.trackingStateReason;
if (reason === 2) {
limitedTrackingStateReason = "Excessive motion";
}
else if (reason === 3) {
limitedTrackingStateReason = "Insufficient features";
}
else if (reason === 1) {
limitedTrackingStateReason = "Initializing";
}
else if (reason === 0) {
limitedTrackingStateReason = "None";
}
}
else if (camera.trackingState === 2) {
trackingState = "Normal";
}
if (trackingState !== null) {
console.log(`Tracking state changed to: ${trackingState}`);
if (limitedTrackingStateReason !== null) {
console.log(`Limited tracking state reason: ${limitedTrackingStateReason}`);
}
}
}
rendererDidAddNodeForAnchor(renderer, node, anchor) {
this.owner.get().renderer = renderer;
if (anchor instanceof ARPlaneAnchor) {
const owner = this.owner.get();
const plane = arplane_1.ARPlane.create(anchor, owner.planeOpacity, armaterialfactory_1.ARMaterialFactory.getMaterial(owner.planeMaterial));
ARState.planes.set(anchor.identifier.UUIDString, plane);
node.addChildNode(plane.ios);
const eventData = {
eventName: ar_common_1.AR.planeDetectedEvent,
object: owner,
plane: plane
};
owner.notify(eventData);
}
}
rendererDidUpdateNodeForAnchor(renderer, node, anchor) {
if (anchor instanceof ARPlaneAnchor) {
const plane = ARState.planes.get(anchor.identifier.UUIDString);
if (plane) {
plane.update(anchor);
}
return;
}
const owner = this.owner.get();
if (!(anchor instanceof ARFaceAnchor)) {
if (this.hasFace) {
this.hasFace = false;
owner.notify({
eventName: ar_common_1.AR.trackingFaceDetectedEvent,
object: owner,
eventType: "LOST"
});
}
return;
}
const faceAnchor = anchor;
let eventType = "UPDATED";
if (!this.hasFace) {
this.hasFace = true;
owner.reset();
eventType = "FOUND";
}
let faceGeometry;
if (this.occlusionNode) {
faceGeometry = this.occlusionNode.geometry;
}
else {
faceGeometry = node.geometry;
}
if (faceGeometry) {
const faceAnchor = anchor;
faceGeometry.updateFromFaceGeometry(faceAnchor.geometry);
}
const blendShapes = faceAnchor.blendShapes;
const eventData = {
eventName: ar_common_1.AR.trackingFaceDetectedEvent,
object: owner,
eventType,
properties: {
eyeBlinkLeft: blendShapes.valueForKey(ARBlendShapeLocationEyeBlinkLeft),
eyeBlinkRight: blendShapes.valueForKey(ARBlendShapeLocationEyeBlinkRight),
jawOpen: blendShapes.valueForKey(ARBlendShapeLocationJawOpen),
lookAtPoint: {
x: faceAnchor.lookAtPoint[0],
y: faceAnchor.lookAtPoint[1],
z: faceAnchor.lookAtPoint[2]
},
mouthFunnel: blendShapes.valueForKey(ARBlendShapeLocationMouthFunnel),
mouthSmileLeft: blendShapes.valueForKey(ARBlendShapeLocationMouthSmileLeft),
mouthSmileRight: blendShapes.valueForKey(ARBlendShapeLocationMouthSmileRight),
tongueOut: blendShapes.valueForKey(ARBlendShapeLocationTongueOut)
}
};
dispatch_async(main_queue, () => owner.notify(eventData));
}
rendererDidRemoveNodeForAnchor(renderer, node, anchor) {
ARState.planes.delete(anchor.identifier.UUIDString);
}
rendererNodeForAnchor(renderer, anchor) {
const node = SCNNode.new();
const owner = this.owner.get();
const sceneViewRenderer = renderer;
if (anchor instanceof ARFaceAnchor) {
let faceGeometry;
if (owner.faceMaterial) {
faceGeometry = ARSCNFaceGeometry.faceGeometryWithDevice(sceneViewRenderer.device);
const material = faceGeometry.firstMaterial;
material.colorBufferWriteMask = 15;
material.diffuse.contents = owner.faceMaterial;
material.lightingModelName = SCNLightingModelPhysicallyBased;
node.addChildNode(SCNNode.nodeWithGeometry(faceGeometry));
}
else {
faceGeometry = ARSCNFaceGeometry.faceGeometryWithDeviceFillMesh(sceneViewRenderer.device, true);
if (faceGeometry) {
faceGeometry.firstMaterial.colorBufferWriteMask = 0;
}
}
this.occlusionNode = SCNNode.nodeWithGeometry(faceGeometry);
this.occlusionNode.renderingOrder = -1;
node.addChildNode(this.occlusionNode);
const eventData = {
eventName: ar_common_1.AR.trackingFaceDetectedEvent,
object: owner,
eventType: "FOUND",
faceTrackingActions: new ARFaceTrackingActionsImpl(renderer, anchor, node, this, owner.sceneView)
};
dispatch_async(main_queue, () => owner.notify(eventData));
}
if (!(anchor instanceof ARImageAnchor)) {
return node;
}
const imageAnchor = anchor;
const plane = SCNPlane.planeWithWidthHeight(imageAnchor.referenceImage.physicalSize.width, imageAnchor.referenceImage.physicalSize.height);
const planeNode = SCNNode.nodeWithGeometry(plane);
planeNode.eulerAngles = {
x: -3.14159265359 / 2,
y: 0,
z: 0
};
planeNode.renderingOrder = -1;
planeNode.opacity = 1;
plane.firstMaterial.diffuse.contents = UIColor.colorWithWhiteAlpha(1, 0);
const eventData = {
eventName: ar_common_1.AR.trackingImageDetectedEvent,
object: owner,
position: planeNode.position,
size: imageAnchor.referenceImage.physicalSize,
imageName: imageAnchor.referenceImage.name,
imageTrackingActions: new ARImageTrackingActionsImpl(plane, planeNode, owner.sceneView, renderer)
};
dispatch_async(main_queue, () => owner.notify(eventData));
node.addChildNode(planeNode);
return node;
}
}
ARSCNViewDelegateImpl.ObjCProtocols = [];
class ARImageTrackingActionsImpl {
constructor(plane, planeNode, sceneView, renderer) {
this.plane = plane;
this.planeNode = planeNode;
this.sceneView = sceneView;
this.renderer = renderer;
}
playVideo(url, loop) {
const videoPlayer = AVPlayer.playerWithURL(NSURL.URLWithString(url));
this.plane.firstMaterial.diffuse.contents = videoPlayer;
if (loop === true) {
this.AVPlayerItemDidPlayToEndTimeNotificationObserver = core_1.Application.ios.addNotificationObserver(AVPlayerItemDidPlayToEndTimeNotification, (notification) => {
if (videoPlayer.currentItem && videoPlayer.currentItem === notification.object) {
videoPlayer.seekToTime(CMTimeMake(5, 100));
videoPlayer.play();
}
});
}
videoPlayer.play();
}
stopVideoLoop() {
if (this.AVPlayerItemDidPlayToEndTimeNotificationObserver) {
core_1.Application.ios.removeNotificationObserver(this.AVPlayerItemDidPlayToEndTimeNotificationObserver, AVPlayerItemDidPlayToEndTimeNotification);
this.AVPlayerItemDidPlayToEndTimeNotificationObserver = undefined;
}
}
addBox(options) {
return addBox(options, this.planeNode, this.renderer);
}
addModel(options) {
return addModel(options, this.planeNode, this.renderer);
}
addImage(options) {
return addImage(options, this.planeNode, this.renderer);
}
addUIView(options) {
return addUIView(options, this.planeNode, this.sceneView, this.renderer);
}
addNode(options) {
return addNode(options, this.planeNode, this.renderer);
}
}
class ARFaceTrackingActionsImpl {
constructor(renderer, anchor, node, owner, sceneView) {
this.renderer = renderer;
this.anchor = anchor;
this.node = node;
this.owner = owner;
this.sceneView = sceneView;
}
addModel(options) {
return addModel(options, this.node, this.renderer);
}
addText(options) {
return addText(options, this.node, this.renderer);
}
addUIView(options) {
return addUIView(options, this.node, this.sceneView, this.renderer);
}
}
class ARSessionDelegateImpl extends NSObject {
constructor() {
super(...arguments);
this.currentTrackingState = 2;
}
static new() {
try {
ARSessionDelegateImpl.ObjCProtocols.push(ARSessionDelegate);
}
catch (ignore) {
}
return super.new();
}
static createWithOwnerResultCallbackAndOptions(owner, callback, options) {
let delegate = ARSessionDelegateImpl.new();
delegate.owner = owner;
delegate.options = options;
delegate.resultCallback = callback;
return delegate;
}
sessionDidUpdateFrame(session, frame) {
console.log("frame updated @ " + new Date().getTime());
}
}
ARSessionDelegateImpl.ObjCProtocols = [];
class SCNPhysicsContactDelegateImpl extends NSObject {
static new() {
return super.new();
}
static createWithOwner(owner) {
let delegate = SCNPhysicsContactDelegateImpl.new();
delegate.owner = owner;
return delegate;
}
physicsWorldDidBeginContact(world, contact) {
const contactMask = contact.nodeA.physicsBody.categoryBitMask | contact.nodeB.physicsBody.categoryBitMask;
if (contactMask === (0 | 1)) {
if (contact.nodeA.physicsBody.categoryBitMask === 0) {
contact.nodeB.removeFromParentNode();
}
else {
contact.nodeA.removeFromParentNode();
}
}
}
physicsWorldDidEndContact(world, contact) {
}
physicsWorldDidUpdateContact(world, contact) {
}
}
SCNPhysicsContactDelegateImpl.ObjCProtocols = [SCNPhysicsContactDelegate];