/* 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 .
*
* 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);
$(document).unbind("joclyhub.webrtc",WebRTCHandler);
}
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 JoclyHub!="undefined") {
LOADING_TEXT_RESOURCE=JoclyHub.hubPath+"/res/images/loading-txt.png";
} else if(typeof JoclyPlazza!="undefined") {
LOADING_TEXT_RESOURCE=JoclyPlazza.config.baseURL+JoclyPlazza.config.pzPath+"/images/loading-txt.png";
}
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;
if(typeof JoclyPlazza!="undefined") {
logger=JoclyPlazza.getLogger("htsm","logHTSM");
WebRTC=JoclyPlazza.webrtc;
} else if(typeof JoclyHub!="undefined")
WebRTC=JoclyHub.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() {
if(typeof JoclyPlazza!="undefined" || (JoclyHub && JoclyHub.request.debughtsm)) {
Log.apply(null,arguments);
};
}
HTStateMachine.prototype.smWarning=function() {
if(typeof JoclyPlazza!="undefined" || (JoclyHub && JoclyHub.request.debughtsm)) {
Log.apply(null,arguments);
}
};
HTStateMachine.prototype.smDebug=function() {
if(typeof JoclyPlazza!="undefined" || (typeof JoclyHub!="undefined" && JoclyHub.request.debughtsm)) {
Log.apply(null,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=$("
").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();
console.info("TODO map",map);
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 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 ").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;i0) {
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=" ";
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;c180)
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; i0) {
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 0) {
vStream.video = video;
} else {
vStream.ownVideoElement = true;
vStream.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 ").attr("src",path+"/face.js").attr("type","text/javascript")
.appendTo($("head"));
$("").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 ").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=$(" ").attr("autoplay","autoplay")./*attr("muted","muted").*/attr("loop","loop").css({
width: width,
height: height,
position: "absolute",
}).append($(" ").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 = $("
").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; i1 || (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;i0 && !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 ]);
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 gamepads = new VRGamepads({
camera: camera,
scene: scene,
resBase: typeof JoclyPlazza!="undefined" ? JoclyPlazza.config.baseURL + JoclyPlazza.config.joclyPath + "/res/vr/" : "todo-setup-res-path",
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=null
if(typeof JoclyHub!="undefined" && JoclyHub.mode=="demo")
camAnim=true;
else
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.nextStop0) {
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
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();
//JoclyPlazza.config.show3DStats = true;
var statsPanel = null;
if(typeof JoclyPlazza!="undefined" && JoclyPlazza.config.show3DStats) {
statsPanel = document.createElement("div");
statsPanel.id = "stats-panel";
Object.assign(statsPanel.style,{
position: "absolute",
bottom: "8px",
left: "8px",
zIndex: 2147483647,
backgroundColor: "rgba(255,255,255,1)",
padding: "4px",
});
area[0].appendChild(statsPanel);
}
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(),
};
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";
if(typeof JoclyPlazza!="undefined") {
ctx.vrButton.setAttribute("data-vr-enter-src",JoclyPlazza.config.baseURL + JoclyPlazza.config.joclyPath + "/res/vr/vr-enter.png");
ctx.vrButton.setAttribute("data-vr-exit-src",JoclyPlazza.config.baseURL + JoclyPlazza.config.joclyPath + "/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 = $(" ").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();
}
}
})();