jocly/src/browser/jocly.xd-view.js
2017-05-11 22:38:01 +02:00

3490 lines
104 KiB
JavaScript

/* Copyright 2017 Jocly
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* As a special exception, the copyright holders give permission to link the
* code of portions of this program with the OpenSSL library under certain
* conditions as described in each individual source file and distribute
* linked combinations including the program with the OpenSSL library. You
* must comply with the GNU Affero General Public License in all respects
* for all of the code used other than as permitted herein. If you modify
* file(s) with this exception, you may extend this exception to your
* version of the file(s), but you are not obligated to do so. If you do not
* wish to do so, delete this exception statement from your version. If you
* delete this exception statement from all source files in the program,
* then also delete it in the license file.
*/
exports.view = View = {
Game: {},
Board: {},
};
if(window.JoclyXdViewCleanup)
window.JoclyXdViewCleanup();
(function() {
window.JoclyXdViewCleanup = function() {
var renderer = threeCtx && threeCtx.renderer;
if(renderer) {
renderer.forceContextLoss();
renderer.context = null;
renderer.domElement = null;
delete threeCtx.renderer;
}
if(arStream)
AR(null);
}
var area, currentSkin, logger, xdv, VSIZE, VHALF, htStateMachine, threeCtx = null,
SCALE3D = 0.001, resourcesMap = {}, resources, arStream = null;
// hack to ensure mouse and touch events do not collide
var lastTouchStart=0, lastJoclyclick=0;
/* ======================================== */
var LOADING_TEXT_RESOURCE="";
if(typeof CustomEvent == "undefined") {
function CustomEvent ( event, params ) {
params = params || { bubbles: false, cancelable: false };
var evt = document.createEvent( 'Event' );
evt.initEvent( event, params.bubbles, params.cancelable );
return evt;
};
CustomEvent.prototype = window.Event.prototype;
window.CustomEvent = CustomEvent;
}
var Class = function() {
};
(function() {
var initializing = false, fnTest = /xyz/.test(function() {
}) ? /\b_super\b/ : /.*/;
Class.extend = function(prop) {
var _super = this.prototype;
initializing = true;
var prototype = new this();
initializing = false;
for ( var name in prop) {
prototype[name] = typeof prop[name] == "function"
&& typeof _super[name] == "function"
&& fnTest.test(prop[name]) ? (function(name, fn) {
return function() {
var tmp = this._super;
this._super = _super[name];
var ret = fn.apply(this, arguments);
this._super = tmp;
return ret;
};
})(name, prop[name]) : prop[name];
}
function Class(args) {
if (!initializing && this.init)
if (arguments.length > 0 && args.jBlocksArgsList)
this.init.apply(this, args);
else
this.init.apply(this, arguments);
}
Class.prototype = prototype;
Class.prototype.constructor = Class;
Class.extend = arguments.callee;
return Class;
};
})();
/* ======================================== */
var WebRTC;
var logResourcesLoad=false;
function Log() {
console.info.apply(console,arguments);
}
View.Board.Log = Log;
View.Game.Log = Log;
function HTStateMachine() {}
HTStateMachine.prototype=new JHStateMachine();
HTStateMachine.prototype.smError=function() {
//console.info("=>",arguments);
}
HTStateMachine.prototype.smWarning=function() {
//console.info("=>",arguments);
};
HTStateMachine.prototype.smDebug=function() {
//console.info("=>",arguments);
}
function Diff(oOld,oNew) {
var diff={};
var diffSet=false;
for(var i in oNew) {
if(oNew.hasOwnProperty(i)) {
if(!oOld.hasOwnProperty(i)) {
diff[i]=oNew[i];
diffSet=true;
} else if(typeof oNew[i]=="object") {
var diff0=Diff(oOld[i],oNew[i]);
if(diff0) {
diff[i]=diff0;
diffSet=true;
}
} else if(oNew[i]!=oOld[i]) {
diff[i]=oNew[i];
diffSet=true;
}
}
}
return diffSet?diff:null;
}
var resLoadingMask=null;
var resLoadingCount=0;
function IncrementResLoading() {
if(resLoadingCount++==0) {
resLoadingMask=$(".jocly-res-loading-mask");
if(resLoadingMask.length==0)
resLoadingMask=$("<div/>").addClass("jocly-res-loading-mask").css({
position: "absolute",
top: 0,
left: 0,
width: $("body").width(),
height: $("body").height(),
"background-color": "rgba(0,0,0,.8)",
"background-image": "url("+LOADING_TEXT_RESOURCE+")",
"background-position": "center center",
"background-repeat": "no-repeat",
"z-index": 100000,
}).appendTo($("body"));
else
resLoadingMask.show();
}
}
function DecrementResLoading() {
if(--resLoadingCount==0) {
if(resLoadingMask)
resLoadingMask.hide();
}
}
var materialMaps={};
function GetMaterialMap(map,callback) {
var $this=this;
if(materialMaps[map])
callback(materialMaps[map]);
else{
var loader = new THREE.TextureLoader();
loader.setCrossOrigin("anonymous");
if(logResourcesLoad)
console.log("Loading map",map);
IncrementResLoading();
loader.load(
// ressource url
map,
// Function when resource is loaded
function(texture){
materialMaps[map]=texture;
if(logResourcesLoad)
console.log("Loaded",map);
DecrementResLoading();
threeCtx.animControl.trigger();
callback(materialMaps[map]);
},
// Function called when download progresses
function ( xhr ) {
//console.log( (xhr.loaded / xhr.total * 100) + '% loaded' );
},
// Function called when download errors
function ( xhr ) {
if(logResourcesLoad)
console.log("(not) Loaded",map);
DecrementResLoading();
threeCtx.animControl.trigger();
callback(null);
});
}
}
var pendingGetResource=[];
function GetResource(res,callback) {
var resource=resources[res];
if(resource===undefined) {
resource=resources[res]={
pending: [callback],
status: "loading",
}
var getResFnt=null;
var m=/^(.*\|)(.*?)$/.exec(res);
if(m) {
var prefix=m[1];
var url=m[2];
for(var r in resourcesMap) {
var m2=/^(.*\|)(.*?)$/.exec(r);
if(m2)
if(prefix==m[1] && url.substr(-m2[2].length)==m2[2]) {
getResFnt=resourcesMap[r];
break;
}
}
}
if(/^image\|/.test(res)) {
var imgSrc=/^image\|(.*)/.exec(res)[1];
if(logResourcesLoad)
console.log("Loading resource",res);
function HandleImage(image) {
resource.image=image;
if(logResourcesLoad)
console.log("Loaded",res);
resource.status="loaded";
resource.imgSrc=imgSrc;
DecrementResLoading();
for(var i=0;i<resource.pending.length;i++)
resource.pending[i](resource.image,imgSrc);
resource.pending=null;
if(threeCtx)
threeCtx.animControl.trigger();
}
IncrementResLoading();
if(getResFnt) {
getResFnt(function(data) {
var image=new Image();
image.onload=function() {
HandleImage(image);
}
image.src=data;
});
} else {
var image=new Image();
image.onload=function() {
HandleImage(image)
}
image.src=imgSrc;
}
} else if(/^smoothedfilegeo\|/.test(res)) {
if(logResourcesLoad)
console.log("Loading resource",res);
if(!threeCtx) {
delete resources[res];
pendingGetResource.push([res,callback]);
return;
}
var m=/^smoothedfilegeo\|([^\|]*)\|(.*)$/.exec(res);
var smooth=parseInt(m[1]);
var file=m[2];
IncrementResLoading();
function HandleGeoMat( geometry , materials ) {
if(logResourcesLoad)
console.log("Loaded",res);
// not sure of the side effects here but this removes the console
// warnings "THREE.DirectGeometry.fromGeometry(): Undefined vertexUv"
for(var i = 0;i<geometry.faceVertexUvs.length;i++) {
for(var j=0;j<geometry.faceVertexUvs[i].length;j++) {
var uv = geometry.faceVertexUvs[i][j];
if(uv===undefined)
geometry.faceVertexUvs[i][j] = [{x:0,y:0},{x:0,y:0},{x:0,y:0}];
}
for(;j<geometry.faces.length;j++)
geometry.faceVertexUvs[i].push([{x:0,y:0},{x:0,y:0},{x:0,y:0}]);
}
if (smooth > 0){
var modifier = new THREE.SubdivisionModifier( smooth );
modifier.modify( geometry );
}
resource.status="loaded";
DecrementResLoading();
resource.geometry=geometry;
resource.materials=materials;
for(var i=0;i<resource.pending.length;i++)
resource.pending[i](geometry,materials);
resource.pending=null;
threeCtx.animControl.trigger(3000);
}
if(getResFnt) {
getResFnt(function(data) {
try {
var parsed=threeCtx.loader.parse(JSON.parse(data));
HandleGeoMat(parsed.geometry,parsed.materials);
} catch(e) {
debugger;
}
});
} else
threeCtx.loader.load(file , HandleGeoMat);
} else if(/^json\|/.test(res)) {
if(logResourcesLoad)
console.log("Loading resource",res);
IncrementResLoading();
var url=/^json\|(.*)/.exec(res)[1];
function JSONResult(event,data) {
if(logResourcesLoad)
console.log("Loaded",res);
var path=/^(\.)?(.*)$/.exec(url);
if(data.url.substr(-path[2].length)==path[2]) {
$(document).unbind("jocly.json-resource",JSONResult);
resource.status="loaded";
DecrementResLoading();
resource.data=data.data;
for(var i=0;i<resource.pending.length;i++)
resource.pending[i](resource.data);
resource.pending=null;
if(threeCtx)
threeCtx.animControl.trigger();
} else
console.warn("Expecting",url,"got",data.url)
}
$(document).bind("jocly.json-resource",JSONResult);
$("<script/>").attr("type","text/javascript").attr("jocly-type","json-resource").attr("src",url).appendTo($("head"));
} else if(/^json2\|/.test(res)) {
if(logResourcesLoad)
console.log("Loading resource",res);
IncrementResLoading();
var url=/^json2\|(.*)/.exec(res)[1];
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState == XMLHttpRequest.DONE) {
if(logResourcesLoad)
console.log("Loaded",res);
var data = JSON.parse(xhr.responseText);
resource.status="loaded";
DecrementResLoading();
resource.data=data;
for(var i=0;i<resource.pending.length;i++)
resource.pending[i](resource.data);
resource.pending=null;
if(threeCtx)
threeCtx.animControl.trigger();
}
}
xhr.open('GET', url, true);
xhr.send(null);
} else if(/^font\|/.test(res)) {
if(logResourcesLoad)
console.info("font path",fontPath);
var fontPath = /^font\|(.*)/.exec(res)[1];
IncrementResLoading();
var fontLoader = new THREE.FontLoader();
fontLoader.load(fontPath,function(font){
DecrementResLoading();
resource.status="loaded";
resource.font = font;
for(var i=0;i<resource.pending.length;i++)
resource.pending[i](font);
resource.pending=null;
if(threeCtx)
threeCtx.animControl.trigger();
});
}
} else if(resource.status=="loading") {
resource.pending.push(callback);
} else {
if(/^image\|/.test(res)) {
callback(resource.image,resource.imgSrc);
} else if(/^smoothedfilegeo\|/.test(res)) {
callback(resource.geometry,resource.materials);
} else if(/^json\|/.test(res)) {
callback(resource.data);
} else if(/^json2\|/.test(res)) {
callback(resource.data);
} else if(/^font\|/.test(res)) {
callback(resource.font);
}
}
}
function ResumePendingResources() {
if(threeCtx)
while(pendingGetResource.length) {
var call=pendingGetResource.shift();
GetResource.call(null,call[0],call[1]);
}
}
/* ======================================== */
var XDView=Class.extend({
init: function() {
this.gadgets = {};
this.resources = {};
this.game = null;
this.initDone=false;
this.ratio=0;
this.center=null;
this.getMaterialMap=GetMaterialMap;
},
createGadget: function(id, options) {
if(this.ratio>0) {
if(options.base===undefined)
options.base={};
options.base.ratio=this.ratio;
options.base.center=this.center;
}
this.gadgets[id] = new Gadget(id,options);
},
updateGadget: function(id, options, delay, callback) {
var gadget = this.gadgets[id];
if (gadget) {
if(arguments.length<3 || delay===undefined)
delay=0;
if(arguments.length<4 || callback===undefined)
callback=function() {}
gadget.update(options,delay,callback);
}
},
removeGadget: function(id) {
var gadget = this.gadgets[id];
if (gadget) {
gadget.unbuild();
delete this.gadgets[id];
}
},
showGadget: function(id) {
this.updateGadget(id,{
base: {
visible: true,
},
});
},
hideGadget: function(id) {
this.updateGadget(id,{
base: {
visible: false,
},
});
},
updateArea: function(ratio,center) {
this.ratio=ratio;
this.center=center;
for ( var gi in this.gadgets)
this.gadgets[gi].update({
base: {
ratio: ratio,
center: center,
},
});
},
redisplayGadgets: function() {
for ( var gi in this.gadgets) {
var gadget = this.gadgets[gi];
gadget.update({});
}
},
unbuildGadgets: function() {
for ( var gi in this.gadgets)
this.gadgets[gi].unbuild();
},
saveGadgetProps: function(id, props, saveName) {
var gadget = this.gadgets[id];
if (gadget)
gadget.saveProps(props, saveName);
},
restoreGadgetProps: function(id, saveName, delay, callback) {
var gadget = this.gadgets[id];
if (gadget)
gadget.restoreProps(saveName,delay,callback);
else if(callback)
callback();
},
listScene: function(){
console.log("listScene:");
var accu=[];
accu["faces"]=0;
//var crlf="<br>";
var crlf=" :: ";
var output="========= Scene summary ===========";
var nbLights=-1;
function getFaces(obj,nbFaces){
if(obj.geometry){
var gg=obj.geometry;
if (gg.faces) nbFaces+=gg.faces.length;
}
if(obj.getDescendants){
var children=obj.getDescendants();
if (children) nbFaces+=getFaces(children,nbFaces);
}
return nbFaces;
}
if(threeCtx)if(threeCtx.scene){
var threeScene=threeCtx.scene;
nbLights=threeScene.__lights.length;
console.log(threeScene);
var obj=threeScene.getDescendants();
for (var o in obj){
var nbf=getFaces(obj[o],0);
accu["faces"]+=nbf;
//console.log(obj[o].name+" has "+nbf+" faces");
/*if(obj[o].geometry){
var gg=obj[o].geometry;
if (gg.faces) accu["faces"]+=gg.faces.length;
console.log(obj[o].name+" has "+gg.faces.length+" faces");
}*/
}
}
output+=crlf+"nb lights: "+nbLights;
output+=crlf+"Nb faces: "+ accu["faces"];
console.log(output);
},
})
/* ======================================== */
function InitGlobals() {
xdv = new XDView();
VSIZE = 12600;
VHALF = VSIZE/2;
htStateMachine = null;
threeCtx = null;
SCALE3D = 0.001;
resourcesMap = {};
resources={};
area=null;
currentSkin=null;
logger=null;
}
InitGlobals();
/* ======================================== */
var Gadget=Class.extend({
init: function(id, options) {
this.id = id;
this.options = $.extend(true, {
base: {
visible: false,
}
}, options);
this.avatar=null;
this.savedProps={};
},
mergeOptions: function() {
return $.extend(true,
{
x: 0,
y: 0,
z: 0,
},
this.options.base,
currentSkin["3d"]?this.options["3d"]:this.options["2d"],
this.options[currentSkin.name]);
},
build: function(options) {
if(this.avatar)
return;
if(arguments.length==0)
options=this.mergeOptions();
},
unbuild: function() {
if(this.avatar) {
this.avatar.remove();
this.avatar=null;
}
},
canDisplay: function(options) {
if(currentSkin===undefined || currentSkin===null)
return false;
if(arguments.length==0)
options=this.mergeOptions();
return options.visible &&
((!currentSkin["3d"] && options.ratio!==undefined && options.center!==undefined) ||
(currentSkin["3d"] /* && 3D requirements */));
},
update: function(options,delay,callback) {
if(arguments.length<2 || delay===undefined)
delay=0;
if(arguments.length<3 || callback===undefined)
callback=function(){};
if(currentSkin!==undefined && currentSkin!==null) {
var xdMap=currentSkin["3d"]?"3d":"2d";
if(options.base)
for(var i in options.base) {
if(this.options[xdMap])
delete this.options[xdMap][i];
if(this.options[currentSkin.name])
delete this.options[currentSkin.name][i];
}
if(options[xdMap])
for(var i in options[xdMap])
if(this.options[currentSkin.name])
delete this.options[currentSkin.name][i];
$.extend(true,this.options,options);
var aOptions=this.mergeOptions();
if(!this.avatar && this.canDisplay(aOptions)) {
var avatarType=avatarTypes[aOptions.type];
if(avatarType!==undefined)
this.avatar=new avatarType(this,aOptions);
}
if(typeof delay=="object") {
if(delay[currentSkin.name]!==undefined)
delay=delay[currentSkin.name];
else if(delay[xdMap]!==undefined)
delay=delay[xdMap];
else if(delay.base!==undefined)
delay=delay.base;
else
delay=0;
}
if(this.avatar)
this.avatar.update(aOptions,delay,callback);
} else
$.extend(true,this.options,options);
},
saveProps: function(props, saveName) {
var save={};
for(var oi in this.options) {
var optCat=this.options[oi];
for(var i in props) {
var prop=props[i];
if(optCat[prop]!==undefined) {
save[oi]=save[oi] || {};
save[oi][prop]=optCat[prop];
}
}
}
this.savedProps[saveName]=save;
},
restoreProps: function(saveName,delay,callback) {
if (this.savedProps[saveName]!==undefined)
this.update(this.savedProps[saveName],delay,callback);
else if(callback)
callback();
},
});
/* ======================================== */
var updateOp=1;
var GadgetAvatar=Class.extend({
init: function(gadget,options) {
this.gadget = gadget;
this.options = options;
this.SCALE3D = SCALE3D;
this.animCounts={};
},
remove: function() {
},
display: function(options) {
},
update: function(options,delay,callback) {
var aOptions=$.extend(true,{},this.options,options);
aOptions.updateOp=updateOp++;
aOptions.updateCallback=callback;
this.display(aOptions,delay,callback);
if(aOptions.visible)
this.show();
else
this.hide();
this.options=aOptions;
},
show: function() {
},
hide: function() {
},
animStart: function(options) {
if(options===undefined) {
console.error("animStart without options");
debugger;
return;
}
if(options.updateOp===undefined) {
console.error("animStart without options");
debugger;
return;
}
if(this.object3d)
this.object3d.matrixAutoUpdate = true;
if(this.animCounts[options.updateOp]===undefined)
this.animCounts[options.updateOp]=1;
else
this.animCounts[options.updateOp]++;
},
animEnd: function(options) {
if(options===undefined) {
console.error("animEnd without options");
debugger;
return;
}
if(options.updateOp===undefined) {
console.error("animEnd without options");
debugger;
return;
}
if(this.animCounts[options.updateOp]===undefined) {
console.error("animEnd without animCount");
debugger;
return;
}
if(--this.animCounts[options.updateOp]==0) {
if(this.object3d)
this.object3d.matrixAutoUpdate = false;
options.updateCallback();
delete this.animCounts[options.updateOp];
}
},
getResource : GetResource,
});
var GadgetElement=GadgetAvatar.extend({
init: function(gadget,options) {
options=$.extend(true,{
display: function() {},
},options);
this._super.apply(this,arguments);
this.options = $.extend(true, {
x : 0,
y : 0,
z : 0,
width : 1000,
height : 1000,
tag: "div",
opacity : 1,
rotate: 0,
css: {},
}, options);
this.element = $("<"+this.options.tag+"/>").css({
"position" : "absolute",
"z-index" : this.options.z,
}).hide().addClass("jocly-gadget").appendTo(area);
if(this.options.initialClasses)
this.element.addClass(this.options.initialClasses);
},
display: function(options,delay) {
var $this=this;
if(this.element) {
this.displayElement.call(this,!this.displayCalled,options,delay);
this.displayCalled=true;
} else if(delay) {
this.animStart(options);
setTimeout(function() { $this.animEnd(options); },delay);
}
},
displayElement: function(force,options,delay) {
var $this=this;
this.element.css($.extend(true,this.options.css,options.css));
if(
force ||
this.aWidth===undefined || this.aHeight===undefined ||
options.ratio != this.options.ratio ||
options.center.x != this.options.center.x ||
options.center.y != this.options.center.y ||
options.width != this.options.width ||
options.height != this.options.height ||
options.x != this.options.x ||
options.y != this.options.y ||
options.z != this.options.z
) {
this.aWidth = options.width * options.ratio;
this.aHeight = options.height * options.ratio;
var left = options.x * options.ratio + options.center.x - this.aWidth / 2;
var top = options.y * options.ratio + options.center.y - this.aHeight / 2;
if(delay) {
this.animStart(options);
this.element.css({
"z-index": options.z,
}).animate({
width : this.aWidth,
height : this.aHeight,
left : left,
top : top,
},delay,function() { $this.animEnd(options); });
} else {
this.element.css({
width : this.aWidth,
height : this.aHeight,
left : left,
top : top,
"z-index": options.z,
});
}
this.options.display(this.element,this.aWidth,this.aHeight);
}
if(force ||
options.classes != this.options.classes) {
if(this.options.classes)
this.element.removeClass(this.options.classes);
this.element.addClass(options.classes);
}
if(force ||
options.click != this.options.click) {
//this.element.unbind(JocGame.CLICK);
this.element.unbind(JocGame.MOUSEMOVE_EVENT);
this.element.unbind(JocGame.MOUSEDOWN_EVENT);
this.element.unbind(JocGame.MOUSEUP_EVENT);
if(options.click) {
var iOS = ( navigator.userAgent.match(/(iPad|iPhone|iPod)/g) ? true : false );
(function() {
var mouseDown=false;
var notified=false;
var downPosition=[0,0];
$this.element.bind(JocGame.MOUSEDOWN_EVENT,function(event) {
event.preventDefault();
if(iOS && event.type=="mousedown")
return;
if(event.type=="touchstart")
lastTouchStart = Date.now();
if(event.type=="mousedown" && Date.now()-lastTouchStart<500)
return;
mouseDown=true;
downPosition=GetEventPosition(event);
});
$this.element.bind(JocGame.MOUSEUP_EVENT,function(event) {
event.preventDefault();
if(iOS && event.type=="mouseup")
return;
mouseDown=false;
if(event.type=="joclyclick")
lastJoclyclick = Date.now();
if(event.type=="mouseup" && Date.now()-lastJoclyclick<500)
return;
if(event.type=='mouseup' || event.type=='joclyclick') {
options.click.call($this);
} else {
event.stopPropagation();
var newevent = new CustomEvent("joclyclick",{});
var x, y;
if(event.originalEvent.changedTouches && event.originalEvent.changedTouches.length>0) {
x = event.originalEvent.changedTouches[0].pageX;
y = event.originalEvent.changedTouches[0].pageY;
} else if(event.originalEvent.touches && event.originalEvent.touches.length>0) {
x = event.originalEvent.touches[0].pageX;
y = event.originalEvent.touches[0].pageY;
} else {
console.warn("Invalid touch event");
return;
}
var target = document.elementFromPoint(x, y);
target.dispatchEvent(newevent);
}
});
$this.element.bind(JocGame.MOUSEMOVE_EVENT,function(event) {
event.preventDefault();
if(iOS && event.type=="mousemove")
return;
if(mouseDown && !notified) {
var position=GetEventPosition(event);
var dx=position[0]-downPosition[0];
var dy=position[1]-downPosition[1];
if(dx*dx+dy*dy>100) {
notified=true;
options.click.call($this);
}
}
});
})();
}
}
/*
if(force ||
options.holdClick != this.options.holdClick) {
this.element.unbind("holdclick");
if(options.holdClick)
this.element.bind("holdclick",options.holdClick);
}
*/
if(force ||
options.rotate != this.options.rotate) {
while(options.rotate<0)
options.rotate+=360;
options.rotate%=360;
var rotate=options.rotate;
var rotate0=this.options.rotate;
if(rotate-this.options.rotate>180)
rotate0=360;
else if(this.options.rotate-rotate>180)
rotate+=360;
if(delay) {
this.animStart(options);
$({deg:rotate0}).animate({deg:rotate},{
step: function(now) {
$this.element.css('transform','rotate('+now+'deg)');
},
duration: delay,
complete: function() {
$this.animEnd(options);
},
});
} else
this.element.css({
"transform": "rotate("+options.rotate+"deg)",
});
} else if(delay) {
this.animStart(options);
setTimeout(function() {
$this.animEnd(options);
},0);
}
if(force ||
options.opacity != this.options.opacity) {
if(delay) {
this.animStart(options);
this.element.stop().animate({
"opacity": options.opacity,
},delay,function() { $this.animEnd(options); });
} else
this.element.css({
"opacity": options.opacity,
});
}
},
show: function() {
this.element.show();
},
hide: function() {
this.element.hide();
},
remove: function() {
this._super.apply(this,arguments);
this.element.unbind(JocGame.CLICK);
//this.element.unbind("holdclick");
this.element.remove();
},
});
var GadgetImage=GadgetElement.extend({
displayElement: function(force,options) {
var $this=this;
this._super.apply(this,arguments);
if(force || this.options.file!=options.file) {
this.options.file=options.file;
GetResource("image|"+options.file, function(image,imgSrc) {
if(imgSrc==$this.options.file)
$this.element.css({
"background-image" : "url(" + image.src + ")",
"background-size" : "100% 100%",
"background-repeat" : "no-repeat",
});
else
console.log("file has changed to",$this.options.file,"(",imgSrc,")");
});
}
},
});
var GadgetCanvas=GadgetElement.extend({
init: function(gadget,options) {
options=$.extend({
tag: "canvas",
draw: function() {},
},options);
this._super.call(this,gadget,options);
this.canvasContext=this.element[0].getContext("2d");
},
displayElement: function(force,options) {
this._super.apply(this,arguments);
this.element.attr("width",this.aWidth).attr("height",this.aHeight);
//this.canvasContext.save();
this.canvasContext.clearRect(0,0,options.width,options.height);
this.canvasContext.translate(this.aWidth/2,this.aHeight/2);
this.canvasContext.scale(options.ratio,options.ratio);
this.options.draw.call(this,this.canvasContext,1/options.ratio);
//this.canvasContext.restore();
}
});
var GadgetHexagon=GadgetCanvas.extend({
init: function(gadget,options) {
var $this=this;
var R=options.radius;
var L=R*Math.sqrt(3)/2;
options=$.extend({
lineWidthFactor: 1,
},options,{
draw: function(ctx,pixSize) {
ctx.lineWidth=pixSize*$this.options.lineWidthFactor;
ctx.beginPath();
ctx.moveTo(-L,L/2);
ctx.lineTo(0,R);
ctx.lineTo(L,L/2);
ctx.lineTo(L,-L/2);
ctx.lineTo(0,-R);
ctx.lineTo(-L,-L/2);
ctx.closePath();
if($this.options.strokeStyle) {
ctx.strokeStyle=$this.options.strokeStyle;
ctx.stroke();
}
if($this.options.fillStyle) {
ctx.fillStyle=$this.options.fillStyle;
ctx.fill();
}
},
});
this._super.call(this,gadget,options);
this.element.attr("width",options.width).attr("height",options.height);
this.canvasContext=this.element[0].getContext("2d");
},
});
/*
var GadgetSprite=GadgetCanvas.extend({
init: function(gadget,options) {
this._super.apply(this,arguments);
this.displayArgs=null;
},
displayElement: function(force,options) {
var $this=this;
this._super.apply(this,arguments);
if(force || this.options.file!=options.file) {
GetResource("image|"+options.file, function(image) {
$this.image=image;
if($this.displayArgs && $this.options.clipx!==undefined && $this.options.clipy!==undefined &&
$this.options.clipwidth!==undefined && $this.options.clipheight!==undefined)
$this.drawImage.apply($this,$this.displayArgs);
});
}
if(force || this.options.clipx!=options.clipx
|| this.options.clipy!=options.clipy
|| this.options.clipwidth!=options.clipwidth
|| this.options.clipheight!=options.clipheight
) {
if(this.image && options.clipx!==undefined && options.clipy!==undefined &&
options.clipwidth!==undefined && options.clipheight!==undefined) {
this.drawImage.call(this,force,options);
} else
this.displayArgs=arguments;
}
if(this.image && options.clipx!==undefined && options.clipy!==undefined &&
options.clipwidth!==undefined && options.clipheight!==undefined)
this.drawImage.apply(this,arguments);
else
this.displayArgs=arguments;
},
drawImage: function(force,options) {
this.canvasContext.save();
var x0=parseInt(options.clipx+.5);
var y0=parseInt(options.clipy+.5);
var cx0=parseInt(options.clipwidth+.5);
var cy0=parseInt(options.clipheight+.5);
var x1=0;
var y1=0;
var cx1=parseInt(this.aWidth+.5);
var cy1=parseInt(this.aHeight+.5);
this.canvasContext.scale(cx1,cy1);
this.canvasContext.imageSmoothingEnabled=true;
this.canvasContext.drawImage(this.image,x0,y0,cx0,cy0,x1,y1,1,1);
//this.canvasContext.drawImage(this.image,x0,y0,cx0,cy0,x1,y1,cx1,cy1);
this.canvasContext.restore();
this.displayArgs=null;
},
});
*/
var GadgetSprite=GadgetCanvas.extend({
init: function(gadget,options) {
this._super.apply(this,arguments);
this.displayArgs=null;
},
displayElement: function(force,options) {
var $this=this;
this._super.apply(this,arguments);
if(force || this.options.file!=options.file) {
GetResource("image|"+options.file, function(image,imgSrc) {
if(imgSrc==$this.options.file){
$this.image=image;
$this.element.css({
"background-image" : "url(" + image.src + ")",
"background-size" : "100% 100%",
"background-repeat" : "no-repeat",
});
}
if($this.displayArgs && $this.options.clipx!==undefined && $this.options.clipy!==undefined &&
$this.options.clipwidth!==undefined && $this.options.clipheight!==undefined)
$this.drawImage.apply($this,$this.displayArgs);
});
}
if(force || this.options.clipx!=options.clipx
|| this.options.clipy!=options.clipy
|| this.options.clipwidth!=options.clipwidth
|| this.options.clipheight!=options.clipheight
) {
if(this.image && options.clipx!==undefined && options.clipy!==undefined &&
options.clipwidth!==undefined && options.clipheight!==undefined) {
this.drawImage.call(this,force,options);
} else
this.displayArgs=arguments;
}
if(this.image && options.clipx!==undefined && options.clipy!==undefined &&
options.clipwidth!==undefined && options.clipheight!==undefined)
this.drawImage.apply(this,arguments);
else
this.displayArgs=arguments;
},
drawImage: function(force,options) {
var rx=(options.clipwidth/this.aWidth);
var ry=(options.clipheight/this.aHeight);
var bcx=parseInt(this.image.width/rx+.5);
var bcy=parseInt(this.image.height/ry+.5);
var bs= ""+bcx+"px "+bcy+"px";
this.element.css({
"width" : parseInt(this.aWidth+.5),
"height" : parseInt(this.aHeight+.5),
"background-image" : options.file,
"background-size" : bs,
"background-position": "-"+(parseInt(options.clipx/rx+.5))+"px -"+(parseInt(options.clipy/ry+.5))+"px",
});
},
});
var GadgetDisk=GadgetElement.extend({
init: function(gadget,options) {
this._super.apply(this,arguments);
},
displayElement: function(force,options) {
this._super.apply(this,arguments);
this.element.css({
"border-radius": "50%",
});
},
});
var GadgetObject3D=GadgetAvatar.extend({
init: function(gadget,options) {
var $this=this;
this._super.apply(this,arguments);
this.displayCalled=false;
this.options = $.extend(true, {
x : 0.0,
y : 0.0,
z : 0.0,
color: null,
castShadow: true,
receiveShadow: false,
harbor: true,
}, options);
this.createObject();
},
createObject: function() {
},
objectReady: function(object3d) {
var $this=this;
this.object3d=object3d;
object3d.castShadow=this.options.castShadow;
object3d.receiveShadow=this.options.receiveShadow;
object3d.name=this.gadget.id;
object3d.matrixAutoUpdate = false;
this.shouldUpdate = true;
this.update(this.options);
//object3d.visible=this.options.visible;
if(this.options.harbor)
threeCtx.harbor.add(object3d);
else
threeCtx.scene.add(object3d);
},
display: function(options,delay) {
var $this=this;
if(this.object3d) {
this.shouldUpdate = false;
this.displayObject3D.call(this,!this.displayCalled,options,delay);
this.displayCalled=true;
if(this.shouldUpdate)
this.object3d.updateMatrix();
}
if(delay) {
$this.animStart(options);
setTimeout(function() { $this.animEnd(options); },delay);
}
},
displayObject3D: function(force,options,delay) {
var $this=this;
threeCtx.animControl.trigger((isNaN(delay)?0:delay)+200);
if(force ||
options.x != this.options.x ||
options.y != this.options.y ||
options.z != this.options.z
) {
this.shouldUpdate = true;
if(delay) {
this.animStart(options);
new TWEEN.Tween(this.object3d.position).to({
x: options.x*SCALE3D,
y: options.z*SCALE3D,
z: options.y*SCALE3D,
},delay).easing(options.positionEasing?options.positionEasing:TWEEN.Easing.Cubic.EaseInOut).onComplete(function() {
$this.animEnd(options);
}).onUpdate(function(ratio) {
if(options.positionEasingUpdate)
options.positionEasingUpdate.call($this,ratio);
}).start();
} else {
this.object3d.position.x=options.x*SCALE3D;
this.object3d.position.y=options.z*SCALE3D;
this.object3d.position.z=options.y*SCALE3D;
}
}
if(force ||
options.click != this.options.click) {
if(this.options.click)
this.object3d.off("mouseup");
if(options.click) {
//if(!threeCtx.cameraControls.hasBeenDragged())
this.object3d.on("mouseup",function() {
//if(!threeCtx.cameraControls.hasBeenDragged())
options.click.call();
});
}
}
/*
if(force ||
options.holdClick != this.options.holdClick) {
if(this.options.holdClick)
this.object3d.off("holdclick");
if(options.holdClick) {
//if(!threeCtx.cameraControls.hasBeenDragged())
this.object3d.on("holdclick",function(eventData){
if(!threeCtx.cameraControls.hasBeenDragged())
options.holdClick.call($this,eventData);
}
);
}
}
*/
if(force ||
options.castShadow != this.options.castShadow) {
this.object3d.castShadow=options.castShadow
}
if(force ||
options.receiveShadow != this.options.receiveShadow) {
this.object3d.receiveShadow=options.receiveShadow
}
},
show: function() {
if(arStream && !this.options.harbor)
return this.hide();
if(this.object3d){
this.object3d.visible=true;
if (this.object3d.children){
for(var c=0;c<this.object3d.children.length;c++) {
var part=this.object3d.children[c];
if(part.joclyVisible===undefined || part.joclyVisible)
part.visible=true;
else
part.visible=false;
}
}
}
},
hide: function() {
if(this.object3d){
this.object3d.visible=false;
if (this.object3d.children){
for(var c=0;c<this.object3d.children.length;c++)
this.object3d.children[c].visible=false;
}
}
},
remove: function() {
this._super.apply(this,arguments);
if(this.object3d) {
if(this.options.click)
this.object3d.off("mouseup");
/*
if(this.options.holdClick)
this.object3d.off("holdclick");
*/
if(this.object3d.parent)
this.object3d.parent.remove(this.object3d);
this.object3d=null;
}
},
getMaterialMap : GetMaterialMap,
});
var GadgetMesh=GadgetObject3D.extend({
init: function(gadget,options) {
options=$.extend(true,{
rotate: 0,
rotateX: 0,
rotateY: 0,
scale: [1,1,1],
materials: {},
smooth: 0,
opacity: 1,
flatShading: false,
morphing: [],
},options,{
});
this._super.call(this,gadget,options);
},
displayObject3D: function(force,options,delay) {
var $this=this;
this._super.apply(this,arguments);
if(force ||
options.rotate != this.options.rotate ||
options.rotateX != this.options.rotateX ||
options.rotateY != this.options.rotateY
) {
this.shouldUpdate = true;
var delta=options.rotate-this.options.rotate;
if(delta>180)
options.rotate-=360;
else if(delta<-180)
options.rotate+=360;
delta=options.rotateX-this.options.rotateX;
if(delta>180)
options.rotateX-=360;
else if(delta<-180)
options.rotateX+=360;
delta=options.rotateY-this.options.rotateY;
if(delta>180)
options.rotateY-=360;
else if(delta<-180)
options.rotateY+=360;
if(delay) {
this.animStart(options);
new TWEEN.Tween(this.object3d.rotation).to({
x: options.rotateX * (Math.PI/180),
y: options.rotate * (Math.PI/180),
z: options.rotateY * (Math.PI/180),
},delay).easing(options.rotateEasing?options.rotateEasing:TWEEN.Easing.Cubic.EaseInOut).onComplete(function() {
$this.animEnd(options);
}).start();
} else {
this.object3d.rotation.x = options.rotateX * (Math.PI/180);
this.object3d.rotation.y = options.rotate * (Math.PI/180);
this.object3d.rotation.z = options.rotateY * (Math.PI/180);
}
}
if(force ||
options.scale[0] != this.options.scale[0] ||
options.scale[1] != this.options.scale[1] ||
options.scale[2] != this.options.scale[2]
) {
this.shouldUpdate = true;
if(delay) {
this.animStart(options);
new TWEEN.Tween(this.object3d.scale).to({
x: options.scale[0],
y: options.scale[2],
z: options.scale[1],
},delay).easing(options.scaleEasing?options.scaleEasing:TWEEN.Easing.Cubic.EaseInOut).onComplete(function() {
$this.animEnd(options);
}).start();
} else {
this.object3d.scale.set(options.scale[0],options.scale[2],options.scale[1]);
/*if ((options.scale[0] > 0) &&
(options.scale[1] > 0) &&
(options.scale[2] > 0)
)
this.object3d.scale.set(options.scale[0],options.scale[2],options.scale[1]);
else{
var g=this.object3d.geometry;
g.dynamic = true;
for(var i = 0; i<g.faces.length; i++) {
g.faces[i].normal.x = -1*g.faces[i].normal.x;
g.faces[i].normal.y = -1*g.faces[i].normal.y;
g.faces[i].normal.z = -1*g.faces[i].normal.z;
}
g.computeVertexNormals();
g.computeFaceNormals();
g.applyMatrix(new THREE.Matrix4().makeScale( options.scale[0], options.scale[2], options.scale[1] ) );
}*/
}
}
if(force ||
options.color != this.options.color
) {
if(this.object3d.material && this.object3d.material.color!==undefined)
if(options.color!==null)
this.object3d.material.color.setHex(options.color);
/*
if(options.color===null)
this.object3d.material.color.setHex(0xffffff);
else
this.object3d.material.color.setHex(options.color);
*/
}
if(force ||
options.opacity != this.options.opacity
) {
if(this.object3d.material && this.object3d.material.opacity!==undefined) {
if(options.opacity===null)
options.opacity=1;
if(delay) {
this.animStart(options);
new TWEEN.Tween(this.object3d.material).to({
opacity: options.opacity,
},delay).easing(options.opacityEasing?options.opacityEasing:TWEEN.Easing.Cubic.EaseInOut).onComplete(function() {
$this.animEnd(options);
}).start();
} else
this.object3d.material.opacity = options.opacity;
}
}
if(force ||
options.morphing.toString()!=this.options.morphing.toString()
) {
this.shouldUpdate = true;
if(options.morphing.length>0) {
if(this.object3d.material && this.object3d.material.materials &&
this.object3d.material.materials.length>0 && !this.object3d.material.materials[0].morphTargets) {
for(var i=0;i<this.object3d.material.materials.length;i++)
this.object3d.material.materials[i].morphTargets=true;
}
if(delay) {
this.animStart(options);
new TWEEN.Tween(this.object3d.morphTargetInfluences).to(options.morphing,
delay).easing(options.morphingEasing?options.morphingEasing:TWEEN.Easing.Cubic.EaseInOut).onComplete(function() {
$this.animEnd(options);
}).start();
} else {
for(var i=0;i<options.morphing.length && i<this.object3d.morphTargetInfluences.length;i++)
this.object3d.morphTargetInfluences[i]=options.morphing[i];
}
}
}
if(this.object3d.material && options.materials) {
if(force) {
if(this.object3d.material.materials) {
for (var m in this.object3d.material.materials) {
var mat=$this.object3d.material.materials[m];
if(options.materials[mat.name]) {
for(var mpi in options.materials[mat.name]) {
var newMatProp=options.materials[mat.name][mpi];
(function(mat,mpi) {
if(mpi=="map") {
GetMaterialMap(newMatProp,function(matMpi) {
mat[mpi] = matMpi;
mat.needsUpdate = true;
});
} else if(mpi=="color") {
if(typeof mat["ambient"] != "undefined")
mat["ambient"].setHex(newMatProp);
mat[mpi].setHex(newMatProp);
}
else
mat[mpi]=newMatProp;
})(mat,mpi,m);
}
}
}
}
} else {
var diffMat=Diff(this.options.materials,options.materials);
if(diffMat) {
for(var mi in diffMat) {
var newMat=diffMat[mi];
if(this.object3d.material.materials) {
for (var m in this.object3d.material.materials) {
var mat=$this.object3d.material.materials[m];
if(mat.name==mi) {
if(newMat) {
for(var mpi in newMat) {
var newMatProp=newMat[mpi];
if(newMatProp!==null) {
(function(mat,mpi) {
if(mpi=="map")
GetMaterialMap(newMatProp,function(matMpi) {
mat[mpi] = matMpi;
mat.needsUpdate = true;
});
else if(mpi=="color") {
if(typeof mat["ambient"] != "undefined")
mat["ambient"].setHex(newMatProp);
mat[mpi].setHex(newMatProp);
} else {
if(delay) {
$this.animStart(options);
if(mat[mpi]===undefined || isNaN(newMatProp)) {
mat[mpi]=newMatProp;
setTimeout(function() {
$this.animEnd(options);
});
} else {
var change={};
change[mpi]=newMatProp;
new TWEEN.Tween(mat).to(change,delay).easing(options.materialEasing?options.materialEasing:
TWEEN.Easing.Cubic.EaseInOut).onComplete(function() {
$this.animEnd(options);
}).start();
}
} else
mat[mpi]=newMatProp;
}
})(mat,mpi);
} else
delete mat[mpi];
}
} else {
delete mat.map;
delete mat.opacity;
delete mat.color;
}
}
}
}
}
}
}
}
},
});
var GadgetCustomMesh3D=GadgetMesh.extend({
init: function(gadget,options) {
options=$.extend(true,{
create: function() { return null },
display: function() { },
},options,{
});
this._super.call(this,gadget,options);
},
createObject: function() {
var $this=this;
function Callback(object3d) {
$this.objectReady(object3d);
}
var object3d=this.options.create.call(this,Callback);
if(object3d)
this.objectReady(object3d);
},
displayObject3D: function(force,options,delay) {
this._super.apply(this,arguments);
this.options.display.call(this,force,options,delay);
},
replaceMesh : function(mesh,options,delay) {
if(this.object3d) {
if(this.options.click)
this.object3d.off("mouseup");
/*
if(this.options.holdClick)
this.object3d.off("holdclick");
*/
if(this.object3d.parent)
this.object3d.parent.remove(this.object3d);
}
this.object3d=mesh;
if(this.options.visible)
this.show();
else
this.hide();
if(this.options.harbor)
threeCtx.harbor.add(this.object3d);
else
threeCtx.scene.add(this.object3d);
if(delay) {
this.displayObject3D(true,this.options);
this.displayObject3D(true,options,delay);
} else
this.displayObject3D(true,options);
},
});
var GadgetPlane3D=GadgetMesh.extend({
init: function(gadget,options) {
options=$.extend(true,{
display: function() {},
sx: 1000,
sy: 1000,
color:0xffffff,
horizontal:true,
texture: null,
material: "basic",
side: null,
},options,{
});
this._super.call(this,gadget,options);
},
createObject: function(){
var gg= new THREE.PlaneGeometry(this.options.sx*SCALE3D, this.options.sy*SCALE3D, 1, 1);
var matData={
color: this.options.data,
opacity: 0,
}
if(this.options.texture) {
var tOptions=this.options.texture;
if(tOptions.file) {
GetMaterialMap(tOptions.file, function(texture) {
if(tOptions.wrapS!==undefined)
texture.wrapS=tOptions.wrapS;
if(tOptions.wrapT!==undefined)
texture.wrapT=tOptions.wrapT;
if(tOptions.repeat)
texture.repeat.set.apply(texture.repeat,tOptions.repeat);
matData.map=texture;
});
}
}
if(this.options.side!==undefined)
matData.side=this.options.side;
if(this.options.transparent!==undefined)
matData.transparent=this.options.transparent;
var gm;
switch(this.options.material) {
case "phong":
gm=new THREE.MeshPhongMaterial(matData);
break;
default:
gm=new THREE.MeshBasicMaterial(matData);
}
var mesh = new THREE.Mesh( gg , gm );
this.objectReady(mesh);
},
});
// should this class be obsoleted in favor of GadgetCustomMesh3D
var GadgetCustom3D=GadgetObject3D.extend({
init: function(gadget,options) {
options=$.extend(true,{
create: function() { return null },
display: function() {},
},options,{
});
this._super.call(this,gadget,options);
},
createObject: function() {
var $this=this;
function Callback(object3d) {
$this.objectReady(object3d);
}
var object3d=this.options.create.call(this,Callback);
if(object3d)
this.objectReady(object3d);
},
displayObject3D: function(force,options,delay) {
this._super.apply(this,arguments);
this.options.display.call(this,force,options,delay);
},
});
var GadgetMeshFile=GadgetMesh.extend({
init: function(gadget,options) {
this._super.apply(this,arguments);
this.meshFileForceDisplay=false;
},
createObject: function() {
var $this=this;
var file=this.options.file;
var smooth=this.options.smooth;
GetResource("smoothedfilegeo|"+this.options.smooth+"|"+file,function(geometry , materials) {
if(file!=$this.options.file)
return;
var materials0=[]
for(var i=0;i<materials.length;i++)
materials0.push(materials[i].clone());
materials=materials0;
if ($this.options.flatShading)
for ( var m=0; m < materials.length ; m++) {
materials[m].shading=THREE.FlatShading;
}
var mesh = new THREE.Mesh( geometry , new THREE.MultiMaterial( materials ) );
$this.objectReady(mesh);
if($this.meshFileForceDisplay) {
$this.displayObject3D(true,$this.meshFileForceDisplay);
$this.meshFileForceDisplay=false;
}
});
},
displayObject3D: function(force,options,delay) {
var fileChange=(options.file!=this.options.file);
if(fileChange) {
options.click=null;
//options.holdClick=null;
}
this._super.apply(this,arguments);
if(fileChange) {
if(this.object3d) {
if(this.options.click)
this.object3d.off("mouseup");
/*
if(this.options.holdClick)
this.object3d.off("holdclick");
*/
if(this.object3d.parent)
this.object3d.parent.remove(this.object3d);
this.object3d=null;
}
this.options.file=options.file;
this.meshFileForceDisplay=options;
this.createObject();
}
},
});
var Gadget3DVideo = GadgetMesh.extend({
init : function(gadget, options) {
options = $.extend(true, {
scale : [ 1, 1, 1 ],
playerSide : 1,
makeMesh : function(videoTexture,ccvVideoTexture) {
var material = new THREE.MeshBasicMaterial({
map : videoTexture,
overdraw : true,
// side:THREE.DoubleSide
});
var geometry = new THREE.PlaneGeometry(12, 9, 1, 1);
var mesh = new THREE.Mesh(geometry, material);
return mesh;
},
videoPlaying : function(on) {
},
ccvLocked : function(on) {
},
ccv: false,
ccvMargin: [.10,.10,.30,.10],
ccvWidth: 80,
ccvHeight: 60,
hideBeforeFirstLock: true,
}, options);
this._super.call(this, gadget, options);
this.videoConnected = false;
this.videoErrorCount = 0;
this.videoSkipError = false;
this.shouldBeVisible = false;
this.gotFirstLock = false;
},
objectReady: function(mesh) {
mesh.visible = false;
for(var i=0;i<mesh.children.length;i++)
mesh.children[i].visible = false;
this.streamReady(Gadget3DVideo.isStreamReady(this.options.playerSide));
this._super.apply(this,arguments);
},
createObject : function() {
Gadget3DVideo.addAvatar(this,this.options.playerSide);
var ccvTexture=null;
if(this.ccvContextKey)
ccvTexture=Gadget3DVideo.getCCVVideoTexture(this.options.playerSide,this.ccvContextKey)
var mesh = this.options.makeMesh.call(this,
Gadget3DVideo.getVideoTexture(this.options.playerSide),ccvTexture);
if (mesh)
this.objectReady(mesh);
},
remove : function() {
Gadget3DVideo.removeAvatar(this,this.options.playerSide);
this._super.apply(this, arguments);
},
show : function() {
this.shouldBeVisible = true;
if (this.videoConnected && (this.options.ccv==false || this.gotFirstLock || !this.options.hideBeforeFirstLock))
this._super();
},
hide : function() {
this.shouldBeVisible = false;
this._super();
},
streamReady : function(on) {
this.videoConnected = on;
if(on)
this.show();
else
this.hide();
},
ccvLocked: function(locked) {
if(locked && this.shouldBeVisible) {
this.gotFirstLock = true;
this.show();
}
this.options.ccvLocked(locked);
},
});
Gadget3DVideo.streams={};
Gadget3DVideo.avatars={"1":[],"-1":[]};
Gadget3DVideo.textures={"1":null,"-1":null};
Gadget3DVideo.renderLoopHooked=false;
Gadget3DVideo.ccvLibRequested=false;
Gadget3DVideo.getStream=function(playerSide) {
if(!this.streams[playerSide]) {
var vStream={
stream: null,
avatars: this.avatars[playerSide],
video: null,
videoImage: null,
videoContext: null,
videoTexture: null,
streamReady: false,
ownVideoElement: false,
errorCount: 0,
loopCount: 0,
local: false,
ccvVideoImage: null,
ccvInProgress: false,
ccvLock: null,
ccvContexts: {},
ccvLastAnalyzed: null,
ccvLastSuccess: null,
}
var video = $("video[joclyhub-video='" + playerSide + "']");
if (video.length > 0) {
vStream.video = video;
} else {
vStream.ownVideoElement = true;
vStream.video = $("<video/>").attr("autoplay", "autoplay").width(
160).height(120).css({
visibility : "hidden",
position: "absolute",
"z-index": -1,
top: 0,
}).attr("joclyhub-video",playerSide).appendTo("body");
}
var canvas = $("canvas[joclyhub-video-canvas='" + playerSide + "']");
if(canvas.length>0) {
vStream.videoImage = canvas;
if(this.textures[playerSide])
vStream.videoTexture = this.textures[playerSide];
else {
vStream.videoTexture = new THREE.Texture(vStream.videoImage[0]);
this.textures[playerSide]=vStream.videoTexture;
}
} else {
vStream.videoImage = this.makeCanvas(160,120).attr("joclyhub-video-canvas",playerSide);
vStream.videoTexture = new THREE.Texture(vStream.videoImage[0]);
this.textures[playerSide]=vStream.videoTexture;
}
vStream.videoTexture.minFilter = THREE.LinearFilter;
vStream.videoTexture.magFilter = THREE.LinearFilter;
vStream.videoImageContext = vStream.videoImage[0].getContext('2d');
this.streams[playerSide]=vStream;
}
return this.streams[playerSide];
}
Gadget3DVideo.addStream=function(playerSide,stream,local) {
var $this=this;
var vStream=this.getStream(playerSide);
vStream.stream = stream;
vStream.local = local;
if(threeCtx)
threeCtx.animControl.trigger(3000);
if(!this.renderLoopHooked) {
this.renderLoopHooked=true;
if(threeCtx)
threeCtx.animateCallbacks["Gadget3DVideo"] = {
_this : $this,
callback : $this.animate,
}
}
}
Gadget3DVideo.removeStream=function(playerSide) {
var vStream=this.streams[playerSide];
if(vStream) {
if(vStream.streamReady)
for(var i=0;i<vStream.avatars.length;i++)
vStream.avatars[i].streamReady(false);
if(vStream.ccvLastSuccess)
vStream.ccvLastSuccess.videoImage.remove();
if(vStream.ccvLastAnalyzed)
vStream.ccvLastAnalyzed.remove();
delete this.streams[playerSide];
if(this.renderLoopHooked) {
var streamCount=0;
for(var s in this.streams)
streamCount++;
if(streamCount==0) {
if(threeCtx)
delete threeCtx.animateCallbacks["Gadget3DVideo"];
this.renderLoopHooked=false;
}
}
}
}
Gadget3DVideo.addAvatar=function(avatar,playerSide) {
this.avatars[playerSide].push(avatar);
var vStream=this.getStream(playerSide);
if(!avatar.ccvContextKey)
avatar.ccvContextKey=""+avatar.options.ccvWidth+","+avatar.options.ccvHeight+","+JSON.stringify(avatar.options.ccvMargin);
if(!vStream.ccvContexts[avatar.ccvContextKey]) {
var ccvContext={
width: avatar.options.ccvWidth,
height: avatar.options.ccvHeight,
margin: avatar.options.ccvMargin,
}
ccvContext.videoImage = this.makeCanvas(ccvContext.width,ccvContext.height);
ccvContext.videoImageContext = ccvContext.videoImage[0].getContext('2d');
ccvContext.videoImageContext.fillStyle = "rgb(0,255,0)";
ccvContext.videoImageContext.fillRect (0, 0, ccvContext.width, ccvContext.height);
ccvContext.videoTexture = new THREE.Texture(ccvContext.videoImage[0]);
ccvContext.videoTexture.minFilter = THREE.LinearFilter;
ccvContext.videoTexture.magFilter = THREE.LinearFilter;
ccvContext.videoTexture.needsUpdate = true;
vStream.ccvContexts[avatar.ccvContextKey]=ccvContext;
//debugger;
}
return vStream.streamReady;
}
Gadget3DVideo.getVideoTexture=function(playerSide) {
var vStream=this.streams[playerSide];
if(vStream)
return vStream.videoTexture;
else
return null;
}
Gadget3DVideo.getCCVVideoTexture=function(playerSide,contextKey) {
var vStream=this.streams[playerSide];
if(vStream) {
var ccvContext=vStream.ccvContexts[contextKey];
if(ccvContext)
return ccvContext.videoTexture;
}
return null;
}
Gadget3DVideo.isStreamReady=function(playerSide) {
return this.streams[playerSide] && this.streams[playerSide].streamReady;
}
Gadget3DVideo.isCCVLocked=function(playerSide) {
return this.streams[playerSide] && this.streams[playerSide].ccvLock;
}
Gadget3DVideo.removeAvatar=function(avatar,playerSide) {
var vStream=this.streams[playerSide];
if(vStream)
for(var i=0;i<vStream.avatars.length;i++)
if(avatar==vStream.avatars[i]) {
vStream.avatars.splice(i,1);
break;
}
}
Gadget3DVideo.animate=function() {
for(var side in this.streams) {
var vStream=this.streams[side];
try {
vStream.loopCount++;
if (vStream.video[0].getAttribute("webrtc-attached")==="1" &&
vStream.video[0].readyState === vStream.video[0].HAVE_ENOUGH_DATA) {
vStream.videoImageContext.drawImage(vStream.video[0], 0, 0,
vStream.videoImage[0].width, vStream.videoImage[0].height);
if(vStream.videoTexture) {
vStream.videoTexture.needsUpdate = true;
if(!vStream.streamReady) {
vStream.streamReady = true;
for(var i=0;i<vStream.avatars.length;i++)
vStream.avatars[i].streamReady(true);
}
}
var ccvLocalRequested=false;
var ccvRequested=false;
for(var i=0;i<vStream.avatars.length;i++)
if(vStream.avatars[i].options.ccv) {
ccvRequested=true;
if(vStream.local) {
ccvLocalRequested=true;
break;
}
}
if(ccvLocalRequested) {
if(typeof(ccv)=="undefined") { // ccv library not loaded
if(!this.ccvLibRequested) {
var path=null;
console.error("No CCV path available");
this.ccvLibRequested=true;
if(path) {
$("<script/>").attr("src",path+"/face.js").attr("type","text/javascript")
.appendTo($("head"));
$("<script/>").attr("src",path+"/ccv.js").attr("type","text/javascript")
.appendTo($("head"));
}
}
} else {
if(!vStream.ccvInProgress)
this.ccvPoll(vStream);
}
}
if(ccvRequested)
this.ccvAnimate(vStream);
threeCtx.animControl.trigger();
}
} catch(e) {
if(vStream.errorCount % 1000000 ==0)
console.warn("Gadget3DVideo.animate error",vStream.errorCount,side,e);
vStream.errorCount++;
}
}
}
Gadget3DVideo.ccvLocked=function(vStream,locking) {
for(var i=0;i<vStream.avatars.length;i++)
vStream.avatars[i].ccvLocked(locking);
}
Gadget3DVideo.ccvPoll=function(vStream) {
vStream.ccvInProgress=true;
var width=vStream.videoImage[0].width;
var height=vStream.videoImage[0].height;
var now=Date.now();
function CCVResult(comp) {
if(comp.length==0) {
if(vStream.ccvLock) {
vStream.ccvLock=null;
Gadget3DVideo.ccvLocked(vStream,false);
WebRTC.sendCCVMessage({
locked: false,
});
}
} else {
var face=comp[0];
var lock=vStream.ccvLock;
vStream.ccvLock={
x: face.x,
y: face.y,
width: face.width,
height: face.height,
}
if(vStream.ccvLastSuccess)
vStream.ccvLastSuccess.videoImage.remove();
vStream.ccvLastSuccess=$.extend({
videoImage: vStream.ccvLastAnalyzed,
copied: false,
},vStream.ccvLock);
vStream.ccvLastAnalyzed=null;
vStream.ccvLastAnalyzedContext=null;
if(!lock)
Gadget3DVideo.ccvLocked(vStream,true);
WebRTC.sendCCVMessage({
locked: true,
x: face.x,
y: face.y,
width: face.width,
height: face.height,
});
}
ReschedulePoll();
}
function ReschedulePoll() {
setTimeout(function() {
vStream.ccvInProgress=false;
},200);
}
if(!vStream.ccvLastAnalyzed) {
vStream.ccvLastAnalyzed = this.makeCanvas(vStream.videoImage[0].width,vStream.videoImage[0].height);
vStream.ccvLastAnalyzedContext = vStream.ccvLastAnalyzed[0].getContext("2d");
}
vStream.ccvLastAnalyzedContext.drawImage(vStream.videoImage[0],0,0,vStream.videoImage[0].width,vStream.videoImage[0].height);
/*
if(WebRTC.webrtcDetectedBrowser=="firefox")
ccv.detect_objects({
//"canvas" : ccv.grayscale(vStream.ccvLastAnalyzed[0]),
"canvas" : ccv.grayscale(vStream.videoImage[0]),
"cascade" : cascade,
"interval" : 5,
"min_neighbors" : 1,
"async" : false,
"async" : true,
"worker" : 1
})(CCVResult);
else
*/
CCVResult(ccv.detect_objects({
//"canvas" : ccv.grayscale(vStream.ccvLastAnalyzed[0]),
"canvas" : ccv.grayscale(vStream.videoImage[0]),
"cascade" : cascade,
"interval" : 5,
"min_neighbors" : 1,
"async" : false,
"worker" : 1
}));
}
Gadget3DVideo.makeCanvas = function(width,height) {
return $("<canvas/>").attr("width", width).attr("height",height).width(width).height(height)
.css({
visibility : "hidden",
position: "absolute",
"z-index": -1,
top: 0,
}).appendTo("body");
}
Gadget3DVideo.ccvAnimate=function(vStream) {
function DrawImage(ccvContext,ccvLock,source) {
var width=ccvLock.width*(1+ccvContext.margin[1]+ccvContext.margin[3]);
var height=ccvLock.height*(1+ccvContext.margin[0]+ccvContext.margin[2]);
var x=ccvLock.x-ccvLock.width*ccvContext.margin[3];
var y=ccvLock.y-ccvLock.height*ccvContext.margin[0];
if(x<0) {
width+=x;
x=0;
}
if(y<0) {
height+=y;
y=0;
}
if(x+width>source.width)
width=source.width-x;
if(y+height>source.height)
height=source.height-y;
ccvContext.videoImageContext.drawImage(source,
x, y, width, height,
0, 0,
ccvContext.width, ccvContext.height);
ccvContext.videoTexture.needsUpdate = true;
}
for(var contextKey in vStream.ccvContexts) {
var ccvContext=vStream.ccvContexts[contextKey];
if(vStream.ccvLock)
DrawImage(ccvContext,vStream.ccvLock,vStream.videoImage[0]);
else if(vStream.ccvLastSuccess && !vStream.ccvLastSuccess.copied) {
vStream.ccvLastSuccess.copied=true;
DrawImage(ccvContext,vStream.ccvLastSuccess,vStream.ccvLastSuccess.videoImage[0]);
}
}
}
Gadget3DVideo.receiveRemoteLock=function(message) {
for(var side in this.streams) {
var vStream=this.streams[side];
if(vStream.local)
continue;
var lock=vStream.ccvLock;
if(message.locked) {
vStream.ccvLock={
x: message.x,
y: message.y,
width: message.width,
height: message.height,
};
if(vStream.ccvLastSuccess)
vStream.ccvLastSuccess.videoImage.remove();
var videoImage=this.makeCanvas(vStream.videoImage[0].width,vStream.videoImage[0].height);
var videoImageContext=videoImage[0].getContext("2d");
videoImageContext.drawImage(vStream.videoImage[0],0,0,vStream.videoImage[0].width,vStream.videoImage[0].height);
vStream.ccvLastSuccess=$.extend({
videoImage: videoImage,
copied: false,
},vStream.ccvLock);
if(!lock)
Gadget3DVideo.ccvLocked(vStream,true);
} else {
if(lock) {
vStream.ccvLock=null;
Gadget3DVideo.ccvLocked(vStream,false);
}
}
}
}
function WebRTCHandler(event, data) {
try {
if (data.webrtcType == "mediaOn") {
if(data.ar)
AR(data.stream);
else
Gadget3DVideo.addStream(data.side,data.stream,data.local);
} if (data.webrtcType == "mediaOff") {
if(arStream)
AR(null);
else
Gadget3DVideo.removeStream(data.side);
} if (data.webrtcType == "ccv")
Gadget3DVideo.receiveRemoteLock(data.message);
} catch(e) {
console.error("xd-view webrtc error",e);
}
}
$(document).bind("joclyhub.webrtc",WebRTCHandler);
var Gadget3DVideoFile = GadgetCustomMesh3D.extend({
init : function(gadget, options) {
options = $.extend(true, {
scale : [ 1, 1, 1 ],
makeMesh : function(videoTexture) {
var material = new THREE.MeshBasicMaterial({
map : videoTexture,
overdraw : true,
});
var geometry = new THREE.PlaneGeometry(this.options.width*this.SCALE3D, this.options.height*this.SCALE3D, 1, 1);
var mesh = new THREE.Mesh(geometry, material);
return mesh;
},
width: 12,
height: 9,
}, options);
this.videoPlayer=Gadget3DVideoFile.GetVideoPlayer(options.src);
this._super.call(this, gadget, options);
},
createObject : function() {
var mesh = this.options.makeMesh.call(this,this.videoPlayer.texture);
if (mesh)
this.objectReady(mesh);
},
remove : function() {
var videoPlayer=videoPlayers[this.options.src];
if(videoPlayer) {
videoPlayer.count--;
if(videoPlayer.count==0) {
delete threeCtx.animateCallbacks["Gadget3DVideoFile."+this.options.src];
videoPlayer.tag.remove();
videoPlayer.canvas.remove();
delete videoPlayers[this.options.src];
}
}
this._super.apply(this, arguments);
},
});
var videoPlayers={};
Gadget3DVideoFile.GetVideoPlayer=function(url) {
var videoPlayer=videoPlayers[url];
if(!videoPlayer) {
var width=638;
var height=360;
var videoTag=$("<video/>").attr("autoplay","autoplay")./*attr("muted","muted").*/attr("loop","loop").css({
width: width,
height: height,
position: "absolute",
}).append($("<source/>").attr("src",url).attr("type","video/webm")).appendTo("body");
videoPlayer={
count: 1,
tag: videoTag,
canvas: Gadget3DVideo.makeCanvas(width,height),
}
videoPlayer.context=videoPlayer.canvas[0].getContext('2d');
videoPlayer.context.fillStyle = "rgb(0,255,0)";
videoPlayer.context.fillRect (0, 0, width, height);
videoPlayer.texture = new THREE.Texture(videoPlayer.canvas[0]);
videoPlayer.texture.minFilter = THREE.LinearFilter;
videoPlayer.texture.magFilter = THREE.LinearFilter;
videoPlayer.texture.needsUpdate = true;
function Animate() {
var ctx=videoPlayer.context;
ctx.drawImage(videoPlayer.tag[0], 0, 0,
width,height);
videoPlayer.texture.needsUpdate = true;
}
threeCtx.animateCallbacks["Gadget3DVideoFile."+url] = {
_this : null,
callback : Animate,
}
videoPlayers[url]=videoPlayer;
} else
videoPlayer.count++;
return videoPlayer;
}
var GadgetCamera=GadgetObject3D.extend({
init : function(gadget, options) {
this._super.call(this, gadget, options);
//this.object3d=threeCtx.camera;
this.object3d=threeCtx.body;
this.cameraObject = this.object3d.children[0];
this.targetAnim=null;
this.camTarget=threeCtx.camTarget;
},
displayObject3D: function(force,options,delay) {
var $this=this;
this.options.x=this.object3d.position.x/SCALE3D;
this.options.y=this.object3d.position.z/SCALE3D;
this.options.z=this.object3d.position.y/SCALE3D;
this._super.apply(this,arguments);
if(force ||
options.targetX*SCALE3D != threeCtx.cameraControls.camTarget.x ||
options.targetY*SCALE3D != threeCtx.cameraControls.camTarget.z ||
options.targetZ*SCALE3D != threeCtx.cameraControls.camTarget.y
) {
if(delay) {
var traveling=options.traveling;
var x0=threeCtx.cameraControls.camTarget.x;
var y0=threeCtx.cameraControls.camTarget.y;
var z0=threeCtx.cameraControls.camTarget.z;
options.traveling=false;
if(this.targetAnim) {
this.targetAnim.stop();
//this.animEnd(this.targetCallback);
this.animEnd(options);
}
//this.targetCallback=callback;
this.animStart(options);
this.targetAnim=new TWEEN.Tween(threeCtx.cameraControls.camTarget).to({
x: options.targetX*SCALE3D,
y: options.targetZ*SCALE3D,
z: options.targetY*SCALE3D,
},delay).easing(options.targetEasing?options.targetEasing:TWEEN.Easing.Cubic.EaseInOut).onComplete(function() {
$this.targetAnim=null;
$this.animEnd(options);
}).onUpdate(function(ratio) {
if(options.targetEasingUpdate)
options.targetEasingUpdate.call($this,ratio);
if(traveling) {
var dx=threeCtx.cameraControls.camTarget.x-x0;
var dy=threeCtx.cameraControls.camTarget.y-y0;
var dz=threeCtx.cameraControls.camTarget.z-z0;
x0=threeCtx.cameraControls.camTarget.x;
y0=threeCtx.cameraControls.camTarget.y;
z0=threeCtx.cameraControls.camTarget.z;
//$this.object3d.position.add(new THREE.Vector3(dx,dy,dz));
}
//$this.object3d.lookAt(threeCtx.cameraControls.camTarget);
$this.cameraObject.lookAt(threeCtx.cameraControls.camTarget);
}).start();
} else {
threeCtx.cameraControls.camTarget.x=options.targetX*SCALE3D;
threeCtx.cameraControls.camTarget.y=options.targetZ*SCALE3D;
threeCtx.cameraControls.camTarget.z=options.targetY*SCALE3D;
}
}
},
});
function CreateCameraGadget() {
xdv.createGadget("camera",{
"3d": {
type: "camera3d",
x: threeCtx.camera.position.x/SCALE3D,
y: threeCtx.camera.position.z/SCALE3D,
z: threeCtx.camera.position.y/SCALE3D,
targetX: threeCtx.cameraControls.camTarget.x/SCALE3D,
targetY: threeCtx.cameraControls.camTarget.z/SCALE3D,
targetZ: threeCtx.cameraControls.camTarget.y/SCALE3D,
},
});
xdv.saveGadgetProps("camera",["targetX","targetY","targetZ"],"initial");
xdv.updateGadget("camera",{
"3d": {
visible: true,
},
});
}
/* ======================================== */
var avatarTypes={
"image": GadgetImage,
"element": GadgetElement,
"canvas": GadgetCanvas,
"hexagon": GadgetHexagon,
"sprite": GadgetSprite,
"disk": GadgetDisk,
"meshfile": GadgetMeshFile,
"custom3d": GadgetCustom3D,
"plane3d": GadgetPlane3D,
"custommesh3d": GadgetCustomMesh3D,
"video3d": Gadget3DVideo,
"camera3d": GadgetCamera,
"videofile3d": Gadget3DVideoFile,
}
/* ======================================== */
var areaElements=null;
View.Game.CamAnim = {
isSupported: function() {
return !!threeCtx;
},
isRunning: function() {
return threeCtx && threeCtx.camAnim;
},
set: function(on) {
if(threeCtx)
threeCtx.setCamAnim(on);
},
}
View.Game.InitView = function() {
resourcesMap = this.resources || {};
if (this!=xdv.game) {
xdv.game=this;
if(this.mWidget.find(".jocly-xdv-area").length==0) {
area = $("<div/>").css({
"position" : "absolute",
"z-index" : 0,
"overflow": "hidden",
}).addClass("jocly-xdv-area").appendTo(this.mWidget);
}
}
if(areaElements) {
areaElements.appendTo(area);
areaElements=null;
}
if(!xdv.initDone) {
this.xdInit(xdv);
xdv.initDone=true;
}
var needs3DUpdate=false;
if(!currentSkin || this.mSkin!=currentSkin.name) {
currentSkin=null;
for(var i=0; i<this.mViewOptions.skins.length;i++) {
var skin=this.mViewOptions.skins[i];
if(skin.name==this.mSkin) {
currentSkin=skin;
break;
}
}
if(currentSkin==null) {
Log("!!! InitView", "skin", this.mSkin, "not found");
return;
}
xdv.unbuildGadgets();
areaElements=null;
if(currentSkin["3d"])
needs3DUpdate=true;
}
var areaWidth = Math.min(this.mGeometry.width, this.mGeometry.height
* (this.mViewOptions.preferredRatio || 1));
var areaHeight = Math.min(this.mGeometry.width / (this.mViewOptions.preferredRatio || 1), this.mGeometry.height);
var areaCenter;
if(currentSkin["3d"]) {
area.css({
left : 0,
top : 0,
width : this.mGeometry.width,
height : this.mGeometry.height,
});
areaCenter={
x: this.mGeometry.width/2,
y: this.mGeometry.height/2,
};
if(!threeCtx) {
if(!THREE.Object3D._threexDomEvent) {
THREE.Object3D._threexDomEvent = new THREEx.DomEvent();
}
threeCtx=BuildThree(this,areaWidth,areaHeight);
CreateCameraGadget();
threeCtx.camera.updateProjectionMatrix();
} else {
threeCtx.renderer.setSize(this.mGeometry.width,this.mGeometry.height);
threeCtx.anaglyphEffect.setSize(this.mGeometry.width,this.mGeometry.height);
threeCtx.camera.aspect=this.mGeometry.width/this.mGeometry.height;
threeCtx.camera.updateProjectionMatrix();
}
THREE.Object3D._threexDomEvent.setDOMElement(threeCtx.renderer.domElement);
THREE.Object3D._threexDomEvent.setBoundContext(THREEx_boundContext);
THREE.Object3D._threexDomEvent.camera(threeCtx.camera);
ResumePendingResources();
threeCtx.animControl.trigger();
if(needs3DUpdate) {
var cameraData = $.extend(true,{
radius: 12,
elevationAngle: 60,
rotationAngle: 90,
distMax : 20,
distMin : 0,
elevationMax: 89,
elevationMin : 10,
startAngle: 90,
camAnim: false,
limitCamMoves: true,
enableDrag: true,
targetBounds: [3000,3000,3000],
target: [0,0,800],
fov:55,
near: .01
},currentSkin.camera);
// update FOV
threeCtx.camera.fov=cameraData.fov;
threeCtx.camera.near=cameraData.near;
threeCtx.camera.updateProjectionMatrix();
$.extend(threeCtx.cameraControls,{
minDistance: cameraData.distMin,
maxDistance: cameraData.distMax,
minPolarAngle: (90-cameraData.elevationMax)*Math.PI/180,
maxPolarAngle: (90-cameraData.elevationMin)*Math.PI/180,
enableDrag: cameraData.enableDrag,
targetBounds: [cameraData.targetBounds[0]*SCALE3D,cameraData.targetBounds[2]*SCALE3D,cameraData.targetBounds[1]*SCALE3D],
});
var camPosition = {
x: cameraData.radius * Math.cos(cameraData.elevationAngle*Math.PI/180) * Math.cos(cameraData.rotationAngle*Math.PI/180),
z: cameraData.radius * Math.cos(cameraData.elevationAngle*Math.PI/180) * Math.sin(cameraData.rotationAngle*Math.PI/180),
y: cameraData.radius * Math.sin(cameraData.elevationAngle*Math.PI/180),
}
var camTarget = {
x: cameraData.target[0],
y: cameraData.target[1],
z: cameraData.target[2],
}
xdv.updateGadget("camera",{
"3d": {
x: camPosition.x/SCALE3D,
y: camPosition.z/SCALE3D,
z: camPosition.y/SCALE3D,
targetX: camTarget.x,
targetY: camTarget.y,
targetZ: camTarget.z,
},
});
threeCtx.cameraControls.camTarget.copy(camTarget);
//threeCtx.cameraControls.camera.position.copy(camPosition);
threeCtx.cameraControls.update();
var world={
color: 0x205D7C,
fog: true,
fogNear: 10,
fogFar: 100,
lightCastShadow: true,
lightIntensity: 1.75,
lightPosition: {x:-12,y:12,z:12},
//lightShadowDarkness: 0.75,
ambientLightColor: 0xbbbbbb,
skyLightPosition: {x:-45,y:45,z:45},
skyLightIntensity: 2,
}
$.extend(true,world,currentSkin.world);
if(threeCtx.scene.fog) {
threeCtx.scene.remove(threeCtx.scene.fog);
delete threeCtx.scene.fog;
}
if(world.fog){
var fogColor = world.color ;
if (world.fogColor) fogColor = world.fogColor ;
threeCtx.scene.fog=new THREE.Fog(fogColor,world.fogNear,world.fogFar);
}
threeCtx.world = world;
threeCtx.renderer.setClearColor(new THREE.Color(world.color), 1);
threeCtx.light.castShadow = world.lightCastShadow;
threeCtx.light.intensity = world.lightIntensity;
threeCtx.light.position.set(world.lightPosition.x,world.lightPosition.y,world.lightPosition.z);
//threeCtx.light.shadowDarkness=world.lightShadowDarkness;
threeCtx.ambientLight.color.setHex(world.ambientLightColor);
threeCtx.skyLight.intensity=world.skyLightIntensity;
threeCtx.skyLight.position.set(world.skyLightPosition.x,world.skyLightPosition.y,world.skyLightPosition.z);
}
threeCtx.renderer.domElement.style.display="block";
} else {
area.css({
left : (this.mGeometry.width - areaWidth) / 2,
top : (this.mGeometry.height - areaHeight) / 2,
width : areaWidth,
height : areaHeight,
});
areaCenter={
x: areaWidth/2,
y: areaHeight/2,
}
if(threeCtx)
threeCtx.renderer.domElement.style.display="none";
}
this.xdBuildScene(xdv);
//xdv.updateArea(Math.min(areaWidth,areaHeight)/VSIZE,areaCenter);
xdv.updateArea(Math.max(areaWidth,areaHeight)/VSIZE,areaCenter);
}
View.Game.DestroyView = function() {
if (!xdv.game) {
Log("!!! InitView", "game already unset");
return;
}
if(resLoadingMask)
resLoadingMask.hide();
xdv.game = null;
areaElements=area.children().detach();
if(threeCtx) {
if(threeCtx.cameraControls.autoRotate)
threeCtx.cameraControls.autoRotate=false;
}
//threeCtx.animControl.stop();
}
View.Game.CloseView = function() {
xdv.unbuildGadgets();
if(threeCtx) {
THREE.Object3D._threexDomEvent.unsetBoundContext(THREEx_boundContext);
threeCtx.cameraControls.destroy();
threeCtx=null;
}
InitGlobals();
}
View.Game.xdResourceLoaded = function(res) {
if(/^map\|/.test(res))
return false;
if(resources[res] && resources[res].status=="loaded")
return true;
else
return false;
}
View.Game.xdLoadResources = function(ress,callback) {
var resCount=0;
function ResLoaded() {
if(--resCount==0)
callback();
}
for(var i=0;i<ress.length;i++) {
resCount++;
var m=/^map\|(.*)$/.exec(ress[i]);
if(m)
GetMaterialMap(m[1],function() {
setTimeout(ResLoaded,0);
});
else
GetResource(ress[i],function() {
setTimeout(ResLoaded,0);
});
}
}
View.Game.xdExternalCommand = function(cmd,scope) {
switch(cmd.type) {
case 'updateCamera':
xdv.updateGadget("camera",{
"3d": cmd.camera,
},cmd.delay || 0);
break;
case 'getCamera':
var resp={
type: "camera",
cameraId: cmd.cameraId,
}
if(threeCtx) {
resp.camera={
x: threeCtx.camera.position.x/SCALE3D,
y: threeCtx.camera.position.z/SCALE3D,
z: threeCtx.camera.position.y/SCALE3D,
targetX: threeCtx.cameraControls.camTarget.x/SCALE3D,
targetY: threeCtx.cameraControls.camTarget.z/SCALE3D,
targetZ: threeCtx.cameraControls.camTarget.y/SCALE3D,
}
} else {
console.warn("cannot get camera without 3D context");
resp.camera=null;
}
scope.sendEmbed(resp);
break;
case 'snapshot':
var resp={
type: "snapshot",
snapshotId: cmd.snapshotId,
}
if(threeCtx) {
var renderer=threeCtx.renderer;
var canvas=renderer.domElement;
renderer.render( threeCtx.scene, threeCtx.camera );
resp.image=canvas.toDataURL("image/png");
} else {
console.warn("cannot get snapshot without 3D context");
resp.image=null;
}
scope.sendEmbed(resp);
break;
}
}
View.Game.ViewControl = function(cmd, options) {
options = options || {};
var promise = new Promise( function(resolve, reject) {
switch(cmd) {
case "enterAnaglyph":
if(threeCtx) {
threeCtx.anaglyph = true;
var factor = 2.5;
threeCtx.scene.scale.set(1/factor,1/factor,1/factor);
threeCtx.camera.scale.set(factor,factor,factor);
threeCtx.animControl.trigger();
};
resolve();
break;
case "exitAnaglyph":
if(threeCtx) {
threeCtx.anaglyph = false;
threeCtx.scene.scale.set(1,1,1);
threeCtx.camera.scale.set(1,1,1);
threeCtx.animControl.trigger();
};
resolve();
break;
case "stopAnimations":
var animCount = TWEEN.getAll().length;
TWEEN.removeAll();
resolve(animCount>0);
break;
case "setPanorama":
if(options.pictureUrl || options.pictureData) {
xdv.removeGadget("panorama");
xdv.createGadget("panorama",{
"3d": {
type: "custommesh3d",
harbor: false,
rotate: options.rotate || 0,
create: function(callback) {
var geometry = new THREE.SphereGeometry( 500, 60, 40 );
geometry.scale( - 1, 1, 1 );
new Promise(function(resolve, reject) {
if(options.pictureData) {
var image = new Image;
image.src = options.pictureData;
var texture = new THREE.Texture( image );
image.onload = function() {
texture.needsUpdate = true;
resolve(texture);
}
} else
resolve(new THREE.TextureLoader().load( options.pictureUrl ))
}).then(function(texture) {
var material = new THREE.MeshBasicMaterial( {
map: texture
} );
mesh = new THREE.Mesh( geometry, material );
callback(mesh);
})
},
}
});
xdv.updateGadget("panorama",{
"3d": {
visible: true
},
});
} else {
xdv.updateGadget("panorama",{
"3d": {
visible: false
},
});
xdv.removeGadget("panorama");
}
resolve();
break;
case "takeSnapshot":
if(threeCtx) {
var canvas = threeCtx.renderer.domElement;
threeCtx.renderer.render( threeCtx.scene, threeCtx.camera );
resolve(canvas.toDataURL("image/"+(options.format || "png")));
} else
reject(new Error("Snapshot only available on 3D views"));
break;
default:
reject(new Error("ViewControl: unsupported command "+cmd));
}
});
return promise;
}
View.Board.Display = function(aGame) {
//Log("### View.Board.Display");
this.xdDisplay(xdv,aGame);
//xdv.listScene();
}
View.Board.xdInput = function(xdv, aGame) {
console.error("View.Board.xdInput must be overriden");
return {
initial: {},
getActions: function(moves,currentInput) {
return {};
},
}
}
View.Board.xdBuildHTStateMachine = function(xdv, htsm, aGame) {
var $this=this;
var inputSpec;
var clickGadgets={},viewGadgets={},highlightGadgets=[];
var inputStack, movesStack,actionStack;
function Click(action,mode) {
if(mode=="select")
htsm.smQueueEvent("E_ACTION",{action:action});
else if(mode=="cancel")
htsm.smQueueEvent("E_CANCEL",{action:action});
}
function Init(args) {
inputSpec=$this.xdInput(xdv,aGame);
inputStack=[inputSpec.initial];
// ensures moves are not duplicated
var movesMap={};
$this.mMoves.forEach(function(move) {
movesMap[JSON.stringify(move)]=move;
});
var moves=[];
for(var m in movesMap)
moves.push(movesMap[m]);
movesStack=[moves];
actionStack=[];
}
function ShowFurnitures(args) {
if(inputSpec.furnitures)
inputSpec.furnitures.forEach(function(gadget) {
xdv.updateGadget(gadget,{
base: {
visible: true,
},
});
});
}
function HideFurnitures(args) {
if(inputSpec.furnitures)
inputSpec.furnitures.forEach(function(gadget) {
xdv.updateGadget(gadget,{
base: {
visible: false,
},
});
});
}
function SetAction(action,mode) {
if(mode=="select") {
if(action.pre)
action.pre.call($this);
if(action.cancel)
action.cancel.forEach(function(gid) {
clickGadgets[gid]=true;
xdv.updateGadget(gid,{
base: {
click: function() {
Click(action,"cancel");
},
},
});
});
}
if(action.click)
action.click.forEach(function(gid) {
clickGadgets[gid]=true;
xdv.updateGadget(gid,{
base: {
click: function() {
Click(action,mode);
},
},
});
});
if(typeof action.highlight=="function") {
if(typeof action.unhighlight!="function")
console.warn("No unhighlight function defined for",action);
else
highlightGadgets.push(function() {
action.unhighlight.call($this,mode);
});
action.highlight.call($this,mode);
}
if(action.view)
action.view.forEach(function(gid) {
viewGadgets[gid]=true;
xdv.updateGadget(gid,{
base: {
visible: true,
},
})
});
}
function PrepareAction(args) {
var nextActions=inputSpec.getActions.call($this,movesStack[movesStack.length-1],inputStack[inputStack.length-1]);
if(nextActions==null) {
htsm.smQueueEvent("E_MOVE_DONE",{move:movesStack[movesStack.length-1][0]});
return;
}
var actionsCount=0;
var action0;
for(var action in nextActions) {
action0=nextActions[action];
actionsCount++;
}
if(actionsCount>1 || (inputStack.length==1 && !inputSpec.allowForced) || (actionsCount==1 && !aGame.mAutoComplete && !action0.skipable)) {
for(var actId in nextActions) {
var action=nextActions[actId];
action.forced=false;
SetAction(action,"select");
}
} else if(actionsCount==0) {
htsm.smQueueEvent("E_MOVE_DONE",{move:actionStack[actionStack.length-1].moves[0]});
} else {
action0.forced=true;
htsm.smQueueEvent("E_ACTION",{action:action0});
}
}
function SendMove(args) {
aGame.HumanMove(args.move);
}
function Clean(args) {
for(var gid in clickGadgets)
xdv.updateGadget(gid,{
base: {
click: null,
},
});
clickGadgets={};
for(var gid in viewGadgets)
xdv.updateGadget(gid,{
base: {
visible: false,
},
});
viewGadgets={};
for(var i=0;i<highlightGadgets.length;i++)
highlightGadgets[i].call($this);
}
function Execute(action,callback) {
if(action.execute) {
var actions=action.execute;
if(typeof actions=="function")
actions=[actions];
var actionsCount=0;
function ActionDone(action) {
if(--actionsCount==0)
callback();
}
actions.forEach(function(action) {
actionsCount++;
setTimeout(function() {
action.call($this,ActionDone);
},0);
});
} else
callback();
}
function Action(args) {
movesStack.push(args.action.moves);
Execute(args.action,function() {
htsm.smQueueEvent("E_DONE",{ action: args.action });
});
}
function PostAction(args) {
if(args.action.post)
args.action.post.call($this);
}
function SetCancel(args) {
if(actionStack.length>0 && !actionStack[actionStack.length-1].noAutoCancel)
SetAction(actionStack[actionStack.length-1],"cancel");
}
function Validate(args) {
inputStack.push($.extend(true,{},inputStack[inputStack.length-1],args.action.validate));
}
function Cancel(args) {
while(actionStack.length>0) {
var action=actionStack.pop();
inputStack.pop();
movesStack.pop();
if(action.unexecute)
action.unexecute.call($this);
if(action.post)
action.post.call($this);
if(action.forced==false)
break;
}
}
function PushAction(args) {
actionStack.push(args.action);
}
htsm.smTransition("S_INIT", "E_INIT", "S_WAIT_ACTION", [ Init, ShowFurnitures ]);
htsm.smEntering("S_WAIT_ACTION",[ PrepareAction, SetCancel ]);
htsm.smLeaving("S_WAIT_ACTION",[ Clean ]);
htsm.smTransition("S_WAIT_ACTION", "E_ACTION", "S_ACTION", [ PushAction, Validate, Action ]);
htsm.smTransition("S_WAIT_ACTION", "E_CANCEL", null, [ Cancel, Clean, PrepareAction, SetCancel]);
htsm.smTransition("S_WAIT_ACTION", "E_MOVE_DONE", "S_DONE", [ SendMove, HideFurnitures ]);
htsm.smTransition(["S_WAIT_ACTION","S_ACTION"], "E_END", "S_DONE", [ ]);
htsm.smTransition("S_ACTION", "E_DONE", "S_WAIT_ACTION", [ PostAction ]);
htsm.smTransition("S_DONE", "E_END", null, [ HideFurnitures ]);
}
View.Board.HumanTurn = function(aGame) {
//Log("### View.Board.HumanTurn");
var $this=this;
htStateMachine=new HTStateMachine();
htStateMachine.init();
this.xdBuildHTStateMachine(xdv,htStateMachine,aGame);
htStateMachine.smSetInitialState("S_INIT");
htStateMachine.smQueueEvent("E_INIT",{});
htStateMachine.smPlay();
}
View.Board.HumanTurnEnd = function(aGame) {
//Log("### View.Board.HumanTurnEnd");
if(htStateMachine) {
htStateMachine.smQueueEvent("E_END",{});
htStateMachine=null;
}
}
View.Board.PlayedMove = function(aGame, aMove) {
//Log("### View.Board.PlayedMove");
return this.xdPlayedMove(xdv,aGame,aMove);
}
View.Board.xdShowEnd = function(xdv, aGame) {
return true;
}
View.Board.ShowEnd=function(aGame) {
return this.xdShowEnd(xdv,aGame);
}
/* ======================================== */
var THREEx_boundContext=""+Math.random();
function BuildThree(aGame, areaWidth,areaHeight) {
var camera = new THREE.PerspectiveCamera( 55, (area.width()/area.height()), 1, 4000 );
var scene = new THREE.Scene();
var body = new THREE.Object3D();
scene.add( body );
body.add(camera);
var harbor = new THREE.Object3D();
scene.add( harbor );
var ambientLight=new THREE.AmbientLight( 0xbbbbbb );
harbor.add( ambientLight );
var light = new THREE.SpotLight( 0xffffff, 1.75, 0, 1.05, 1, 2); // test params here https://threejs.org/docs/?q=SpotLight#Reference/Lights/SpotLight
light.position.set( -12, 12, 12 );
light.castShadow = true;
//light.shadowDarkness = .75;
light.shadow.camera.near = 1;
light.shadow.camera.far = 27;
light.shadow.camera.fov = 90;
light.shadow.mapSize.width = 4096;
light.shadow.mapSize.height = 4096;
light.target = harbor;
harbor.add( light );
var skylight = new THREE.PointLight( 0xcccccc, 2, 150);//, Math.PI/5, 10);
skylight.position.set(-45,45,45);
harbor.add(skylight);
//light.shadowCameraVisible = false;
// skylight.shadowCameraVisible = true; nonsens! PointLight objects don't have shadow feature
var renderer = new THREE.WebGLRenderer( { antialias: true, alpha: true } );
renderer.setSize( area.width(), area.height() );
//renderer.setClearColor( scene.fog.color, 1 );
var projector = new THREE.Projector();
area.append( $(renderer.domElement) );
renderer.gammaInput = true;
renderer.gammaOutput = true;
//renderer.shadowMapEnabled = true;
renderer.shadowMap.enabled = true;
renderer.shadowMapSoft = true;
//renderer.physicallyBasedShading = true; // gives high level of shininess specular
//renderer.shadowMapCascade = true;
var stereo = false;
var stereoEffect = new THREE.StereoEffect(renderer);
stereoEffect.setSize( area.width(), area.height() );
var anaglyphEffect = new THREE.AnaglyphEffect( renderer );
anaglyphEffect.setSize( area.width(), area.height() );
var gamepads = new VRGamepads({
camera: camera,
scene: scene,
resBase: aGame.config.baseURL + "res/vr/",
drag: function(position,direction,pointerObject,pointerRescale) {
var intersectPoint = null;
var pointedObject = null;
VRGetIntersect(position,direction,function(object,point) {
intersectPoint = point;
pointedObject = object;
});
return intersectPoint ? {
point: intersectPoint,
object: pointedObject
} : null;
},
click: function(position,direction) {
VRGetIntersect(position,direction,function(object,point) {
if(object)
THREE.Object3D._threexDomEvent._notify("mouseup", object, null, point);
});
},
move: function(step) {
body.position.add(step);
}
});
var vrRay = new THREE.Raycaster();
var camAnim = !!aGame.mViewOptions.camAnim;
var animateCallbacks={};
var frameBacklog = 0;
function AnimControl() {
this.animating=false;
this.animateTimer=null;
this.nextStop=0;
}
AnimControl.prototype={
start: function() {
body.updateMatrixWorld();
if(this.animateTimer!=null) {
clearTimeout(this.animateTimer);
this.animateTimer=null;
}
if(this.animating==false) {
this.animating=true;
this.animate();
}
},
stop: function(delay) {
if(threeCtx && vr.vrEffect && vr.vrEffect.isPresenting) {
if(this.animateTimer!=null) {
clearTimeout(this.animateTimer);
this.animateTimer = null;
}
return;
}
if(delay===undefined)
delay=200;
var now=Date.now();
var $this=this;
if(this.animating) {
if(this.animateTimer!=null) {
if(this.nextStop<now+delay)
clearTimeout(this.animateTimer);
else
return;
}
this.nextStop=Math.max(this.nextStop,now+delay);
this.animateTimer=setTimeout(function() {
$this.animateTimer=null;
$this.animating=false;
},this.nextStop-now);
}
},
trigger: function() {
if(!this.animating || this.animateTimer!=null) {
this.start();
this.stop.apply(this,arguments);
}
},
animate: function() {
var $this=this;
var statsCurrentSec=0;
var statsTic=0;
var renderSum=0;
var renderCount=0;
function Animate(timestamp) {
frameBacklog--;
var t0,t1;
var showStats=false;
if(showStats) {
var sec=Math.floor(Date.now()/1000);
if(sec==statsCurrentSec)
statsTic++;
else {
if(statsTic>0) {
var rate=Math.round(1000*renderSum/renderCount)/1000;
var lag= Math.round(1000*(window.performance.now()-timestamp))/1000;
/*
console.log("fps",statsTic,"render",rate,"ms","",
"lag",lag,"ms","",
"frame backlog",frameBacklog);
*/
$(statsPanel).text("fps "+statsTic);
}
statsTic=1;
statsCurrentSec=sec;
}
}
if($this.animating) {
frameBacklog++;
requestAnimationFrame( Animate );
}
TWEEN.update();
if(showStats)
t0=Date.now();
if(vr.vrEffect && vr.vrEffect.isPresenting) {
gamepads.update();
var harborpad = gamepads.getHarborPad();
if(harborpad) {
harborpad.visible = false;
harborpad.getWorldPosition(ctx.harbor.position);
var scale = (harborpad.getAxes()[1] + 1.1) * .03;
ctx.harbor.scale.set(scale,scale,scale);
harborpad.getWorldQuaternion(ctx.harbor.quaternion);
} else {
ctx.harbor.position.set(0,0,0);
ctx.harbor.scale.set(1,1,1);
ctx.harbor.quaternion.copy(ctx.defaultHarborQuaternion);
}
vr.vrControls.update();
vr.vrEffect.render( scene, camera);
} else {
if(!arStream) {
ctx.harbor.position.set(0,0,0);
ctx.harbor.scale.set(1,1,1);
ctx.harbor.quaternion.copy(ctx.defaultHarborQuaternion);
}
/*
if(gamepads)
gamepads.clearAll();
*/
if(!arStream) {
cameraControls.update();
cameraOrientationControls.update();
}
if(stereo) {
gamepads.update();
stereoEffect.render( scene, camera );
} else if(ctx.anaglyph || aGame.mAnaglyph)
anaglyphEffect.render(scene,camera);
else
renderer.render( scene, camera );
}
if(showStats) {
t1=Date.now();
renderSum+=t1-t0;
renderCount++;
}
for(var cbi in animateCallbacks) {
var cb=animateCallbacks[cbi];
cb.callback.call(cb._this);
}
}
frameBacklog++;
Animate(window.performance.now());
},
}
var animControl=new AnimControl();
var statsPanel = null;
var cameraControls = new THREE.OrbitControls( camera, body, renderer.domElement);
$.extend(cameraControls,{
autoRotate: camAnim,
animControl: animControl,
});
cameraControls.camTarget.set(0,0.8,0);
var canOrientation = false;
var cameraOrientationControls = new THREE.DeviceOrientationControls(body,function(controls) {
if(typeof vr!= "undefined")
animControl.trigger();
if(!canOrientation && controls.enabled) {
canOrientation = true;
area.find(".vr-button").show();
}
});
if(typeof cameraControls.addEventListener=="function")
cameraControls.addEventListener( 'change', function() {
animControl.trigger();
} );
var ctx = {
scene: scene,
renderer: renderer,
light: light,
skyLight: skylight,
ambientLight: ambientLight,
loader: new THREE.JSONLoader(),
camera: camera,
cameraControls: cameraControls,
animateCallbacks: animateCallbacks,
camTarget: cameraControls.camTarget,
animControl: animControl,
body: body,
harbor: harbor,
defaultHarborQuaternion: harbor.quaternion.clone(),
anaglyphEffect: anaglyphEffect,
anaglyph: false
};
function VRGetIntersect(position,direction,callback) {
var threexDomEvent = THREE.Object3D._threexDomEvent;
vrRay.set(position,direction);
try {
var intersects = vrRay.intersectObjects( threexDomEvent._boundObjs[threexDomEvent._boundContext] );
} catch(e) {
return callback(null,null);
}
if(intersects.length==0)
return callback(null,null);
var intersect = intersects[0];
var object3d = threexDomEvent.getRootObject(intersect.object);
var objectCtx = threexDomEvent._objectCtxGet(object3d);
if(!objectCtx)
callback(null,null);
else
callback(object3d,intersect.point);
}
function VRSetup(ctx) {
function LookAtHarbor() {
vr.vrControls.resetPose();
}
function MakeButton() {
ctx.vrButton = document.createElement("img");
ctx.vrButton.className = "vr-button";
ctx.vrButton.setAttribute("data-vr-enter-src",aGame.config.baseURL + "res/vr/vr-enter.png");
ctx.vrButton.setAttribute("data-vr-exit-src",aGame.config.baseURL + "res/vr/vr-exit.png");
ctx.vrButton.setAttribute("src",ctx.vrButton.getAttribute("data-vr-enter-src"));
Object.assign(ctx.vrButton.style,{
position: "absolute",
bottom: "8px",
right: "8px",
cursor: "pointer",
"z-index": 2147483647
});
area[0].appendChild(ctx.vrButton);
}
function CardboardVR() {
MakeButton();
ctx.vrButton.style.display = "none";
ctx.vrButton.addEventListener("click",function() {
if(stereo) {
stereo = false;
ctx.vrButton.setAttribute("src",ctx.vrButton.getAttribute("data-vr-enter-src"));
var size = renderer.getSize();
renderer.setViewport( 0, 0, size.width, size.height );
} else {
stereo = true;
ctx.vrButton.setAttribute("src",ctx.vrButton.getAttribute("data-vr-exit-src"));
}
animControl.trigger();
});
}
function PureVR() {
MakeButton();
var vrControls = new THREE.VRControls( ctx.camera );
vr.vrControls = vrControls;
if(window.lastVrEffect) {
if(window.lastVrEffect.isPresenting)
window.lastVrEffect.exitPresent();
}
var vrEffect = new THREE.VREffect( ctx.renderer );
vr.vrEffect = vrEffect;
window.lastVrEffect = vrEffect;
window.addEventListener( 'vrdisplaypresentchange', function ( event ) {
ctx.animControl.trigger()
}, false );
ctx.vrButton.addEventListener("click",function() {
if(vrEffect.isPresenting) {
vrEffect.exitPresent();
ctx.vrButton.setAttribute("src",ctx.vrButton.getAttribute("data-vr-enter-src"));
} else {
vrEffect.requestPresent();
ctx.vrButton.setAttribute("src",ctx.vrButton.getAttribute("data-vr-exit-src"));
LookAtHarbor();
}
animControl.trigger();
});
}
vr = {};
if(typeof navigator.getVRDisplays != "undefined") {
navigator.getVRDisplays()
.then( function(displays) {
if(displays.length==0)
CardboardVR();
else
PureVR();
} ).catch( function() {
CardboardVR();
});
} else
CardboardVR();
return vr;
}
var vr = VRSetup(ctx);
return $.extend(ctx,vr);
}
function GetEventPosition(event) {
if(event.originalEvent)
return GetEventPosition(event.originalEvent);
if(event.changedTouches && event.changedTouches.length>0)
return [event.changedTouches[0].pageX,event.changedTouches[0].pageY];
if(event.touches && event.touches.length>0)
return [event.touches[0].pageX,event.touches[0].pageY];
return [event.pageX,event.pageY];
}
function AR(stream) {
if(!!arStream==!!stream) {
console.warn("AR is already",!!stream);
return;
}
arStream = stream;
if(arStream) {
var video = $("<video/>").addClass("ar-video").attr("autoplay", "autoplay").css({
position: "absolute",
top: 0,
width: "100%",
height: "100%",
left: 0,
"z-index": -1,
backgroundColor: "#0f0",
objectFit: "cover"
}).appendTo(area.parent());
JoclyAR.attach({
element: video[0],
stream: arStream,
threeCtx: threeCtx
});
xdv.redisplayGadgets();
threeCtx.renderer.setClearColor(new THREE.Color(threeCtx.world.color), 0);
threeCtx.animControl.trigger();
} else {
var video = area.parent().find(".ar-video");
if(video.length) {
JoclyAR.detach({
element: video[0]
});
video.remove();
}
xdv.redisplayGadgets();
threeCtx.renderer.setClearColor(new THREE.Color(threeCtx.world.color), 1);
threeCtx.animControl.trigger();
}
}
})();