Skip to content

Instantly share code, notes, and snippets.

@benursu
Created March 21, 2019 18:33
Show Gist options
  • Save benursu/eb515e259d3a04982ba8903a02da2216 to your computer and use it in GitHub Desktop.
Save benursu/eb515e259d3a04982ba8903a02da2216 to your computer and use it in GitHub Desktop.
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// 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