3437 lines
103 KiB
JavaScript
3437 lines
103 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);
|
|
|
|
$(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=$("<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();
|
|
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<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;
|
|
if(typeof JoclyHub!="undefined")
|
|
path=JoclyHub.hubPath+"/lib";
|
|
else if(typeof JoclyPlazza!="undefined")
|
|
path=JoclyPlazza.config.baseURL+JoclyPlazza.config.joclyPath+"/ccv";
|
|
else
|
|
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);
|
|
var areaHeight = Math.min(this.mGeometry.width / this.mViewOptions.preferredRatio, 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.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.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 ]);
|
|
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.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=typeof JoclyPlazza!="undefined" && JoclyPlazza.config.show3DStats;
|
|
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
|
|
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 = $("<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();
|
|
}
|
|
}
|
|
|
|
})();
|