diff --git a/analysis/server-replay/main.js b/analysis/server-replay/main.js index 9cb84d8..9e974cc 100644 --- a/analysis/server-replay/main.js +++ b/analysis/server-replay/main.js @@ -1,5 +1,7 @@ "use strict"; +require('app-module-path').addPath(__dirname + '/../../server/lib/'); + function pad(n, width, z) { z = z || '0'; n = n + ''; @@ -8,15 +10,28 @@ function pad(n, width, z) { let fs = require('fs'); let THREE = require('./three.js'); +let L3D = require('../../static/js/l3d.min.js'); +let Serial = require('Serial'); +let XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest; +// Start let width = Math.floor(1134); let height = Math.floor(768); +let imageNumber = 0; +let info = {}; +let progLoader; +let modelMap; +let smallMap; +let smallModel; +let triangleMeshes = []; let renderer = new THREE.CanvasRenderer(); renderer.domElement.style = renderer.domElement; renderer.setSize(width,height); renderer.setClearColor(0x000000); +let id = process.argv[2] === undefined ? 56 : parseInt(process.argv[2]); + let scene = new THREE.Scene(); let camera = new THREE.PerspectiveCamera(75, width / height, 1, 10000); @@ -30,15 +45,153 @@ scene.add(mesh); scene.add(camera); let counter = 0; -for (let i = 0; i < 1; i += 0.005) { - mesh.rotation.x = i * 2 * Math.PI; - mesh.rotation.y = i * Math.PI; +init() - camera.lookAt(new THREE.Vector3()); +function init() { + let xhr = new XMLHttpRequest(); + xhr.open("GET", "http://localhost:4000/prototype/replay-info/" + id, true); - renderer.render(scene, camera); + xhr.onreadystatechange = function() { + if (xhr.readyState == 4 && xhr.status == 200) { - console.log(i); - fs.writeFileSync(__dirname + '/' + pad(counter++, 4) + '.png', renderer.domElement.toBuffer()); + let data = JSON.parse(xhr.responseText); + + console.log('sceneId = ' + data.sceneInfo.sceneId + ';'); + console.log('recoStyle = ' + data.sceneInfo.recommendationStyle + ';'); + console.log('expId = ' + id + ';'); + console.log(); + console.log('M = ['); + + camera = new L3D.ReplayCamera(50, width / height, 0.01, 100000, [], data, () => finished = true); + let path = initElements(camera, data.sceneInfo, data.redCoins); + + if (process.argv[3] === 'null') { + info.camera = null; + info.sort = false; + } else if (process.argv[3] === 'cull') { + info.camera = camera; + info.sort = false; + } else if (process.argv[3] === 'reco') { + info.camera = camera; + info.sort = true; + } + + progLoader = new L3D.ProgressiveLoader( + buildPathBigObj(path), scene, info.camera, null, function(a,b) { /* process.stderr.write((100*a/b) + '%\n'); */ }, false, info.sort + ); + + // Init variables + modelMap = JSON.parse(fs.readFileSync(buildPathMap(path), 'utf-8')); + let bigModel = Serial.loadFromFile(buildPathBigObjStatic(path)); + smallModel = Serial.loadFromFile(buildPathSmallObjStatic(path)); + + + // Build triangleMeshes + let material = new THREE.MeshFaceMaterial(); + + // For each small triangle + let counter = 0; + for (let key in modelMap) { + + let geometry = new THREE.Geometry(); + geometry.vertices = bigModel.children[0].geometry.vertices; + + for (let i = 0; i < modelMap[key].length; i++) { + let face = modelMap[key][i]; + let split = face.split('-').map(function(o) { return parseInt(o,10) - 1; }); + let face3 = new THREE.Face3(split[0], split[1], split[2]); + + face3.materialIndex = counter; + material.materials.push(new THREE.MeshBasicMaterial({color: counter, overdraw: true})); + + geometry.faces.push(face3); + + counter++; + } + + let name = key.split('-').map(function(o) { return parseInt(o,10)-1; }).join('-'); + triangleMeshes[name] = new THREE.Mesh(geometry, material); + scene.add(triangleMeshes[name]); + + } + + process.stderr.write('Loading complete.\n'); + + progLoader.onBeforeEmit = loop; + + progLoader.load(function() { + process.stderr.write("Loading complete\n"); + forceFinished = true; + }); + + scene.add(camera); + + camera.reset(); + camera.speed = 0.001; + camera.start(); + + } + }; + xhr.send(); } + +function buildPathSmallObj(path) { return '/static/data/' + path.folder + '/' + path.name + '.obj'; } +function buildPathBigObj(path) { return '/static/data/' + path.folder + '/' + path.name + '_sub' + '.obj'; } +function buildPathSmallObjStatic(path) { return './models/' + path.name + '.json'; } +function buildPathBigObjStatic(path) { return './models/' + path.name + '_sub' + '.json'; } +function buildPathMap(path) { return './maps/' + path.name + '.json'; } + +function loop() { + + process.stderr.write(imageNumber + '\n'); + + camera.update(20); + + camera.look(); + + process.stderr.write('Rendering...\n'); + let lastTime = Date.now(); + renderer.render(scene, camera); + process.stderr.write('Renderered in ' + (Date.now() - lastTime) + '\n'); + + fs.writeFileSync(__dirname + '/img/' + pad(imageNumber++, 5) + '.png', renderer.domElement.toBuffer()); + +} + + +function initElements(camera, sceneInfo, redCoins) { + switch (sceneInfo.sceneId) { + case 1: + camera.resetElements = L3D.resetPeachElements(); + camera.cameras = L3D.createPeachRecommendations(width, height); + camera.speed = 0.001; + camera.coins = L3D.generateCoins(L3D.createPeachCoins(), redCoins); + return '/static/data/castle/princess peaches castle (outside)_sub.obj'; + case 2: + camera.resetElements = L3D.resetBobombElements(); + camera.cameras = L3D.createBobombRecommendations(width, height); + camera.speed = 0.005; + // camera.coins = L3D.generateCoins(L3D.createBobombCoins(), redCoins); + return {name: 'bobomb battlefeild', folder:'bobomb'}; + case 3: + camera.resetElements = L3D.resetMountainElements(); + camera.cameras = L3D.createMountainRecommendations(width, height); + camera.speed = 0.005; + // camera.coins = L3D.generateCoins(L3D.createMountainCoins(), redCoins); + return {name :'coocoolmountain', folder:'mountain'}; + case 4: + camera.resetElements = L3D.resetWhompElements(); + camera.cameras = L3D.createWhompRecommendations(width, height); + camera.speed = 0.002; + // camera.coins = L3D.generateCoins(L3D.createWhompCoins(), redCoins); + return {name:'Whomps Fortress', folder:'whomp'}; + default: + process.stderr.write('This sceneId doesn\'t exist\n'); + process.exit(-1); + + + } +} + + diff --git a/analysis/server-replay/three.js b/analysis/server-replay/three.js index 22e95f0..0b2bb2a 100644 --- a/analysis/server-replay/three.js +++ b/analysis/server-replay/three.js @@ -24,6 +24,9 @@ Canvas.Image.prototype.addEventListener = function(type, listener, useCapture) { this['on' + type] = listener; }; +Canvas.prototype.addEventListener = function(type, listener, useCapture) { + this['on' + type] = listener; +}; eval('(function(window, document) {' + src.toString('utf-8').replace('var THREE', 'var THREE = window.THREE') + '})(window, document);' @@ -1068,6 +1071,7 @@ THREE.CanvasRenderer = function ( parameters ) { _normal = new THREE.Vector3(), _normalViewMatrix = new THREE.Matrix3(); + _context.antialias = 'none'; // dash+gap fallbacks for Firefox and everything else if ( _context.setLineDash === undefined ) { @@ -1261,12 +1265,12 @@ THREE.CanvasRenderer = function ( parameters ) { this.render = function ( scene, camera ) { - if ( camera instanceof THREE.Camera === false ) { + // if ( camera instanceof THREE.Camera === false ) { - console.error( 'THREE.CanvasRenderer.render: camera is not an instance of THREE.Camera.' ); - return; + // console.error( 'THREE.CanvasRenderer.render: camera is not an instance of THREE.Camera.' ); + // return; - } + // } if ( this.autoClear === true ) this.clear(); diff --git a/js/l3d/src/cameras/ReplayCamera.js b/js/l3d/src/cameras/ReplayCamera.js index c53b049..d56e07d 100644 --- a/js/l3d/src/cameras/ReplayCamera.js +++ b/js/l3d/src/cameras/ReplayCamera.js @@ -275,9 +275,9 @@ L3D.ReplayCamera.prototype.toList = function() { camera.updateMatrix(); camera.updateMatrixWorld(); + camera.matrixWorldInverse.getInverse(camera.matrixWorld); + var frustum = new THREE.Frustum(); - var projScreenMatrix = new THREE.Matrix4(); - projScreenMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse); frustum.setFromMatrix(new THREE.Matrix4().multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse)); diff --git a/js/l3d/src/l3d.js b/js/l3d/src/l3d.js index 24f245b..db3083b 100644 --- a/js/l3d/src/l3d.js +++ b/js/l3d/src/l3d.js @@ -5,7 +5,7 @@ var L3D = {}; if (typeof module !== 'undefined' && module.exports) { - var THREE = require('three'); + var THREE = require('../../analysis/server-replay/three.js'); // three'); module.exports = L3D; } diff --git a/js/l3d/src/loaders/ProgressiveLoader.js b/js/l3d/src/loaders/ProgressiveLoader.js index 62b629d..fb05556 100644 --- a/js/l3d/src/loaders/ProgressiveLoader.js +++ b/js/l3d/src/loaders/ProgressiveLoader.js @@ -243,7 +243,6 @@ var ProgressiveLoader = function(path, scene, camera, callback, log, laggy, pref this.mapFace = {}; this.prefetch = prefetch === undefined ? true : (!!prefetch); - console.log(this.prefetch); }; diff --git a/js/l3d/src/scenes/initScene.js b/js/l3d/src/scenes/initScene.js index c04d44f..96561fb 100644 --- a/js/l3d/src/scenes/initScene.js +++ b/js/l3d/src/scenes/initScene.js @@ -169,7 +169,7 @@ L3D.initBobombScene = function(scene, collidableObjects, recommendation, clickab var loader = new L3D.ProgressiveLoader( '/static/data/bobomb/bobomb battlefeild.obj', scene, - null, + recommendation, function(object) { if (clickable !== undefined) clickable.push(object); diff --git a/server/geo/MeshStreamer.js b/server/geo/MeshStreamer.js index b155a94..dca4174 100644 --- a/server/geo/MeshStreamer.js +++ b/server/geo/MeshStreamer.js @@ -159,7 +159,7 @@ geo.MeshStreamer = function(path) { * Number of element to send by packet * @type {Number} */ - this.chunk = 5000; + this.chunk = 1250; this.previousReco = 0; @@ -203,24 +203,24 @@ geo.MeshStreamer.prototype.faceComparator = function(camera) { var self = this; - var direction = { - x: camera.target.x - camera.position.x, - y: camera.target.y - camera.position.y, - z: camera.target.z - camera.position.z - }; + // var direction = { + // x: camera.target.x - camera.position.x, + // y: camera.target.y - camera.position.y, + // z: camera.target.z - camera.position.z + // }; - var norm = Math.sqrt(direction.x * direction.x + direction.y * direction.y + direction.z * direction.z); + // var norm = Math.sqrt(direction.x * direction.x + direction.y * direction.y + direction.z * direction.z); - direction.x /= norm; - direction.y /= norm; - direction.z /= norm; + // direction.x /= norm; + // direction.y /= norm; + // direction.z /= norm; return function(face1, face2) { var center1 = { - x: (self.mesh.vertices[face1.a].x + self.mesh.vertices[face1.b].x + self.mesh.vertices[face1.b].x) / 3, - y: (self.mesh.vertices[face1.a].y + self.mesh.vertices[face1.b].y + self.mesh.vertices[face1.b].y) / 3, - z: (self.mesh.vertices[face1.a].z + self.mesh.vertices[face1.b].z + self.mesh.vertices[face1.b].z) / 3 + x: (self.mesh.vertices[face1.a].x + self.mesh.vertices[face1.b].x + self.mesh.vertices[face1.c].x) / 3, + y: (self.mesh.vertices[face1.a].y + self.mesh.vertices[face1.b].y + self.mesh.vertices[face1.c].y) / 3, + z: (self.mesh.vertices[face1.a].z + self.mesh.vertices[face1.b].z + self.mesh.vertices[face1.c].z) / 3 }; @@ -230,18 +230,18 @@ geo.MeshStreamer.prototype.faceComparator = function(camera) { z: center1.z - camera.position.z }; - var norm1 = Math.sqrt(dir1.x * dir1.x + dir1.y * dir1.y + dir1.z + dir1.z); + // var norm1 = Math.sqrt(dir1.x * dir1.x + dir1.y * dir1.y + dir1.z + dir1.z); - dir1.x /= norm1; - dir1.y /= norm1; - dir1.z /= norm1; + // dir1.x /= norm1; + // dir1.y /= norm1; + // dir1.z /= norm1; - var dot1 = direction.x * dir1.x + direction.y * dir1.y + direction.z * dir1.z; + var dot1 = dir1.x * dir1.x + dir1.y * dir1.y + dir1.z * dir1.z; var center2 = { - x: (self.mesh.vertices[face2.a].x + self.mesh.vertices[face2.b].x + self.mesh.vertices[face2.b].x) / 3, - y: (self.mesh.vertices[face2.a].y + self.mesh.vertices[face2.b].y + self.mesh.vertices[face2.b].y) / 3, - z: (self.mesh.vertices[face2.a].z + self.mesh.vertices[face2.b].z + self.mesh.vertices[face2.b].z) / 3 + x: (self.mesh.vertices[face2.a].x + self.mesh.vertices[face2.b].x + self.mesh.vertices[face2.c].x) / 3, + y: (self.mesh.vertices[face2.a].y + self.mesh.vertices[face2.b].y + self.mesh.vertices[face2.c].y) / 3, + z: (self.mesh.vertices[face2.a].z + self.mesh.vertices[face2.b].z + self.mesh.vertices[face2.c].z) / 3 }; var dir2 = { @@ -250,19 +250,19 @@ geo.MeshStreamer.prototype.faceComparator = function(camera) { z: center2.z - camera.position.z }; - var norm2 = Math.sqrt(dir2.x * dir2.x + dir2.y * dir2.y + dir2.z + dir2.z); + // var norm2 = Math.sqrt(dir2.x * dir2.x + dir2.y * dir2.y + dir2.z + dir2.z); - dir2.x /= norm2; - dir2.y /= norm2; - dir2.z /= norm2; + // dir2.x /= norm2; + // dir2.y /= norm2; + // dir2.z /= norm2; - var dot2 = direction.x * dir2.x + direction.y * dir2.y + direction.z * dir2.z; + var dot2 = dir2.x * dir2.x + dir2.y * dir2.y + dir2.z * dir2.z; // Decreasing order - if (dot1 > dot2) { + if (dot1 < dot2) { return -1; } - if (dot1 < dot2) { + if (dot1 > dot2) { return 1; } return 0; @@ -457,7 +457,7 @@ geo.MeshStreamer.prototype.start = function(socket) { // console.log('Time to generate chunk : ' + (Date.now() - oldTime) + 'ms'); - if (self.prefetch && next.size < self.chunk) { + if (self.prefetch && next.size < self.chunk) { // Recompute config var newConfig = []; @@ -493,7 +493,7 @@ geo.MeshStreamer.prototype.start = function(socket) { } - if (next.data.length === 0) { + if (next.size < self.chunk) { // If nothing, just serve stuff var tmp = self.nextElements([ @@ -501,20 +501,24 @@ geo.MeshStreamer.prototype.start = function(socket) { // proportion: 1, // frustum: cameraFrustum // } - ]); - next.data = tmp.data; - next.size = tmp.size; + ], self.chunk - next.size); + + next.data.push.apply(next.data, tmp.data); + next.size += tmp.size; } // console.log('Chunk of size ' + next.size); + // console.log('Time to generate chunk : ' + (Date.now() - oldTime) + 'ms'); - socket.emit('elements', next.data); - - if (next.finished) { + if (next.data.length === 0) { socket.disconnect(); + } else { + + socket.emit('elements', next.data); + } }); @@ -567,6 +571,7 @@ geo.MeshStreamer.prototype.nextElements = function(config, chunk) { var data = []; var configSizes = []; + var buffers = []; var mightBeCompletetlyFinished = true; @@ -584,6 +589,7 @@ geo.MeshStreamer.prototype.nextElements = function(config, chunk) { for (var configIndex = 0; configIndex < config.length; configIndex++) { configSizes[configIndex] = 0; + buffers[configIndex] = []; } @@ -608,142 +614,160 @@ geo.MeshStreamer.prototype.nextElements = function(config, chunk) { var currentConfig = config[configIndex]; - if (configSizes[configIndex] < chunk * currentConfig.proportion) { + var display = false; + var exitToContinue = false; + var threeVertices = [vertex1, vertex2, vertex3]; - var display = false; - var exitToContinue = false; - var threeVertices = [vertex1, vertex2, vertex3]; + // Frustum culling + if (currentConfig.frustum === undefined || (isInFrustum(threeVertices, currentConfig.frustum.planes) && !this.isBackFace(currentConfig.frustum, currentFace))) { - // Frustum culling - if (currentConfig.frustum === undefined || (isInFrustum(threeVertices, currentConfig.frustum.planes) && !this.isBackFace(currentConfig.frustum, currentFace))) { - - // Send face - if (!this.vertices[currentFace.a]) { - - data.push(vertex1.toList()); - this.vertices[currentFace.a] = true; - configSizes[configIndex]++; - totalSize++; - - } - - if (!this.vertices[currentFace.b]) { - - data.push(vertex2.toList()); - this.vertices[currentFace.b] = true; - configSizes[configIndex]++; - totalSize++; - - } - - if (!this.vertices[currentFace.c]) { - - data.push(vertex3.toList()); - this.vertices[currentFace.c] = true; - configSizes[configIndex]++; - totalSize++; - - } - - 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; - configSizes[configIndex]++; - totalSize++; - - } - - if (normal2 !== undefined && !this.normals[currentFace.bNormal]) { - - data.push(normal2.toList()); - this.normals[currentFace.bNormal] = true; - configSizes[configIndex]++; - totalSize++; - - } - - if (normal3 !== undefined && !this.normals[currentFace.cNormal]) { - - data.push(normal3.toList()); - this.normals[currentFace.cNormal] = true; - configSizes[configIndex]++; - totalSize++; - - } - - 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; - configSizes[configIndex]++; - totalSize++; - - } - - if (tex2 !== undefined && !this.texCoords[currentFace.bTexture]) { - - data.push(tex2.toList()); - this.texCoords[currentFace.bTexture] = true; - configSizes[configIndex]++; - totalSize++; - - } - - if (tex3 !== undefined && !this.texCoords[currentFace.cTexture]) { - - data.push(tex3.toList()); - this.texCoords[currentFace.cTexture] = true; - configSizes[configIndex]++; - totalSize++; - - } - - data.push(currentFace.toList()); - // this.meshFaces[meshIndex] = this.meshFaces[meshIndex] || []; - this.faces[currentFace.index] = true; - configSizes[configIndex]+=3; - totalSize+=3; - // this.meshFaces[meshIndex].counter++; - // currentMesh.faceIndex++; - - // if (totalSize > chunk) { - - // // console.log(configIndex, sent/(chunk * currentConfig.proportion)); - // return {data: data, finsihed:false, configSizes: configSizes, size: totalSize}; - - // } - - // Loop on next face - continue faceloop; - - } + buffers[configIndex].push(currentFace); + continue faceloop; } - if (totalSize > chunk) { + } - // console.log(configIndex, sent/(chunk * currentConfig.proportion)); - return {data: data, finsihed:false, configSizes: configSizes, size: totalSize}; + } + + var totalSize = 0; + var configSize = 0; + + for (var configIndex = 0; configIndex < config.length; configIndex++) { + + // Sort buffer + if (config[configIndex].frustum !== undefined) { + buffers[configIndex].sort(this.faceComparator(config[configIndex].frustum)); + } else { + // console.log("Did not sort"); + } + + // Fill chunk + for(var i = 0; i < buffers[configIndex].length; i++) { + + var size = this.pushFace(buffers[configIndex][i], data); + + totalSize += size; + configSize += size; + + if (configSize > chunk * config[configIndex].proportion) { + + break; } } + if (totalSize > chunk) { + + // console.log(configIndex, sent/(chunk * currentConfig.proportion)); + return {data: data, finsihed:false, configSizes: configSizes, size: totalSize}; + + } + } return {data: data, finished: mightBeCompletetlyFinished, configSizes: configSizes, size:totalSize}; }; +geo.MeshStreamer.prototype.pushFace = function(face, buffer) { + + var totalSize = 0; + + var vertex1 = this.mesh.vertices[face.a]; + var vertex2 = this.mesh.vertices[face.b]; + var vertex3 = this.mesh.vertices[face.c]; + + // Send face + if (!this.vertices[face.a]) { + + buffer.push(vertex1.toList()); + this.vertices[face.a] = true; + totalSize++; + + } + + if (!this.vertices[face.b]) { + + buffer.push(vertex2.toList()); + this.vertices[face.b] = true; + totalSize++; + + } + + if (!this.vertices[face.c]) { + + buffer.push(vertex3.toList()); + this.vertices[face.c] = true; + totalSize++; + + } + + var normal1 = this.mesh.normals[face.aNormal]; + var normal2 = this.mesh.normals[face.bNormal]; + var normal3 = this.mesh.normals[face.cNormal]; + + if (normal1 !== undefined && !this.normals[face.aNormal]) { + + buffer.push(normal1.toList()); + this.normals[face.aNormal] = true; + totalSize++; + + } + + if (normal2 !== undefined && !this.normals[face.bNormal]) { + + buffer.push(normal2.toList()); + this.normals[face.bNormal] = true; + totalSize++; + + } + + if (normal3 !== undefined && !this.normals[face.cNormal]) { + + buffer.push(normal3.toList()); + this.normals[face.cNormal] = true; + totalSize++; + + } + + var tex1 = this.mesh.texCoords[face.aTexture]; + var tex2 = this.mesh.texCoords[face.bTexture]; + var tex3 = this.mesh.texCoords[face.cTexture]; + + if (tex1 !== undefined && !this.texCoords[face.aTexture]) { + + buffer.push(tex1.toList()); + this.texCoords[face.aTexture] = true; + totalSize++; + + } + + if (tex2 !== undefined && !this.texCoords[face.bTexture]) { + + buffer.push(tex2.toList()); + this.texCoords[face.bTexture] = true; + totalSize++; + + } + + if (tex3 !== undefined && !this.texCoords[face.cTexture]) { + + buffer.push(tex3.toList()); + this.texCoords[face.cTexture] = true; + totalSize++; + + } + + buffer.push(face.toList()); + // this.meshFaces[meshIndex] = this.meshFaces[meshIndex] || []; + this.faces[face.index] = true; + totalSize+=3; + + return totalSize; +}; + geo.MeshStreamer.prototype.isFinished = function(i) { return this.meshFaces[i].counter === this.meshFaces[i].array.length; diff --git a/server/lib/Serial.js b/server/lib/Serial.js index ba350ff..b481e4d 100644 --- a/server/lib/Serial.js +++ b/server/lib/Serial.js @@ -1,7 +1,7 @@ "use strict"; let fs = require('fs'); -let THREE = require('three'); +let THREE = require('../../analysis/server-replay/three.js'); let L3D = require('../../static/js/l3d.min.js'); function serialize(object) {