Changed stuff and added doc

This commit is contained in:
Thomas FORGIONE 2015-06-23 15:31:23 +02:00
parent 6242e6f122
commit 5e445a8c28
8 changed files with 475 additions and 35200 deletions

View File

@ -5,13 +5,12 @@ models written with Node.js.
It is hosted by [OpenShift](https://www.openshift.com/) at
[3dinterface.no-ip.org](http://3dinterface.no-ip.org).
## To run a local server
### Database setup
## Database setup
First you need to configure the database. You can create a postgres database
where you want, and you can initialize it by running the script
`sql/backup.pgsql`.
### Nodejs configuration
## Nodejs configuration
Then, you have to set up nodejs to access to your database. Basically, you have
to create a file `private.js` at the root of the repository looking like this :
@ -20,21 +19,21 @@ module.exports.url = ... // the url to connect to your database
module.exports.secret = ... // A secret string used to hash stuff
```
### Compiling the static js files
## Compiling the static js files
There are two ways to compile the static js files :
- either you don't mind not minifying the code, and you can use `compiler.sh` to compile
- or you absolutely want to minify the code, and you'll need
[closure-compiler](https://github.com/google/closure-compiler)
#### Compiling without minifying
### Compiling without minifying
To compile without minifying, simply run
```
make
```
in `static/js`.
#### Compiling and minifying
### Compiling and minifying
To compile and minify the js files, you have to run
```
make TYPE=RELEASE
@ -43,10 +42,10 @@ make TYPE=RELEASE
If it doesn't work, check in the `Makefile` that the path to `closure-compiler.jar`
is correct.
#### Check if it worked
### Check if it worked
If it worked, you should see lots of files in `*.min.js` in your `static/js` directory.
### Running the server
## Running the server
As usual with NodeJS, it's quite easy to test. Just ensure you have `node`
installed on your machine, clone this repo somewhere, and then, in the repo do

View File

@ -1,8 +1,14 @@
var fs = require('fs');
var mesh = require('./Mesh.js');
/**
* @namespace
*/
var geo = {};
/**
* @private
*/
function bisect(items, x, lo, hi) {
var mid;
if (typeof(lo) == 'undefined') lo = 0;
@ -15,10 +21,16 @@ function bisect(items, x, lo, hi) {
return lo;
}
/**
* @private
*/
function insort(items, x) {
items.splice(bisect(items, x), 0, x);
}
/**
* @private
*/
function partialSort(items, k, comparator) {
var smallest = items.slice(0, k).sort(),
max = smallest[k-1];
@ -35,18 +47,54 @@ function partialSort(items, k, comparator) {
return smallest;
}
/**
* A class that streams easily a mesh via socket.io
* @memberOf geo
* @constructor
* @param {string} path to the mesh
* @param {function} callback to execute when the mesh streamer is loaded
*/
geo.MeshStreamer = function(path, callback) {
// Different parts of a obj (a mesh per material)
/**
* array of each part of the mesh
* @type {mesh.Mesh[]}
*/
this.meshes = [];
// In meshes, vertices and texture coords are shared
/**
* array of the vertices of the meshes (all merged)
* @type {mesh.Vertex[]}
*/
this.vertices = [];
/**
* array of the faces of the meshes (all merged)
* @type {mesh.Face[]}
*/
this.faces = [];
/**
* array of the normals of the meshes (all merged)
* @type {mesh.Normal[]}
*/
this.normals = [];
/**
* array of the texture coordinates (all merged)
* @type {mesh.TexCoord[]}
*/
this.texCoords = [];
/**
* array of the faces in a nice order for sending
* @type {mesh.Face[]}
*/
this.orderedFaces = [];
// Chunk size
/**
* Number of element to send by packet
* @type {Number}
*/
this.chunk = 1000;
if (path !== undefined) {
@ -59,7 +107,11 @@ geo.MeshStreamer = function(path, callback) {
}
}
// Returns the function that compares two faces
/**
* Compute a function that can compare two faces
* @param {Camera} camera a camera seeing or not face
* @returns the function that compares two faces : the higher face is the most interesting for the camera
*/
geo.MeshStreamer.prototype.faceComparator = function(camera) {
var self = this;
@ -131,6 +183,11 @@ geo.MeshStreamer.prototype.faceComparator = function(camera) {
}
}
/**
* Loads a obj file
* @param {string} path the path to the file
* @param {function} callback the callback to call when the mesh is loaded
*/
geo.MeshStreamer.prototype.loadFromFile = function(path, callback) {
var self = this;
fs.readFile(path, function(err, data) {
@ -228,6 +285,10 @@ geo.MeshStreamer.prototype.loadFromFile = function(path, callback) {
});
}
/**
* Initialize the socket.io callback
* @param {socket} socket the socket to initialize
*/
geo.MeshStreamer.prototype.start = function(socket) {
this.meshIndex = 0;
@ -278,6 +339,10 @@ geo.MeshStreamer.prototype.start = function(socket) {
});
}
/**
* Prepare the array of materials
* @return array the array to send with all materials of the current mesh
*/
geo.MeshStreamer.prototype.nextMaterials = function() {
var data = [];
@ -302,6 +367,12 @@ geo.MeshStreamer.prototype.nextMaterials = function() {
}
/**
* Prepare the next elements
* @param {camera} _camera a camera that can be usefull to do smart streaming (stream
* only interesting parts according to the camera
* @returns {array} an array of elements ready to send
*/
geo.MeshStreamer.prototype.nextElements = function(_camera) {
// Prepare camera (and scale to model)

77
js/History.js Normal file
View File

@ -0,0 +1,77 @@
/**
* Represents the history of an object
* @constructor
*/
var History = function() {
/**
* Stores the different states of the object
* @type {Object[]}
*/
this.states = [];
/**
* Represents the position in the history we're at
* @type {Number}
*/
this.index = -1;
/**
* Represents the number of elements in the history
* @type {Number}
*/
this.size = 0;
}
/**
* Appends a new state at the end of the history
* @param {Object} state the state to append
*/
History.prototype.addState = function(state) {
++this.index;
this.size = this.index + 1;
this.states[this.size-1] = state;
}
/**
* Returns the previous state and change the index to the previous state (so you can redo)
*/
History.prototype.undo = function() {
if (this.undoable()) {
this.index--;
return this.currentState();
}
}
/**
* Returns the next state and change the index to the next state (so you can re-undo)
*/
History.prototype.redo = function() {
if (this.redoable()) {
this.index++;
return this.currentState();
}
}
/**
* Checks if there is a undo possibility
* @returns {Boolean} true if undo is possible, false otherwise
*/
History.prototype.undoable = function() {
return this.index > 0;
}
/**
* Checks if there is a redo possibility
* @returns {Boolean} true if redo is possible, false otherwise
*/
History.prototype.redoable = function() {
return this.index < this.size - 1;
}
/**
* Returns the current state in the history
* @returns {Object} the current state in the history
*/
History.prototype.currentState = function() {
return this.states[this.index];
}

View File

@ -70,6 +70,7 @@ StreamingSimulator:
PrototypeTools:
$(CLOSURE) $(OPT) \
--js History.js \
--js StaticPath.js \
--js Hermite.js \
--js Camera.js \

View File

@ -1,7 +1,15 @@
// class camera extends THREE.PerspectiveCamera
/**
* Represents a camera that can be used easily
* @constructor
* @augments THREE.PerspectiveCamera
*/
var PointerCamera = function() {
THREE.PerspectiveCamera.apply(this, arguments);
/**
* A reference to the renderer
* @type {THREE.Renderer}
*/
this.renderer = arguments[4];
if (arguments[5] === undefined)
@ -9,38 +17,118 @@ var PointerCamera = function() {
else
listenerTarget = arguments[5];
// Set Position
/**
* Theta angle of the camera
* @type {Number}
*/
this.theta = Math.PI;
/**
* Phi angle of the camera
* @type {Number}
*/
this.phi = Math.PI;
// this.keyboard = undefined;
/**
* Indicates if the camera is following a linear motion
* @type {Boolean}
*/
this.moving = false;
/**
* Indicates if the user is dragging the camera
* @type {Boolean}
*/
this.dragging = false;
/**
* Current position of the cursor
* @type {Object}
*/
this.mouse = {x: 0, y: 0};
/**
* Current movement of the cursor
* @type {Object}
*/
this.mouseMove = {x: 0, y: 0};
// Stuff for rendering
/**
* Current position of the camera (optical center)
* @type {THREE.Vector}
*/
this.position = new THREE.Vector3();
/**
* Current direction of the camera
* @type {THREE.Vector}
*/
this.forward = new THREE.Vector3();
/**
* Vector pointing to the left of the camera
* @type {THREE.Vector}
*/
this.left = new THREE.Vector3();
/**
* Point that the camera is targeting
* @type {THREE.Vector}
*/
this.target = new THREE.Vector3(0,1,0);
// Stuff for events
/**
* Indicates the different motions that the camera should have according to the keyboard events
* @type {Object}
* @description Contains the following booleans
* <ul>
* <li>increasePhi</li>
* <li>decreasePhi</li>
* <li>increaseTheta</li>
* <li>decreaseTheta</li>
* <li>boost</li>
* <li>moveForward</li>
* <li>moveBackward</li>
* <li>moveLeft</li>
* <li>moveRight</li>
* </ul>
*/
this.motion = {};
/**
* Sentitivity of the mouse
* @type {Number}
*/
this.sensitivity = 0.05;
/**
* Speed of the camera
* @type {Number}
*/
this.speed = 1;
// Raycaster for collisions
/**
* Raycaster used to compute collisions
* @type {THREE.Raycaster}
*/
this.raycaster = new THREE.Raycaster();
// Create history object
/**
* History of the moves of the camera
* @type {History}
*/
this.history = new History();
// Variable for lock pointer
/**
* Option to enable or disable the pointer lock
* @type {Boolean}
*/
this.shouldLock = true;
/**
* Current state of the pointer (locked or not)
* @type {Boolean}
*/
this.pointerLocked = false;
// Set events from the document
@ -66,15 +154,32 @@ var PointerCamera = function() {
// listenerTarget.addEventListener('mouseup', function() { console.log("mouseup");}, false);
listenerTarget.addEventListener('mouseout', onMouseUp, false);
/**
* Option to enable or disable the collisions
* @type {Boolean}
*/
this.collisions = true;
/**
* Is true when we should log the camera angles. It will be set to false
* once is done, and reset to true after a certain period of time
* @param {Boolean}
*/
this.shouldLogCameraAngles = true;
/**
* The camera we will move to when we'll reset the camera
* @param {Object}
*/
this.resetElements = resetBobombElements();
}
PointerCamera.prototype = Object.create(THREE.PerspectiveCamera.prototype);
PointerCamera.prototype.constructor = PointerCamera;
/**
* Locks the pointer inside the canvas, and displays a gun sight at the middle of the renderer
* This method works only if the browser supports requestPointerLock
*/
PointerCamera.prototype.lockPointer = function() {
if (this.shouldLock) {
@ -97,6 +202,9 @@ PointerCamera.prototype.lockPointer = function() {
}
/**
* Update the camera when the pointer lock changes state
*/
PointerCamera.prototype.onPointerLockChange = function() {
document.pointerLockElement =
@ -117,7 +225,10 @@ PointerCamera.prototype.onPointerLockChange = function() {
}
// Update function
/**
* Update the position of the camera
* @param {Number} time number of milliseconds between the previous and the next frame
*/
PointerCamera.prototype.update = function(time) {
if (this.moving) {
this.linearMotion(time);
@ -128,6 +239,10 @@ PointerCamera.prototype.update = function(time) {
}
}
/**
* Update the camera according to its linear motion
* @param {Number} time number of milliseconds between the previous and the next frame
*/
PointerCamera.prototype.linearMotion = function(time) {
var position_direction = Tools.diff(this.new_position, this.position);
var target_direction = Tools.diff(this.new_target, this.target);
@ -142,6 +257,10 @@ PointerCamera.prototype.linearMotion = function(time) {
}
}
/**
* Update the camera according to its hermite motion
* @param {Number} time number of milliseconds between the previous and the next frame
*/
PointerCamera.prototype.hermiteMotion = function(time) {
var eval = this.hermitePosition.eval(this.t);
this.position.x = eval.x;
@ -158,6 +277,10 @@ PointerCamera.prototype.hermiteMotion = function(time) {
}
}
/**
* Update the camera according to the user's input
* @param {Number} time number of milliseconds between the previous and the next frame
*/
PointerCamera.prototype.normalMotion = function(time) {
// Update angles
if (this.motion.increasePhi) {this.phi += this.sensitivity * time / 20; this.changed = true; }
@ -224,6 +347,9 @@ PointerCamera.prototype.normalMotion = function(time) {
this.target.add(forward);
}
/**
* Reset the camera to its resetElements, and finishes any motion
*/
PointerCamera.prototype.reset = function() {
this.resetPosition();
this.moving = false;
@ -231,12 +357,18 @@ PointerCamera.prototype.reset = function() {
(new BD.Event.ResetClicked()).send();
}
/**
* Reset the position of th camera
*/
PointerCamera.prototype.resetPosition = function() {
this.position.copy(this.resetElements.position);
this.target.copy(this.resetElements.target);
this.anglesFromVectors();
}
/**
* Computes the vectors (forward, left, ...) according to theta and phi
*/
PointerCamera.prototype.vectorsFromAngles = function() {
// Update direction
this.forward.y = Math.sin(this.phi);
@ -248,6 +380,9 @@ PointerCamera.prototype.vectorsFromAngles = function() {
}
/**
* Computes theta and phi according to the vectors (forward, left, ...)
*/
PointerCamera.prototype.anglesFromVectors = function() {
var forward = Tools.diff(this.target, this.position);
forward.normalize();
@ -259,6 +394,11 @@ PointerCamera.prototype.anglesFromVectors = function() {
this.theta = Math.atan2(forward.x, forward.z);
}
/**
* Creates a linear motion to another camera
* @param {Camera} camera Camera to move to
* @param {Boolean} [toSave=true] true if you want to save the current state of the camera
*/
PointerCamera.prototype.move = function(otherCamera, toSave) {
if (toSave === undefined)
toSave = true;
@ -281,6 +421,11 @@ PointerCamera.prototype.move = function(otherCamera, toSave) {
}
}
/**
* Creates a hermite motion to another camera
* @param {Camera} camera Camera to move to
* @param {Boolean} [toSave=true] true if you want to save the current state of the camera
*/
PointerCamera.prototype.moveHermite = function(otherCamera, toSave) {
if (toSave === undefined)
toSave = true;
@ -309,6 +454,11 @@ PointerCamera.prototype.moveHermite = function(otherCamera, toSave) {
}
}
/**
* Checks the collisions between the collidables objects and the camera
* @param {THREE.Vector3} direction the direction of the camera
* @returns {Boolean} true if there is a collision, false otherwise
*/
PointerCamera.prototype.isColliding = function(direction) {
this.raycaster.set(this.position, direction.clone().normalize());
var intersects = this.raycaster.intersectObjects(this.collidableObjects, true);
@ -323,15 +473,25 @@ PointerCamera.prototype.isColliding = function(direction) {
return false;
}
// Look function
/**
* Look method. Equivalent to gluLookAt for the current camera
*/
PointerCamera.prototype.look = function() {
this.lookAt(this.target);
}
/**
* Adds the camera to the scene
*/
PointerCamera.prototype.addToScene = function(scene) {
scene.add(this);
}
/**
* Manages keyboard events
* @param {event} event the event that happened
* @param {Booelean} toSet true if the key was pressed, false if released
*/
PointerCamera.prototype.onKeyEvent = function(event, toSet) {
// Create copy of state
var motionJsonCopy = JSON.stringify(this.motion);
@ -365,14 +525,26 @@ PointerCamera.prototype.onKeyEvent = function(event, toSet) {
}
}
/**
* Manages the key pressed events
* @param {event} event the event to manage
*/
PointerCamera.prototype.onKeyDown = function(event) {
this.onKeyEvent(event, true);
}
/**
* Manages the key released events
* @param {event} event the event to manage
*/
PointerCamera.prototype.onKeyUp = function(event) {
this.onKeyEvent(event, false);
}
/**
* Manages the mouse down events. Start drag'n'dropping if the options are set to drag'n'drop
* @param {event} event the event to manage
*/
PointerCamera.prototype.onMouseDown = function(event) {
if (!this.shouldLock) {
this.mouse.x = ( ( event.clientX - this.renderer.domElement.offsetLeft ) / this.renderer.domElement.width ) * 2 - 1;
@ -383,6 +555,10 @@ PointerCamera.prototype.onMouseDown = function(event) {
}
}
/**
* Manages the mouse move events. Modifies the target of the camera according to the drag'n'drop motion
* @param {event} event the event to manage
*/
PointerCamera.prototype.onMouseMove = function(event) {
if (!this.shouldLock && this.dragging) {
var mouse = {x: this.mouse.x, y: this.mouse.y};
@ -395,6 +571,10 @@ PointerCamera.prototype.onMouseMove = function(event) {
}
}
/**
* Manages the mouse move envent in case of pointer lock
* @param {event} event the event to manage
*/
PointerCamera.prototype.onMouseMovePointer = function(e) {
if (this.pointerLocked) {
@ -410,6 +590,10 @@ PointerCamera.prototype.onMouseMovePointer = function(e) {
}
/**
* Manages the mouse up event. Stops the dragging
* @param {event} event the event to manage
*/
PointerCamera.prototype.onMouseUp = function(event) {
this.onMouseMove(event);
@ -423,11 +607,17 @@ PointerCamera.prototype.onMouseUp = function(event) {
this.dragging = false;
}
/**
* Logs the camera to the terminal (pratical to create recommended views)
*/
PointerCamera.prototype.log = function() {
console.log("createCamera(\nnew THREE.Vector3(" + this.position.x + "," + this.position.y + ',' + this.position.z + '),\n'
+ "new THREE.Vector3(" + this.target.x + "," + this.target.y + ',' + this.target.z + ')\n)');
}
/**
* Save the current state of the camera in the history
*/
PointerCamera.prototype.save = function() {
var backup = {};
backup.position = this.position.clone();
@ -435,6 +625,9 @@ PointerCamera.prototype.save = function() {
this.history.addState(backup);
}
/**
* Undo last motion according to the history
*/
PointerCamera.prototype.undo = function() {
var move = this.history.undo();
if (move !== undefined) {
@ -447,6 +640,9 @@ PointerCamera.prototype.undo = function() {
}
}
/**
* Redo last motion according to the history
*/
PointerCamera.prototype.redo = function() {
var move = this.history.redo();
if (move !== undefined) {
@ -459,48 +655,19 @@ PointerCamera.prototype.redo = function() {
}
}
/**
* Checks if there is a undo possibility in the history
* @returns {Boolean} true if undo is possible, false otherwise
*/
PointerCamera.prototype.undoable = function() {
return this.history.undoable();
}
/**
* Checks if there is a redo possibility in the history
* @returns {Boolean} true if redo is possible, false otherwise
*/
PointerCamera.prototype.redoable = function() {
return this.history.redoable();
}
var History = function() {
this.states = new Array();
this.index = -1;
this.size = 0;
}
History.prototype.addState = function(state) {
++this.index;
this.size = this.index + 1;
this.states[this.size-1] = state;
}
History.prototype.undo = function() {
if (this.undoable()) {
this.index--;
return this.currentState();
}
}
History.prototype.redo = function() {
if (this.redoable()) {
this.index++;
return this.currentState();
}
}
History.prototype.undoable = function() {
return this.index > 0;
}
History.prototype.redoable = function() {
return this.index < this.size - 1;
}
History.prototype.currentState = function() {
return this.states[this.index];
}

View File

@ -1,3 +1,7 @@
/**
* Parse a list as it is sent by the server and gives a slightly more comprehensible result
* @private
*/
var _parseList2 = function(arr) {
var ret = {};
@ -64,37 +68,118 @@ var _parseList2 = function(arr) {
return ret;
}
/**
* Loads a mesh from socket.io
* @param {string} path path to the .obj file
* @param {THREE.Scene} scene to add the object
* @param {PointerCamera} camera the camera that will be sent to server for smart
* streaming (can be null, then the server will stream the mesh in the .obj
* order)
* @param {function} callback callback to call on the objects when they're created
* @constructor
*/
var ProgressiveLoaderGeometry = function(path, scene, camera, callback) {
// Init attributes
/**
* Path to the .obj file
* @type {string}
*/
this.objPath = path;
/**
* Path to the folder where the textures are
* @type {string}
*/
this.texturesPath = path.substring(0, path.lastIndexOf('/')) + '/';
/**
* Path to the .mtl file
* @type {string}
*/
this.mtlPath = path.replace('.obj', '.mtl');
/**
* Reference to the scene in which the object should be added
*/
this.scene = scene;
/**
* Callback to call on the object when they're created
*/
this.callback = callback;
/**
* Counter (not used)
* @private
*/
this.counter = 0;
/**
* Group where the sub-objects will be added
* @type {THREE.Object3D}
*/
this.obj = new THREE.Object3D();
scene.add(this.obj);
/**
* Array of the vertices of the mesh
* @type {THREE.Vector3[]}
*/
this.vertices = [];
/**
* Array of the texture coordinates of the mesh
* @type {THREE.Vector2[]}
*/
this.texCoords = [];
/**
* Array of the normal of the mesh
* @type {THREE.Vector3[]}
*/
this.normals = [];
/**
* Array of the UV mapping
* @description Each element is an array of 3 elements that are the indices
* of the element in <code>this.texCoords</code> that should be
* used as texture coordinates for the current vertex of the face
* @type {Number[][]}
*/
this.uvs = [];
/**
* Array of all the meshes that will be added to the main object
* @type {THREE.Mesh[]}
*/
this.meshes = [];
// Init MTLLoader
/**
* Loader for the material file
* @type {THREE.MTLLoader}
*/
this.loader = new THREE.MTLLoader(this.texturesPath);
// Init io stuff
/**
* Socket to connect to get the mesh
* @type {socket}
*/
this.socket = io();
this.initIOCallbacks();
/**
* Reference to the camera
* @type {PointerCamera}
*/
this.camera = camera;
}
/**
* Starts the loading of the mesh
*/
ProgressiveLoaderGeometry.prototype.load = function() {
var self = this;
@ -110,6 +195,9 @@ ProgressiveLoaderGeometry.prototype.load = function() {
});
}
/**
* Will return a list representation of the camera (to be sent to the server)
*/
ProgressiveLoaderGeometry.prototype.getCamera = function() {
if (this.camera === null)
return null;
@ -118,6 +206,9 @@ ProgressiveLoaderGeometry.prototype.getCamera = function() {
this.camera.target.x, this.camera.target.y, this.camera.target.z];
}
/**
* Initializes the socket.io functions so that it can discuss with the server
*/
ProgressiveLoaderGeometry.prototype.initIOCallbacks = function() {
var self = this;
@ -268,6 +359,9 @@ ProgressiveLoaderGeometry.prototype.initIOCallbacks = function() {
});
}
/**
* Starts the communication with the server
*/
ProgressiveLoaderGeometry.prototype.start = function() {
this.socket.emit('request', this.objPath);
}

View File

@ -1 +0,0 @@
static_path = "/static/";

35133
js/three.js

File diff suppressed because one or more lines are too long