From 844982e552cb790e32be2aa81d1450f366eef8ce Mon Sep 17 00:00:00 2001 From: Thomas FORGIONE Date: Mon, 16 Nov 2015 09:55:09 +0100 Subject: [PATCH] Should work but seems slow --- analysis/replay/main.js | 1 - js/l3d/src/cameras/ReplayCamera.js | 74 ++- js/l3d/src/loaders/ProgressiveLoader.js | 24 +- .../recommendations/ArrowRecommendation.js | 2 +- .../src/recommendations/BaseRecommendation.js | 2 +- js/l3d/src/scenes/initScene.js | 22 +- server/geo/MeshContainer.js | 6 +- server/geo/MeshStreamer.js | 439 +++++++++++------- server/lib/Serial.js | 2 +- 9 files changed, 385 insertions(+), 187 deletions(-) diff --git a/analysis/replay/main.js b/analysis/replay/main.js index a03ccb0..2679756 100644 --- a/analysis/replay/main.js +++ b/analysis/replay/main.js @@ -7,7 +7,6 @@ let height = Math.floor(768 /10); let XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest; let THREE = require('three'); let L3D = require('../../static/js/l3d.min.js'); -let math = require('mathjs'); let fs = require('fs'); let scene = new THREE.Scene(); diff --git a/js/l3d/src/cameras/ReplayCamera.js b/js/l3d/src/cameras/ReplayCamera.js index 3f19a02..c53b049 100644 --- a/js/l3d/src/cameras/ReplayCamera.js +++ b/js/l3d/src/cameras/ReplayCamera.js @@ -22,6 +22,10 @@ L3D.ReplayCamera = function() { this.theta = Math.PI; this.phi = Math.PI; + this.shouldRecover = false; + + this.recommendationClicked = null; + }; L3D.ReplayCamera.prototype = Object.create(THREE.PerspectiveCamera.prototype); L3D.ReplayCamera.prototype.constructor = L3D.ReplayCamera; @@ -71,10 +75,17 @@ L3D.ReplayCamera.prototype.cameraMotion = function(time) { var tmp = L3D.Tools.sum(L3D.Tools.mul(this.oldPosition, 1-this.t), L3D.Tools.mul(this.newPosition, this.t)); this.position.copy(tmp); this.target = L3D.Tools.sum(L3D.Tools.mul(this.oldTarget, 1-this.t), L3D.Tools.mul(this.newTarget, this.t)); - this.t += 1 / (((new Date(this.path[this.counter].time)).getTime() - (new Date(this.path[this.counter-1].time)).getTime()) / 20); + this.t += this.recovering ? 0.01 : 1 / (((new Date(this.path[this.counter].time)).getTime() - (new Date(this.path[this.counter-1].time)).getTime()) / 20); if (this.t > 1) { - this.nextEvent(); + this.recommendationClicked = null; + if (typeof this.recoverCallback === 'function') { + this.recoverCallback(); + this.recoverCallback = null; + } else { + this.nextEvent(); + } + } }; @@ -87,11 +98,15 @@ L3D.ReplayCamera.prototype.hermiteMotion = function(time) { this.t += 0.01 * time / 20; if (this.t > 1) { + this.recommendationClicked = null; this.nextEvent(); } }; L3D.ReplayCamera.prototype.nextEvent = function() { + + var self = this; + this.counter++; // Finished @@ -124,7 +139,22 @@ L3D.ReplayCamera.prototype.nextEvent = function() { // },500); // })(this); } else if (this.event.type == 'arrow') { - this.moveHermite(this.cameras[this.event.id]); + if (this.shouldRecover) { + (function(self, tmp) { + self.event.type = 'camera'; + self.recovering = true; + self.move({ + position: self.position.clone(), + target: self.cameras[self.event.id].camera.position.clone() + }, function() { + self.recovering = false; + self.event.type = 'arrow'; + self.moveReco(tmp); + }); + })(this, this.event.id); + } else { + this.moveReco(this.event.id); + } } else if (this.event.type == 'reset') { this.reset(); this.nextEvent(); @@ -176,7 +206,13 @@ L3D.ReplayCamera.prototype.anglesFromVectors = function() { this.theta = Math.atan2(forward.x, forward.z); }; -L3D.ReplayCamera.prototype.move = function(recommendation) { +L3D.ReplayCamera.prototype.move = function(recommendation, callback) { + + if (typeof callback === 'function') { + + this.recoverCallback = callback; + + } var otherCamera = recommendation.camera || recommendation; @@ -185,12 +221,17 @@ L3D.ReplayCamera.prototype.move = function(recommendation) { this.oldPosition = this.position.clone(); this.newTarget = new THREE.Vector3(otherCamera.target.x, otherCamera.target.y, otherCamera.target.z); this.newPosition = new THREE.Vector3(otherCamera.position.x, otherCamera.position.y, otherCamera.position.z); + this.t = 0; }; L3D.ReplayCamera.prototype.moveHermite = function(recommendation) { + if (this.shouldRecover === false) { + this.shouldRecover = true; + } + var otherCamera = recommendation.camera || recommendation; this.movingHermite = true; @@ -209,6 +250,14 @@ L3D.ReplayCamera.prototype.moveHermite = function(recommendation) { ); }; +L3D.ReplayCamera.prototype.moveReco = function(recommendationId) { + + this.recommendationClicked = this.cameras[recommendationId].camera; + + this.moveHermite(this.cameras[recommendationId]); + +}; + L3D.ReplayCamera.prototype.save = function() {}; /** @@ -220,18 +269,23 @@ L3D.ReplayCamera.prototype.save = function() {}; * */ L3D.ReplayCamera.prototype.toList = function() { - this.updateMatrix(); - this.updateMatrixWorld(); + + var camera = (this.recommendationClicked === null ? this : this.recommendationClicked); + + camera.updateMatrix(); + camera.updateMatrixWorld(); var frustum = new THREE.Frustum(); var projScreenMatrix = new THREE.Matrix4(); - projScreenMatrix.multiplyMatrices(this.projectionMatrix, this.matrixWorldInverse); + projScreenMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse); - frustum.setFromMatrix(new THREE.Matrix4().multiplyMatrices(this.projectionMatrix, this.matrixWorldInverse)); + frustum.setFromMatrix(new THREE.Matrix4().multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse)); var ret = - [[this.position.x, this.position.y, this.position.z], - [this.target.x, this.target.y, this.target.z]]; + [[camera.position.x, camera.position.y, camera.position.z], + [camera.target.x, camera.target.y, camera.target.z], + this.recommendationClicked !== null + ]; for (var i = 0; i < frustum.planes.length; i++) { diff --git a/js/l3d/src/loaders/ProgressiveLoader.js b/js/l3d/src/loaders/ProgressiveLoader.js index 9976728..f599276 100644 --- a/js/l3d/src/loaders/ProgressiveLoader.js +++ b/js/l3d/src/loaders/ProgressiveLoader.js @@ -90,6 +90,8 @@ var _parseList = function(arr) { */ var ProgressiveLoader = function(path, scene, camera, callback, log, laggy) { + var self = this; + /** * Path to the .obj file * @type {string} @@ -184,6 +186,12 @@ var ProgressiveLoader = function(path, scene, camera, callback, log, laggy) { */ this.camera = camera; + this.camera._moveReco = this.camera.moveReco; + + this.camera.moveReco = function(param) { + self.socket.emit('reco', param); + self.camera._moveReco.apply(self.camera, arguments); + }; /** * Number of total elements for loading @@ -210,6 +218,14 @@ var ProgressiveLoader = function(path, scene, camera, callback, log, laggy) { */ this.log = log; + this.mapFace = {}; + +}; + +ProgressiveLoader.prototype.hasFace = function(face) { + + return this.mapFace[(face.a) + '-' + (face.b) + '-' + (face.c)] === true; + }; /** @@ -376,6 +392,8 @@ ProgressiveLoader.prototype.initIOCallbacks = function() { } + self.mapFace[elt.a + '-' + elt.b + '-' + elt.c] = true; + } else if (elt.type === 'global') { @@ -386,6 +404,10 @@ ProgressiveLoader.prototype.initIOCallbacks = function() { } + if (typeof self.onBeforeEmit === 'function') { + self.onBeforeEmit(); + } + // Ask for next elements if (!self.laggy) { self.socket.emit('next', self.getCamera()); @@ -396,7 +418,7 @@ ProgressiveLoader.prototype.initIOCallbacks = function() { this.socket.on('disconnect', function() { if (typeof self.log === 'function') - self.log(self.numberOfFacesReceived, self.numberOfFaces); + self.log(self.numberOfFaces, self.numberOfFaces); self.finished = true; if (typeof L3D.ProgressiveLoader.onFinished === 'function') { diff --git a/js/l3d/src/recommendations/ArrowRecommendation.js b/js/l3d/src/recommendations/ArrowRecommendation.js index 6ec4ea3..0dff861 100644 --- a/js/l3d/src/recommendations/ArrowRecommendation.js +++ b/js/l3d/src/recommendations/ArrowRecommendation.js @@ -7,7 +7,7 @@ */ L3D.ArrowRecommendation = function(arg1, arg2, arg3, arg4, position, target) { - L3D.BaseRecommendation.apply(this); + L3D.BaseRecommendation.apply(this, arguments); /** * @type {L3D.FixedCamera} diff --git a/js/l3d/src/recommendations/BaseRecommendation.js b/js/l3d/src/recommendations/BaseRecommendation.js index 03e423a..e50fdfe 100644 --- a/js/l3d/src/recommendations/BaseRecommendation.js +++ b/js/l3d/src/recommendations/BaseRecommendation.js @@ -5,7 +5,7 @@ */ L3D.BaseRecommendation = function() { - THREE.Object3D.apply(this); + THREE.Object3D.apply(this, arguments); this.camera = new THREE.PerspectiveCamera(); this.camera.position.copy(arguments[4]); diff --git a/js/l3d/src/scenes/initScene.js b/js/l3d/src/scenes/initScene.js index 62879a8..8e3c34f 100644 --- a/js/l3d/src/scenes/initScene.js +++ b/js/l3d/src/scenes/initScene.js @@ -712,11 +712,27 @@ L3D.initSponzaScene = function(scene, collidableObjects, recommendation, clickab }; -L3D.createSponzaCoins = function() { - return []; +L3D.createSponzaRecommendations = function(width, height) { + var createRecommendation = function(position, target) { + return new Recommendation( + 50, + width / height, + 1, + 100000, + position, + target + ); + }; + + return [ + createRecommendation( + new THREE.Vector3(97.36225946503932,10.925697484337014,12.852363038244272), + new THREE.Vector3(133.315101552449,18.576354168001703,-2.9229530646577633) + ) + ]; }; -L3D.createSponzaRecommendations = function() { +L3D.createSponzaCoins = function() { return []; }; diff --git a/server/geo/MeshContainer.js b/server/geo/MeshContainer.js index 0cb1a9d..35e9360 100644 --- a/server/geo/MeshContainer.js +++ b/server/geo/MeshContainer.js @@ -316,7 +316,8 @@ var availableMeshNames = { }, '/static/data/sponza/sponza.obj': { - done: false + done: false, + recommendations : L3D.createSponzaRecommendations(1134,768) } }; @@ -361,7 +362,8 @@ function pushMesh(name) { geo.availableMeshes[name].recommendations.push({ position: reco.position, - frustum: frustum.planes + target: reco.target, + planes: frustum.planes }); } diff --git a/server/geo/MeshStreamer.js b/server/geo/MeshStreamer.js index 2ffe8f4..0a46236 100644 --- a/server/geo/MeshStreamer.js +++ b/server/geo/MeshStreamer.js @@ -1,6 +1,19 @@ +var fs = require('fs'); var THREE = require('three'); var L3D = require('../../static/js/l3d.min.js'); +try +{ + var predictionTables = [ + JSON.parse(fs.readFileSync('./mat1.json')), + JSON.parse(fs.readFileSync('./mat2.json')), + JSON.parse(fs.readFileSync('./mat3.json')) + ]; +} catch (e) { + process.stderr.write('No prefetching will be done !'); + predictionTables = []; +} + function isInFrustum(element, planes) { if (element instanceof Array) { @@ -143,7 +156,9 @@ geo.MeshStreamer = function(path) { * Number of element to send by packet * @type {Number} */ - this.chunk = 1000; + this.chunk = 500; + + this.previousReco = 0; if (path !== undefined) { @@ -155,7 +170,18 @@ geo.MeshStreamer = function(path) { geo.MeshStreamer.prototype.isBackFace = function(camera, face) { - var directionCamera = L3D.Tools.diff(camera.target, camera.position); + var directionCamera = L3D.Tools.diff( + L3D.Tools.mul( + L3D.Tools.sum( + L3D.Tools.sum( + this.mesh.vertices[face.a], + this.mesh.vertices[face.b] + ), + this.mesh.vertices[face.c] + ), + 1/3), + camera.position + ); var v1 = L3D.Tools.diff(this.mesh.vertices[face.b], this.mesh.vertices[face.a]); var v2 = L3D.Tools.diff(this.mesh.vertices[face.c], this.mesh.vertices[face.a]); @@ -260,6 +286,21 @@ geo.MeshStreamer.prototype.start = function(socket) { self.mesh = geo.availableMeshes[path]; + switch (path) { + case '/static/data/bobomb/bobomb battlefeild.obj': + case '/static/data/bobomb/bobomb battlefeild_sub.obj': + self.predictionTable = predictionTables[0]; + break; + case '/static/data/mountain/coocoolmountain.obj': + case '/static/data/mountain/coocoolmountain_sub.obj': + self.predictionTable = predictionTables[1]; + break; + case '/static/data/whomp/Whomps Fortress.obj': + case '/static/data/whomp/Whomps Fortress_sub.obj': + self.predictionTable = predictionTables[2]; + break; + }; + if (self.mesh === undefined) { process.stderr.write('Wrong path for model : ' + path); socket.emit('refused'); @@ -290,16 +331,102 @@ geo.MeshStreamer.prototype.start = function(socket) { }); - socket.on('next', function(camera) { + socket.on('reco', function(recoId) { + self.previousReco = recoId + 1; + + }); + + socket.on('next', function(_camera) { + + var cameraFrustum = {}; + + // Clean camera attribute + if (_camera !== null) { + + cameraFrustum = { + position: { + x: _camera[0][0], + y: _camera[0][1], + z: _camera[0][2] + }, + target: { + x: _camera[1][0], + y: _camera[1][1], + z: _camera[1][2] + }, + planes: [] + }; + + var shouldPrefetch = _camera[2]; + + for (i = 3; i < _camera.length; i++) { + + cameraFrustum.planes.push({ + normal: { + x: _camera[i][0], + y: _camera[i][1], + z: _camera[i][2] + }, + constant: _camera[i][3] + }); + + } + + } + + // Find best recommendation + var bestReco; + var bestScore = -Infinity; + var bestIndex = null; + + + if (self.predictionTable !== undefined) { + + for (var i = 0; i < self.mesh.recommendations.length; i++) { + + if (self.predictionTable[self.previousReco][i+1] > bestScore) { + + bestReco = self.mesh.recommendations[i]; + bestScore = self.predictionTable[self.previousReco][i+1]; + bestIndex = i; + + } + + } + + } else { + + // For sponza + bestReco = self.mesh.recommendations[0]; + + } + + // Create config for proportions of chunks + var config = []; + + if (shouldPrefetch) { + + // Camera cull + config.push({ + proportion: 0.5, + frustum: cameraFrustum + }); + + config.push({ + proportion: 0.5, + frustum: bestReco + }); + + } // Send next elements - var next = self.nextElements(camera); + var next = self.nextElements(config); if (next.data.length === 0) { // If nothing, just serve stuff - var tmp = self.nextElements(camera, true); + var tmp = self.nextElements(); next.data = tmp.data; } @@ -352,59 +479,10 @@ geo.MeshStreamer.prototype.nextMaterials = function() { * only interesting parts according to the camera * @returns {array} an array of elements ready to send */ -geo.MeshStreamer.prototype.nextElements = function(_camera, force) { +geo.MeshStreamer.prototype.nextElements = function(config) { var i; - if (force === undefined) { - - force = false; - - } - - // Prepare camera (and scale to model) - var camera = null; - var planes = []; - var direction; - - if (_camera !== null) { - - camera = { - position: { - x: _camera[0][0], - y: _camera[0][1], - z: _camera[0][2] - }, - target: { - x: _camera[1][0], - y: _camera[1][1], - z: _camera[1][2] - } - }; - - for (i = 2; i < _camera.length; i++) { - - planes.push({ - normal: { - x: _camera[i][0], - y: _camera[i][1], - z: _camera[i][2] - }, - constant: _camera[i][3] - }); - - } - - // Compute camera direction - direction = { - x: camera.target.x - camera.position.x, - y: camera.target.y - camera.position.y, - z: camera.target.z - camera.position.z - }; - - } - - var sent = 0; var data = []; var mightBeCompletetlyFinished = true; @@ -413,133 +491,160 @@ geo.MeshStreamer.prototype.nextElements = function(_camera, force) { // if (camera != null) // this.mesh.faces.sort(this.faceComparator(camera)); - for (var faceIndex = 0; faceIndex < this.mesh.faces.length; faceIndex++) { + if (config === undefined) { config = [] } - var currentFace = this.mesh.faces[faceIndex]; + configloop: + for (var configIndex = 0; configIndex < config.length + 1; configIndex++) { - if (this.faces[currentFace.index] === true) { - - continue; + var sent = 0; + // config[0] should always be cameraFrustum, with eventually 0 proportion + var currentConfig = configIndex !== config.length ? config[configIndex] : config[0]; + if (currentConfig === undefined) { + currentConfig = { + proportion : Infinity, + } } - mightBeCompletetlyFinished = false; + for (var faceIndex = 0; faceIndex < this.mesh.faces.length; faceIndex++) { - var vertex1 = this.mesh.vertices[currentFace.a]; - var vertex2 = this.mesh.vertices[currentFace.b]; - var vertex3 = this.mesh.vertices[currentFace.c]; + var currentFace = this.mesh.faces[faceIndex]; - if (!force && camera !== null) { + if (this.faces[currentFace.index] === true) { - var display = false; - var exitToContinue = false; - threeVertices = [vertex1, vertex2, vertex3]; - - // Frustum culling - if (!isInFrustum(threeVertices, planes)) { continue; + } - // Backface culling - if (this.isBackFace(camera, currentFace)) { - continue; + mightBeCompletetlyFinished = false; + + var vertex1 = this.mesh.vertices[currentFace.a]; + var vertex2 = this.mesh.vertices[currentFace.b]; + var vertex3 = this.mesh.vertices[currentFace.c]; + + if (currentConfig.frustum !== undefined) { + + var display = false; + var exitToContinue = false; + threeVertices = [vertex1, vertex2, vertex3]; + + // Frustum culling + if (!isInFrustum(threeVertices, currentConfig.frustum.planes)) { + continue; + } + + // Backface culling + if (this.isBackFace(currentConfig.frustum, currentFace)) { + continue; + } + + } + + if (!this.vertices[currentFace.a]) { + + data.push(vertex1.toList()); + this.vertices[currentFace.a] = true; + sent++; + + } + + if (!this.vertices[currentFace.b]) { + + data.push(vertex2.toList()); + this.vertices[currentFace.b] = true; + sent++; + + } + + if (!this.vertices[currentFace.c]) { + + data.push(vertex3.toList()); + this.vertices[currentFace.c] = true; + sent++; + + } + + var normal1 = this.mesh.normals[currentFace.aNormal]; + var normal2 = this.mesh.normals[currentFace.bNormal]; + var normal3 = this.mesh.normals[currentFace.cNormal]; + + if (normal1 !== undefined && !this.normals[currentFace.aNormal]) { + + data.push(normal1.toList()); + this.normals[currentFace.aNormal] = true; + sent++; + + } + + if (normal2 !== undefined && !this.normals[currentFace.bNormal]) { + + data.push(normal2.toList()); + this.normals[currentFace.bNormal] = true; + sent++; + + } + + if (normal3 !== undefined && !this.normals[currentFace.cNormal]) { + + data.push(normal3.toList()); + this.normals[currentFace.cNormal] = true; + sent++; + + } + + var tex1 = this.mesh.texCoords[currentFace.aTexture]; + var tex2 = this.mesh.texCoords[currentFace.bTexture]; + var tex3 = this.mesh.texCoords[currentFace.cTexture]; + + if (tex1 !== undefined && !this.texCoords[currentFace.aTexture]) { + + data.push(tex1.toList()); + this.texCoords[currentFace.aTexture] = true; + sent++; + + } + + if (tex2 !== undefined && !this.texCoords[currentFace.bTexture]) { + + data.push(tex2.toList()); + this.texCoords[currentFace.bTexture] = true; + sent++; + + } + + if (tex3 !== undefined && !this.texCoords[currentFace.cTexture]) { + + data.push(tex3.toList()); + this.texCoords[currentFace.cTexture] = true; + sent++; + + } + + data.push(currentFace.toList()); + // this.meshFaces[meshIndex] = this.meshFaces[meshIndex] || []; + this.faces[currentFace.index] = true; + // this.meshFaces[meshIndex].counter++; + // currentMesh.faceIndex++; + + sent++; + + if (data.length > this.chunk) { + + // console.log(configIndex, sent/(this.chunk * currentConfig.proportion)); + return {data: data, finsihed:false}; + + } + + if (sent > this.chunk * currentConfig.proportion) { + + // console.log(configIndex, sent/(this.chunk * currentConfig.proportion)); + continue configloop; + } } - if (!this.vertices[currentFace.a]) { - - data.push(vertex1.toList()); - this.vertices[currentFace.a] = true; - sent++; - - } - - if (!this.vertices[currentFace.b]) { - - data.push(vertex2.toList()); - this.vertices[currentFace.b] = true; - sent++; - - } - - if (!this.vertices[currentFace.c]) { - - data.push(vertex3.toList()); - this.vertices[currentFace.c] = true; - sent++; - - } - - var normal1 = this.mesh.normals[currentFace.aNormal]; - var normal2 = this.mesh.normals[currentFace.bNormal]; - var normal3 = this.mesh.normals[currentFace.cNormal]; - - if (normal1 !== undefined && !this.normals[currentFace.aNormal]) { - - data.push(normal1.toList()); - this.normals[currentFace.aNormal] = true; - sent++; - - } - - if (normal2 !== undefined && !this.normals[currentFace.bNormal]) { - - data.push(normal2.toList()); - this.normals[currentFace.bNormal] = true; - sent++; - - } - - if (normal3 !== undefined && !this.normals[currentFace.cNormal]) { - - data.push(normal3.toList()); - this.normals[currentFace.cNormal] = true; - sent++; - - } - - var tex1 = this.mesh.texCoords[currentFace.aTexture]; - var tex2 = this.mesh.texCoords[currentFace.bTexture]; - var tex3 = this.mesh.texCoords[currentFace.cTexture]; - - if (tex1 !== undefined && !this.texCoords[currentFace.aTexture]) { - - data.push(tex1.toList()); - this.texCoords[currentFace.aTexture] = true; - sent++; - - } - - if (tex2 !== undefined && !this.texCoords[currentFace.bTexture]) { - - data.push(tex2.toList()); - this.texCoords[currentFace.bTexture] = true; - sent++; - - } - - if (tex3 !== undefined && !this.texCoords[currentFace.cTexture]) { - - data.push(tex3.toList()); - this.texCoords[currentFace.cTexture] = true; - sent++; - - } - - data.push(currentFace.toList()); - // this.meshFaces[meshIndex] = this.meshFaces[meshIndex] || []; - this.faces[currentFace.index] = true; - // this.meshFaces[meshIndex].counter++; - // currentMesh.faceIndex++; - - sent++; - - if (sent > this.chunk) { - - return {data: data, finished: false}; - - } + return {data: data, finished: mightBeCompletetlyFinished}; } return {data: data, finished: mightBeCompletetlyFinished}; diff --git a/server/lib/Serial.js b/server/lib/Serial.js index 5d642d7..ba350ff 100644 --- a/server/lib/Serial.js +++ b/server/lib/Serial.js @@ -2,7 +2,7 @@ let fs = require('fs'); let THREE = require('three'); -let L3D = require('../static/js/l3d.min.js'); +let L3D = require('../../static/js/l3d.min.js'); function serialize(object) {