Should work but seems slow

This commit is contained in:
Thomas FORGIONE 2015-11-16 09:55:09 +01:00
parent 5e0a6c3121
commit 844982e552
9 changed files with 385 additions and 187 deletions

View File

@ -7,7 +7,6 @@ let height = Math.floor(768 /10);
let XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest; let XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest;
let THREE = require('three'); let THREE = require('three');
let L3D = require('../../static/js/l3d.min.js'); let L3D = require('../../static/js/l3d.min.js');
let math = require('mathjs');
let fs = require('fs'); let fs = require('fs');
let scene = new THREE.Scene(); let scene = new THREE.Scene();

View File

@ -22,6 +22,10 @@ L3D.ReplayCamera = function() {
this.theta = Math.PI; this.theta = Math.PI;
this.phi = Math.PI; this.phi = Math.PI;
this.shouldRecover = false;
this.recommendationClicked = null;
}; };
L3D.ReplayCamera.prototype = Object.create(THREE.PerspectiveCamera.prototype); L3D.ReplayCamera.prototype = Object.create(THREE.PerspectiveCamera.prototype);
L3D.ReplayCamera.prototype.constructor = L3D.ReplayCamera; 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)); 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.position.copy(tmp);
this.target = L3D.Tools.sum(L3D.Tools.mul(this.oldTarget, 1-this.t), L3D.Tools.mul(this.newTarget, this.t)); 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) { 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; this.t += 0.01 * time / 20;
if (this.t > 1) { if (this.t > 1) {
this.recommendationClicked = null;
this.nextEvent(); this.nextEvent();
} }
}; };
L3D.ReplayCamera.prototype.nextEvent = function() { L3D.ReplayCamera.prototype.nextEvent = function() {
var self = this;
this.counter++; this.counter++;
// Finished // Finished
@ -124,7 +139,22 @@ L3D.ReplayCamera.prototype.nextEvent = function() {
// },500); // },500);
// })(this); // })(this);
} else if (this.event.type == 'arrow') { } 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') { } else if (this.event.type == 'reset') {
this.reset(); this.reset();
this.nextEvent(); this.nextEvent();
@ -176,7 +206,13 @@ L3D.ReplayCamera.prototype.anglesFromVectors = function() {
this.theta = Math.atan2(forward.x, forward.z); 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; var otherCamera = recommendation.camera || recommendation;
@ -185,12 +221,17 @@ L3D.ReplayCamera.prototype.move = function(recommendation) {
this.oldPosition = this.position.clone(); this.oldPosition = this.position.clone();
this.newTarget = new THREE.Vector3(otherCamera.target.x, otherCamera.target.y, otherCamera.target.z); 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.newPosition = new THREE.Vector3(otherCamera.position.x, otherCamera.position.y, otherCamera.position.z);
this.t = 0; this.t = 0;
}; };
L3D.ReplayCamera.prototype.moveHermite = function(recommendation) { L3D.ReplayCamera.prototype.moveHermite = function(recommendation) {
if (this.shouldRecover === false) {
this.shouldRecover = true;
}
var otherCamera = recommendation.camera || recommendation; var otherCamera = recommendation.camera || recommendation;
this.movingHermite = true; 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() {}; L3D.ReplayCamera.prototype.save = function() {};
/** /**
@ -220,18 +269,23 @@ L3D.ReplayCamera.prototype.save = function() {};
* </ol> * </ol>
*/ */
L3D.ReplayCamera.prototype.toList = 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 frustum = new THREE.Frustum();
var projScreenMatrix = new THREE.Matrix4(); 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 = var ret =
[[this.position.x, this.position.y, this.position.z], [[camera.position.x, camera.position.y, camera.position.z],
[this.target.x, this.target.y, this.target.z]]; [camera.target.x, camera.target.y, camera.target.z],
this.recommendationClicked !== null
];
for (var i = 0; i < frustum.planes.length; i++) { for (var i = 0; i < frustum.planes.length; i++) {

View File

@ -90,6 +90,8 @@ var _parseList = function(arr) {
*/ */
var ProgressiveLoader = function(path, scene, camera, callback, log, laggy) { var ProgressiveLoader = function(path, scene, camera, callback, log, laggy) {
var self = this;
/** /**
* Path to the .obj file * Path to the .obj file
* @type {string} * @type {string}
@ -184,6 +186,12 @@ var ProgressiveLoader = function(path, scene, camera, callback, log, laggy) {
*/ */
this.camera = camera; 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 * Number of total elements for loading
@ -210,6 +218,14 @@ var ProgressiveLoader = function(path, scene, camera, callback, log, laggy) {
*/ */
this.log = log; 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') { } else if (elt.type === 'global') {
@ -386,6 +404,10 @@ ProgressiveLoader.prototype.initIOCallbacks = function() {
} }
if (typeof self.onBeforeEmit === 'function') {
self.onBeforeEmit();
}
// Ask for next elements // Ask for next elements
if (!self.laggy) { if (!self.laggy) {
self.socket.emit('next', self.getCamera()); self.socket.emit('next', self.getCamera());
@ -396,7 +418,7 @@ ProgressiveLoader.prototype.initIOCallbacks = function() {
this.socket.on('disconnect', function() { this.socket.on('disconnect', function() {
if (typeof self.log === 'function') if (typeof self.log === 'function')
self.log(self.numberOfFacesReceived, self.numberOfFaces); self.log(self.numberOfFaces, self.numberOfFaces);
self.finished = true; self.finished = true;
if (typeof L3D.ProgressiveLoader.onFinished === 'function') { if (typeof L3D.ProgressiveLoader.onFinished === 'function') {

View File

@ -7,7 +7,7 @@
*/ */
L3D.ArrowRecommendation = function(arg1, arg2, arg3, arg4, position, target) { L3D.ArrowRecommendation = function(arg1, arg2, arg3, arg4, position, target) {
L3D.BaseRecommendation.apply(this); L3D.BaseRecommendation.apply(this, arguments);
/** /**
* @type {L3D.FixedCamera} * @type {L3D.FixedCamera}

View File

@ -5,7 +5,7 @@
*/ */
L3D.BaseRecommendation = function() { L3D.BaseRecommendation = function() {
THREE.Object3D.apply(this); THREE.Object3D.apply(this, arguments);
this.camera = new THREE.PerspectiveCamera(); this.camera = new THREE.PerspectiveCamera();
this.camera.position.copy(arguments[4]); this.camera.position.copy(arguments[4]);

View File

@ -712,11 +712,27 @@ L3D.initSponzaScene = function(scene, collidableObjects, recommendation, clickab
}; };
L3D.createSponzaCoins = function() { L3D.createSponzaRecommendations = function(width, height) {
return []; 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 []; return [];
}; };

View File

@ -316,7 +316,8 @@ var availableMeshNames = {
}, },
'/static/data/sponza/sponza.obj': { '/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({ geo.availableMeshes[name].recommendations.push({
position: reco.position, position: reco.position,
frustum: frustum.planes target: reco.target,
planes: frustum.planes
}); });
} }

View File

@ -1,6 +1,19 @@
var fs = require('fs');
var THREE = require('three'); var THREE = require('three');
var L3D = require('../../static/js/l3d.min.js'); 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) { function isInFrustum(element, planes) {
if (element instanceof Array) { if (element instanceof Array) {
@ -143,7 +156,9 @@ geo.MeshStreamer = function(path) {
* Number of element to send by packet * Number of element to send by packet
* @type {Number} * @type {Number}
*/ */
this.chunk = 1000; this.chunk = 500;
this.previousReco = 0;
if (path !== undefined) { if (path !== undefined) {
@ -155,7 +170,18 @@ geo.MeshStreamer = function(path) {
geo.MeshStreamer.prototype.isBackFace = function(camera, face) { 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 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]); 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]; 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) { if (self.mesh === undefined) {
process.stderr.write('Wrong path for model : ' + path); process.stderr.write('Wrong path for model : ' + path);
socket.emit('refused'); 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 // Send next elements
var next = self.nextElements(camera); var next = self.nextElements(config);
if (next.data.length === 0) { if (next.data.length === 0) {
// If nothing, just serve stuff // If nothing, just serve stuff
var tmp = self.nextElements(camera, true); var tmp = self.nextElements();
next.data = tmp.data; next.data = tmp.data;
} }
@ -352,59 +479,10 @@ geo.MeshStreamer.prototype.nextMaterials = function() {
* only interesting parts according to the camera * only interesting parts according to the camera
* @returns {array} an array of elements ready to send * @returns {array} an array of elements ready to send
*/ */
geo.MeshStreamer.prototype.nextElements = function(_camera, force) { geo.MeshStreamer.prototype.nextElements = function(config) {
var i; 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 data = [];
var mightBeCompletetlyFinished = true; var mightBeCompletetlyFinished = true;
@ -413,133 +491,160 @@ geo.MeshStreamer.prototype.nextElements = function(_camera, force) {
// if (camera != null) // if (camera != null)
// this.mesh.faces.sort(this.faceComparator(camera)); // 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) { var sent = 0;
continue;
// 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 currentFace = this.mesh.faces[faceIndex];
var vertex2 = this.mesh.vertices[currentFace.b];
var vertex3 = this.mesh.vertices[currentFace.c];
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; continue;
} }
// Backface culling mightBeCompletetlyFinished = false;
if (this.isBackFace(camera, currentFace)) {
continue; 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]) { return {data: data, finished: mightBeCompletetlyFinished};
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};

View File

@ -2,7 +2,7 @@
let fs = require('fs'); let fs = require('fs');
let THREE = require('three'); let THREE = require('three');
let L3D = require('../static/js/l3d.min.js'); let L3D = require('../../static/js/l3d.min.js');
function serialize(object) { function serialize(object) {