mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-08 13:23:06 +02:00
fix lin endings of some JS files
This commit is contained in:
parent
502fb9cca5
commit
4ff05e8c2a
12 changed files with 2612 additions and 2612 deletions
|
@ -1,26 +1,26 @@
|
|||
/**
|
||||
* Copyright (c) 2010 Maxim Vasiliev
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author Maxim Vasiliev
|
||||
* Date: 09.09.2010
|
||||
* Time: 19:02:33
|
||||
*/
|
||||
(function(e,t){if(typeof define==="function"&&define.amd){define(t)}else{e.form2js=t()}})(this,function(){"use strict";function e(e,r,i,s,o,u){u=u?true:false;if(typeof i=="undefined"||i==null)i=true;if(typeof r=="undefined"||r==null)r=".";if(arguments.length<5)o=false;e=typeof e=="string"?document.getElementById(e):e;var a=[],f,l=0;if(e.constructor==Array||typeof NodeList!="undefined"&&e.constructor==NodeList){while(f=e[l++]){a=a.concat(n(f,s,o,u))}}else{a=n(e,s,o,u)}return t(a,i,r)}function t(e,t,n){var r={},i={},s,o,u,a,f,l,c,h,p,d,v,m,g;for(s=0;s<e.length;s++){f=e[s].value;if(t&&(f===""||f===null))continue;m=e[s].name;g=m.split(n);l=[];c=r;h="";for(o=0;o<g.length;o++){v=g[o].split("][");if(v.length>1){for(u=0;u<v.length;u++){if(u==0){v[u]=v[u]+"]"}else if(u==v.length-1){v[u]="["+v[u]}else{v[u]="["+v[u]+"]"}d=v[u].match(/([a-z_]+)?\[([a-z_][a-z0-9_]+?)\]/i);if(d){for(a=1;a<d.length;a++){if(d[a])l.push(d[a])}}else{l.push(v[u])}}}else l=l.concat(v)}for(o=0;o<l.length;o++){v=l[o];if(v.indexOf("[]")>-1&&o==l.length-1){p=v.substr(0,v.indexOf("["));h+=p;if(!c[p])c[p]=[];c[p].push(f)}else if(v.indexOf("[")>-1){p=v.substr(0,v.indexOf("["));d=v.replace(/(^([a-z_]+)?\[)|(\]$)/gi,"");h+="_"+p+"_"+d;if(!i[h])i[h]={};if(p!=""&&!c[p])c[p]=[];if(o==l.length-1){if(p==""){c.push(f);i[h][d]=c[c.length-1]}else{c[p].push(f);i[h][d]=c[p][c[p].length-1]}}else{if(!i[h][d]){if(/^[0-9a-z_]+\[?/i.test(l[o+1]))c[p].push({});else c[p].push([]);i[h][d]=c[p][c[p].length-1]}}c=i[h][d]}else{h+=v;if(o<l.length-1){if(!c[v])c[v]={};c=c[v]}else{c[v]=f}}}}return r}function n(e,t,n,s){var o=i(e,t,n,s);return o.length>0?o:r(e,t,n,s)}function r(e,t,n,r){var s=[],o=e.firstChild;while(o){s=s.concat(i(o,t,n,r));o=o.nextSibling}return s}function i(e,t,n,i){if(e.disabled&&!i)return[];var u,a,f,l=s(e,n);u=t&&t(e);if(u&&u.name){f=[u]}else if(l!=""&&e.nodeName.match(/INPUT|TEXTAREA/i)){a=o(e,i);if(null===a){f=[]}else{f=[{name:l,value:a}]}}else if(l!=""&&e.nodeName.match(/SELECT/i)){a=o(e,i);f=[{name:l.replace(/\[\]$/,""),value:a}]}else{f=r(e,t,n,i)}return f}function s(e,t){if(e.name&&e.name!="")return e.name;else if(t&&e.id&&e.id!="")return e.id;else return""}function o(e,t){if(e.disabled&&!t)return null;switch(e.nodeName){case"INPUT":case"TEXTAREA":switch(e.type.toLowerCase()){case"radio":if(e.checked&&e.value==="false")return false;case"checkbox":if(e.checked&&e.value==="true")return true;if(!e.checked&&e.value==="true")return false;if(e.checked)return e.value;break;case"button":case"reset":case"submit":case"image":return"";break;default:return e.value;break}break;case"SELECT":return u(e);break;default:break}return null}function u(e){var t=e.multiple,n=[],r,i,s;if(!t)return e.value;for(r=e.getElementsByTagName("option"),i=0,s=r.length;i<s;i++){if(r[i].selected)n.push(r[i].value)}return n}return e})
|
||||
/**
|
||||
* Copyright (c) 2010 Maxim Vasiliev
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author Maxim Vasiliev
|
||||
* Date: 09.09.2010
|
||||
* Time: 19:02:33
|
||||
*/
|
||||
(function(e,t){if(typeof define==="function"&&define.amd){define(t)}else{e.form2js=t()}})(this,function(){"use strict";function e(e,r,i,s,o,u){u=u?true:false;if(typeof i=="undefined"||i==null)i=true;if(typeof r=="undefined"||r==null)r=".";if(arguments.length<5)o=false;e=typeof e=="string"?document.getElementById(e):e;var a=[],f,l=0;if(e.constructor==Array||typeof NodeList!="undefined"&&e.constructor==NodeList){while(f=e[l++]){a=a.concat(n(f,s,o,u))}}else{a=n(e,s,o,u)}return t(a,i,r)}function t(e,t,n){var r={},i={},s,o,u,a,f,l,c,h,p,d,v,m,g;for(s=0;s<e.length;s++){f=e[s].value;if(t&&(f===""||f===null))continue;m=e[s].name;g=m.split(n);l=[];c=r;h="";for(o=0;o<g.length;o++){v=g[o].split("][");if(v.length>1){for(u=0;u<v.length;u++){if(u==0){v[u]=v[u]+"]"}else if(u==v.length-1){v[u]="["+v[u]}else{v[u]="["+v[u]+"]"}d=v[u].match(/([a-z_]+)?\[([a-z_][a-z0-9_]+?)\]/i);if(d){for(a=1;a<d.length;a++){if(d[a])l.push(d[a])}}else{l.push(v[u])}}}else l=l.concat(v)}for(o=0;o<l.length;o++){v=l[o];if(v.indexOf("[]")>-1&&o==l.length-1){p=v.substr(0,v.indexOf("["));h+=p;if(!c[p])c[p]=[];c[p].push(f)}else if(v.indexOf("[")>-1){p=v.substr(0,v.indexOf("["));d=v.replace(/(^([a-z_]+)?\[)|(\]$)/gi,"");h+="_"+p+"_"+d;if(!i[h])i[h]={};if(p!=""&&!c[p])c[p]=[];if(o==l.length-1){if(p==""){c.push(f);i[h][d]=c[c.length-1]}else{c[p].push(f);i[h][d]=c[p][c[p].length-1]}}else{if(!i[h][d]){if(/^[0-9a-z_]+\[?/i.test(l[o+1]))c[p].push({});else c[p].push([]);i[h][d]=c[p][c[p].length-1]}}c=i[h][d]}else{h+=v;if(o<l.length-1){if(!c[v])c[v]={};c=c[v]}else{c[v]=f}}}}return r}function n(e,t,n,s){var o=i(e,t,n,s);return o.length>0?o:r(e,t,n,s)}function r(e,t,n,r){var s=[],o=e.firstChild;while(o){s=s.concat(i(o,t,n,r));o=o.nextSibling}return s}function i(e,t,n,i){if(e.disabled&&!i)return[];var u,a,f,l=s(e,n);u=t&&t(e);if(u&&u.name){f=[u]}else if(l!=""&&e.nodeName.match(/INPUT|TEXTAREA/i)){a=o(e,i);if(null===a){f=[]}else{f=[{name:l,value:a}]}}else if(l!=""&&e.nodeName.match(/SELECT/i)){a=o(e,i);f=[{name:l.replace(/\[\]$/,""),value:a}]}else{f=r(e,t,n,i)}return f}function s(e,t){if(e.name&&e.name!="")return e.name;else if(t&&e.id&&e.id!="")return e.id;else return""}function o(e,t){if(e.disabled&&!t)return null;switch(e.nodeName){case"INPUT":case"TEXTAREA":switch(e.type.toLowerCase()){case"radio":if(e.checked&&e.value==="false")return false;case"checkbox":if(e.checked&&e.value==="true")return true;if(!e.checked&&e.value==="true")return false;if(e.checked)return e.value;break;case"button":case"reset":case"submit":case"image":return"";break;default:return e.value;break}break;case"SELECT":return u(e);break;default:break}return null}function u(e){var t=e.multiple,n=[],r,i,s;if(!t)return e.value;for(r=e.getElementsByTagName("option"),i=0,s=r.length;i<s;i++){if(r[i].selected)n.push(r[i].value)}return n}return e})
|
||||
|
|
|
@ -244,4 +244,4 @@ B(this,"mouseOut");!c||a.stickyTracking||c.shared&&!this.noSharedTooltip||c.hide
|
|||
f=(c.visible=a=c.userOptions.visible=a===u?!h:a)?"show":"hide";t(["group","dataLabelsGroup","markerGroup","tracker"],function(a){if(c[a])c[a][f]()});if(d.hoverSeries===c)c.onMouseOut();e&&d.legend.colorizeItem(c,a);c.isDirty=!0;c.options.stacking&&t(d.series,function(a){a.options.stacking&&a.visible&&(a.isDirty=!0)});t(c.linkedSeries,function(b){b.setVisible(a,!1)});g&&(d.isDirtyBox=!0);!1!==b&&d.redraw();B(c,f)},setTooltipPoints:function(a){var b=[],c,d,e=this.xAxis,f=e&&e.getExtremes(),g=e?e.tooltipLen||
|
||||
e.len:this.chart.plotSizeX,h,k,l=[];if(!1!==this.options.enableMouseTracking&&!this.singularTooltips){a&&(this.tooltipPoints=null);t(this.segments||this.points,function(a){b=b.concat(a)});e&&e.reversed&&(b=b.reverse());this.orderTooltipPoints&&this.orderTooltipPoints(b);a=b.length;for(k=0;k<a;k++)if(e=b[k],c=e.x,c>=f.min&&c<=f.max)for(h=b[k+1],c=d===u?0:d+1,d=b[k+1]?T(x(0,K((e.clientX+(h?h.wrappedClientX||h.clientX:g))/2)),g):g;0<=c&&c<=d;)l[c++]=e;this.tooltipPoints=l}},show:function(){this.setVisible(!0)},
|
||||
hide:function(){this.setVisible(!1)},select:function(a){this.selected=a=a===u?!this.selected:a;this.checkbox&&(this.checkbox.checked=a);B(this,a?"select":"unselect")},drawTracker:$a.drawTrackerGraph});v(U,{Axis:ma,Chart:Ea,Color:za,Point:Ca,Tick:xa,Renderer:Ja,Series:fa,SVGElement:O,SVGRenderer:Ja,arrayMin:La,arrayMax:va,charts:Y,dateFormat:Ka,format:ua,pathAnim:ib,getOptions:function(){return M},hasBidiBug:Db,isTouchDevice:wb,numberFormat:ka,seriesTypes:P,setOptions:function(a){M=G(!0,M,a);pb();
|
||||
return M},addEvent:F,removeEvent:L,createElement:ja,discardElement:Na,css:J,each:t,extend:v,map:kb,merge:G,pick:n,splat:ea,extendClass:nb,pInt:E,wrap:bb,svg:Z,canvas:ca,vml:!Z&&!ca,product:"Highcharts 4.0.4",version:"/Highstock 2.0.4"})})();
|
||||
return M},addEvent:F,removeEvent:L,createElement:ja,discardElement:Na,css:J,each:t,extend:v,map:kb,merge:G,pick:n,splat:ea,extendClass:nb,pInt:E,wrap:bb,svg:Z,canvas:ca,vml:!Z&&!ca,product:"Highcharts 4.0.4",version:"/Highstock 2.0.4"})})();
|
||||
|
|
|
@ -1,114 +1,114 @@
|
|||
//
|
||||
// SunLightExample.js
|
||||
// examples
|
||||
// Sam Gateau
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
Script.include("../../utilities/tools/cookies.js");
|
||||
|
||||
var panel = new Panel(10, 400);
|
||||
|
||||
panel.newSlider("Origin Longitude", -180, 180,
|
||||
function(value) { Scene.setStageLocation(value, Scene.getStageLocationLatitude(), Scene.getStageLocationAltitude()); },
|
||||
function() { return Scene.getStageLocationLongitude(); },
|
||||
function(value) { return value.toFixed(0) + " deg"; }
|
||||
);
|
||||
|
||||
panel.newSlider("Origin Latitude", -90, 90,
|
||||
function(value) { Scene.setStageLocation(Scene.getStageLocationLongitude(), value, Scene.getStageLocationAltitude()); },
|
||||
function() { return Scene.getStageLocationLatitude(); },
|
||||
function(value) { return value.toFixed(0) + " deg"; }
|
||||
);
|
||||
|
||||
panel.newSlider("Origin Altitude", 0, 1000,
|
||||
function(value) { Scene.setStageLocation(Scene.getStageLocationLongitude(), Scene.getStageLocationLatitude(), value); },
|
||||
function() { return Scene.getStageLocationAltitude(); },
|
||||
function(value) { return (value).toFixed(0) + " km"; }
|
||||
);
|
||||
|
||||
panel.newSlider("Year Time", 0, 364,
|
||||
function(value) { Scene.setStageYearTime(value); },
|
||||
function() { return Scene.getStageYearTime(); },
|
||||
function(value) {
|
||||
var numDaysPerMonth = 365.0 / 12.0;
|
||||
var monthly = (value / numDaysPerMonth);
|
||||
var month = Math.floor(monthly);
|
||||
return (month + 1).toFixed(0) + "/" + Math.ceil(0.5 + (monthly - month)*Math.ceil(numDaysPerMonth)).toFixed(0); }
|
||||
);
|
||||
|
||||
panel.newSlider("Day Time", 0, 24,
|
||||
|
||||
function(value) { Scene.setStageDayTime(value); panel.update("Light Direction"); },
|
||||
function() { return Scene.getStageDayTime(); },
|
||||
function(value) {
|
||||
var hour = Math.floor(value);
|
||||
return (hour).toFixed(0) + ":" + ((value - hour)*60.0).toFixed(0);
|
||||
}
|
||||
);
|
||||
|
||||
var tickTackPeriod = 50;
|
||||
var tickTackSpeed = 0.0;
|
||||
panel.newSlider("Tick tack time", -1.0, 1.0,
|
||||
function(value) { tickTackSpeed = value; },
|
||||
function() { return tickTackSpeed; },
|
||||
function(value) { return (value).toFixed(2); }
|
||||
);
|
||||
|
||||
function runStageTime() {
|
||||
if (tickTackSpeed != 0.0) {
|
||||
var hour = panel.get("Day Time");
|
||||
hour += tickTackSpeed;
|
||||
panel.set("Day Time", hour);
|
||||
|
||||
if (hour >= 24.0) {
|
||||
panel.set("Year Time", panel.get("Year Time") + 1);
|
||||
} else if (hour < 0.0) {
|
||||
panel.set("Year Time", panel.get("Year Time") - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
Script.setInterval(runStageTime, tickTackPeriod);
|
||||
|
||||
panel.newCheckbox("Enable Sun Model",
|
||||
function(value) { Scene.setStageSunModelEnable((value != 0)); },
|
||||
function() { return Scene.isStageSunModelEnabled(); },
|
||||
function(value) { return (value); }
|
||||
);
|
||||
|
||||
panel.newDirectionBox("Light Direction",
|
||||
function(value) { Scene.setKeyLightDirection(value); },
|
||||
function() { return Scene.getKeyLightDirection(); },
|
||||
function(value) { return value.x.toFixed(2) + "," + value.y.toFixed(2) + "," + value.z.toFixed(2); }
|
||||
);
|
||||
|
||||
panel.newSlider("Light Intensity", 0.0, 5,
|
||||
function(value) { Scene.setKeyLightIntensity(value); },
|
||||
function() { return Scene.getKeyLightIntensity(); },
|
||||
function(value) { return (value).toFixed(2); }
|
||||
);
|
||||
|
||||
panel.newSlider("Ambient Light Intensity", 0.0, 1.0,
|
||||
function(value) { Scene.setKeyLightAmbientIntensity(value); },
|
||||
function() { return Scene.getKeyLightAmbientIntensity(); },
|
||||
function(value) { return (value).toFixed(2); }
|
||||
);
|
||||
|
||||
panel.newColorBox("Light Color",
|
||||
function(value) { Scene.setKeyLightColor(value); },
|
||||
function() { return Scene.getKeyLightColor(); },
|
||||
function(value) { return (value); } // "(" + value.x + "," = value.y + "," + value.z + ")"; }
|
||||
);
|
||||
|
||||
Controller.mouseMoveEvent.connect(function panelMouseMoveEvent(event) { return panel.mouseMoveEvent(event); });
|
||||
Controller.mousePressEvent.connect( function panelMousePressEvent(event) { return panel.mousePressEvent(event); });
|
||||
Controller.mouseReleaseEvent.connect(function(event) { return panel.mouseReleaseEvent(event); });
|
||||
|
||||
function scriptEnding() {
|
||||
Menu.removeMenu("Developer > Scene");
|
||||
panel.destroy();
|
||||
}
|
||||
Script.scriptEnding.connect(scriptEnding);
|
||||
//
|
||||
// SunLightExample.js
|
||||
// examples
|
||||
// Sam Gateau
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
Script.include("../../utilities/tools/cookies.js");
|
||||
|
||||
var panel = new Panel(10, 400);
|
||||
|
||||
panel.newSlider("Origin Longitude", -180, 180,
|
||||
function(value) { Scene.setStageLocation(value, Scene.getStageLocationLatitude(), Scene.getStageLocationAltitude()); },
|
||||
function() { return Scene.getStageLocationLongitude(); },
|
||||
function(value) { return value.toFixed(0) + " deg"; }
|
||||
);
|
||||
|
||||
panel.newSlider("Origin Latitude", -90, 90,
|
||||
function(value) { Scene.setStageLocation(Scene.getStageLocationLongitude(), value, Scene.getStageLocationAltitude()); },
|
||||
function() { return Scene.getStageLocationLatitude(); },
|
||||
function(value) { return value.toFixed(0) + " deg"; }
|
||||
);
|
||||
|
||||
panel.newSlider("Origin Altitude", 0, 1000,
|
||||
function(value) { Scene.setStageLocation(Scene.getStageLocationLongitude(), Scene.getStageLocationLatitude(), value); },
|
||||
function() { return Scene.getStageLocationAltitude(); },
|
||||
function(value) { return (value).toFixed(0) + " km"; }
|
||||
);
|
||||
|
||||
panel.newSlider("Year Time", 0, 364,
|
||||
function(value) { Scene.setStageYearTime(value); },
|
||||
function() { return Scene.getStageYearTime(); },
|
||||
function(value) {
|
||||
var numDaysPerMonth = 365.0 / 12.0;
|
||||
var monthly = (value / numDaysPerMonth);
|
||||
var month = Math.floor(monthly);
|
||||
return (month + 1).toFixed(0) + "/" + Math.ceil(0.5 + (monthly - month)*Math.ceil(numDaysPerMonth)).toFixed(0); }
|
||||
);
|
||||
|
||||
panel.newSlider("Day Time", 0, 24,
|
||||
|
||||
function(value) { Scene.setStageDayTime(value); panel.update("Light Direction"); },
|
||||
function() { return Scene.getStageDayTime(); },
|
||||
function(value) {
|
||||
var hour = Math.floor(value);
|
||||
return (hour).toFixed(0) + ":" + ((value - hour)*60.0).toFixed(0);
|
||||
}
|
||||
);
|
||||
|
||||
var tickTackPeriod = 50;
|
||||
var tickTackSpeed = 0.0;
|
||||
panel.newSlider("Tick tack time", -1.0, 1.0,
|
||||
function(value) { tickTackSpeed = value; },
|
||||
function() { return tickTackSpeed; },
|
||||
function(value) { return (value).toFixed(2); }
|
||||
);
|
||||
|
||||
function runStageTime() {
|
||||
if (tickTackSpeed != 0.0) {
|
||||
var hour = panel.get("Day Time");
|
||||
hour += tickTackSpeed;
|
||||
panel.set("Day Time", hour);
|
||||
|
||||
if (hour >= 24.0) {
|
||||
panel.set("Year Time", panel.get("Year Time") + 1);
|
||||
} else if (hour < 0.0) {
|
||||
panel.set("Year Time", panel.get("Year Time") - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
Script.setInterval(runStageTime, tickTackPeriod);
|
||||
|
||||
panel.newCheckbox("Enable Sun Model",
|
||||
function(value) { Scene.setStageSunModelEnable((value != 0)); },
|
||||
function() { return Scene.isStageSunModelEnabled(); },
|
||||
function(value) { return (value); }
|
||||
);
|
||||
|
||||
panel.newDirectionBox("Light Direction",
|
||||
function(value) { Scene.setKeyLightDirection(value); },
|
||||
function() { return Scene.getKeyLightDirection(); },
|
||||
function(value) { return value.x.toFixed(2) + "," + value.y.toFixed(2) + "," + value.z.toFixed(2); }
|
||||
);
|
||||
|
||||
panel.newSlider("Light Intensity", 0.0, 5,
|
||||
function(value) { Scene.setKeyLightIntensity(value); },
|
||||
function() { return Scene.getKeyLightIntensity(); },
|
||||
function(value) { return (value).toFixed(2); }
|
||||
);
|
||||
|
||||
panel.newSlider("Ambient Light Intensity", 0.0, 1.0,
|
||||
function(value) { Scene.setKeyLightAmbientIntensity(value); },
|
||||
function() { return Scene.getKeyLightAmbientIntensity(); },
|
||||
function(value) { return (value).toFixed(2); }
|
||||
);
|
||||
|
||||
panel.newColorBox("Light Color",
|
||||
function(value) { Scene.setKeyLightColor(value); },
|
||||
function() { return Scene.getKeyLightColor(); },
|
||||
function(value) { return (value); } // "(" + value.x + "," = value.y + "," + value.z + ")"; }
|
||||
);
|
||||
|
||||
Controller.mouseMoveEvent.connect(function panelMouseMoveEvent(event) { return panel.mouseMoveEvent(event); });
|
||||
Controller.mousePressEvent.connect( function panelMousePressEvent(event) { return panel.mousePressEvent(event); });
|
||||
Controller.mouseReleaseEvent.connect(function(event) { return panel.mouseReleaseEvent(event); });
|
||||
|
||||
function scriptEnding() {
|
||||
Menu.removeMenu("Developer > Scene");
|
||||
panel.destroy();
|
||||
}
|
||||
Script.scriptEnding.connect(scriptEnding);
|
||||
|
|
|
@ -1,331 +1,331 @@
|
|||
//
|
||||
// Leaves.js
|
||||
// examples
|
||||
//
|
||||
// Created by Bing Shearer on 14 Jul 2015
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
var leafName = "scriptLeaf";
|
||||
var leafSquall = function (properties) {
|
||||
var // Properties
|
||||
squallOrigin,
|
||||
squallRadius,
|
||||
leavesPerMinute = 60,
|
||||
leafSize = {
|
||||
x: 0.1,
|
||||
y: 0.1,
|
||||
z: 0.1
|
||||
},
|
||||
leafFallSpeed = 1, // m/s
|
||||
leafLifetime = 60, // Seconds
|
||||
leafSpinMax = 0, // Maximum angular velocity per axis; deg/s
|
||||
debug = false, // Display origin circle; don't use running on Stack Manager
|
||||
// Other
|
||||
squallCircle,
|
||||
SQUALL_CIRCLE_COLOR = {
|
||||
red: 255,
|
||||
green: 0,
|
||||
blue: 0
|
||||
},
|
||||
SQUALL_CIRCLE_ALPHA = 0.5,
|
||||
SQUALL_CIRCLE_ROTATION = Quat.fromPitchYawRollDegrees(90, 0, 0),
|
||||
leafProperties,
|
||||
leaf_MODEL_URL = "https://hifi-public.s3.amazonaws.com/ozan/support/forBing/palmLeaf.fbx",
|
||||
leafTimer,
|
||||
leaves = [], // HACK: Work around leaves not always getting velocities
|
||||
leafVelocities = [], // HACK: Work around leaves not always getting velocities
|
||||
DEGREES_TO_RADIANS = Math.PI / 180,
|
||||
leafDeleteOnTearDown = true,
|
||||
maxLeaves,
|
||||
leafCount,
|
||||
nearbyEntities,
|
||||
complexMovement = false,
|
||||
movementTime = 0,
|
||||
maxSpinRadians = properties.leafSpinMax * DEGREES_TO_RADIANS,
|
||||
windFactor,
|
||||
leafDeleteOnGround = false,
|
||||
floorHeight = null;
|
||||
|
||||
|
||||
function processProperties() {
|
||||
if (!properties.hasOwnProperty("origin")) {
|
||||
print("ERROR: Leaf squall origin must be specified");
|
||||
return;
|
||||
}
|
||||
squallOrigin = properties.origin;
|
||||
|
||||
if (!properties.hasOwnProperty("radius")) {
|
||||
print("ERROR: Leaf squall radius must be specified");
|
||||
return;
|
||||
}
|
||||
squallRadius = properties.radius;
|
||||
|
||||
if (properties.hasOwnProperty("leavesPerMinute")) {
|
||||
leavesPerMinute = properties.leavesPerMinute;
|
||||
}
|
||||
|
||||
if (properties.hasOwnProperty("leafSize")) {
|
||||
leafSize = properties.leafSize;
|
||||
}
|
||||
|
||||
if (properties.hasOwnProperty("leafFallSpeed")) {
|
||||
leafFallSpeed = properties.leafFallSpeed;
|
||||
}
|
||||
|
||||
if (properties.hasOwnProperty("leafLifetime")) {
|
||||
leafLifetime = properties.leafLifetime;
|
||||
}
|
||||
|
||||
if (properties.hasOwnProperty("leafSpinMax")) {
|
||||
leafSpinMax = properties.leafSpinMax;
|
||||
}
|
||||
|
||||
if (properties.hasOwnProperty("debug")) {
|
||||
debug = properties.debug;
|
||||
}
|
||||
if (properties.hasOwnProperty("floorHeight")) {
|
||||
floorHeight = properties.floorHeight;
|
||||
}
|
||||
if (properties.hasOwnProperty("maxLeaves")) {
|
||||
maxLeaves = properties.maxLeaves;
|
||||
}
|
||||
if (properties.hasOwnProperty("complexMovement")) {
|
||||
complexMovement = properties.complexMovement;
|
||||
}
|
||||
if (properties.hasOwnProperty("leafDeleteOnGround")) {
|
||||
leafDeleteOnGround = properties.leafDeleteOnGround;
|
||||
}
|
||||
if (properties.hasOwnProperty("windFactor")) {
|
||||
windFactor = properties.windFactor;
|
||||
} else if (complexMovement == true){
|
||||
print("ERROR: Wind Factor must be defined for complex movement")
|
||||
}
|
||||
|
||||
leafProperties = {
|
||||
type: "Model",
|
||||
name: leafName,
|
||||
modelURL: leaf_MODEL_URL,
|
||||
lifetime: leafLifetime,
|
||||
dimensions: leafSize,
|
||||
velocity: {
|
||||
x: 0,
|
||||
y: -leafFallSpeed,
|
||||
z: 0
|
||||
},
|
||||
damping: 0,
|
||||
angularDamping: 0,
|
||||
ignoreForCollisions: true
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
function createleaf() {
|
||||
var angle,
|
||||
radius,
|
||||
offset,
|
||||
leaf,
|
||||
spin = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
},
|
||||
i;
|
||||
|
||||
// HACK: Work around leaves not always getting velocities set at creation
|
||||
for (i = 0; i < leaves.length; i++) {
|
||||
Entities.editEntity(leaves[i], leafVelocities[i]);
|
||||
}
|
||||
|
||||
angle = Math.random() * leafSpinMax;
|
||||
radius = Math.random() * squallRadius;
|
||||
offset = Vec3.multiplyQbyV(Quat.fromPitchYawRollDegrees(0, angle, 0), {
|
||||
x: 0,
|
||||
y: -0.1,
|
||||
z: radius
|
||||
});
|
||||
leafProperties.position = Vec3.sum(squallOrigin, offset);
|
||||
if (properties.leafSpinMax > 0 && !complexMovement) {
|
||||
spin = {
|
||||
x: Math.random() * maxSpinRadians,
|
||||
y: Math.random() * maxSpinRadians,
|
||||
z: Math.random() * maxSpinRadians
|
||||
};
|
||||
leafProperties.angularVelocity = spin;
|
||||
} else if (complexMovement) {
|
||||
spin = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
};
|
||||
leafProperties.angularVelocity = spin
|
||||
}
|
||||
leaf = Entities.addEntity(leafProperties);
|
||||
|
||||
// HACK: Work around leaves not always getting velocities set at creation
|
||||
leaves.push(leaf);
|
||||
leafVelocities.push({
|
||||
velocity: leafProperties.velocity,
|
||||
angularVelocity: spin
|
||||
});
|
||||
if (leaves.length > 5) {
|
||||
leaves.shift();
|
||||
leafVelocities.shift();
|
||||
}
|
||||
}
|
||||
|
||||
function setUp() {
|
||||
if (debug) {
|
||||
squallCircle = Overlays.addOverlay("circle3d", {
|
||||
size: {
|
||||
x: 2 * squallRadius,
|
||||
y: 2 * squallRadius
|
||||
},
|
||||
color: SQUALL_CIRCLE_COLOR,
|
||||
alpha: SQUALL_CIRCLE_ALPHA,
|
||||
solid: true,
|
||||
visible: debug,
|
||||
position: squallOrigin,
|
||||
rotation: SQUALL_CIRCLE_ROTATION
|
||||
});
|
||||
}
|
||||
|
||||
leafTimer = Script.setInterval(function () {
|
||||
if (leafCount <= maxLeaves - 1) {
|
||||
createleaf()
|
||||
}
|
||||
}, 60000 / leavesPerMinute);
|
||||
}
|
||||
Script.setInterval(function () {
|
||||
nearbyEntities = Entities.findEntities(squallOrigin, squallRadius);
|
||||
newLeafMovement()
|
||||
}, 100);
|
||||
|
||||
function newLeafMovement() { //new additions to leaf code. Operates at 10 Hz or every 100 ms
|
||||
movementTime += 0.1;
|
||||
var currentLeaf,
|
||||
randomRotationSpeed = {
|
||||
x: maxSpinRadians * Math.sin(movementTime),
|
||||
y: maxSpinRadians * Math.random(),
|
||||
z: maxSpinRadians * Math.sin(movementTime / 7)
|
||||
};
|
||||
for (var i = 0; i < nearbyEntities.length; i++) {
|
||||
var entityProperties = Entities.getEntityProperties(nearbyEntities[i]);
|
||||
var entityName = entityProperties.name;
|
||||
if (leafName === entityName) {
|
||||
currentLeaf = nearbyEntities[i];
|
||||
var leafHeight = entityProperties.position.y;
|
||||
if (complexMovement && leafHeight > floorHeight || complexMovement && floorHeight == null) { //actual new movement code;
|
||||
var leafCurrentVel = entityProperties.velocity,
|
||||
leafCurrentRot = entityProperties.rotation,
|
||||
yVec = {
|
||||
x: 0,
|
||||
y: 1,
|
||||
z: 0
|
||||
},
|
||||
leafYinWFVec = Vec3.multiplyQbyV(leafCurrentRot, yVec),
|
||||
leafLocalHorVec = Vec3.cross(leafYinWFVec, yVec),
|
||||
leafMostDownVec = Vec3.cross(leafYinWFVec, leafLocalHorVec),
|
||||
leafDesiredVel = Vec3.multiply(leafMostDownVec, windFactor),
|
||||
leafVelDelt = Vec3.subtract(leafDesiredVel, leafCurrentVel),
|
||||
leafNewVel = Vec3.sum(leafCurrentVel, Vec3.multiply(leafVelDelt, windFactor));
|
||||
Entities.editEntity(currentLeaf, {
|
||||
angularVelocity: randomRotationSpeed,
|
||||
velocity: leafNewVel
|
||||
})
|
||||
} else if (leafHeight <= floorHeight) {
|
||||
if (!leafDeleteOnGround) {
|
||||
Entities.editEntity(nearbyEntities[i], {
|
||||
locked: false,
|
||||
velocity: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
},
|
||||
angularVelocity: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
}
|
||||
})
|
||||
} else {
|
||||
Entity.deleteEntity(currentLeaf);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
getLeafCount = Script.setInterval(function () {
|
||||
leafCount = 0
|
||||
for (var i = 0; i < nearbyEntities.length; i++) {
|
||||
var entityName = Entities.getEntityProperties(nearbyEntities[i]).name;
|
||||
//Stop Leaves at floorHeight
|
||||
if (leafName === entityName) {
|
||||
leafCount++;
|
||||
if (i == nearbyEntities.length - 1) {
|
||||
//print(leafCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, 1000)
|
||||
|
||||
|
||||
|
||||
function tearDown() {
|
||||
Script.clearInterval(leafTimer);
|
||||
Overlays.deleteOverlay(squallCircle);
|
||||
if (leafDeleteOnTearDown) {
|
||||
for (var i = 0; i < nearbyEntities.length; i++) {
|
||||
var entityName = Entities.getEntityProperties(nearbyEntities[i]).name;
|
||||
if (leafName === entityName) {
|
||||
//We have a match - delete this entity
|
||||
Entities.editEntity(nearbyEntities[i], {
|
||||
locked: false
|
||||
});
|
||||
Entities.deleteEntity(nearbyEntities[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
processProperties();
|
||||
setUp();
|
||||
Script.scriptEnding.connect(tearDown);
|
||||
|
||||
return {};
|
||||
};
|
||||
|
||||
var leafSquall1 = new leafSquall({
|
||||
origin: {
|
||||
x: 3071.5,
|
||||
y: 2170,
|
||||
z: 6765.3
|
||||
},
|
||||
radius: 100,
|
||||
leavesPerMinute: 30,
|
||||
leafSize: {
|
||||
x: 0.3,
|
||||
y: 0.00,
|
||||
z: 0.3
|
||||
},
|
||||
leafFallSpeed: 0.4,
|
||||
leafLifetime: 100,
|
||||
leafSpinMax: 30,
|
||||
debug: false,
|
||||
maxLeaves: 100,
|
||||
leafDeleteOnTearDown: true,
|
||||
complexMovement: true,
|
||||
floorHeight: 2143.5,
|
||||
windFactor: 0.5,
|
||||
leafDeleteOnGround: false
|
||||
});
|
||||
|
||||
// todo
|
||||
//deal with depth issue
|
||||
//
|
||||
// Leaves.js
|
||||
// examples
|
||||
//
|
||||
// Created by Bing Shearer on 14 Jul 2015
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
var leafName = "scriptLeaf";
|
||||
var leafSquall = function (properties) {
|
||||
var // Properties
|
||||
squallOrigin,
|
||||
squallRadius,
|
||||
leavesPerMinute = 60,
|
||||
leafSize = {
|
||||
x: 0.1,
|
||||
y: 0.1,
|
||||
z: 0.1
|
||||
},
|
||||
leafFallSpeed = 1, // m/s
|
||||
leafLifetime = 60, // Seconds
|
||||
leafSpinMax = 0, // Maximum angular velocity per axis; deg/s
|
||||
debug = false, // Display origin circle; don't use running on Stack Manager
|
||||
// Other
|
||||
squallCircle,
|
||||
SQUALL_CIRCLE_COLOR = {
|
||||
red: 255,
|
||||
green: 0,
|
||||
blue: 0
|
||||
},
|
||||
SQUALL_CIRCLE_ALPHA = 0.5,
|
||||
SQUALL_CIRCLE_ROTATION = Quat.fromPitchYawRollDegrees(90, 0, 0),
|
||||
leafProperties,
|
||||
leaf_MODEL_URL = "https://hifi-public.s3.amazonaws.com/ozan/support/forBing/palmLeaf.fbx",
|
||||
leafTimer,
|
||||
leaves = [], // HACK: Work around leaves not always getting velocities
|
||||
leafVelocities = [], // HACK: Work around leaves not always getting velocities
|
||||
DEGREES_TO_RADIANS = Math.PI / 180,
|
||||
leafDeleteOnTearDown = true,
|
||||
maxLeaves,
|
||||
leafCount,
|
||||
nearbyEntities,
|
||||
complexMovement = false,
|
||||
movementTime = 0,
|
||||
maxSpinRadians = properties.leafSpinMax * DEGREES_TO_RADIANS,
|
||||
windFactor,
|
||||
leafDeleteOnGround = false,
|
||||
floorHeight = null;
|
||||
|
||||
|
||||
function processProperties() {
|
||||
if (!properties.hasOwnProperty("origin")) {
|
||||
print("ERROR: Leaf squall origin must be specified");
|
||||
return;
|
||||
}
|
||||
squallOrigin = properties.origin;
|
||||
|
||||
if (!properties.hasOwnProperty("radius")) {
|
||||
print("ERROR: Leaf squall radius must be specified");
|
||||
return;
|
||||
}
|
||||
squallRadius = properties.radius;
|
||||
|
||||
if (properties.hasOwnProperty("leavesPerMinute")) {
|
||||
leavesPerMinute = properties.leavesPerMinute;
|
||||
}
|
||||
|
||||
if (properties.hasOwnProperty("leafSize")) {
|
||||
leafSize = properties.leafSize;
|
||||
}
|
||||
|
||||
if (properties.hasOwnProperty("leafFallSpeed")) {
|
||||
leafFallSpeed = properties.leafFallSpeed;
|
||||
}
|
||||
|
||||
if (properties.hasOwnProperty("leafLifetime")) {
|
||||
leafLifetime = properties.leafLifetime;
|
||||
}
|
||||
|
||||
if (properties.hasOwnProperty("leafSpinMax")) {
|
||||
leafSpinMax = properties.leafSpinMax;
|
||||
}
|
||||
|
||||
if (properties.hasOwnProperty("debug")) {
|
||||
debug = properties.debug;
|
||||
}
|
||||
if (properties.hasOwnProperty("floorHeight")) {
|
||||
floorHeight = properties.floorHeight;
|
||||
}
|
||||
if (properties.hasOwnProperty("maxLeaves")) {
|
||||
maxLeaves = properties.maxLeaves;
|
||||
}
|
||||
if (properties.hasOwnProperty("complexMovement")) {
|
||||
complexMovement = properties.complexMovement;
|
||||
}
|
||||
if (properties.hasOwnProperty("leafDeleteOnGround")) {
|
||||
leafDeleteOnGround = properties.leafDeleteOnGround;
|
||||
}
|
||||
if (properties.hasOwnProperty("windFactor")) {
|
||||
windFactor = properties.windFactor;
|
||||
} else if (complexMovement == true){
|
||||
print("ERROR: Wind Factor must be defined for complex movement")
|
||||
}
|
||||
|
||||
leafProperties = {
|
||||
type: "Model",
|
||||
name: leafName,
|
||||
modelURL: leaf_MODEL_URL,
|
||||
lifetime: leafLifetime,
|
||||
dimensions: leafSize,
|
||||
velocity: {
|
||||
x: 0,
|
||||
y: -leafFallSpeed,
|
||||
z: 0
|
||||
},
|
||||
damping: 0,
|
||||
angularDamping: 0,
|
||||
ignoreForCollisions: true
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
function createleaf() {
|
||||
var angle,
|
||||
radius,
|
||||
offset,
|
||||
leaf,
|
||||
spin = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
},
|
||||
i;
|
||||
|
||||
// HACK: Work around leaves not always getting velocities set at creation
|
||||
for (i = 0; i < leaves.length; i++) {
|
||||
Entities.editEntity(leaves[i], leafVelocities[i]);
|
||||
}
|
||||
|
||||
angle = Math.random() * leafSpinMax;
|
||||
radius = Math.random() * squallRadius;
|
||||
offset = Vec3.multiplyQbyV(Quat.fromPitchYawRollDegrees(0, angle, 0), {
|
||||
x: 0,
|
||||
y: -0.1,
|
||||
z: radius
|
||||
});
|
||||
leafProperties.position = Vec3.sum(squallOrigin, offset);
|
||||
if (properties.leafSpinMax > 0 && !complexMovement) {
|
||||
spin = {
|
||||
x: Math.random() * maxSpinRadians,
|
||||
y: Math.random() * maxSpinRadians,
|
||||
z: Math.random() * maxSpinRadians
|
||||
};
|
||||
leafProperties.angularVelocity = spin;
|
||||
} else if (complexMovement) {
|
||||
spin = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
};
|
||||
leafProperties.angularVelocity = spin
|
||||
}
|
||||
leaf = Entities.addEntity(leafProperties);
|
||||
|
||||
// HACK: Work around leaves not always getting velocities set at creation
|
||||
leaves.push(leaf);
|
||||
leafVelocities.push({
|
||||
velocity: leafProperties.velocity,
|
||||
angularVelocity: spin
|
||||
});
|
||||
if (leaves.length > 5) {
|
||||
leaves.shift();
|
||||
leafVelocities.shift();
|
||||
}
|
||||
}
|
||||
|
||||
function setUp() {
|
||||
if (debug) {
|
||||
squallCircle = Overlays.addOverlay("circle3d", {
|
||||
size: {
|
||||
x: 2 * squallRadius,
|
||||
y: 2 * squallRadius
|
||||
},
|
||||
color: SQUALL_CIRCLE_COLOR,
|
||||
alpha: SQUALL_CIRCLE_ALPHA,
|
||||
solid: true,
|
||||
visible: debug,
|
||||
position: squallOrigin,
|
||||
rotation: SQUALL_CIRCLE_ROTATION
|
||||
});
|
||||
}
|
||||
|
||||
leafTimer = Script.setInterval(function () {
|
||||
if (leafCount <= maxLeaves - 1) {
|
||||
createleaf()
|
||||
}
|
||||
}, 60000 / leavesPerMinute);
|
||||
}
|
||||
Script.setInterval(function () {
|
||||
nearbyEntities = Entities.findEntities(squallOrigin, squallRadius);
|
||||
newLeafMovement()
|
||||
}, 100);
|
||||
|
||||
function newLeafMovement() { //new additions to leaf code. Operates at 10 Hz or every 100 ms
|
||||
movementTime += 0.1;
|
||||
var currentLeaf,
|
||||
randomRotationSpeed = {
|
||||
x: maxSpinRadians * Math.sin(movementTime),
|
||||
y: maxSpinRadians * Math.random(),
|
||||
z: maxSpinRadians * Math.sin(movementTime / 7)
|
||||
};
|
||||
for (var i = 0; i < nearbyEntities.length; i++) {
|
||||
var entityProperties = Entities.getEntityProperties(nearbyEntities[i]);
|
||||
var entityName = entityProperties.name;
|
||||
if (leafName === entityName) {
|
||||
currentLeaf = nearbyEntities[i];
|
||||
var leafHeight = entityProperties.position.y;
|
||||
if (complexMovement && leafHeight > floorHeight || complexMovement && floorHeight == null) { //actual new movement code;
|
||||
var leafCurrentVel = entityProperties.velocity,
|
||||
leafCurrentRot = entityProperties.rotation,
|
||||
yVec = {
|
||||
x: 0,
|
||||
y: 1,
|
||||
z: 0
|
||||
},
|
||||
leafYinWFVec = Vec3.multiplyQbyV(leafCurrentRot, yVec),
|
||||
leafLocalHorVec = Vec3.cross(leafYinWFVec, yVec),
|
||||
leafMostDownVec = Vec3.cross(leafYinWFVec, leafLocalHorVec),
|
||||
leafDesiredVel = Vec3.multiply(leafMostDownVec, windFactor),
|
||||
leafVelDelt = Vec3.subtract(leafDesiredVel, leafCurrentVel),
|
||||
leafNewVel = Vec3.sum(leafCurrentVel, Vec3.multiply(leafVelDelt, windFactor));
|
||||
Entities.editEntity(currentLeaf, {
|
||||
angularVelocity: randomRotationSpeed,
|
||||
velocity: leafNewVel
|
||||
})
|
||||
} else if (leafHeight <= floorHeight) {
|
||||
if (!leafDeleteOnGround) {
|
||||
Entities.editEntity(nearbyEntities[i], {
|
||||
locked: false,
|
||||
velocity: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
},
|
||||
angularVelocity: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
}
|
||||
})
|
||||
} else {
|
||||
Entity.deleteEntity(currentLeaf);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
getLeafCount = Script.setInterval(function () {
|
||||
leafCount = 0
|
||||
for (var i = 0; i < nearbyEntities.length; i++) {
|
||||
var entityName = Entities.getEntityProperties(nearbyEntities[i]).name;
|
||||
//Stop Leaves at floorHeight
|
||||
if (leafName === entityName) {
|
||||
leafCount++;
|
||||
if (i == nearbyEntities.length - 1) {
|
||||
//print(leafCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, 1000)
|
||||
|
||||
|
||||
|
||||
function tearDown() {
|
||||
Script.clearInterval(leafTimer);
|
||||
Overlays.deleteOverlay(squallCircle);
|
||||
if (leafDeleteOnTearDown) {
|
||||
for (var i = 0; i < nearbyEntities.length; i++) {
|
||||
var entityName = Entities.getEntityProperties(nearbyEntities[i]).name;
|
||||
if (leafName === entityName) {
|
||||
//We have a match - delete this entity
|
||||
Entities.editEntity(nearbyEntities[i], {
|
||||
locked: false
|
||||
});
|
||||
Entities.deleteEntity(nearbyEntities[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
processProperties();
|
||||
setUp();
|
||||
Script.scriptEnding.connect(tearDown);
|
||||
|
||||
return {};
|
||||
};
|
||||
|
||||
var leafSquall1 = new leafSquall({
|
||||
origin: {
|
||||
x: 3071.5,
|
||||
y: 2170,
|
||||
z: 6765.3
|
||||
},
|
||||
radius: 100,
|
||||
leavesPerMinute: 30,
|
||||
leafSize: {
|
||||
x: 0.3,
|
||||
y: 0.00,
|
||||
z: 0.3
|
||||
},
|
||||
leafFallSpeed: 0.4,
|
||||
leafLifetime: 100,
|
||||
leafSpinMax: 30,
|
||||
debug: false,
|
||||
maxLeaves: 100,
|
||||
leafDeleteOnTearDown: true,
|
||||
complexMovement: true,
|
||||
floorHeight: 2143.5,
|
||||
windFactor: 0.5,
|
||||
leafDeleteOnGround: false
|
||||
});
|
||||
|
||||
// todo
|
||||
//deal with depth issue
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,54 +1,54 @@
|
|||
//
|
||||
// walkConstants.js
|
||||
// version 1.0
|
||||
//
|
||||
// Created by David Wooldridge, June 2015
|
||||
// Copyright © 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Provides constants necessary for the operation of the walk.js script and the walkApi.js script
|
||||
//
|
||||
// Editing tools for animation data files available here: https://github.com/DaveDubUK/walkTools
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
// locomotion states
|
||||
STATIC = 1;
|
||||
SURFACE_MOTION = 2;
|
||||
AIR_MOTION = 4;
|
||||
|
||||
// directions
|
||||
UP = 1;
|
||||
DOWN = 2;
|
||||
LEFT = 4;
|
||||
RIGHT = 8;
|
||||
FORWARDS = 16;
|
||||
BACKWARDS = 32;
|
||||
NONE = 64;
|
||||
|
||||
// waveshapes
|
||||
SAWTOOTH = 1;
|
||||
TRIANGLE = 2;
|
||||
SQUARE = 4;
|
||||
|
||||
// used by walk.js and walkApi.js
|
||||
MAX_WALK_SPEED = 2.9; // peak, by observation
|
||||
MAX_FT_WHEEL_INCREMENT = 25; // avoid fast walk when landing
|
||||
TOP_SPEED = 300;
|
||||
ON_SURFACE_THRESHOLD = 0.1; // height above surface to be considered as on the surface
|
||||
TRANSITION_COMPLETE = 1000;
|
||||
PITCH_MAX = 60; // maximum speed induced pitch
|
||||
ROLL_MAX = 80; // maximum speed induced leaning / banking
|
||||
DELTA_YAW_MAX = 1.7; // maximum change in yaw in rad/s
|
||||
|
||||
// used by walkApi.js only
|
||||
MOVE_THRESHOLD = 0.075; // movement dead zone
|
||||
ACCELERATION_THRESHOLD = 0.2; // detect stop to walking
|
||||
DECELERATION_THRESHOLD = -6; // detect walking to stop
|
||||
FAST_DECELERATION_THRESHOLD = -150; // detect flying to stop
|
||||
BOUNCE_ACCELERATION_THRESHOLD = 25; // used to ignore gravity influence fluctuations after landing
|
||||
GRAVITY_THRESHOLD = 3.0; // height above surface where gravity is in effect
|
||||
OVERCOME_GRAVITY_SPEED = 0.5; // reaction sensitivity to jumping under gravity
|
||||
LANDING_THRESHOLD = 0.35; // metres from a surface below which need to prepare for impact
|
||||
MAX_TRANSITION_RECURSION = 10; // how many nested transitions are permitted
|
||||
//
|
||||
// walkConstants.js
|
||||
// version 1.0
|
||||
//
|
||||
// Created by David Wooldridge, June 2015
|
||||
// Copyright © 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Provides constants necessary for the operation of the walk.js script and the walkApi.js script
|
||||
//
|
||||
// Editing tools for animation data files available here: https://github.com/DaveDubUK/walkTools
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
// locomotion states
|
||||
STATIC = 1;
|
||||
SURFACE_MOTION = 2;
|
||||
AIR_MOTION = 4;
|
||||
|
||||
// directions
|
||||
UP = 1;
|
||||
DOWN = 2;
|
||||
LEFT = 4;
|
||||
RIGHT = 8;
|
||||
FORWARDS = 16;
|
||||
BACKWARDS = 32;
|
||||
NONE = 64;
|
||||
|
||||
// waveshapes
|
||||
SAWTOOTH = 1;
|
||||
TRIANGLE = 2;
|
||||
SQUARE = 4;
|
||||
|
||||
// used by walk.js and walkApi.js
|
||||
MAX_WALK_SPEED = 2.9; // peak, by observation
|
||||
MAX_FT_WHEEL_INCREMENT = 25; // avoid fast walk when landing
|
||||
TOP_SPEED = 300;
|
||||
ON_SURFACE_THRESHOLD = 0.1; // height above surface to be considered as on the surface
|
||||
TRANSITION_COMPLETE = 1000;
|
||||
PITCH_MAX = 60; // maximum speed induced pitch
|
||||
ROLL_MAX = 80; // maximum speed induced leaning / banking
|
||||
DELTA_YAW_MAX = 1.7; // maximum change in yaw in rad/s
|
||||
|
||||
// used by walkApi.js only
|
||||
MOVE_THRESHOLD = 0.075; // movement dead zone
|
||||
ACCELERATION_THRESHOLD = 0.2; // detect stop to walking
|
||||
DECELERATION_THRESHOLD = -6; // detect walking to stop
|
||||
FAST_DECELERATION_THRESHOLD = -150; // detect flying to stop
|
||||
BOUNCE_ACCELERATION_THRESHOLD = 25; // used to ignore gravity influence fluctuations after landing
|
||||
GRAVITY_THRESHOLD = 3.0; // height above surface where gravity is in effect
|
||||
OVERCOME_GRAVITY_SPEED = 0.5; // reaction sensitivity to jumping under gravity
|
||||
LANDING_THRESHOLD = 0.35; // metres from a surface below which need to prepare for impact
|
||||
MAX_TRANSITION_RECURSION = 10; // how many nested transitions are permitted
|
||||
|
|
|
@ -1,204 +1,204 @@
|
|||
//
|
||||
// walkFilters.js
|
||||
// version 1.1
|
||||
//
|
||||
// Created by David Wooldridge, June 2015
|
||||
// Copyright © 2014 - 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Provides a variety of filters for use by the walk.js script v1.2+
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
// simple averaging (LP) filter for damping / smoothing
|
||||
AveragingFilter = function(length) {
|
||||
// initialise the array of past values
|
||||
this.pastValues = [];
|
||||
for (var i = 0; i < length; i++) {
|
||||
this.pastValues.push(0);
|
||||
}
|
||||
// single arg is the nextInputValue
|
||||
this.process = function() {
|
||||
if (this.pastValues.length === 0 && arguments[0]) {
|
||||
return arguments[0];
|
||||
} else if (arguments[0] !== null) {
|
||||
this.pastValues.push(arguments[0]);
|
||||
this.pastValues.shift();
|
||||
var nextOutputValue = 0;
|
||||
for (var value in this.pastValues) nextOutputValue += this.pastValues[value];
|
||||
return nextOutputValue / this.pastValues.length;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// 2nd order 2Hz Butterworth LP filter
|
||||
ButterworthFilter = function() {
|
||||
// coefficients calculated at: http://www-users.cs.york.ac.uk/~fisher/mkfilter/trad.html
|
||||
this.gain = 104.9784742;
|
||||
this.coeffOne = -0.7436551950;
|
||||
this.coeffTwo = 1.7055521455;
|
||||
|
||||
// initialise the arrays
|
||||
this.xv = [];
|
||||
this.yv = [];
|
||||
for (var i = 0; i < 3; i++) {
|
||||
this.xv.push(0);
|
||||
this.yv.push(0);
|
||||
}
|
||||
|
||||
// process values
|
||||
this.process = function(nextInputValue) {
|
||||
this.xv[0] = this.xv[1];
|
||||
this.xv[1] = this.xv[2];
|
||||
this.xv[2] = nextInputValue / this.gain;
|
||||
this.yv[0] = this.yv[1];
|
||||
this.yv[1] = this.yv[2];
|
||||
this.yv[2] = (this.xv[0] + this.xv[2]) +
|
||||
2 * this.xv[1] +
|
||||
(this.coeffOne * this.yv[0]) +
|
||||
(this.coeffTwo * this.yv[1]);
|
||||
return this.yv[2];
|
||||
};
|
||||
}; // end Butterworth filter constructor
|
||||
|
||||
// Add harmonics to a given sine wave to form square, sawtooth or triangle waves
|
||||
// Geometric wave synthesis fundamentals taken from: http://hyperphysics.phy-astr.gsu.edu/hbase/audio/geowv.html
|
||||
WaveSynth = function(waveShape, numHarmonics, smoothing) {
|
||||
this.numHarmonics = numHarmonics;
|
||||
this.waveShape = waveShape;
|
||||
this.smoothingFilter = new AveragingFilter(smoothing);
|
||||
|
||||
// NB: frequency in radians
|
||||
this.calculate = function(frequency) {
|
||||
// make some shapes
|
||||
var harmonics = 0;
|
||||
var multiplier = 0;
|
||||
var iterations = this.numHarmonics * 2 + 2;
|
||||
if (this.waveShape === TRIANGLE) {
|
||||
iterations++;
|
||||
}
|
||||
for (var n = 1; n < iterations; n++) {
|
||||
switch (this.waveShape) {
|
||||
case SAWTOOTH: {
|
||||
multiplier = 1 / n;
|
||||
harmonics += multiplier * Math.sin(n * frequency);
|
||||
break;
|
||||
}
|
||||
|
||||
case TRIANGLE: {
|
||||
if (n % 2 === 1) {
|
||||
var mulitplier = 1 / (n * n);
|
||||
// multiply (4n-1)th harmonics by -1
|
||||
if (n === 3 || n === 7 || n === 11 || n === 15) {
|
||||
mulitplier *= -1;
|
||||
}
|
||||
harmonics += mulitplier * Math.sin(n * frequency);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SQUARE: {
|
||||
if (n % 2 === 1) {
|
||||
multiplier = 1 / n;
|
||||
harmonics += multiplier * Math.sin(n * frequency);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// smooth the result and return
|
||||
return this.smoothingFilter.process(harmonics);
|
||||
};
|
||||
};
|
||||
|
||||
// Create a motion wave by summing pre-calculated harmonics (Fourier synthesis)
|
||||
HarmonicsFilter = function(magnitudes, phaseAngles) {
|
||||
this.magnitudes = magnitudes;
|
||||
this.phaseAngles = phaseAngles;
|
||||
|
||||
this.calculate = function(twoPiFT) {
|
||||
var harmonics = 0;
|
||||
var numHarmonics = magnitudes.length;
|
||||
for (var n = 0; n < numHarmonics; n++) {
|
||||
harmonics += this.magnitudes[n] * Math.cos(n * twoPiFT - this.phaseAngles[n]);
|
||||
}
|
||||
return harmonics;
|
||||
};
|
||||
};
|
||||
|
||||
// the main filter object literal
|
||||
filter = (function() {
|
||||
|
||||
const HALF_CYCLE = 180;
|
||||
|
||||
// Bezier private variables
|
||||
var _C1 = {x:0, y:0};
|
||||
var _C4 = {x:1, y:1};
|
||||
|
||||
// Bezier private functions
|
||||
function _B1(t) { return t * t * t };
|
||||
function _B2(t) { return 3 * t * t * (1 - t) };
|
||||
function _B3(t) { return 3 * t * (1 - t) * (1 - t) };
|
||||
function _B4(t) { return (1 - t) * (1 - t) * (1 - t) };
|
||||
|
||||
return {
|
||||
|
||||
// helper methods
|
||||
degToRad: function(degrees) {
|
||||
var convertedValue = degrees * Math.PI / HALF_CYCLE;
|
||||
return convertedValue;
|
||||
},
|
||||
|
||||
radToDeg: function(radians) {
|
||||
var convertedValue = radians * HALF_CYCLE / Math.PI;
|
||||
return convertedValue;
|
||||
},
|
||||
|
||||
// these filters need instantiating, as they hold arrays of previous values
|
||||
|
||||
// simple averaging (LP) filter for damping / smoothing
|
||||
createAveragingFilter: function(length) {
|
||||
var newAveragingFilter = new AveragingFilter(length);
|
||||
return newAveragingFilter;
|
||||
},
|
||||
|
||||
// provides LP filtering with improved frequency / phase response
|
||||
createButterworthFilter: function() {
|
||||
var newButterworthFilter = new ButterworthFilter();
|
||||
return newButterworthFilter;
|
||||
},
|
||||
|
||||
// generates sawtooth, triangle or square waves using harmonics
|
||||
createWaveSynth: function(waveShape, numHarmonics, smoothing) {
|
||||
var newWaveSynth = new WaveSynth(waveShape, numHarmonics, smoothing);
|
||||
return newWaveSynth;
|
||||
},
|
||||
|
||||
// generates arbitrary waveforms using pre-calculated harmonics
|
||||
createHarmonicsFilter: function(magnitudes, phaseAngles) {
|
||||
var newHarmonicsFilter = new HarmonicsFilter(magnitudes, phaseAngles);
|
||||
return newHarmonicsFilter;
|
||||
},
|
||||
|
||||
// the following filters do not need separate instances, as they hold no previous values
|
||||
|
||||
// Bezier response curve shaping for more natural transitions
|
||||
bezier: function(input, C2, C3) {
|
||||
// based on script by Dan Pupius (www.pupius.net) http://13thparallel.com/archive/bezier-curves/
|
||||
input = 1 - input;
|
||||
return _C1.y * _B1(input) + C2.y * _B2(input) + C3.y * _B3(input) + _C4.y * _B4(input);
|
||||
},
|
||||
|
||||
// simple clipping filter (special case for hips y-axis skeleton offset for walk animation)
|
||||
clipTrough: function(inputValue, peak, strength) {
|
||||
var outputValue = inputValue * strength;
|
||||
if (outputValue < -peak) {
|
||||
outputValue = -peak;
|
||||
}
|
||||
return outputValue;
|
||||
}
|
||||
}
|
||||
})();
|
||||
//
|
||||
// walkFilters.js
|
||||
// version 1.1
|
||||
//
|
||||
// Created by David Wooldridge, June 2015
|
||||
// Copyright © 2014 - 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Provides a variety of filters for use by the walk.js script v1.2+
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
// simple averaging (LP) filter for damping / smoothing
|
||||
AveragingFilter = function(length) {
|
||||
// initialise the array of past values
|
||||
this.pastValues = [];
|
||||
for (var i = 0; i < length; i++) {
|
||||
this.pastValues.push(0);
|
||||
}
|
||||
// single arg is the nextInputValue
|
||||
this.process = function() {
|
||||
if (this.pastValues.length === 0 && arguments[0]) {
|
||||
return arguments[0];
|
||||
} else if (arguments[0] !== null) {
|
||||
this.pastValues.push(arguments[0]);
|
||||
this.pastValues.shift();
|
||||
var nextOutputValue = 0;
|
||||
for (var value in this.pastValues) nextOutputValue += this.pastValues[value];
|
||||
return nextOutputValue / this.pastValues.length;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// 2nd order 2Hz Butterworth LP filter
|
||||
ButterworthFilter = function() {
|
||||
// coefficients calculated at: http://www-users.cs.york.ac.uk/~fisher/mkfilter/trad.html
|
||||
this.gain = 104.9784742;
|
||||
this.coeffOne = -0.7436551950;
|
||||
this.coeffTwo = 1.7055521455;
|
||||
|
||||
// initialise the arrays
|
||||
this.xv = [];
|
||||
this.yv = [];
|
||||
for (var i = 0; i < 3; i++) {
|
||||
this.xv.push(0);
|
||||
this.yv.push(0);
|
||||
}
|
||||
|
||||
// process values
|
||||
this.process = function(nextInputValue) {
|
||||
this.xv[0] = this.xv[1];
|
||||
this.xv[1] = this.xv[2];
|
||||
this.xv[2] = nextInputValue / this.gain;
|
||||
this.yv[0] = this.yv[1];
|
||||
this.yv[1] = this.yv[2];
|
||||
this.yv[2] = (this.xv[0] + this.xv[2]) +
|
||||
2 * this.xv[1] +
|
||||
(this.coeffOne * this.yv[0]) +
|
||||
(this.coeffTwo * this.yv[1]);
|
||||
return this.yv[2];
|
||||
};
|
||||
}; // end Butterworth filter constructor
|
||||
|
||||
// Add harmonics to a given sine wave to form square, sawtooth or triangle waves
|
||||
// Geometric wave synthesis fundamentals taken from: http://hyperphysics.phy-astr.gsu.edu/hbase/audio/geowv.html
|
||||
WaveSynth = function(waveShape, numHarmonics, smoothing) {
|
||||
this.numHarmonics = numHarmonics;
|
||||
this.waveShape = waveShape;
|
||||
this.smoothingFilter = new AveragingFilter(smoothing);
|
||||
|
||||
// NB: frequency in radians
|
||||
this.calculate = function(frequency) {
|
||||
// make some shapes
|
||||
var harmonics = 0;
|
||||
var multiplier = 0;
|
||||
var iterations = this.numHarmonics * 2 + 2;
|
||||
if (this.waveShape === TRIANGLE) {
|
||||
iterations++;
|
||||
}
|
||||
for (var n = 1; n < iterations; n++) {
|
||||
switch (this.waveShape) {
|
||||
case SAWTOOTH: {
|
||||
multiplier = 1 / n;
|
||||
harmonics += multiplier * Math.sin(n * frequency);
|
||||
break;
|
||||
}
|
||||
|
||||
case TRIANGLE: {
|
||||
if (n % 2 === 1) {
|
||||
var mulitplier = 1 / (n * n);
|
||||
// multiply (4n-1)th harmonics by -1
|
||||
if (n === 3 || n === 7 || n === 11 || n === 15) {
|
||||
mulitplier *= -1;
|
||||
}
|
||||
harmonics += mulitplier * Math.sin(n * frequency);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SQUARE: {
|
||||
if (n % 2 === 1) {
|
||||
multiplier = 1 / n;
|
||||
harmonics += multiplier * Math.sin(n * frequency);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// smooth the result and return
|
||||
return this.smoothingFilter.process(harmonics);
|
||||
};
|
||||
};
|
||||
|
||||
// Create a motion wave by summing pre-calculated harmonics (Fourier synthesis)
|
||||
HarmonicsFilter = function(magnitudes, phaseAngles) {
|
||||
this.magnitudes = magnitudes;
|
||||
this.phaseAngles = phaseAngles;
|
||||
|
||||
this.calculate = function(twoPiFT) {
|
||||
var harmonics = 0;
|
||||
var numHarmonics = magnitudes.length;
|
||||
for (var n = 0; n < numHarmonics; n++) {
|
||||
harmonics += this.magnitudes[n] * Math.cos(n * twoPiFT - this.phaseAngles[n]);
|
||||
}
|
||||
return harmonics;
|
||||
};
|
||||
};
|
||||
|
||||
// the main filter object literal
|
||||
filter = (function() {
|
||||
|
||||
const HALF_CYCLE = 180;
|
||||
|
||||
// Bezier private variables
|
||||
var _C1 = {x:0, y:0};
|
||||
var _C4 = {x:1, y:1};
|
||||
|
||||
// Bezier private functions
|
||||
function _B1(t) { return t * t * t };
|
||||
function _B2(t) { return 3 * t * t * (1 - t) };
|
||||
function _B3(t) { return 3 * t * (1 - t) * (1 - t) };
|
||||
function _B4(t) { return (1 - t) * (1 - t) * (1 - t) };
|
||||
|
||||
return {
|
||||
|
||||
// helper methods
|
||||
degToRad: function(degrees) {
|
||||
var convertedValue = degrees * Math.PI / HALF_CYCLE;
|
||||
return convertedValue;
|
||||
},
|
||||
|
||||
radToDeg: function(radians) {
|
||||
var convertedValue = radians * HALF_CYCLE / Math.PI;
|
||||
return convertedValue;
|
||||
},
|
||||
|
||||
// these filters need instantiating, as they hold arrays of previous values
|
||||
|
||||
// simple averaging (LP) filter for damping / smoothing
|
||||
createAveragingFilter: function(length) {
|
||||
var newAveragingFilter = new AveragingFilter(length);
|
||||
return newAveragingFilter;
|
||||
},
|
||||
|
||||
// provides LP filtering with improved frequency / phase response
|
||||
createButterworthFilter: function() {
|
||||
var newButterworthFilter = new ButterworthFilter();
|
||||
return newButterworthFilter;
|
||||
},
|
||||
|
||||
// generates sawtooth, triangle or square waves using harmonics
|
||||
createWaveSynth: function(waveShape, numHarmonics, smoothing) {
|
||||
var newWaveSynth = new WaveSynth(waveShape, numHarmonics, smoothing);
|
||||
return newWaveSynth;
|
||||
},
|
||||
|
||||
// generates arbitrary waveforms using pre-calculated harmonics
|
||||
createHarmonicsFilter: function(magnitudes, phaseAngles) {
|
||||
var newHarmonicsFilter = new HarmonicsFilter(magnitudes, phaseAngles);
|
||||
return newHarmonicsFilter;
|
||||
},
|
||||
|
||||
// the following filters do not need separate instances, as they hold no previous values
|
||||
|
||||
// Bezier response curve shaping for more natural transitions
|
||||
bezier: function(input, C2, C3) {
|
||||
// based on script by Dan Pupius (www.pupius.net) http://13thparallel.com/archive/bezier-curves/
|
||||
input = 1 - input;
|
||||
return _C1.y * _B1(input) + C2.y * _B2(input) + C3.y * _B3(input) + _C4.y * _B4(input);
|
||||
},
|
||||
|
||||
// simple clipping filter (special case for hips y-axis skeleton offset for walk animation)
|
||||
clipTrough: function(inputValue, peak, strength) {
|
||||
var outputValue = inputValue * strength;
|
||||
if (outputValue < -peak) {
|
||||
outputValue = -peak;
|
||||
}
|
||||
return outputValue;
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
|
|
@ -1,97 +1,97 @@
|
|||
//
|
||||
// walkSettings.js
|
||||
// version 0.1
|
||||
//
|
||||
// Created by David Wooldridge, June 2015
|
||||
// Copyright © 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Presents settings for walk.js
|
||||
//
|
||||
// Editing tools for animation data files available here: https://github.com/DaveDubUK/walkTools
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
WalkSettings = function() {
|
||||
var _visible = false;
|
||||
var _innerWidth = Window.innerWidth;
|
||||
const MARGIN_RIGHT = 58;
|
||||
const MARGIN_TOP = 145;
|
||||
const ICON_SIZE = 50;
|
||||
const ICON_ALPHA = 0.9;
|
||||
|
||||
var minimisedTab = Overlays.addOverlay("image", {
|
||||
x: _innerWidth - MARGIN_RIGHT, y: Window.innerHeight - MARGIN_TOP,
|
||||
width: ICON_SIZE, height: ICON_SIZE,
|
||||
imageURL: pathToAssets + 'overlay-images/ddpa-minimised-ddpa-tab.png',
|
||||
visible: true, alpha: ICON_ALPHA
|
||||
});
|
||||
|
||||
function mousePressEvent(event) {
|
||||
if (Overlays.getOverlayAtPoint(event) === minimisedTab) {
|
||||
_visible = !_visible;
|
||||
_webWindow.setVisible(_visible);
|
||||
}
|
||||
}
|
||||
Controller.mousePressEvent.connect(mousePressEvent);
|
||||
|
||||
Script.update.connect(function(deltaTime) {
|
||||
if (window.innerWidth !== _innerWidth) {
|
||||
_innerWidth = window.innerWidth;
|
||||
Overlays.EditOverlay(minimisedTab, {x: _innerWidth - MARGIN_RIGHT});
|
||||
}
|
||||
});
|
||||
|
||||
function cleanup() {
|
||||
Overlays.deleteOverlay(minimisedTab);
|
||||
}
|
||||
Script.scriptEnding.connect(cleanup);
|
||||
|
||||
var _shift = false;
|
||||
function keyPressEvent(event) {
|
||||
if (event.text === "SHIFT") {
|
||||
_shift = true;
|
||||
}
|
||||
if (_shift && (event.text === 'o' || event.text === 'O')) {
|
||||
_visible = !_visible;
|
||||
_webWindow.setVisible(_visible);
|
||||
}
|
||||
}
|
||||
function keyReleaseEvent(event) {
|
||||
if (event.text === "SHIFT") {
|
||||
_shift = false;
|
||||
}
|
||||
}
|
||||
Controller.keyPressEvent.connect(keyPressEvent);
|
||||
Controller.keyReleaseEvent.connect(keyReleaseEvent);
|
||||
|
||||
// web window
|
||||
const PANEL_WIDTH = 200;
|
||||
const PANEL_HEIGHT = 180;
|
||||
var _url = Script.resolvePath('html/walkSettings.html');
|
||||
var _webWindow = new WebWindow('Walk Settings', _url, PANEL_WIDTH, PANEL_HEIGHT, false);
|
||||
_webWindow.setVisible(false);
|
||||
_webWindow.eventBridge.webEventReceived.connect(function(data) {
|
||||
data = JSON.parse(data);
|
||||
|
||||
if (data.type == "init") {
|
||||
// send the current settings to the window
|
||||
_webWindow.eventBridge.emitScriptEvent(JSON.stringify({
|
||||
type: "update",
|
||||
armsFree: avatar.armsFree,
|
||||
makesFootStepSounds: avatar.makesFootStepSounds,
|
||||
blenderPreRotations: avatar.blenderPreRotations
|
||||
}));
|
||||
} else if (data.type == "powerToggle") {
|
||||
motion.isLive = !motion.isLive;
|
||||
} else if (data.type == "update") {
|
||||
// receive settings from the window
|
||||
avatar.armsFree = data.armsFree;
|
||||
avatar.makesFootStepSounds = data.makesFootStepSounds;
|
||||
avatar.blenderPreRotations = data.blenderPreRotations;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
walkSettings = WalkSettings();
|
||||
//
|
||||
// walkSettings.js
|
||||
// version 0.1
|
||||
//
|
||||
// Created by David Wooldridge, June 2015
|
||||
// Copyright © 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Presents settings for walk.js
|
||||
//
|
||||
// Editing tools for animation data files available here: https://github.com/DaveDubUK/walkTools
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
WalkSettings = function() {
|
||||
var _visible = false;
|
||||
var _innerWidth = Window.innerWidth;
|
||||
const MARGIN_RIGHT = 58;
|
||||
const MARGIN_TOP = 145;
|
||||
const ICON_SIZE = 50;
|
||||
const ICON_ALPHA = 0.9;
|
||||
|
||||
var minimisedTab = Overlays.addOverlay("image", {
|
||||
x: _innerWidth - MARGIN_RIGHT, y: Window.innerHeight - MARGIN_TOP,
|
||||
width: ICON_SIZE, height: ICON_SIZE,
|
||||
imageURL: pathToAssets + 'overlay-images/ddpa-minimised-ddpa-tab.png',
|
||||
visible: true, alpha: ICON_ALPHA
|
||||
});
|
||||
|
||||
function mousePressEvent(event) {
|
||||
if (Overlays.getOverlayAtPoint(event) === minimisedTab) {
|
||||
_visible = !_visible;
|
||||
_webWindow.setVisible(_visible);
|
||||
}
|
||||
}
|
||||
Controller.mousePressEvent.connect(mousePressEvent);
|
||||
|
||||
Script.update.connect(function(deltaTime) {
|
||||
if (window.innerWidth !== _innerWidth) {
|
||||
_innerWidth = window.innerWidth;
|
||||
Overlays.EditOverlay(minimisedTab, {x: _innerWidth - MARGIN_RIGHT});
|
||||
}
|
||||
});
|
||||
|
||||
function cleanup() {
|
||||
Overlays.deleteOverlay(minimisedTab);
|
||||
}
|
||||
Script.scriptEnding.connect(cleanup);
|
||||
|
||||
var _shift = false;
|
||||
function keyPressEvent(event) {
|
||||
if (event.text === "SHIFT") {
|
||||
_shift = true;
|
||||
}
|
||||
if (_shift && (event.text === 'o' || event.text === 'O')) {
|
||||
_visible = !_visible;
|
||||
_webWindow.setVisible(_visible);
|
||||
}
|
||||
}
|
||||
function keyReleaseEvent(event) {
|
||||
if (event.text === "SHIFT") {
|
||||
_shift = false;
|
||||
}
|
||||
}
|
||||
Controller.keyPressEvent.connect(keyPressEvent);
|
||||
Controller.keyReleaseEvent.connect(keyReleaseEvent);
|
||||
|
||||
// web window
|
||||
const PANEL_WIDTH = 200;
|
||||
const PANEL_HEIGHT = 180;
|
||||
var _url = Script.resolvePath('html/walkSettings.html');
|
||||
var _webWindow = new WebWindow('Walk Settings', _url, PANEL_WIDTH, PANEL_HEIGHT, false);
|
||||
_webWindow.setVisible(false);
|
||||
_webWindow.eventBridge.webEventReceived.connect(function(data) {
|
||||
data = JSON.parse(data);
|
||||
|
||||
if (data.type == "init") {
|
||||
// send the current settings to the window
|
||||
_webWindow.eventBridge.emitScriptEvent(JSON.stringify({
|
||||
type: "update",
|
||||
armsFree: avatar.armsFree,
|
||||
makesFootStepSounds: avatar.makesFootStepSounds,
|
||||
blenderPreRotations: avatar.blenderPreRotations
|
||||
}));
|
||||
} else if (data.type == "powerToggle") {
|
||||
motion.isLive = !motion.isLive;
|
||||
} else if (data.type == "update") {
|
||||
// receive settings from the window
|
||||
avatar.armsFree = data.armsFree;
|
||||
avatar.makesFootStepSounds = data.makesFootStepSounds;
|
||||
avatar.blenderPreRotations = data.blenderPreRotations;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
walkSettings = WalkSettings();
|
||||
|
|
|
@ -1,32 +1,32 @@
|
|||
//
|
||||
// createFlashlight.js
|
||||
// examples/entityScripts
|
||||
//
|
||||
// Created by Sam Gateau on 9/9/15.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// This is a toy script that create a flashlight entity that lit when grabbed
|
||||
// This can be run from an interface and the flashlight will get deleted from the domain when quitting
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
/*global MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt */
|
||||
Script.include("https://hifi-public.s3.amazonaws.com/scripts/utilities.js");
|
||||
|
||||
|
||||
var scriptURL = Script.resolvePath('flashlight.js?123123');
|
||||
|
||||
var modelURL = "https://hifi-public.s3.amazonaws.com/models/props/flashlight.fbx";
|
||||
|
||||
var center = Vec3.sum(Vec3.sum(MyAvatar.position, {x: 0, y: 0.5, z: 0}), Vec3.multiply(0.5, Quat.getFront(Camera.getOrientation())));
|
||||
|
||||
var flashlight = Entities.addEntity({
|
||||
type: "Model",
|
||||
modelURL: modelURL,
|
||||
position: center,
|
||||
dimensions: { x: 0.08, y: 0.30, z: 0.08},
|
||||
collisionsWillMove: true,
|
||||
shapeType: 'box',
|
||||
script: scriptURL
|
||||
});
|
||||
//
|
||||
// createFlashlight.js
|
||||
// examples/entityScripts
|
||||
//
|
||||
// Created by Sam Gateau on 9/9/15.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// This is a toy script that create a flashlight entity that lit when grabbed
|
||||
// This can be run from an interface and the flashlight will get deleted from the domain when quitting
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
/*global MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt */
|
||||
Script.include("https://hifi-public.s3.amazonaws.com/scripts/utilities.js");
|
||||
|
||||
|
||||
var scriptURL = Script.resolvePath('flashlight.js?123123');
|
||||
|
||||
var modelURL = "https://hifi-public.s3.amazonaws.com/models/props/flashlight.fbx";
|
||||
|
||||
var center = Vec3.sum(Vec3.sum(MyAvatar.position, {x: 0, y: 0.5, z: 0}), Vec3.multiply(0.5, Quat.getFront(Camera.getOrientation())));
|
||||
|
||||
var flashlight = Entities.addEntity({
|
||||
type: "Model",
|
||||
modelURL: modelURL,
|
||||
position: center,
|
||||
dimensions: { x: 0.08, y: 0.30, z: 0.08},
|
||||
collisionsWillMove: true,
|
||||
shapeType: 'box',
|
||||
script: scriptURL
|
||||
});
|
||||
|
|
|
@ -1,269 +1,269 @@
|
|||
//
|
||||
// flashlight.js
|
||||
//
|
||||
// Script Type: Entity
|
||||
//
|
||||
// Created by Sam Gateau on 9/9/15.
|
||||
// Additions by James B. Pollack @imgntn on 9/21/2015
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// This is a toy script that can be added to the Flashlight model entity:
|
||||
// "https://hifi-public.s3.amazonaws.com/models/props/flashlight.fbx"
|
||||
// that creates a spotlight attached with the flashlight model while the entity is grabbed
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
/*global MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, Audio, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt */
|
||||
(function() {
|
||||
|
||||
Script.include("../../libraries/utils.js");
|
||||
|
||||
var ON_SOUND_URL = 'http://hifi-public.s3.amazonaws.com/sounds/Switches%20and%20sliders/flashlight_on.wav';
|
||||
var OFF_SOUND_URL = 'http://hifi-public.s3.amazonaws.com/sounds/Switches%20and%20sliders/flashlight_off.wav';
|
||||
|
||||
//we are creating lights that we don't want to get stranded so lets make sure that we can get rid of them
|
||||
var startTime = Date.now();
|
||||
//if you're going to be using this in a dungeon or something and holding it for a long time, increase this lifetime value.
|
||||
var LIFETIME = 25;
|
||||
var MSEC_PER_SEC = 1000.0;
|
||||
|
||||
// this is the "constructor" for the entity as a JS object we don't do much here, but we do want to remember
|
||||
// our this object, so we can access it in cases where we're called without a this (like in the case of various global signals)
|
||||
function Flashlight() {
|
||||
return;
|
||||
}
|
||||
|
||||
//if the trigger value goes below this while held, the flashlight will turn off. if it goes above, it will
|
||||
var DISABLE_LIGHT_THRESHOLD = 0.7;
|
||||
|
||||
// These constants define the Spotlight position and orientation relative to the model
|
||||
var MODEL_LIGHT_POSITION = {
|
||||
x: 0,
|
||||
y: -0.3,
|
||||
z: 0
|
||||
};
|
||||
var MODEL_LIGHT_ROTATION = Quat.angleAxis(-90, {
|
||||
x: 1,
|
||||
y: 0,
|
||||
z: 0
|
||||
});
|
||||
|
||||
var GLOW_LIGHT_POSITION = {
|
||||
x: 0,
|
||||
y: -0.1,
|
||||
z: 0
|
||||
};
|
||||
|
||||
// Evaluate the world light entity positions and orientations from the model ones
|
||||
function evalLightWorldTransform(modelPos, modelRot) {
|
||||
|
||||
return {
|
||||
p: Vec3.sum(modelPos, Vec3.multiplyQbyV(modelRot, MODEL_LIGHT_POSITION)),
|
||||
q: Quat.multiply(modelRot, MODEL_LIGHT_ROTATION)
|
||||
};
|
||||
}
|
||||
|
||||
function glowLightWorldTransform(modelPos, modelRot) {
|
||||
return {
|
||||
p: Vec3.sum(modelPos, Vec3.multiplyQbyV(modelRot, GLOW_LIGHT_POSITION)),
|
||||
q: Quat.multiply(modelRot, MODEL_LIGHT_ROTATION)
|
||||
};
|
||||
}
|
||||
|
||||
Flashlight.prototype = {
|
||||
lightOn: false,
|
||||
hand: null,
|
||||
whichHand: null,
|
||||
hasSpotlight: false,
|
||||
spotlight: null,
|
||||
setRightHand: function() {
|
||||
this.hand = 'RIGHT';
|
||||
},
|
||||
|
||||
setLeftHand: function() {
|
||||
this.hand = 'LEFT';
|
||||
},
|
||||
|
||||
startNearGrab: function() {
|
||||
if (!this.hasSpotlight) {
|
||||
|
||||
//this light casts the beam
|
||||
this.spotlight = Entities.addEntity({
|
||||
type: "Light",
|
||||
isSpotlight: true,
|
||||
dimensions: {
|
||||
x: 2,
|
||||
y: 2,
|
||||
z: 20
|
||||
},
|
||||
color: {
|
||||
red: 255,
|
||||
green: 255,
|
||||
blue: 255
|
||||
},
|
||||
intensity: 2,
|
||||
exponent: 0.3,
|
||||
cutoff: 20,
|
||||
lifetime: LIFETIME
|
||||
});
|
||||
|
||||
//this light creates the effect of a bulb at the end of the flashlight
|
||||
this.glowLight = Entities.addEntity({
|
||||
type: "Light",
|
||||
dimensions: {
|
||||
x: 0.25,
|
||||
y: 0.25,
|
||||
z: 0.25
|
||||
},
|
||||
isSpotlight: false,
|
||||
color: {
|
||||
red: 255,
|
||||
green: 255,
|
||||
blue: 255
|
||||
},
|
||||
exponent: 0,
|
||||
cutoff: 90, // in degrees
|
||||
lifetime: LIFETIME
|
||||
});
|
||||
|
||||
this.hasSpotlight = true;
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
setWhichHand: function() {
|
||||
this.whichHand = this.hand;
|
||||
},
|
||||
|
||||
continueNearGrab: function() {
|
||||
if (this.whichHand === null) {
|
||||
//only set the active hand once -- if we always read the current hand, our 'holding' hand will get overwritten
|
||||
this.setWhichHand();
|
||||
} else {
|
||||
this.updateLightPositions();
|
||||
this.changeLightWithTriggerPressure(this.whichHand);
|
||||
}
|
||||
},
|
||||
|
||||
releaseGrab: function() {
|
||||
//delete the lights and reset state
|
||||
if (this.hasSpotlight) {
|
||||
Entities.deleteEntity(this.spotlight);
|
||||
Entities.deleteEntity(this.glowLight);
|
||||
this.hasSpotlight = false;
|
||||
this.glowLight = null;
|
||||
this.spotlight = null;
|
||||
this.whichHand = null;
|
||||
this.lightOn = false;
|
||||
}
|
||||
},
|
||||
|
||||
updateLightPositions: function() {
|
||||
var modelProperties = Entities.getEntityProperties(this.entityID, ['position', 'rotation']);
|
||||
|
||||
//move the two lights along the vectors we set above
|
||||
var lightTransform = evalLightWorldTransform(modelProperties.position, modelProperties.rotation);
|
||||
var glowLightTransform = glowLightWorldTransform(modelProperties.position, modelProperties.rotation);
|
||||
|
||||
//move them with the entity model
|
||||
Entities.editEntity(this.spotlight, {
|
||||
position: lightTransform.p,
|
||||
rotation: lightTransform.q,
|
||||
lifetime: (Date.now() - startTime) / MSEC_PER_SEC + LIFETIME
|
||||
});
|
||||
|
||||
Entities.editEntity(this.glowLight, {
|
||||
position: glowLightTransform.p,
|
||||
rotation: glowLightTransform.q,
|
||||
lifetime: (Date.now() - startTime) / MSEC_PER_SEC + LIFETIME
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
changeLightWithTriggerPressure: function(flashLightHand) {
|
||||
var handClickString = flashLightHand + "_HAND_CLICK";
|
||||
|
||||
var handClick = Controller.findAction(handClickString);
|
||||
|
||||
this.triggerValue = Controller.getActionValue(handClick);
|
||||
|
||||
if (this.triggerValue < DISABLE_LIGHT_THRESHOLD && this.lightOn === true) {
|
||||
this.turnLightOff();
|
||||
} else if (this.triggerValue >= DISABLE_LIGHT_THRESHOLD && this.lightOn === false) {
|
||||
this.turnLightOn();
|
||||
}
|
||||
return;
|
||||
},
|
||||
|
||||
turnLightOff: function() {
|
||||
this.playSoundAtCurrentPosition(false);
|
||||
Entities.editEntity(this.spotlight, {
|
||||
intensity: 0
|
||||
});
|
||||
Entities.editEntity(this.glowLight, {
|
||||
intensity: 0
|
||||
});
|
||||
this.lightOn = false;
|
||||
},
|
||||
|
||||
turnLightOn: function() {
|
||||
this.playSoundAtCurrentPosition(true);
|
||||
|
||||
Entities.editEntity(this.glowLight, {
|
||||
intensity: 2
|
||||
});
|
||||
Entities.editEntity(this.spotlight, {
|
||||
intensity: 2
|
||||
});
|
||||
this.lightOn = true;
|
||||
},
|
||||
|
||||
playSoundAtCurrentPosition: function(playOnSound) {
|
||||
var position = Entities.getEntityProperties(this.entityID, "position").position;
|
||||
|
||||
var audioProperties = {
|
||||
volume: 0.25,
|
||||
position: position
|
||||
};
|
||||
|
||||
if (playOnSound) {
|
||||
Audio.playSound(this.ON_SOUND, audioProperties);
|
||||
} else {
|
||||
Audio.playSound(this.OFF_SOUND, audioProperties);
|
||||
}
|
||||
},
|
||||
|
||||
preload: function(entityID) {
|
||||
|
||||
// preload() will be called when the entity has become visible (or known) to the interface
|
||||
// it gives us a chance to set our local JavaScript object up. In this case it means:
|
||||
// * remembering our entityID, so we can access it in cases where we're called without an entityID
|
||||
// * preloading sounds
|
||||
this.entityID = entityID;
|
||||
this.ON_SOUND = SoundCache.getSound(ON_SOUND_URL);
|
||||
this.OFF_SOUND = SoundCache.getSound(OFF_SOUND_URL);
|
||||
|
||||
},
|
||||
|
||||
unload: function() {
|
||||
// unload() will be called when our entity is no longer available. It may be because we were deleted,
|
||||
// or because we've left the domain or quit the application.
|
||||
if (this.hasSpotlight) {
|
||||
Entities.deleteEntity(this.spotlight);
|
||||
Entities.deleteEntity(this.glowLight);
|
||||
this.hasSpotlight = false;
|
||||
this.glowLight = null;
|
||||
this.spotlight = null;
|
||||
this.whichHand = null;
|
||||
this.lightOn = false;
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
// entity scripts always need to return a newly constructed object of our type
|
||||
return new Flashlight();
|
||||
});
|
||||
//
|
||||
// flashlight.js
|
||||
//
|
||||
// Script Type: Entity
|
||||
//
|
||||
// Created by Sam Gateau on 9/9/15.
|
||||
// Additions by James B. Pollack @imgntn on 9/21/2015
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// This is a toy script that can be added to the Flashlight model entity:
|
||||
// "https://hifi-public.s3.amazonaws.com/models/props/flashlight.fbx"
|
||||
// that creates a spotlight attached with the flashlight model while the entity is grabbed
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
/*global MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, Audio, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt */
|
||||
(function() {
|
||||
|
||||
Script.include("../../libraries/utils.js");
|
||||
|
||||
var ON_SOUND_URL = 'http://hifi-public.s3.amazonaws.com/sounds/Switches%20and%20sliders/flashlight_on.wav';
|
||||
var OFF_SOUND_URL = 'http://hifi-public.s3.amazonaws.com/sounds/Switches%20and%20sliders/flashlight_off.wav';
|
||||
|
||||
//we are creating lights that we don't want to get stranded so lets make sure that we can get rid of them
|
||||
var startTime = Date.now();
|
||||
//if you're going to be using this in a dungeon or something and holding it for a long time, increase this lifetime value.
|
||||
var LIFETIME = 25;
|
||||
var MSEC_PER_SEC = 1000.0;
|
||||
|
||||
// this is the "constructor" for the entity as a JS object we don't do much here, but we do want to remember
|
||||
// our this object, so we can access it in cases where we're called without a this (like in the case of various global signals)
|
||||
function Flashlight() {
|
||||
return;
|
||||
}
|
||||
|
||||
//if the trigger value goes below this while held, the flashlight will turn off. if it goes above, it will
|
||||
var DISABLE_LIGHT_THRESHOLD = 0.7;
|
||||
|
||||
// These constants define the Spotlight position and orientation relative to the model
|
||||
var MODEL_LIGHT_POSITION = {
|
||||
x: 0,
|
||||
y: -0.3,
|
||||
z: 0
|
||||
};
|
||||
var MODEL_LIGHT_ROTATION = Quat.angleAxis(-90, {
|
||||
x: 1,
|
||||
y: 0,
|
||||
z: 0
|
||||
});
|
||||
|
||||
var GLOW_LIGHT_POSITION = {
|
||||
x: 0,
|
||||
y: -0.1,
|
||||
z: 0
|
||||
};
|
||||
|
||||
// Evaluate the world light entity positions and orientations from the model ones
|
||||
function evalLightWorldTransform(modelPos, modelRot) {
|
||||
|
||||
return {
|
||||
p: Vec3.sum(modelPos, Vec3.multiplyQbyV(modelRot, MODEL_LIGHT_POSITION)),
|
||||
q: Quat.multiply(modelRot, MODEL_LIGHT_ROTATION)
|
||||
};
|
||||
}
|
||||
|
||||
function glowLightWorldTransform(modelPos, modelRot) {
|
||||
return {
|
||||
p: Vec3.sum(modelPos, Vec3.multiplyQbyV(modelRot, GLOW_LIGHT_POSITION)),
|
||||
q: Quat.multiply(modelRot, MODEL_LIGHT_ROTATION)
|
||||
};
|
||||
}
|
||||
|
||||
Flashlight.prototype = {
|
||||
lightOn: false,
|
||||
hand: null,
|
||||
whichHand: null,
|
||||
hasSpotlight: false,
|
||||
spotlight: null,
|
||||
setRightHand: function() {
|
||||
this.hand = 'RIGHT';
|
||||
},
|
||||
|
||||
setLeftHand: function() {
|
||||
this.hand = 'LEFT';
|
||||
},
|
||||
|
||||
startNearGrab: function() {
|
||||
if (!this.hasSpotlight) {
|
||||
|
||||
//this light casts the beam
|
||||
this.spotlight = Entities.addEntity({
|
||||
type: "Light",
|
||||
isSpotlight: true,
|
||||
dimensions: {
|
||||
x: 2,
|
||||
y: 2,
|
||||
z: 20
|
||||
},
|
||||
color: {
|
||||
red: 255,
|
||||
green: 255,
|
||||
blue: 255
|
||||
},
|
||||
intensity: 2,
|
||||
exponent: 0.3,
|
||||
cutoff: 20,
|
||||
lifetime: LIFETIME
|
||||
});
|
||||
|
||||
//this light creates the effect of a bulb at the end of the flashlight
|
||||
this.glowLight = Entities.addEntity({
|
||||
type: "Light",
|
||||
dimensions: {
|
||||
x: 0.25,
|
||||
y: 0.25,
|
||||
z: 0.25
|
||||
},
|
||||
isSpotlight: false,
|
||||
color: {
|
||||
red: 255,
|
||||
green: 255,
|
||||
blue: 255
|
||||
},
|
||||
exponent: 0,
|
||||
cutoff: 90, // in degrees
|
||||
lifetime: LIFETIME
|
||||
});
|
||||
|
||||
this.hasSpotlight = true;
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
setWhichHand: function() {
|
||||
this.whichHand = this.hand;
|
||||
},
|
||||
|
||||
continueNearGrab: function() {
|
||||
if (this.whichHand === null) {
|
||||
//only set the active hand once -- if we always read the current hand, our 'holding' hand will get overwritten
|
||||
this.setWhichHand();
|
||||
} else {
|
||||
this.updateLightPositions();
|
||||
this.changeLightWithTriggerPressure(this.whichHand);
|
||||
}
|
||||
},
|
||||
|
||||
releaseGrab: function() {
|
||||
//delete the lights and reset state
|
||||
if (this.hasSpotlight) {
|
||||
Entities.deleteEntity(this.spotlight);
|
||||
Entities.deleteEntity(this.glowLight);
|
||||
this.hasSpotlight = false;
|
||||
this.glowLight = null;
|
||||
this.spotlight = null;
|
||||
this.whichHand = null;
|
||||
this.lightOn = false;
|
||||
}
|
||||
},
|
||||
|
||||
updateLightPositions: function() {
|
||||
var modelProperties = Entities.getEntityProperties(this.entityID, ['position', 'rotation']);
|
||||
|
||||
//move the two lights along the vectors we set above
|
||||
var lightTransform = evalLightWorldTransform(modelProperties.position, modelProperties.rotation);
|
||||
var glowLightTransform = glowLightWorldTransform(modelProperties.position, modelProperties.rotation);
|
||||
|
||||
//move them with the entity model
|
||||
Entities.editEntity(this.spotlight, {
|
||||
position: lightTransform.p,
|
||||
rotation: lightTransform.q,
|
||||
lifetime: (Date.now() - startTime) / MSEC_PER_SEC + LIFETIME
|
||||
});
|
||||
|
||||
Entities.editEntity(this.glowLight, {
|
||||
position: glowLightTransform.p,
|
||||
rotation: glowLightTransform.q,
|
||||
lifetime: (Date.now() - startTime) / MSEC_PER_SEC + LIFETIME
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
changeLightWithTriggerPressure: function(flashLightHand) {
|
||||
var handClickString = flashLightHand + "_HAND_CLICK";
|
||||
|
||||
var handClick = Controller.findAction(handClickString);
|
||||
|
||||
this.triggerValue = Controller.getActionValue(handClick);
|
||||
|
||||
if (this.triggerValue < DISABLE_LIGHT_THRESHOLD && this.lightOn === true) {
|
||||
this.turnLightOff();
|
||||
} else if (this.triggerValue >= DISABLE_LIGHT_THRESHOLD && this.lightOn === false) {
|
||||
this.turnLightOn();
|
||||
}
|
||||
return;
|
||||
},
|
||||
|
||||
turnLightOff: function() {
|
||||
this.playSoundAtCurrentPosition(false);
|
||||
Entities.editEntity(this.spotlight, {
|
||||
intensity: 0
|
||||
});
|
||||
Entities.editEntity(this.glowLight, {
|
||||
intensity: 0
|
||||
});
|
||||
this.lightOn = false;
|
||||
},
|
||||
|
||||
turnLightOn: function() {
|
||||
this.playSoundAtCurrentPosition(true);
|
||||
|
||||
Entities.editEntity(this.glowLight, {
|
||||
intensity: 2
|
||||
});
|
||||
Entities.editEntity(this.spotlight, {
|
||||
intensity: 2
|
||||
});
|
||||
this.lightOn = true;
|
||||
},
|
||||
|
||||
playSoundAtCurrentPosition: function(playOnSound) {
|
||||
var position = Entities.getEntityProperties(this.entityID, "position").position;
|
||||
|
||||
var audioProperties = {
|
||||
volume: 0.25,
|
||||
position: position
|
||||
};
|
||||
|
||||
if (playOnSound) {
|
||||
Audio.playSound(this.ON_SOUND, audioProperties);
|
||||
} else {
|
||||
Audio.playSound(this.OFF_SOUND, audioProperties);
|
||||
}
|
||||
},
|
||||
|
||||
preload: function(entityID) {
|
||||
|
||||
// preload() will be called when the entity has become visible (or known) to the interface
|
||||
// it gives us a chance to set our local JavaScript object up. In this case it means:
|
||||
// * remembering our entityID, so we can access it in cases where we're called without an entityID
|
||||
// * preloading sounds
|
||||
this.entityID = entityID;
|
||||
this.ON_SOUND = SoundCache.getSound(ON_SOUND_URL);
|
||||
this.OFF_SOUND = SoundCache.getSound(OFF_SOUND_URL);
|
||||
|
||||
},
|
||||
|
||||
unload: function() {
|
||||
// unload() will be called when our entity is no longer available. It may be because we were deleted,
|
||||
// or because we've left the domain or quit the application.
|
||||
if (this.hasSpotlight) {
|
||||
Entities.deleteEntity(this.spotlight);
|
||||
Entities.deleteEntity(this.glowLight);
|
||||
this.hasSpotlight = false;
|
||||
this.glowLight = null;
|
||||
this.spotlight = null;
|
||||
this.whichHand = null;
|
||||
this.lightOn = false;
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
// entity scripts always need to return a newly constructed object of our type
|
||||
return new Flashlight();
|
||||
});
|
||||
|
|
|
@ -1,87 +1,87 @@
|
|||
//
|
||||
// SunLightExample.js
|
||||
// examples
|
||||
// Sam Gateau
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
Script.include("cookies.js");
|
||||
|
||||
var panel = new Panel(10, 100);
|
||||
|
||||
function CounterWidget(parentPanel, name, feedGetter, drawGetter, capSetter, capGetter) {
|
||||
this.subPanel = panel.newSubPanel(name);
|
||||
|
||||
this.subPanel.newSlider("Num Feed", 0, 1,
|
||||
function(value) { },
|
||||
feedGetter,
|
||||
function(value) { return (value); });
|
||||
this.subPanel.newSlider("Num Drawn", 0, 1,
|
||||
function(value) { },
|
||||
drawGetter,
|
||||
function(value) { return (value); });
|
||||
this.subPanel.newSlider("Max Drawn", -1, 1,
|
||||
capSetter,
|
||||
capGetter,
|
||||
function(value) { return (value); });
|
||||
|
||||
this.update = function () {
|
||||
var numFeed = this.subPanel.get("Num Feed");
|
||||
this.subPanel.set("Num Feed", numFeed);
|
||||
this.subPanel.set("Num Drawn", this.subPanel.get("Num Drawn"));
|
||||
|
||||
var numMax = Math.max(numFeed, 1);
|
||||
this.subPanel.getWidget("Num Feed").setMaxValue(numMax);
|
||||
this.subPanel.getWidget("Num Drawn").setMaxValue(numMax);
|
||||
this.subPanel.getWidget("Max Drawn").setMaxValue(numMax);
|
||||
};
|
||||
};
|
||||
|
||||
var opaquesCounter = new CounterWidget(panel, "Opaques",
|
||||
function () { return Scene.getEngineNumFeedOpaqueItems(); },
|
||||
function () { return Scene.getEngineNumDrawnOpaqueItems(); },
|
||||
function(value) { Scene.setEngineMaxDrawnOpaqueItems(value); },
|
||||
function () { return Scene.getEngineMaxDrawnOpaqueItems(); }
|
||||
);
|
||||
|
||||
var transparentsCounter = new CounterWidget(panel, "Transparents",
|
||||
function () { return Scene.getEngineNumFeedTransparentItems(); },
|
||||
function () { return Scene.getEngineNumDrawnTransparentItems(); },
|
||||
function(value) { Scene.setEngineMaxDrawnTransparentItems(value); },
|
||||
function () { return Scene.getEngineMaxDrawnTransparentItems(); }
|
||||
);
|
||||
|
||||
var overlaysCounter = new CounterWidget(panel, "Overlays",
|
||||
function () { return Scene.getEngineNumFeedOverlay3DItems(); },
|
||||
function () { return Scene.getEngineNumDrawnOverlay3DItems(); },
|
||||
function(value) { Scene.setEngineMaxDrawnOverlay3DItems(value); },
|
||||
function () { return Scene.getEngineMaxDrawnOverlay3DItems(); }
|
||||
);
|
||||
|
||||
|
||||
panel.newCheckbox("Display status",
|
||||
function(value) { Scene.setEngineDisplayItemStatus(value); },
|
||||
function() { return Scene.doEngineDisplayItemStatus(); },
|
||||
function(value) { return (value); }
|
||||
);
|
||||
|
||||
var tickTackPeriod = 500;
|
||||
|
||||
function updateCounters() {
|
||||
opaquesCounter.update();
|
||||
transparentsCounter.update();
|
||||
overlaysCounter.update();
|
||||
}
|
||||
Script.setInterval(updateCounters, tickTackPeriod);
|
||||
|
||||
Controller.mouseMoveEvent.connect(function panelMouseMoveEvent(event) { return panel.mouseMoveEvent(event); });
|
||||
Controller.mousePressEvent.connect( function panelMousePressEvent(event) { return panel.mousePressEvent(event); });
|
||||
Controller.mouseReleaseEvent.connect(function(event) { return panel.mouseReleaseEvent(event); });
|
||||
|
||||
function scriptEnding() {
|
||||
panel.destroy();
|
||||
}
|
||||
Script.scriptEnding.connect(scriptEnding);
|
||||
//
|
||||
// SunLightExample.js
|
||||
// examples
|
||||
// Sam Gateau
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
Script.include("cookies.js");
|
||||
|
||||
var panel = new Panel(10, 100);
|
||||
|
||||
function CounterWidget(parentPanel, name, feedGetter, drawGetter, capSetter, capGetter) {
|
||||
this.subPanel = panel.newSubPanel(name);
|
||||
|
||||
this.subPanel.newSlider("Num Feed", 0, 1,
|
||||
function(value) { },
|
||||
feedGetter,
|
||||
function(value) { return (value); });
|
||||
this.subPanel.newSlider("Num Drawn", 0, 1,
|
||||
function(value) { },
|
||||
drawGetter,
|
||||
function(value) { return (value); });
|
||||
this.subPanel.newSlider("Max Drawn", -1, 1,
|
||||
capSetter,
|
||||
capGetter,
|
||||
function(value) { return (value); });
|
||||
|
||||
this.update = function () {
|
||||
var numFeed = this.subPanel.get("Num Feed");
|
||||
this.subPanel.set("Num Feed", numFeed);
|
||||
this.subPanel.set("Num Drawn", this.subPanel.get("Num Drawn"));
|
||||
|
||||
var numMax = Math.max(numFeed, 1);
|
||||
this.subPanel.getWidget("Num Feed").setMaxValue(numMax);
|
||||
this.subPanel.getWidget("Num Drawn").setMaxValue(numMax);
|
||||
this.subPanel.getWidget("Max Drawn").setMaxValue(numMax);
|
||||
};
|
||||
};
|
||||
|
||||
var opaquesCounter = new CounterWidget(panel, "Opaques",
|
||||
function () { return Scene.getEngineNumFeedOpaqueItems(); },
|
||||
function () { return Scene.getEngineNumDrawnOpaqueItems(); },
|
||||
function(value) { Scene.setEngineMaxDrawnOpaqueItems(value); },
|
||||
function () { return Scene.getEngineMaxDrawnOpaqueItems(); }
|
||||
);
|
||||
|
||||
var transparentsCounter = new CounterWidget(panel, "Transparents",
|
||||
function () { return Scene.getEngineNumFeedTransparentItems(); },
|
||||
function () { return Scene.getEngineNumDrawnTransparentItems(); },
|
||||
function(value) { Scene.setEngineMaxDrawnTransparentItems(value); },
|
||||
function () { return Scene.getEngineMaxDrawnTransparentItems(); }
|
||||
);
|
||||
|
||||
var overlaysCounter = new CounterWidget(panel, "Overlays",
|
||||
function () { return Scene.getEngineNumFeedOverlay3DItems(); },
|
||||
function () { return Scene.getEngineNumDrawnOverlay3DItems(); },
|
||||
function(value) { Scene.setEngineMaxDrawnOverlay3DItems(value); },
|
||||
function () { return Scene.getEngineMaxDrawnOverlay3DItems(); }
|
||||
);
|
||||
|
||||
|
||||
panel.newCheckbox("Display status",
|
||||
function(value) { Scene.setEngineDisplayItemStatus(value); },
|
||||
function() { return Scene.doEngineDisplayItemStatus(); },
|
||||
function(value) { return (value); }
|
||||
);
|
||||
|
||||
var tickTackPeriod = 500;
|
||||
|
||||
function updateCounters() {
|
||||
opaquesCounter.update();
|
||||
transparentsCounter.update();
|
||||
overlaysCounter.update();
|
||||
}
|
||||
Script.setInterval(updateCounters, tickTackPeriod);
|
||||
|
||||
Controller.mouseMoveEvent.connect(function panelMouseMoveEvent(event) { return panel.mouseMoveEvent(event); });
|
||||
Controller.mousePressEvent.connect( function panelMousePressEvent(event) { return panel.mousePressEvent(event); });
|
||||
Controller.mouseReleaseEvent.connect(function(event) { return panel.mouseReleaseEvent(event); });
|
||||
|
||||
function scriptEnding() {
|
||||
panel.destroy();
|
||||
}
|
||||
Script.scriptEnding.connect(scriptEnding);
|
||||
|
|
908
examples/walk.js
908
examples/walk.js
|
@ -1,454 +1,454 @@
|
|||
//
|
||||
// walk.js
|
||||
// version 1.25
|
||||
//
|
||||
// Created by David Wooldridge, June 2015
|
||||
// Copyright © 2014 - 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Animates an avatar using procedural animation techniques.
|
||||
//
|
||||
// Editing tools for animation data files available here: https://github.com/DaveDubUK/walkTools
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
// animations, reach poses, reach pose parameters, transitions, transition parameters, sounds, image/s and reference files
|
||||
const HIFI_PUBLIC_BUCKET = "https://hifi-public.s3.amazonaws.com/";
|
||||
var pathToAssets = HIFI_PUBLIC_BUCKET + "procedural-animator/assets/";
|
||||
|
||||
Script.include([
|
||||
"./libraries/walkConstants.js",
|
||||
"./libraries/walkFilters.js",
|
||||
"./libraries/walkApi.js",
|
||||
pathToAssets + "walkAssets.js"
|
||||
]);
|
||||
|
||||
// construct Avatar, Motion and (null) Transition
|
||||
var avatar = new Avatar();
|
||||
var motion = new Motion();
|
||||
var nullTransition = new Transition();
|
||||
motion.currentTransition = nullTransition;
|
||||
|
||||
// create settings (gets initial values from avatar)
|
||||
Script.include("./libraries/walkSettings.js");
|
||||
|
||||
// Main loop
|
||||
Script.update.connect(function(deltaTime) {
|
||||
|
||||
if (motion.isLive) {
|
||||
|
||||
// assess current locomotion state
|
||||
motion.assess(deltaTime);
|
||||
|
||||
// decide which animation should be playing
|
||||
selectAnimation();
|
||||
|
||||
// advance the animation cycle/s by the correct amount/s
|
||||
advanceAnimations();
|
||||
|
||||
// update the progress of any live transitions
|
||||
updateTransitions();
|
||||
|
||||
// apply translation and rotations
|
||||
renderMotion();
|
||||
|
||||
// save this frame's parameters
|
||||
motion.saveHistory();
|
||||
}
|
||||
});
|
||||
|
||||
// helper function for selectAnimation()
|
||||
function setTransition(nextAnimation, playTransitionReachPoses) {
|
||||
var lastTransition = motion.currentTransition;
|
||||
var lastAnimation = avatar.currentAnimation;
|
||||
|
||||
// if already transitioning from a blended walk need to maintain the previous walk's direction
|
||||
if (lastAnimation.lastDirection) {
|
||||
switch(lastAnimation.lastDirection) {
|
||||
|
||||
case FORWARDS:
|
||||
lastAnimation = avatar.selectedWalk;
|
||||
break;
|
||||
|
||||
case BACKWARDS:
|
||||
lastAnimation = avatar.selectedWalkBackwards;
|
||||
break;
|
||||
|
||||
case LEFT:
|
||||
lastAnimation = avatar.selectedSideStepLeft;
|
||||
break;
|
||||
|
||||
case RIGHT:
|
||||
lastAnimation = avatar.selectedSideStepRight;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
motion.currentTransition = new Transition(nextAnimation, lastAnimation, lastTransition, playTransitionReachPoses);
|
||||
avatar.currentAnimation = nextAnimation;
|
||||
|
||||
// reset default first footstep
|
||||
if (nextAnimation === avatar.selectedWalkBlend && lastTransition === nullTransition) {
|
||||
avatar.nextStep = RIGHT;
|
||||
}
|
||||
}
|
||||
|
||||
// fly animation blending: smoothing / damping filters
|
||||
const FLY_BLEND_DAMPING = 50;
|
||||
var flyUpFilter = filter.createAveragingFilter(FLY_BLEND_DAMPING);
|
||||
var flyDownFilter = filter.createAveragingFilter(FLY_BLEND_DAMPING);
|
||||
var flyForwardFilter = filter.createAveragingFilter(FLY_BLEND_DAMPING);
|
||||
var flyBackwardFilter = filter.createAveragingFilter(FLY_BLEND_DAMPING);
|
||||
|
||||
// select / blend the appropriate animation for the current state of motion
|
||||
function selectAnimation() {
|
||||
var playTransitionReachPoses = true;
|
||||
|
||||
// select appropriate animation. create transitions where appropriate
|
||||
switch (motion.nextState) {
|
||||
case STATIC: {
|
||||
if (avatar.distanceFromSurface < ON_SURFACE_THRESHOLD &&
|
||||
avatar.currentAnimation !== avatar.selectedIdle) {
|
||||
setTransition(avatar.selectedIdle, playTransitionReachPoses);
|
||||
} else if (!(avatar.distanceFromSurface < ON_SURFACE_THRESHOLD) &&
|
||||
avatar.currentAnimation !== avatar.selectedHover) {
|
||||
setTransition(avatar.selectedHover, playTransitionReachPoses);
|
||||
}
|
||||
motion.state = STATIC;
|
||||
avatar.selectedWalkBlend.lastDirection = NONE;
|
||||
break;
|
||||
}
|
||||
|
||||
case SURFACE_MOTION: {
|
||||
// walk transition reach poses are currently only specified for starting to walk forwards
|
||||
playTransitionReachPoses = (motion.direction === FORWARDS);
|
||||
var isAlreadyWalking = (avatar.currentAnimation === avatar.selectedWalkBlend);
|
||||
|
||||
switch (motion.direction) {
|
||||
case FORWARDS:
|
||||
if (avatar.selectedWalkBlend.lastDirection !== FORWARDS) {
|
||||
animationOperations.deepCopy(avatar.selectedWalk, avatar.selectedWalkBlend);
|
||||
avatar.calibration.strideLength = avatar.selectedWalk.calibration.strideLength;
|
||||
}
|
||||
avatar.selectedWalkBlend.lastDirection = FORWARDS;
|
||||
break;
|
||||
|
||||
case BACKWARDS:
|
||||
if (avatar.selectedWalkBlend.lastDirection !== BACKWARDS) {
|
||||
animationOperations.deepCopy(avatar.selectedWalkBackwards, avatar.selectedWalkBlend);
|
||||
avatar.calibration.strideLength = avatar.selectedWalkBackwards.calibration.strideLength;
|
||||
}
|
||||
avatar.selectedWalkBlend.lastDirection = BACKWARDS;
|
||||
break;
|
||||
|
||||
case LEFT:
|
||||
animationOperations.deepCopy(avatar.selectedSideStepLeft, avatar.selectedWalkBlend);
|
||||
avatar.selectedWalkBlend.lastDirection = LEFT;
|
||||
avatar.calibration.strideLength = avatar.selectedSideStepLeft.calibration.strideLength;
|
||||
break
|
||||
|
||||
case RIGHT:
|
||||
animationOperations.deepCopy(avatar.selectedSideStepRight, avatar.selectedWalkBlend);
|
||||
avatar.selectedWalkBlend.lastDirection = RIGHT;
|
||||
avatar.calibration.strideLength = avatar.selectedSideStepRight.calibration.strideLength;
|
||||
break;
|
||||
|
||||
default:
|
||||
// condition occurs when the avi goes through the floor due to collision hull errors
|
||||
animationOperations.deepCopy(avatar.selectedWalk, avatar.selectedWalkBlend);
|
||||
avatar.selectedWalkBlend.lastDirection = FORWARDS;
|
||||
avatar.calibration.strideLength = avatar.selectedWalk.calibration.strideLength;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!isAlreadyWalking && !motion.isComingToHalt) {
|
||||
setTransition(avatar.selectedWalkBlend, playTransitionReachPoses);
|
||||
}
|
||||
motion.state = SURFACE_MOTION;
|
||||
break;
|
||||
}
|
||||
|
||||
case AIR_MOTION: {
|
||||
// blend the up, down, forward and backward flying animations relative to motion speed and direction
|
||||
animationOperations.zeroAnimation(avatar.selectedFlyBlend);
|
||||
|
||||
// calculate influences based on velocity and direction
|
||||
var velocityMagnitude = Vec3.length(motion.velocity);
|
||||
var verticalProportion = motion.velocity.y / velocityMagnitude;
|
||||
var thrustProportion = motion.velocity.z / velocityMagnitude / 2;
|
||||
|
||||
// directional components
|
||||
var upComponent = motion.velocity.y > 0 ? verticalProportion : 0;
|
||||
var downComponent = motion.velocity.y < 0 ? -verticalProportion : 0;
|
||||
var forwardComponent = motion.velocity.z < 0 ? -thrustProportion : 0;
|
||||
var backwardComponent = motion.velocity.z > 0 ? thrustProportion : 0;
|
||||
|
||||
// smooth / damp directional components to add visual 'weight'
|
||||
upComponent = flyUpFilter.process(upComponent);
|
||||
downComponent = flyDownFilter.process(downComponent);
|
||||
forwardComponent = flyForwardFilter.process(forwardComponent);
|
||||
backwardComponent = flyBackwardFilter.process(backwardComponent);
|
||||
|
||||
// normalise directional components
|
||||
var normaliser = upComponent + downComponent + forwardComponent + backwardComponent;
|
||||
upComponent = upComponent / normaliser;
|
||||
downComponent = downComponent / normaliser;
|
||||
forwardComponent = forwardComponent / normaliser;
|
||||
backwardComponent = backwardComponent / normaliser;
|
||||
|
||||
// blend animations proportionally
|
||||
if (upComponent > 0) {
|
||||
animationOperations.blendAnimation(avatar.selectedFlyUp,
|
||||
avatar.selectedFlyBlend,
|
||||
upComponent);
|
||||
}
|
||||
if (downComponent > 0) {
|
||||
animationOperations.blendAnimation(avatar.selectedFlyDown,
|
||||
avatar.selectedFlyBlend,
|
||||
downComponent);
|
||||
}
|
||||
if (forwardComponent > 0) {
|
||||
animationOperations.blendAnimation(avatar.selectedFly,
|
||||
avatar.selectedFlyBlend,
|
||||
Math.abs(forwardComponent));
|
||||
}
|
||||
if (backwardComponent > 0) {
|
||||
animationOperations.blendAnimation(avatar.selectedFlyBackwards,
|
||||
avatar.selectedFlyBlend,
|
||||
Math.abs(backwardComponent));
|
||||
}
|
||||
|
||||
if (avatar.currentAnimation !== avatar.selectedFlyBlend) {
|
||||
setTransition(avatar.selectedFlyBlend, playTransitionReachPoses);
|
||||
}
|
||||
motion.state = AIR_MOTION;
|
||||
avatar.selectedWalkBlend.lastDirection = NONE;
|
||||
break;
|
||||
}
|
||||
} // end switch next state of motion
|
||||
}
|
||||
|
||||
// determine the length of stride. advance the frequency time wheels. advance frequency time wheels for any live transitions
|
||||
function advanceAnimations() {
|
||||
var wheelAdvance = 0;
|
||||
|
||||
// turn the frequency time wheel
|
||||
if (avatar.currentAnimation === avatar.selectedWalkBlend) {
|
||||
// Using technique described here: http://www.gdcvault.com/play/1020583/Animation-Bootcamp-An-Indie-Approach
|
||||
// wrap the stride length around a 'surveyor's wheel' twice and calculate the angular speed at the given (linear) speed
|
||||
// omega = v / r , where r = circumference / 2 PI and circumference = 2 * stride length
|
||||
var speed = Vec3.length(motion.velocity);
|
||||
motion.frequencyTimeWheelRadius = avatar.calibration.strideLength / Math.PI;
|
||||
var ftWheelAngularVelocity = speed / motion.frequencyTimeWheelRadius;
|
||||
// calculate the degrees turned (at this angular speed) since last frame
|
||||
wheelAdvance = filter.radToDeg(motion.deltaTime * ftWheelAngularVelocity);
|
||||
} else {
|
||||
// turn the frequency time wheel by the amount specified for this animation
|
||||
wheelAdvance = filter.radToDeg(avatar.currentAnimation.calibration.frequency * motion.deltaTime);
|
||||
}
|
||||
|
||||
if (motion.currentTransition !== nullTransition) {
|
||||
// the last animation is still playing so we turn it's frequency time wheel to maintain the animation
|
||||
if (motion.currentTransition.lastAnimation === motion.selectedWalkBlend) {
|
||||
// if at a stop angle (i.e. feet now under the avi) hold the wheel position for remainder of transition
|
||||
var tolerance = motion.currentTransition.lastFrequencyTimeIncrement + 0.1;
|
||||
if ((motion.currentTransition.lastFrequencyTimeWheelPos >
|
||||
(motion.currentTransition.stopAngle - tolerance)) &&
|
||||
(motion.currentTransition.lastFrequencyTimeWheelPos <
|
||||
(motion.currentTransition.stopAngle + tolerance))) {
|
||||
motion.currentTransition.lastFrequencyTimeIncrement = 0;
|
||||
}
|
||||
}
|
||||
motion.currentTransition.advancePreviousFrequencyTimeWheel(motion.deltaTime);
|
||||
}
|
||||
|
||||
// avoid unnaturally fast walking when landing at speed - simulates skimming / skidding
|
||||
if (Math.abs(wheelAdvance) > MAX_FT_WHEEL_INCREMENT) {
|
||||
wheelAdvance = 0;
|
||||
}
|
||||
|
||||
// advance the walk wheel the appropriate amount
|
||||
motion.advanceFrequencyTimeWheel(wheelAdvance);
|
||||
|
||||
// walking? then see if it's a good time to measure the stride length (needs to be at least 97% of max walking speed)
|
||||
const ALMOST_ONE = 0.97;
|
||||
if (avatar.currentAnimation === avatar.selectedWalkBlend &&
|
||||
(Vec3.length(motion.velocity) / MAX_WALK_SPEED > ALMOST_ONE)) {
|
||||
|
||||
var strideMaxAt = avatar.currentAnimation.calibration.strideMaxAt;
|
||||
const TOLERANCE = 1.0;
|
||||
|
||||
if (motion.frequencyTimeWheelPos < (strideMaxAt + TOLERANCE) &&
|
||||
motion.frequencyTimeWheelPos > (strideMaxAt - TOLERANCE) &&
|
||||
motion.currentTransition === nullTransition) {
|
||||
// measure and save stride length
|
||||
var footRPos = MyAvatar.getJointPosition("RightFoot");
|
||||
var footLPos = MyAvatar.getJointPosition("LeftFoot");
|
||||
avatar.calibration.strideLength = Vec3.distance(footRPos, footLPos);
|
||||
avatar.currentAnimation.calibration.strideLength = avatar.calibration.strideLength;
|
||||
} else {
|
||||
// use the previously saved value for stride length
|
||||
avatar.calibration.strideLength = avatar.currentAnimation.calibration.strideLength;
|
||||
}
|
||||
} // end get walk stride length
|
||||
}
|
||||
|
||||
// initialise a new transition. update progress of a live transition
|
||||
function updateTransitions() {
|
||||
|
||||
if (motion.currentTransition !== nullTransition) {
|
||||
// is this a new transition?
|
||||
if (motion.currentTransition.progress === 0) {
|
||||
// do we have overlapping transitions?
|
||||
if (motion.currentTransition.lastTransition !== nullTransition) {
|
||||
// is the last animation for the nested transition the same as the new animation?
|
||||
if (motion.currentTransition.lastTransition.lastAnimation === avatar.currentAnimation) {
|
||||
// then sync the nested transition's frequency time wheel for a smooth animation blend
|
||||
motion.frequencyTimeWheelPos = motion.currentTransition.lastTransition.lastFrequencyTimeWheelPos;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (motion.currentTransition.updateProgress() === TRANSITION_COMPLETE) {
|
||||
motion.currentTransition = nullTransition;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// helper function for renderMotion(). calculate the amount to lean forwards (or backwards) based on the avi's velocity
|
||||
var leanPitchSmoothingFilter = filter.createButterworthFilter();
|
||||
function getLeanPitch() {
|
||||
var leanProgress = 0;
|
||||
|
||||
if (motion.direction === DOWN ||
|
||||
motion.direction === FORWARDS ||
|
||||
motion.direction === BACKWARDS) {
|
||||
leanProgress = -motion.velocity.z / TOP_SPEED;
|
||||
}
|
||||
// use filters to shape the walking acceleration response
|
||||
leanProgress = leanPitchSmoothingFilter.process(leanProgress);
|
||||
return PITCH_MAX * leanProgress;
|
||||
}
|
||||
|
||||
// helper function for renderMotion(). calculate the angle at which to bank into corners whilst turning
|
||||
var leanRollSmoothingFilter = filter.createButterworthFilter();
|
||||
function getLeanRoll() {
|
||||
var leanRollProgress = 0;
|
||||
var linearContribution = 0;
|
||||
const LOG_SCALER = 8;
|
||||
|
||||
if (Vec3.length(motion.velocity) > 0) {
|
||||
linearContribution = (Math.log(Vec3.length(motion.velocity) / TOP_SPEED) + LOG_SCALER) / LOG_SCALER;
|
||||
}
|
||||
var angularContribution = Math.abs(motion.yawDelta) / DELTA_YAW_MAX;
|
||||
leanRollProgress = linearContribution;
|
||||
leanRollProgress *= angularContribution;
|
||||
// shape the response curve
|
||||
leanRollProgress = filter.bezier(leanRollProgress, {x: 1, y: 0}, {x: 1, y: 0});
|
||||
// which way to lean?
|
||||
var turnSign = (motion.yawDelta >= 0) ? 1 : -1;
|
||||
|
||||
if (motion.direction === BACKWARDS ||
|
||||
motion.direction === LEFT) {
|
||||
turnSign *= -1;
|
||||
}
|
||||
// filter progress
|
||||
leanRollProgress = leanRollSmoothingFilter.process(turnSign * leanRollProgress);
|
||||
return ROLL_MAX * leanRollProgress;
|
||||
}
|
||||
|
||||
// animate the avatar using sine waves, geometric waveforms and harmonic generators
|
||||
function renderMotion() {
|
||||
// leaning in response to speed and acceleration
|
||||
var leanPitch = motion.state === STATIC ? 0 : getLeanPitch();
|
||||
var leanRoll = motion.state === STATIC ? 0 : getLeanRoll();
|
||||
var lastDirection = motion.lastDirection;
|
||||
// hips translations from currently playing animations
|
||||
var hipsTranslations = {x:0, y:0, z:0};
|
||||
|
||||
if (motion.currentTransition !== nullTransition) {
|
||||
// maintain previous direction when transitioning from a walk
|
||||
if (motion.currentTransition.lastAnimation === avatar.selectedWalkBlend) {
|
||||
motion.lastDirection = motion.currentTransition.lastDirection;
|
||||
}
|
||||
hipsTranslations = motion.currentTransition.blendTranslations(motion.frequencyTimeWheelPos,
|
||||
motion.lastDirection);
|
||||
} else {
|
||||
hipsTranslations = animationOperations.calculateTranslations(avatar.currentAnimation,
|
||||
motion.frequencyTimeWheelPos,
|
||||
motion.direction);
|
||||
}
|
||||
// factor any leaning into the hips offset
|
||||
hipsTranslations.z += avatar.calibration.hipsToFeet * Math.sin(filter.degToRad(leanPitch));
|
||||
hipsTranslations.x += avatar.calibration.hipsToFeet * Math.sin(filter.degToRad(leanRoll));
|
||||
|
||||
// ensure skeleton offsets are within the 1m limit
|
||||
hipsTranslations.x = hipsTranslations.x > 1 ? 1 : hipsTranslations.x;
|
||||
hipsTranslations.x = hipsTranslations.x < -1 ? -1 : hipsTranslations.x;
|
||||
hipsTranslations.y = hipsTranslations.y > 1 ? 1 : hipsTranslations.y;
|
||||
hipsTranslations.y = hipsTranslations.y < -1 ? -1 : hipsTranslations.y;
|
||||
hipsTranslations.z = hipsTranslations.z > 1 ? 1 : hipsTranslations.z;
|
||||
hipsTranslations.z = hipsTranslations.z < -1 ? -1 : hipsTranslations.z;
|
||||
// apply translations
|
||||
MyAvatar.setSkeletonOffset(hipsTranslations);
|
||||
|
||||
// play footfall sound?
|
||||
var producingFootstepSounds = (avatar.currentAnimation === avatar.selectedWalkBlend) && avatar.makesFootStepSounds;
|
||||
|
||||
if (motion.currentTransition !== nullTransition && avatar.makesFootStepSounds) {
|
||||
if (motion.currentTransition.nextAnimation === avatar.selectedWalkBlend ||
|
||||
motion.currentTransition.lastAnimation === avatar.selectedWalkBlend) {
|
||||
producingFootstepSounds = true;
|
||||
}
|
||||
}
|
||||
if (producingFootstepSounds) {
|
||||
const QUARTER_CYCLE = 90;
|
||||
const THREE_QUARTER_CYCLE = 270;
|
||||
var ftWheelPosition = motion.frequencyTimeWheelPos;
|
||||
|
||||
if (motion.currentTransition !== nullTransition &&
|
||||
motion.currentTransition.lastAnimation === avatar.selectedWalkBlend) {
|
||||
ftWheelPosition = motion.currentTransition.lastFrequencyTimeWheelPos;
|
||||
}
|
||||
if (avatar.nextStep === LEFT && ftWheelPosition > THREE_QUARTER_CYCLE) {
|
||||
avatar.makeFootStepSound();
|
||||
} else if (avatar.nextStep === RIGHT && (ftWheelPosition < THREE_QUARTER_CYCLE && ftWheelPosition > QUARTER_CYCLE)) {
|
||||
avatar.makeFootStepSound();
|
||||
}
|
||||
}
|
||||
|
||||
// apply joint rotations
|
||||
for (jointName in avatar.currentAnimation.joints) {
|
||||
var joint = walkAssets.animationReference.joints[jointName];
|
||||
var jointRotations = undefined;
|
||||
|
||||
// ignore arms / head rotations if options are selected in the settings
|
||||
if (avatar.armsFree && (joint.IKChain === "LeftArm" || joint.IKChain === "RightArm")) {
|
||||
continue;
|
||||
}
|
||||
if (avatar.headFree && joint.IKChain === "Head") {
|
||||
continue;
|
||||
}
|
||||
|
||||
// if there's a live transition, blend the rotations with the last animation's rotations
|
||||
if (motion.currentTransition !== nullTransition) {
|
||||
jointRotations = motion.currentTransition.blendRotations(jointName,
|
||||
motion.frequencyTimeWheelPos,
|
||||
motion.lastDirection);
|
||||
} else {
|
||||
jointRotations = animationOperations.calculateRotations(jointName,
|
||||
avatar.currentAnimation,
|
||||
motion.frequencyTimeWheelPos,
|
||||
motion.direction);
|
||||
}
|
||||
|
||||
// apply angular velocity and speed induced leaning
|
||||
if (jointName === "Hips") {
|
||||
jointRotations.x += leanPitch;
|
||||
jointRotations.z += leanRoll;
|
||||
}
|
||||
|
||||
// apply rotations
|
||||
MyAvatar.setJointRotation(jointName, Quat.fromVec3Degrees(jointRotations));
|
||||
}
|
||||
}
|
||||
//
|
||||
// walk.js
|
||||
// version 1.25
|
||||
//
|
||||
// Created by David Wooldridge, June 2015
|
||||
// Copyright © 2014 - 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Animates an avatar using procedural animation techniques.
|
||||
//
|
||||
// Editing tools for animation data files available here: https://github.com/DaveDubUK/walkTools
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
// animations, reach poses, reach pose parameters, transitions, transition parameters, sounds, image/s and reference files
|
||||
const HIFI_PUBLIC_BUCKET = "https://hifi-public.s3.amazonaws.com/";
|
||||
var pathToAssets = HIFI_PUBLIC_BUCKET + "procedural-animator/assets/";
|
||||
|
||||
Script.include([
|
||||
"./libraries/walkConstants.js",
|
||||
"./libraries/walkFilters.js",
|
||||
"./libraries/walkApi.js",
|
||||
pathToAssets + "walkAssets.js"
|
||||
]);
|
||||
|
||||
// construct Avatar, Motion and (null) Transition
|
||||
var avatar = new Avatar();
|
||||
var motion = new Motion();
|
||||
var nullTransition = new Transition();
|
||||
motion.currentTransition = nullTransition;
|
||||
|
||||
// create settings (gets initial values from avatar)
|
||||
Script.include("./libraries/walkSettings.js");
|
||||
|
||||
// Main loop
|
||||
Script.update.connect(function(deltaTime) {
|
||||
|
||||
if (motion.isLive) {
|
||||
|
||||
// assess current locomotion state
|
||||
motion.assess(deltaTime);
|
||||
|
||||
// decide which animation should be playing
|
||||
selectAnimation();
|
||||
|
||||
// advance the animation cycle/s by the correct amount/s
|
||||
advanceAnimations();
|
||||
|
||||
// update the progress of any live transitions
|
||||
updateTransitions();
|
||||
|
||||
// apply translation and rotations
|
||||
renderMotion();
|
||||
|
||||
// save this frame's parameters
|
||||
motion.saveHistory();
|
||||
}
|
||||
});
|
||||
|
||||
// helper function for selectAnimation()
|
||||
function setTransition(nextAnimation, playTransitionReachPoses) {
|
||||
var lastTransition = motion.currentTransition;
|
||||
var lastAnimation = avatar.currentAnimation;
|
||||
|
||||
// if already transitioning from a blended walk need to maintain the previous walk's direction
|
||||
if (lastAnimation.lastDirection) {
|
||||
switch(lastAnimation.lastDirection) {
|
||||
|
||||
case FORWARDS:
|
||||
lastAnimation = avatar.selectedWalk;
|
||||
break;
|
||||
|
||||
case BACKWARDS:
|
||||
lastAnimation = avatar.selectedWalkBackwards;
|
||||
break;
|
||||
|
||||
case LEFT:
|
||||
lastAnimation = avatar.selectedSideStepLeft;
|
||||
break;
|
||||
|
||||
case RIGHT:
|
||||
lastAnimation = avatar.selectedSideStepRight;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
motion.currentTransition = new Transition(nextAnimation, lastAnimation, lastTransition, playTransitionReachPoses);
|
||||
avatar.currentAnimation = nextAnimation;
|
||||
|
||||
// reset default first footstep
|
||||
if (nextAnimation === avatar.selectedWalkBlend && lastTransition === nullTransition) {
|
||||
avatar.nextStep = RIGHT;
|
||||
}
|
||||
}
|
||||
|
||||
// fly animation blending: smoothing / damping filters
|
||||
const FLY_BLEND_DAMPING = 50;
|
||||
var flyUpFilter = filter.createAveragingFilter(FLY_BLEND_DAMPING);
|
||||
var flyDownFilter = filter.createAveragingFilter(FLY_BLEND_DAMPING);
|
||||
var flyForwardFilter = filter.createAveragingFilter(FLY_BLEND_DAMPING);
|
||||
var flyBackwardFilter = filter.createAveragingFilter(FLY_BLEND_DAMPING);
|
||||
|
||||
// select / blend the appropriate animation for the current state of motion
|
||||
function selectAnimation() {
|
||||
var playTransitionReachPoses = true;
|
||||
|
||||
// select appropriate animation. create transitions where appropriate
|
||||
switch (motion.nextState) {
|
||||
case STATIC: {
|
||||
if (avatar.distanceFromSurface < ON_SURFACE_THRESHOLD &&
|
||||
avatar.currentAnimation !== avatar.selectedIdle) {
|
||||
setTransition(avatar.selectedIdle, playTransitionReachPoses);
|
||||
} else if (!(avatar.distanceFromSurface < ON_SURFACE_THRESHOLD) &&
|
||||
avatar.currentAnimation !== avatar.selectedHover) {
|
||||
setTransition(avatar.selectedHover, playTransitionReachPoses);
|
||||
}
|
||||
motion.state = STATIC;
|
||||
avatar.selectedWalkBlend.lastDirection = NONE;
|
||||
break;
|
||||
}
|
||||
|
||||
case SURFACE_MOTION: {
|
||||
// walk transition reach poses are currently only specified for starting to walk forwards
|
||||
playTransitionReachPoses = (motion.direction === FORWARDS);
|
||||
var isAlreadyWalking = (avatar.currentAnimation === avatar.selectedWalkBlend);
|
||||
|
||||
switch (motion.direction) {
|
||||
case FORWARDS:
|
||||
if (avatar.selectedWalkBlend.lastDirection !== FORWARDS) {
|
||||
animationOperations.deepCopy(avatar.selectedWalk, avatar.selectedWalkBlend);
|
||||
avatar.calibration.strideLength = avatar.selectedWalk.calibration.strideLength;
|
||||
}
|
||||
avatar.selectedWalkBlend.lastDirection = FORWARDS;
|
||||
break;
|
||||
|
||||
case BACKWARDS:
|
||||
if (avatar.selectedWalkBlend.lastDirection !== BACKWARDS) {
|
||||
animationOperations.deepCopy(avatar.selectedWalkBackwards, avatar.selectedWalkBlend);
|
||||
avatar.calibration.strideLength = avatar.selectedWalkBackwards.calibration.strideLength;
|
||||
}
|
||||
avatar.selectedWalkBlend.lastDirection = BACKWARDS;
|
||||
break;
|
||||
|
||||
case LEFT:
|
||||
animationOperations.deepCopy(avatar.selectedSideStepLeft, avatar.selectedWalkBlend);
|
||||
avatar.selectedWalkBlend.lastDirection = LEFT;
|
||||
avatar.calibration.strideLength = avatar.selectedSideStepLeft.calibration.strideLength;
|
||||
break
|
||||
|
||||
case RIGHT:
|
||||
animationOperations.deepCopy(avatar.selectedSideStepRight, avatar.selectedWalkBlend);
|
||||
avatar.selectedWalkBlend.lastDirection = RIGHT;
|
||||
avatar.calibration.strideLength = avatar.selectedSideStepRight.calibration.strideLength;
|
||||
break;
|
||||
|
||||
default:
|
||||
// condition occurs when the avi goes through the floor due to collision hull errors
|
||||
animationOperations.deepCopy(avatar.selectedWalk, avatar.selectedWalkBlend);
|
||||
avatar.selectedWalkBlend.lastDirection = FORWARDS;
|
||||
avatar.calibration.strideLength = avatar.selectedWalk.calibration.strideLength;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!isAlreadyWalking && !motion.isComingToHalt) {
|
||||
setTransition(avatar.selectedWalkBlend, playTransitionReachPoses);
|
||||
}
|
||||
motion.state = SURFACE_MOTION;
|
||||
break;
|
||||
}
|
||||
|
||||
case AIR_MOTION: {
|
||||
// blend the up, down, forward and backward flying animations relative to motion speed and direction
|
||||
animationOperations.zeroAnimation(avatar.selectedFlyBlend);
|
||||
|
||||
// calculate influences based on velocity and direction
|
||||
var velocityMagnitude = Vec3.length(motion.velocity);
|
||||
var verticalProportion = motion.velocity.y / velocityMagnitude;
|
||||
var thrustProportion = motion.velocity.z / velocityMagnitude / 2;
|
||||
|
||||
// directional components
|
||||
var upComponent = motion.velocity.y > 0 ? verticalProportion : 0;
|
||||
var downComponent = motion.velocity.y < 0 ? -verticalProportion : 0;
|
||||
var forwardComponent = motion.velocity.z < 0 ? -thrustProportion : 0;
|
||||
var backwardComponent = motion.velocity.z > 0 ? thrustProportion : 0;
|
||||
|
||||
// smooth / damp directional components to add visual 'weight'
|
||||
upComponent = flyUpFilter.process(upComponent);
|
||||
downComponent = flyDownFilter.process(downComponent);
|
||||
forwardComponent = flyForwardFilter.process(forwardComponent);
|
||||
backwardComponent = flyBackwardFilter.process(backwardComponent);
|
||||
|
||||
// normalise directional components
|
||||
var normaliser = upComponent + downComponent + forwardComponent + backwardComponent;
|
||||
upComponent = upComponent / normaliser;
|
||||
downComponent = downComponent / normaliser;
|
||||
forwardComponent = forwardComponent / normaliser;
|
||||
backwardComponent = backwardComponent / normaliser;
|
||||
|
||||
// blend animations proportionally
|
||||
if (upComponent > 0) {
|
||||
animationOperations.blendAnimation(avatar.selectedFlyUp,
|
||||
avatar.selectedFlyBlend,
|
||||
upComponent);
|
||||
}
|
||||
if (downComponent > 0) {
|
||||
animationOperations.blendAnimation(avatar.selectedFlyDown,
|
||||
avatar.selectedFlyBlend,
|
||||
downComponent);
|
||||
}
|
||||
if (forwardComponent > 0) {
|
||||
animationOperations.blendAnimation(avatar.selectedFly,
|
||||
avatar.selectedFlyBlend,
|
||||
Math.abs(forwardComponent));
|
||||
}
|
||||
if (backwardComponent > 0) {
|
||||
animationOperations.blendAnimation(avatar.selectedFlyBackwards,
|
||||
avatar.selectedFlyBlend,
|
||||
Math.abs(backwardComponent));
|
||||
}
|
||||
|
||||
if (avatar.currentAnimation !== avatar.selectedFlyBlend) {
|
||||
setTransition(avatar.selectedFlyBlend, playTransitionReachPoses);
|
||||
}
|
||||
motion.state = AIR_MOTION;
|
||||
avatar.selectedWalkBlend.lastDirection = NONE;
|
||||
break;
|
||||
}
|
||||
} // end switch next state of motion
|
||||
}
|
||||
|
||||
// determine the length of stride. advance the frequency time wheels. advance frequency time wheels for any live transitions
|
||||
function advanceAnimations() {
|
||||
var wheelAdvance = 0;
|
||||
|
||||
// turn the frequency time wheel
|
||||
if (avatar.currentAnimation === avatar.selectedWalkBlend) {
|
||||
// Using technique described here: http://www.gdcvault.com/play/1020583/Animation-Bootcamp-An-Indie-Approach
|
||||
// wrap the stride length around a 'surveyor's wheel' twice and calculate the angular speed at the given (linear) speed
|
||||
// omega = v / r , where r = circumference / 2 PI and circumference = 2 * stride length
|
||||
var speed = Vec3.length(motion.velocity);
|
||||
motion.frequencyTimeWheelRadius = avatar.calibration.strideLength / Math.PI;
|
||||
var ftWheelAngularVelocity = speed / motion.frequencyTimeWheelRadius;
|
||||
// calculate the degrees turned (at this angular speed) since last frame
|
||||
wheelAdvance = filter.radToDeg(motion.deltaTime * ftWheelAngularVelocity);
|
||||
} else {
|
||||
// turn the frequency time wheel by the amount specified for this animation
|
||||
wheelAdvance = filter.radToDeg(avatar.currentAnimation.calibration.frequency * motion.deltaTime);
|
||||
}
|
||||
|
||||
if (motion.currentTransition !== nullTransition) {
|
||||
// the last animation is still playing so we turn it's frequency time wheel to maintain the animation
|
||||
if (motion.currentTransition.lastAnimation === motion.selectedWalkBlend) {
|
||||
// if at a stop angle (i.e. feet now under the avi) hold the wheel position for remainder of transition
|
||||
var tolerance = motion.currentTransition.lastFrequencyTimeIncrement + 0.1;
|
||||
if ((motion.currentTransition.lastFrequencyTimeWheelPos >
|
||||
(motion.currentTransition.stopAngle - tolerance)) &&
|
||||
(motion.currentTransition.lastFrequencyTimeWheelPos <
|
||||
(motion.currentTransition.stopAngle + tolerance))) {
|
||||
motion.currentTransition.lastFrequencyTimeIncrement = 0;
|
||||
}
|
||||
}
|
||||
motion.currentTransition.advancePreviousFrequencyTimeWheel(motion.deltaTime);
|
||||
}
|
||||
|
||||
// avoid unnaturally fast walking when landing at speed - simulates skimming / skidding
|
||||
if (Math.abs(wheelAdvance) > MAX_FT_WHEEL_INCREMENT) {
|
||||
wheelAdvance = 0;
|
||||
}
|
||||
|
||||
// advance the walk wheel the appropriate amount
|
||||
motion.advanceFrequencyTimeWheel(wheelAdvance);
|
||||
|
||||
// walking? then see if it's a good time to measure the stride length (needs to be at least 97% of max walking speed)
|
||||
const ALMOST_ONE = 0.97;
|
||||
if (avatar.currentAnimation === avatar.selectedWalkBlend &&
|
||||
(Vec3.length(motion.velocity) / MAX_WALK_SPEED > ALMOST_ONE)) {
|
||||
|
||||
var strideMaxAt = avatar.currentAnimation.calibration.strideMaxAt;
|
||||
const TOLERANCE = 1.0;
|
||||
|
||||
if (motion.frequencyTimeWheelPos < (strideMaxAt + TOLERANCE) &&
|
||||
motion.frequencyTimeWheelPos > (strideMaxAt - TOLERANCE) &&
|
||||
motion.currentTransition === nullTransition) {
|
||||
// measure and save stride length
|
||||
var footRPos = MyAvatar.getJointPosition("RightFoot");
|
||||
var footLPos = MyAvatar.getJointPosition("LeftFoot");
|
||||
avatar.calibration.strideLength = Vec3.distance(footRPos, footLPos);
|
||||
avatar.currentAnimation.calibration.strideLength = avatar.calibration.strideLength;
|
||||
} else {
|
||||
// use the previously saved value for stride length
|
||||
avatar.calibration.strideLength = avatar.currentAnimation.calibration.strideLength;
|
||||
}
|
||||
} // end get walk stride length
|
||||
}
|
||||
|
||||
// initialise a new transition. update progress of a live transition
|
||||
function updateTransitions() {
|
||||
|
||||
if (motion.currentTransition !== nullTransition) {
|
||||
// is this a new transition?
|
||||
if (motion.currentTransition.progress === 0) {
|
||||
// do we have overlapping transitions?
|
||||
if (motion.currentTransition.lastTransition !== nullTransition) {
|
||||
// is the last animation for the nested transition the same as the new animation?
|
||||
if (motion.currentTransition.lastTransition.lastAnimation === avatar.currentAnimation) {
|
||||
// then sync the nested transition's frequency time wheel for a smooth animation blend
|
||||
motion.frequencyTimeWheelPos = motion.currentTransition.lastTransition.lastFrequencyTimeWheelPos;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (motion.currentTransition.updateProgress() === TRANSITION_COMPLETE) {
|
||||
motion.currentTransition = nullTransition;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// helper function for renderMotion(). calculate the amount to lean forwards (or backwards) based on the avi's velocity
|
||||
var leanPitchSmoothingFilter = filter.createButterworthFilter();
|
||||
function getLeanPitch() {
|
||||
var leanProgress = 0;
|
||||
|
||||
if (motion.direction === DOWN ||
|
||||
motion.direction === FORWARDS ||
|
||||
motion.direction === BACKWARDS) {
|
||||
leanProgress = -motion.velocity.z / TOP_SPEED;
|
||||
}
|
||||
// use filters to shape the walking acceleration response
|
||||
leanProgress = leanPitchSmoothingFilter.process(leanProgress);
|
||||
return PITCH_MAX * leanProgress;
|
||||
}
|
||||
|
||||
// helper function for renderMotion(). calculate the angle at which to bank into corners whilst turning
|
||||
var leanRollSmoothingFilter = filter.createButterworthFilter();
|
||||
function getLeanRoll() {
|
||||
var leanRollProgress = 0;
|
||||
var linearContribution = 0;
|
||||
const LOG_SCALER = 8;
|
||||
|
||||
if (Vec3.length(motion.velocity) > 0) {
|
||||
linearContribution = (Math.log(Vec3.length(motion.velocity) / TOP_SPEED) + LOG_SCALER) / LOG_SCALER;
|
||||
}
|
||||
var angularContribution = Math.abs(motion.yawDelta) / DELTA_YAW_MAX;
|
||||
leanRollProgress = linearContribution;
|
||||
leanRollProgress *= angularContribution;
|
||||
// shape the response curve
|
||||
leanRollProgress = filter.bezier(leanRollProgress, {x: 1, y: 0}, {x: 1, y: 0});
|
||||
// which way to lean?
|
||||
var turnSign = (motion.yawDelta >= 0) ? 1 : -1;
|
||||
|
||||
if (motion.direction === BACKWARDS ||
|
||||
motion.direction === LEFT) {
|
||||
turnSign *= -1;
|
||||
}
|
||||
// filter progress
|
||||
leanRollProgress = leanRollSmoothingFilter.process(turnSign * leanRollProgress);
|
||||
return ROLL_MAX * leanRollProgress;
|
||||
}
|
||||
|
||||
// animate the avatar using sine waves, geometric waveforms and harmonic generators
|
||||
function renderMotion() {
|
||||
// leaning in response to speed and acceleration
|
||||
var leanPitch = motion.state === STATIC ? 0 : getLeanPitch();
|
||||
var leanRoll = motion.state === STATIC ? 0 : getLeanRoll();
|
||||
var lastDirection = motion.lastDirection;
|
||||
// hips translations from currently playing animations
|
||||
var hipsTranslations = {x:0, y:0, z:0};
|
||||
|
||||
if (motion.currentTransition !== nullTransition) {
|
||||
// maintain previous direction when transitioning from a walk
|
||||
if (motion.currentTransition.lastAnimation === avatar.selectedWalkBlend) {
|
||||
motion.lastDirection = motion.currentTransition.lastDirection;
|
||||
}
|
||||
hipsTranslations = motion.currentTransition.blendTranslations(motion.frequencyTimeWheelPos,
|
||||
motion.lastDirection);
|
||||
} else {
|
||||
hipsTranslations = animationOperations.calculateTranslations(avatar.currentAnimation,
|
||||
motion.frequencyTimeWheelPos,
|
||||
motion.direction);
|
||||
}
|
||||
// factor any leaning into the hips offset
|
||||
hipsTranslations.z += avatar.calibration.hipsToFeet * Math.sin(filter.degToRad(leanPitch));
|
||||
hipsTranslations.x += avatar.calibration.hipsToFeet * Math.sin(filter.degToRad(leanRoll));
|
||||
|
||||
// ensure skeleton offsets are within the 1m limit
|
||||
hipsTranslations.x = hipsTranslations.x > 1 ? 1 : hipsTranslations.x;
|
||||
hipsTranslations.x = hipsTranslations.x < -1 ? -1 : hipsTranslations.x;
|
||||
hipsTranslations.y = hipsTranslations.y > 1 ? 1 : hipsTranslations.y;
|
||||
hipsTranslations.y = hipsTranslations.y < -1 ? -1 : hipsTranslations.y;
|
||||
hipsTranslations.z = hipsTranslations.z > 1 ? 1 : hipsTranslations.z;
|
||||
hipsTranslations.z = hipsTranslations.z < -1 ? -1 : hipsTranslations.z;
|
||||
// apply translations
|
||||
MyAvatar.setSkeletonOffset(hipsTranslations);
|
||||
|
||||
// play footfall sound?
|
||||
var producingFootstepSounds = (avatar.currentAnimation === avatar.selectedWalkBlend) && avatar.makesFootStepSounds;
|
||||
|
||||
if (motion.currentTransition !== nullTransition && avatar.makesFootStepSounds) {
|
||||
if (motion.currentTransition.nextAnimation === avatar.selectedWalkBlend ||
|
||||
motion.currentTransition.lastAnimation === avatar.selectedWalkBlend) {
|
||||
producingFootstepSounds = true;
|
||||
}
|
||||
}
|
||||
if (producingFootstepSounds) {
|
||||
const QUARTER_CYCLE = 90;
|
||||
const THREE_QUARTER_CYCLE = 270;
|
||||
var ftWheelPosition = motion.frequencyTimeWheelPos;
|
||||
|
||||
if (motion.currentTransition !== nullTransition &&
|
||||
motion.currentTransition.lastAnimation === avatar.selectedWalkBlend) {
|
||||
ftWheelPosition = motion.currentTransition.lastFrequencyTimeWheelPos;
|
||||
}
|
||||
if (avatar.nextStep === LEFT && ftWheelPosition > THREE_QUARTER_CYCLE) {
|
||||
avatar.makeFootStepSound();
|
||||
} else if (avatar.nextStep === RIGHT && (ftWheelPosition < THREE_QUARTER_CYCLE && ftWheelPosition > QUARTER_CYCLE)) {
|
||||
avatar.makeFootStepSound();
|
||||
}
|
||||
}
|
||||
|
||||
// apply joint rotations
|
||||
for (jointName in avatar.currentAnimation.joints) {
|
||||
var joint = walkAssets.animationReference.joints[jointName];
|
||||
var jointRotations = undefined;
|
||||
|
||||
// ignore arms / head rotations if options are selected in the settings
|
||||
if (avatar.armsFree && (joint.IKChain === "LeftArm" || joint.IKChain === "RightArm")) {
|
||||
continue;
|
||||
}
|
||||
if (avatar.headFree && joint.IKChain === "Head") {
|
||||
continue;
|
||||
}
|
||||
|
||||
// if there's a live transition, blend the rotations with the last animation's rotations
|
||||
if (motion.currentTransition !== nullTransition) {
|
||||
jointRotations = motion.currentTransition.blendRotations(jointName,
|
||||
motion.frequencyTimeWheelPos,
|
||||
motion.lastDirection);
|
||||
} else {
|
||||
jointRotations = animationOperations.calculateRotations(jointName,
|
||||
avatar.currentAnimation,
|
||||
motion.frequencyTimeWheelPos,
|
||||
motion.direction);
|
||||
}
|
||||
|
||||
// apply angular velocity and speed induced leaning
|
||||
if (jointName === "Hips") {
|
||||
jointRotations.x += leanPitch;
|
||||
jointRotations.z += leanRoll;
|
||||
}
|
||||
|
||||
// apply rotations
|
||||
MyAvatar.setJointRotation(jointName, Quat.fromVec3Degrees(jointRotations));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue