Created
March 21, 2019 18:33
-
-
Save benursu/eb515e259d3a04982ba8903a02da2216 to your computer and use it in GitHub Desktop.
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
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
// Plexus | |
// Collab by Ben Ursu & Dan Cronin | Afrosquared | |
// Spark AR Studio | |
// Instagram | https://www.instagram.com/a/r/?effect_id=2029175904053487 | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
//require | |
const Scene = require('Scene'); | |
const Animation = require('Animation'); | |
const R = require('Reactive'); | |
const D = require('Diagnostics'); | |
const Time = require('Time'); | |
const Materials = require('Materials'); | |
const Textures = require('Textures'); | |
const Patches = require('Patches'); | |
const FT = require('FaceTracking'); | |
const Audio = require('Audio'); | |
const TouchGestures = require('TouchGestures'); | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
// utils | |
var vectorForward = R.vector(1, 0, 0); | |
var rotation90 = Math.PI / 2; | |
var rotation180 = Math.PI; | |
var rotation360 = Math.PI * 2; | |
function toRadians(angle) { | |
return angle * (Math.PI / 180); | |
} | |
function toDegrees(angle) { | |
return angle * (180 / Math.PI); | |
} | |
function getRandom(min, max) { | |
return Math.random() * (max - min) + min; | |
} | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
// scene | |
var facetracker = Scene.root.find('facetracker'); | |
var facetrackerCameraTransform = FT.face(0).cameraTransform; | |
var head = Scene.root.find('head'); | |
var headHole = head.child('hole'); | |
var faceMesh = head.child('faceMesh'); | |
var headHoleContainer = headHole.child('F8_A2_Yup'); | |
var headHoleContainer2 = headHole.child('F8_A2_Yup_2'); | |
var headHoldBackground = headHole.child('background'); | |
var headOffset = Scene.root.find('headOffset'); | |
var faceMaterial = Materials.get('face'); | |
var faceMaterialOpacityDriver; | |
var stars0Material = Materials.get('stars0'); | |
var stars1Material = Materials.get('stars1'); | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
// vars | |
var startOnTap = false; | |
var facetrackerZOffsetAdd = 53.5; | |
var facetrackerZStartScene = -7; | |
// var facetrackerZStartScene = 8; | |
// var facetrackerZStartScene = 20; | |
var facetrackerZHoleStart = facetrackerZStartScene - facetrackerZOffsetAdd; | |
var facetrackerZHoleEnd = -14; | |
var facetrackerZHoleLength = Math.abs(facetrackerZHoleStart - facetrackerZHoleEnd); | |
var facetrackerState = 'close'; | |
var headClosePositionX = 0; | |
var headClosePositionY = -7.5908; | |
var headClosePositionZ = 52; | |
var headCloseRotationX = 0; | |
var headCloseRotationY = 0; | |
var headCloseRotationZ = 0; | |
var headHoleCloseScale = 0.01; | |
var headHoleFarScale = 0.0015; | |
var headHoleScaleDriver; | |
var headHoleScaleDurationFar = 200; | |
var headHoleScaleDurationClose = 800; | |
var faceMeshClosePosition = -1; | |
var faceMeshPositionDriver; | |
var faceMeshPositionDurationFar = 200; | |
var faceMeshTimeout; | |
var headTransformExpSmoothMax = 200; | |
var headTransformExpSmooth = headTransformExpSmoothMax; | |
var headTransformDuration = 250; | |
var headTransformTimeout; | |
var holeObjsExpSmooth = 250; | |
var holdObjsStartOffset = -0.185; | |
var holeObjects = []; | |
var holeObjectsFarSceneObjectName = 'Tri mask 2'; | |
var holeObjectsFarSceneObject; | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
// face tracker init | |
function facetrackerInit(){ | |
function facetrackerCameraTransformFar(snapshots){ | |
// | |
headOffset.transform.x = facetrackerCameraTransform.x; | |
headOffset.transform.y = facetrackerCameraTransform.y; | |
headOffset.transform.z = facetrackerCameraTransform.z.add(facetrackerZOffsetAdd); | |
headOffset.transform.rotationX = facetrackerCameraTransform.rotationX; | |
headOffset.transform.rotationY = facetrackerCameraTransform.rotationY; | |
headOffset.transform.rotationZ = facetrackerCameraTransform.rotationZ; | |
// | |
if(headHoleScaleDriver != null){ | |
headHoleScaleDriver.stop(); | |
} | |
headHoleScaleDriver = Animation.timeDriver({ durationMilliseconds: headHoleScaleDurationFar }); | |
var sampler = Animation.samplers.easeInOutCubic(snapshots['headHole.transform.scaleX'], headHoleFarScale); | |
headHole.transform.scaleX = Animation.animate(headHoleScaleDriver, sampler); | |
headHole.transform.scaleY = Animation.animate(headHoleScaleDriver, sampler); | |
headHole.transform.scaleZ = Animation.animate(headHoleScaleDriver, sampler); | |
headHoleScaleDriver.start(); | |
// | |
if(faceMeshTimeout){ | |
Time.clearTimeout(faceMeshTimeout); | |
} | |
if(faceMeshPositionDriver != null){ | |
faceMeshPositionDriver.stop(); | |
} | |
faceMeshPositionDriver = Animation.timeDriver({ durationMilliseconds: faceMeshPositionDurationFar }); | |
var sampler = Animation.samplers.easeOutCubic(faceMeshClosePosition, 0); | |
faceMesh.transform.z = Animation.animate(faceMeshPositionDriver, sampler); | |
faceMeshPositionDriver.start(); | |
faceMesh.hidden = true; | |
// | |
headHoleContainer2.hidden = true; | |
// | |
if(headTransformTimeout){ | |
Time.clearTimeout(headTransformTimeout); | |
} | |
headTransformTimeout = Time.setTimeoutWithSnapshot({ }, function(delay, snapshots){ | |
headTransformExpSmooth = 0; | |
head.transform.x = headOffset.transform.x.expSmooth(headTransformExpSmooth); | |
head.transform.y = headOffset.transform.y.expSmooth(headTransformExpSmooth); | |
head.transform.z = headOffset.transform.z.expSmooth(headTransformExpSmooth); | |
head.transform.rotationX = headOffset.transform.rotationX.expSmooth(headTransformExpSmooth); | |
head.transform.rotationY = headOffset.transform.rotationY.expSmooth(headTransformExpSmooth); | |
head.transform.rotationZ = headOffset.transform.rotationZ.expSmooth(headTransformExpSmooth); | |
facetrackerState = 'far'; | |
for(var i = 0; i < holeObjects.length; i++){ | |
holeObjects[i]['sceneObject'].hidden = true; | |
} | |
holeObjectsFarSceneObject.hidden = false; | |
headHoldBackground.hidden = true; | |
}, headTransformDuration); | |
} | |
function facetrackerCameraTransformClose(snapshots){ | |
facetrackerState = 'close'; | |
// | |
headTransformExpSmooth = headTransformExpSmoothMax; | |
head.transform.x = headOffset.transform.x.expSmooth(headTransformExpSmooth); | |
head.transform.y = headOffset.transform.y.expSmooth(headTransformExpSmooth); | |
head.transform.z = headOffset.transform.z.expSmooth(headTransformExpSmooth); | |
head.transform.rotationX = headOffset.transform.rotationX.expSmooth(headTransformExpSmooth); | |
head.transform.rotationY = headOffset.transform.rotationY.expSmooth(headTransformExpSmooth); | |
head.transform.rotationZ = headOffset.transform.rotationZ.expSmooth(headTransformExpSmooth); | |
// | |
if(headHoleScaleDriver != null){ | |
headHoleScaleDriver.stop(); | |
} | |
headHoleScaleDriver = Animation.timeDriver({ durationMilliseconds: headHoleScaleDurationClose }); | |
var sampler = Animation.samplers.easeInOutExpo(snapshots['headHole.transform.scaleX'], headHoleCloseScale); | |
headHole.transform.scaleX = Animation.animate(headHoleScaleDriver, sampler); | |
headHole.transform.scaleY = Animation.animate(headHoleScaleDriver, sampler); | |
headHole.transform.scaleZ = Animation.animate(headHoleScaleDriver, sampler); | |
headHoleScaleDriver.start(); | |
// | |
if(faceMeshTimeout){ | |
Time.clearTimeout(faceMeshTimeout); | |
} | |
faceMeshTimeout = Time.setTimeoutWithSnapshot({ }, function(delay, snapshots){ | |
faceMesh.hidden = true; | |
}, headHoleScaleDurationClose); | |
if(faceMeshPositionDriver != null){ | |
faceMeshPositionDriver.stop(); | |
} | |
faceMesh.transform.z = faceMeshClosePosition; | |
faceMesh.hidden = false; | |
// | |
for(var i = 0; i < holeObjects.length; i++){ | |
holeObjects[i]['sceneObject'].hidden = false; | |
} | |
headHoldBackground.hidden = false; | |
// | |
headHoleContainer2.hidden = false; | |
// | |
if(headTransformTimeout){ | |
Time.clearTimeout(headTransformTimeout); | |
} | |
headTransformTimeout = Time.setTimeoutWithSnapshot({ }, function(delay, snapshots){ | |
headOffset.transform.x = R.val(headClosePositionX); | |
headOffset.transform.y = R.val(headClosePositionY); | |
headOffset.transform.z = R.val(headClosePositionZ); | |
headOffset.transform.rotationX = R.val(headCloseRotationX); | |
headOffset.transform.rotationY = R.val(headCloseRotationY); | |
headOffset.transform.rotationZ = R.val(headCloseRotationZ); | |
}, 0); | |
} | |
facetrackerCameraTransform.z.add(facetrackerZOffsetAdd).lt(facetrackerZStartScene).onOn({fireOnInitialValue: true}).subscribeWithSnapshot({ 'headHole.transform.scaleX': headHole.transform.scaleX }, function (e, snapshots) { | |
facetrackerCameraTransformFar(snapshots); | |
}); | |
facetrackerCameraTransform.z.add(facetrackerZOffsetAdd).lt(facetrackerZStartScene).onOff({fireOnInitialValue: true}).subscribeWithSnapshot({ 'headHole.transform.scaleX': headHole.transform.scaleX }, function (e, snapshots) { | |
facetrackerCameraTransformClose(snapshots); | |
}); | |
var facetrackerHeadSub = FT.count.monitor().subscribe(function(e) { | |
if(e.newValue == 0){ | |
head.hidden = true; | |
}else if(e.newValue == 1){ | |
head.hidden = false; | |
} | |
}); | |
holeObjects.push( { name: 'Mask 1', start: 0.185, end: 0.2, type: 'scale', value: 300 } ); | |
holeObjects.push( { name: 'Mask 1', start: 0.185, end: 0.285, type: 'rotation', value: 40 } ); | |
holeObjects.push( { name: 'Mask 2', start: 0.192, end: 0.308, type: 'scale', value: 1447.076 } ); | |
holeObjects.push( { name: 'Mask 2', start: 0.192, end: 0.308, type: 'rotation', value: 40 } ); | |
holeObjects.push( { name: 'Mask 3', start: 0.18, end: 0.2, type: 'scale', value: 740.033 } ); | |
holeObjects.push( { name: 'Mask 3', start: 0.2, end: 0.3, type: 'rotation', value: 40 } ); | |
holeObjects.push( { name: 'Mask 4', start: 0.208, end: 0.308, type: 'scale', value: 566.828 } ); | |
holeObjects.push( { name: 'Mask 4', start: 0.208, end: 0.308, type: 'rotation', value: 40 } ); | |
holeObjects.push( { name: 'Mask 5', start: 0.179, end: 0.279, type: 'scale', value: 356.387 } ); | |
holeObjects.push( { name: 'Mask 5', start: 0.179, end: 0.279, type: 'rotation', value: 40 } ); | |
holeObjects.push( { name: 'Mask 6', start: 0.221, end: 0.321, type: 'scale', value: 420.995 } ); | |
holeObjects.push( { name: 'Mask 6', start: 0.221, end: 0.543, type: 'rotation', value: 40 } ); | |
holeObjects.push( { name: 'Mask 7', start: 0.25, end: 0.35, type: 'scale', value: 239.32 } ); | |
holeObjects.push( { name: 'Mask 7', start: 0.25, end: 0.35, type: 'rotation', value: 40 } ); | |
holeObjects.push( { name: 'Tri mask 1', start: 0.221, end: 0.329, type: 'scale', value: 944.628 } ); | |
holeObjects.push( { name: 'Tri Gradient', start: 0.271, end: 0.435, type: 'scale', value: 944.628 } ); | |
holeObjects.push( { name: 'Tri mask 2', start: 0.271, end: 0.435, type: 'scale', value: 500.628 } ); | |
holeObjects.push( { name: 'Tri mask 3', start: 0.648, end: 0.715, type: 'scale', value: 712.08 } ); | |
holeObjects.push( { name: 'Tri mask 3', start: 0.648, end: 0.715, type: 'rotation', value: -120 } ); | |
holeObjects.push( { name: 'Tri mask 4', start: 0.558, end: 0.694, type: 'scale', value: 712.08 } ); | |
holeObjects.push( { name: 'Tri mask 4', start: 0.558, end: 0.694, type: 'rotation', value: -120 } ); | |
holeObjects.push( { name: 'Tri mask 5', start: 0.468, end: 0.674, type: 'scale', value: 712.08 } ); | |
holeObjects.push( { name: 'Tri mask 5', start: 0.468, end: 0.674, type: 'rotation', value: -120 } ); | |
holeObjects.push( { name: 'Tri mask 6', start: 0.378, end: 0.65, type: 'scale', value: 712.08 } ); | |
holeObjects.push( { name: 'Tri mask 6', start: 0.378, end: 0.65, type: 'rotation', value: -120 } ); | |
holeObjects.push( { name: 'moon mask', start: 0.465, end: 0.665, type: 'scale', value: 110.355 } ); | |
// holeObjects.push( { name: 'moon mask', start: 0.665, end: 0.865, type: 'x', value: 0 } ); | |
holeObjects.push( { name: 'Sky', start: 0.528, end: 0.715, type: 'scale', value: 13303.625 } ); | |
// holeObjects.push( { name: 'Sky', start: 0.528, end: 0.715, type: 'z', value: 0 } ); | |
holeObjects.push( { name: 'Tri frame', type: 'static' } ); | |
holeObjects.push( { name: 'Dune 1', type: 'static' } ); | |
holeObjects.push( { name: 'Dune 2', type: 'static' } ); | |
holeObjects.push( { name: 'Dune 3', type: 'static' } ); | |
holeObjects.push( { name: 'Cloud 1', type: 'static' } ); | |
holeObjects.push( { name: 'Cloud 2', type: 'static' } ); | |
holeObjects.push( { name: 'Mountain', type: 'static' } ); | |
holeObjects.push( { name: 'moon', type: 'static' } ); | |
holeObjects.push( { name: 'Stars', type: 'static' } ); | |
var minZ; | |
var maxZ; | |
var diff; | |
var ratio; | |
for(var i = 0; i < holeObjects.length; i++){ | |
if(holeObjects[i]['type'] != 'static'){ | |
minZ = facetrackerZHoleStart + (facetrackerZHoleLength * (holeObjects[i]['start'] + holdObjsStartOffset)); | |
maxZ = facetrackerZHoleStart + (facetrackerZHoleLength * (holeObjects[i]['end'] + holdObjsStartOffset)); | |
diff = maxZ - minZ; | |
ratio = R.clamp(facetrackerCameraTransform.z, minZ, maxZ).sub(minZ).div(diff); | |
} | |
if(holeObjects[i]['type'] == 'scale'){ | |
var parent = headHoleContainer.child(holeObjects[i].name + '_$AssimpFbx$_Translation'); | |
var obj = parent.child(holeObjects[i].name + '_$AssimpFbx$_PreRotation'); | |
var valueDiff = R.val(holeObjects[i].value / 100); | |
obj.transform.scaleX = R.val(1).add(valueDiff.mul(ratio)).expSmooth(holeObjsExpSmooth); | |
obj.transform.scaleY = R.val(1).add(valueDiff.mul(ratio)).expSmooth(holeObjsExpSmooth); | |
obj.transform.scaleZ = R.val(1).add(valueDiff.mul(ratio)).expSmooth(holeObjsExpSmooth); | |
holeObjects[i]['sceneObject'] = parent; | |
parent.hidden = true; | |
}else if(holeObjects[i]['type'] == 'rotation'){ | |
var obj = headHoleContainer.child(holeObjects[i].name + '_$AssimpFbx$_Translation'); | |
var valueDiff = R.val(toRadians(holeObjects[i].value)); | |
obj.transform.rotationZ = R.val(1).add(valueDiff.mul(ratio)).expSmooth(holeObjsExpSmooth); | |
holeObjects[i]['sceneObject'] = obj; | |
obj.hidden = true; | |
}else if(holeObjects[i]['type'] == 'static'){ | |
var obj = headHoleContainer.child(holeObjects[i].name + '_$AssimpFbx$_Translation'); | |
holeObjects[i]['sceneObject'] = obj; | |
obj.hidden = true; | |
} | |
if(holeObjects[i].name == holeObjectsFarSceneObjectName){ | |
holeObjectsFarSceneObject = holeObjects[i]['sceneObject']; | |
holeObjectsFarSceneObject.hidden = false; | |
} | |
} | |
// headHoleContainer | |
var headHoleContainerStart = 0.7; | |
var headHoleContainerEnd = 0.99; | |
var headHoleContainerValueDiff = R.val(15); | |
var headHoleContainerMinZ = facetrackerZHoleStart + (facetrackerZHoleLength * (headHoleContainerStart + holdObjsStartOffset)); | |
var headHoleContainerMaxZ = facetrackerZHoleStart + (facetrackerZHoleLength * (headHoleContainerEnd + holdObjsStartOffset)); | |
var headHoleContainerDiff = headHoleContainerMaxZ - headHoleContainerMinZ; | |
var headHoleContainerRatio = R.clamp(facetrackerCameraTransform.z, headHoleContainerMinZ, headHoleContainerMaxZ).sub(headHoleContainerMinZ).div(headHoleContainerDiff); | |
headHoleContainer.transform.z = R.val(1).add(headHoleContainerValueDiff.mul(headHoleContainerRatio)).expSmooth(holeObjsExpSmooth); | |
headHoleContainer2.transform.z = R.val(1).add(headHoleContainerValueDiff.mul(headHoleContainerRatio)).expSmooth(holeObjsExpSmooth); | |
} | |
head.transform.x = headOffset.transform.x.expSmooth(headTransformExpSmooth); | |
head.transform.y = headOffset.transform.y.expSmooth(headTransformExpSmooth); | |
head.transform.z = headOffset.transform.z.expSmooth(headTransformExpSmooth); | |
head.transform.rotationX = headOffset.transform.rotationX.expSmooth(headTransformExpSmooth); | |
head.transform.rotationY = headOffset.transform.rotationY.expSmooth(headTransformExpSmooth); | |
head.transform.rotationZ = headOffset.transform.rotationZ.expSmooth(headTransformExpSmooth); | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
// audio | |
var audioTotal = 4; | |
var audioExpSmooth = 0; | |
var audioVolumnes = [0.3, 0.5, 0.5, 0]; | |
var audioPlaybackControllers = []; | |
for(var i = 0; i < audioTotal; i++){ | |
var loopSpeaker = Scene.root.find('loop' + i); | |
audioPlaybackControllers.push(Audio.getPlaybackController('loop' + i)); | |
var minZ = facetrackerZHoleStart + (facetrackerZHoleLength * (i * (1/audioTotal))); | |
var maxZ = facetrackerZHoleStart + (facetrackerZHoleLength * ((i+1) * (1/audioTotal))); | |
var diff = maxZ - minZ; | |
var valueDiff = R.val(audioVolumnes[i]); | |
var ratio = R.clamp(facetrackerCameraTransform.z, minZ, maxZ).sub(minZ).div(diff); | |
loopSpeaker.volume = valueDiff.sub(valueDiff.mul(ratio)).expSmooth(holeObjsExpSmooth); | |
} | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
// stars | |
// this works better in patch with runtime | |
// var starsFlickerDurationMin = 2; | |
// var starsFlickerDurationMax = 5; | |
// function stars0Flicker(){ | |
// var duration = getRandom(starsFlickerDurationMin, starsFlickerDurationMax); | |
// var driver = Animation.timeDriver({ durationMilliseconds: duration }); | |
// var sampler = Animation.samplers.linear(0, 1); | |
// stars0Material.opacity = Animation.animate(driver, sampler); | |
// driver.start(); | |
// driver.onCompleted().subscribe(function () { | |
// stars0Flicker(); | |
// }); | |
// } | |
// stars0Flicker(); | |
// function stars1Flicker(){ | |
// var duration = getRandom(starsFlickerDurationMin, starsFlickerDurationMax); | |
// var driver = Animation.timeDriver({ durationMilliseconds: duration }); | |
// var sampler = Animation.samplers.linear(0, 1); | |
// stars1Material.opacity = Animation.animate(driver, sampler); | |
// driver.start(); | |
// driver.onCompleted().subscribe(function () { | |
// stars1Flicker(); | |
// }); | |
// } | |
// stars1Flicker(); | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
////////////////////////////////////////////////////////////////////////// | |
// start | |
var touchStartSub; | |
function start(){ | |
if(touchStartSub != null){ | |
touchStartSub.unsubscribe(); | |
} | |
Time.setTimeoutWithSnapshot({ 'headHole.transform.scaleX': headHole.transform.scaleX }, function (e, snapshots) { | |
headOffset.transform.x = facetrackerCameraTransform.x; | |
headOffset.transform.y = facetrackerCameraTransform.y; | |
headOffset.transform.z = facetrackerCameraTransform.z.add(facetrackerZOffsetAdd); | |
headOffset.transform.rotationX = facetrackerCameraTransform.rotationX; | |
headOffset.transform.rotationY = facetrackerCameraTransform.rotationY; | |
headOffset.transform.rotationZ = facetrackerCameraTransform.rotationZ; | |
}, 0); | |
Time.setTimeoutWithSnapshot({}, function (e, snapshots) { | |
facetrackerInit(); | |
for(var i = 0; i < audioTotal; i++){ | |
audioPlaybackControllers[i].loop(); | |
} | |
}, 300); | |
} | |
if(startOnTap){ | |
touchStartSub = TouchGestures.onTap().subscribe(start); | |
}else{ | |
start(); | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment