init
python
This commit is contained in:
124
PCSurvey/libs/other/BinaryHeap.js
Normal file
124
PCSurvey/libs/other/BinaryHeap.js
Normal file
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
** Binary Heap implementation in Javascript
|
||||
** From: http://eloquentjavascript.net/1st_edition/appendix2.htmlt
|
||||
**
|
||||
** Copyright (c) 2007 Marijn Haverbeke, last modified on November 28 2013.
|
||||
**
|
||||
** Licensed under a Creative Commons attribution-noncommercial license.
|
||||
** All code in this book may also be considered licensed under an MIT license.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
function BinaryHeap(scoreFunction){
|
||||
this.content = [];
|
||||
this.scoreFunction = scoreFunction;
|
||||
}
|
||||
|
||||
BinaryHeap.prototype = {
|
||||
push: function(element) {
|
||||
// Add the new element to the end of the array.
|
||||
this.content.push(element);
|
||||
// Allow it to bubble up.
|
||||
this.bubbleUp(this.content.length - 1);
|
||||
},
|
||||
|
||||
pop: function() {
|
||||
// Store the first element so we can return it later.
|
||||
var result = this.content[0];
|
||||
// Get the element at the end of the array.
|
||||
var end = this.content.pop();
|
||||
// If there are any elements left, put the end element at the
|
||||
// start, and let it sink down.
|
||||
if (this.content.length > 0) {
|
||||
this.content[0] = end;
|
||||
this.sinkDown(0);
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
remove: function(node) {
|
||||
var length = this.content.length;
|
||||
// To remove a value, we must search through the array to find
|
||||
// it.
|
||||
for (var i = 0; i < length; i++) {
|
||||
if (this.content[i] != node) continue;
|
||||
// When it is found, the process seen in 'pop' is repeated
|
||||
// to fill up the hole.
|
||||
var end = this.content.pop();
|
||||
// If the element we popped was the one we needed to remove,
|
||||
// we're done.
|
||||
if (i == length - 1) break;
|
||||
// Otherwise, we replace the removed element with the popped
|
||||
// one, and allow it to float up or sink down as appropriate.
|
||||
this.content[i] = end;
|
||||
this.bubbleUp(i);
|
||||
this.sinkDown(i);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
size: function() {
|
||||
return this.content.length;
|
||||
},
|
||||
|
||||
bubbleUp: function(n) {
|
||||
// Fetch the element that has to be moved.
|
||||
var element = this.content[n], score = this.scoreFunction(element);
|
||||
// When at 0, an element can not go up any further.
|
||||
while (n > 0) {
|
||||
// Compute the parent element's index, and fetch it.
|
||||
var parentN = Math.floor((n + 1) / 2) - 1,
|
||||
parent = this.content[parentN];
|
||||
// If the parent has a lesser score, things are in order and we
|
||||
// are done.
|
||||
if (score >= this.scoreFunction(parent))
|
||||
break;
|
||||
|
||||
// Otherwise, swap the parent with the current element and
|
||||
// continue.
|
||||
this.content[parentN] = element;
|
||||
this.content[n] = parent;
|
||||
n = parentN;
|
||||
}
|
||||
},
|
||||
|
||||
sinkDown: function(n) {
|
||||
// Look up the target element and its score.
|
||||
var length = this.content.length,
|
||||
element = this.content[n],
|
||||
elemScore = this.scoreFunction(element);
|
||||
|
||||
while(true) {
|
||||
// Compute the indices of the child elements.
|
||||
var child2N = (n + 1) * 2, child1N = child2N - 1;
|
||||
// This is used to store the new position of the element,
|
||||
// if any.
|
||||
var swap = null;
|
||||
// If the first child exists (is inside the array)...
|
||||
if (child1N < length) {
|
||||
// Look it up and compute its score.
|
||||
var child1 = this.content[child1N],
|
||||
child1Score = this.scoreFunction(child1);
|
||||
// If the score is less than our element's, we need to swap.
|
||||
if (child1Score < elemScore)
|
||||
swap = child1N;
|
||||
}
|
||||
// Do the same checks for the other child.
|
||||
if (child2N < length) {
|
||||
var child2 = this.content[child2N],
|
||||
child2Score = this.scoreFunction(child2);
|
||||
if (child2Score < (swap == null ? elemScore : child1Score))
|
||||
swap = child2N;
|
||||
}
|
||||
|
||||
// No need to swap further, we are done.
|
||||
if (swap == null) break;
|
||||
|
||||
// Otherwise, swap and continue.
|
||||
this.content[n] = this.content[swap];
|
||||
this.content[swap] = element;
|
||||
n = swap;
|
||||
}
|
||||
}
|
||||
};
|
||||
715
PCSurvey/libs/other/OBJLoader.js
Normal file
715
PCSurvey/libs/other/OBJLoader.js
Normal file
@@ -0,0 +1,715 @@
|
||||
/**
|
||||
* @author mrdoob / http://mrdoob.com/
|
||||
*/
|
||||
|
||||
THREE.OBJLoader = ( function () {
|
||||
|
||||
// o object_name | g group_name
|
||||
var object_pattern = /^[og]\s*(.+)?/;
|
||||
// mtllib file_reference
|
||||
var material_library_pattern = /^mtllib /;
|
||||
// usemtl material_name
|
||||
var material_use_pattern = /^usemtl /;
|
||||
|
||||
function ParserState() {
|
||||
|
||||
var state = {
|
||||
objects: [],
|
||||
object: {},
|
||||
|
||||
vertices: [],
|
||||
normals: [],
|
||||
colors: [],
|
||||
uvs: [],
|
||||
|
||||
materialLibraries: [],
|
||||
|
||||
startObject: function ( name, fromDeclaration ) {
|
||||
|
||||
// If the current object (initial from reset) is not from a g/o declaration in the parsed
|
||||
// file. We need to use it for the first parsed g/o to keep things in sync.
|
||||
if ( this.object && this.object.fromDeclaration === false ) {
|
||||
|
||||
this.object.name = name;
|
||||
this.object.fromDeclaration = ( fromDeclaration !== false );
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
var previousMaterial = ( this.object && typeof this.object.currentMaterial === 'function' ? this.object.currentMaterial() : undefined );
|
||||
|
||||
if ( this.object && typeof this.object._finalize === 'function' ) {
|
||||
|
||||
this.object._finalize( true );
|
||||
|
||||
}
|
||||
|
||||
this.object = {
|
||||
name: name || '',
|
||||
fromDeclaration: ( fromDeclaration !== false ),
|
||||
|
||||
geometry: {
|
||||
vertices: [],
|
||||
normals: [],
|
||||
colors: [],
|
||||
uvs: []
|
||||
},
|
||||
materials: [],
|
||||
smooth: true,
|
||||
|
||||
startMaterial: function ( name, libraries ) {
|
||||
|
||||
var previous = this._finalize( false );
|
||||
|
||||
// New usemtl declaration overwrites an inherited material, except if faces were declared
|
||||
// after the material, then it must be preserved for proper MultiMaterial continuation.
|
||||
if ( previous && ( previous.inherited || previous.groupCount <= 0 ) ) {
|
||||
|
||||
this.materials.splice( previous.index, 1 );
|
||||
|
||||
}
|
||||
|
||||
var material = {
|
||||
index: this.materials.length,
|
||||
name: name || '',
|
||||
mtllib: ( Array.isArray( libraries ) && libraries.length > 0 ? libraries[ libraries.length - 1 ] : '' ),
|
||||
smooth: ( previous !== undefined ? previous.smooth : this.smooth ),
|
||||
groupStart: ( previous !== undefined ? previous.groupEnd : 0 ),
|
||||
groupEnd: - 1,
|
||||
groupCount: - 1,
|
||||
inherited: false,
|
||||
|
||||
clone: function ( index ) {
|
||||
|
||||
var cloned = {
|
||||
index: ( typeof index === 'number' ? index : this.index ),
|
||||
name: this.name,
|
||||
mtllib: this.mtllib,
|
||||
smooth: this.smooth,
|
||||
groupStart: 0,
|
||||
groupEnd: - 1,
|
||||
groupCount: - 1,
|
||||
inherited: false
|
||||
};
|
||||
cloned.clone = this.clone.bind( cloned );
|
||||
return cloned;
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
this.materials.push( material );
|
||||
|
||||
return material;
|
||||
|
||||
},
|
||||
|
||||
currentMaterial: function () {
|
||||
|
||||
if ( this.materials.length > 0 ) {
|
||||
|
||||
return this.materials[ this.materials.length - 1 ];
|
||||
|
||||
}
|
||||
|
||||
return undefined;
|
||||
|
||||
},
|
||||
|
||||
_finalize: function ( end ) {
|
||||
|
||||
var lastMultiMaterial = this.currentMaterial();
|
||||
if ( lastMultiMaterial && lastMultiMaterial.groupEnd === - 1 ) {
|
||||
|
||||
lastMultiMaterial.groupEnd = this.geometry.vertices.length / 3;
|
||||
lastMultiMaterial.groupCount = lastMultiMaterial.groupEnd - lastMultiMaterial.groupStart;
|
||||
lastMultiMaterial.inherited = false;
|
||||
|
||||
}
|
||||
|
||||
// Ignore objects tail materials if no face declarations followed them before a new o/g started.
|
||||
if ( end && this.materials.length > 1 ) {
|
||||
|
||||
for ( var mi = this.materials.length - 1; mi >= 0; mi -- ) {
|
||||
|
||||
if ( this.materials[ mi ].groupCount <= 0 ) {
|
||||
|
||||
this.materials.splice( mi, 1 );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Guarantee at least one empty material, this makes the creation later more straight forward.
|
||||
if ( end && this.materials.length === 0 ) {
|
||||
|
||||
this.materials.push( {
|
||||
name: '',
|
||||
smooth: this.smooth
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
return lastMultiMaterial;
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
// Inherit previous objects material.
|
||||
// Spec tells us that a declared material must be set to all objects until a new material is declared.
|
||||
// If a usemtl declaration is encountered while this new object is being parsed, it will
|
||||
// overwrite the inherited material. Exception being that there was already face declarations
|
||||
// to the inherited material, then it will be preserved for proper MultiMaterial continuation.
|
||||
|
||||
if ( previousMaterial && previousMaterial.name && typeof previousMaterial.clone === 'function' ) {
|
||||
|
||||
var declared = previousMaterial.clone( 0 );
|
||||
declared.inherited = true;
|
||||
this.object.materials.push( declared );
|
||||
|
||||
}
|
||||
|
||||
this.objects.push( this.object );
|
||||
|
||||
},
|
||||
|
||||
finalize: function () {
|
||||
|
||||
if ( this.object && typeof this.object._finalize === 'function' ) {
|
||||
|
||||
this.object._finalize( true );
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
parseVertexIndex: function ( value, len ) {
|
||||
|
||||
var index = parseInt( value, 10 );
|
||||
return ( index >= 0 ? index - 1 : index + len / 3 ) * 3;
|
||||
|
||||
},
|
||||
|
||||
parseNormalIndex: function ( value, len ) {
|
||||
|
||||
var index = parseInt( value, 10 );
|
||||
return ( index >= 0 ? index - 1 : index + len / 3 ) * 3;
|
||||
|
||||
},
|
||||
|
||||
parseUVIndex: function ( value, len ) {
|
||||
|
||||
var index = parseInt( value, 10 );
|
||||
return ( index >= 0 ? index - 1 : index + len / 2 ) * 2;
|
||||
|
||||
},
|
||||
|
||||
addVertex: function ( a, b, c ) {
|
||||
|
||||
var src = this.vertices;
|
||||
var dst = this.object.geometry.vertices;
|
||||
|
||||
dst.push( src[ a + 0 ], src[ a + 1 ], src[ a + 2 ] );
|
||||
dst.push( src[ b + 0 ], src[ b + 1 ], src[ b + 2 ] );
|
||||
dst.push( src[ c + 0 ], src[ c + 1 ], src[ c + 2 ] );
|
||||
|
||||
},
|
||||
|
||||
addVertexLine: function ( a ) {
|
||||
|
||||
var src = this.vertices;
|
||||
var dst = this.object.geometry.vertices;
|
||||
|
||||
dst.push( src[ a + 0 ], src[ a + 1 ], src[ a + 2 ] );
|
||||
|
||||
},
|
||||
|
||||
addNormal: function ( a, b, c ) {
|
||||
|
||||
var src = this.normals;
|
||||
var dst = this.object.geometry.normals;
|
||||
|
||||
dst.push( src[ a + 0 ], src[ a + 1 ], src[ a + 2 ] );
|
||||
dst.push( src[ b + 0 ], src[ b + 1 ], src[ b + 2 ] );
|
||||
dst.push( src[ c + 0 ], src[ c + 1 ], src[ c + 2 ] );
|
||||
|
||||
},
|
||||
|
||||
addColor: function ( a, b, c ) {
|
||||
|
||||
var src = this.colors;
|
||||
var dst = this.object.geometry.colors;
|
||||
|
||||
dst.push( src[ a + 0 ], src[ a + 1 ], src[ a + 2 ] );
|
||||
dst.push( src[ b + 0 ], src[ b + 1 ], src[ b + 2 ] );
|
||||
dst.push( src[ c + 0 ], src[ c + 1 ], src[ c + 2 ] );
|
||||
|
||||
},
|
||||
|
||||
addUV: function ( a, b, c ) {
|
||||
|
||||
var src = this.uvs;
|
||||
var dst = this.object.geometry.uvs;
|
||||
|
||||
dst.push( src[ a + 0 ], src[ a + 1 ] );
|
||||
dst.push( src[ b + 0 ], src[ b + 1 ] );
|
||||
dst.push( src[ c + 0 ], src[ c + 1 ] );
|
||||
|
||||
},
|
||||
|
||||
addUVLine: function ( a ) {
|
||||
|
||||
var src = this.uvs;
|
||||
var dst = this.object.geometry.uvs;
|
||||
|
||||
dst.push( src[ a + 0 ], src[ a + 1 ] );
|
||||
|
||||
},
|
||||
|
||||
addFace: function ( a, b, c, ua, ub, uc, na, nb, nc ) {
|
||||
|
||||
var vLen = this.vertices.length;
|
||||
|
||||
var ia = this.parseVertexIndex( a, vLen );
|
||||
var ib = this.parseVertexIndex( b, vLen );
|
||||
var ic = this.parseVertexIndex( c, vLen );
|
||||
|
||||
this.addVertex( ia, ib, ic );
|
||||
|
||||
if ( ua !== undefined ) {
|
||||
|
||||
var uvLen = this.uvs.length;
|
||||
|
||||
ia = this.parseUVIndex( ua, uvLen );
|
||||
ib = this.parseUVIndex( ub, uvLen );
|
||||
ic = this.parseUVIndex( uc, uvLen );
|
||||
|
||||
this.addUV( ia, ib, ic );
|
||||
|
||||
}
|
||||
|
||||
if ( na !== undefined ) {
|
||||
|
||||
// Normals are many times the same. If so, skip function call and parseInt.
|
||||
var nLen = this.normals.length;
|
||||
ia = this.parseNormalIndex( na, nLen );
|
||||
|
||||
ib = na === nb ? ia : this.parseNormalIndex( nb, nLen );
|
||||
ic = na === nc ? ia : this.parseNormalIndex( nc, nLen );
|
||||
|
||||
this.addNormal( ia, ib, ic );
|
||||
|
||||
}
|
||||
|
||||
if ( this.colors.length > 0 ) {
|
||||
|
||||
this.addColor( ia, ib, ic );
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
addLineGeometry: function ( vertices, uvs ) {
|
||||
|
||||
this.object.geometry.type = 'Line';
|
||||
|
||||
var vLen = this.vertices.length;
|
||||
var uvLen = this.uvs.length;
|
||||
|
||||
for ( var vi = 0, l = vertices.length; vi < l; vi ++ ) {
|
||||
|
||||
this.addVertexLine( this.parseVertexIndex( vertices[ vi ], vLen ) );
|
||||
|
||||
}
|
||||
|
||||
for ( var uvi = 0, l = uvs.length; uvi < l; uvi ++ ) {
|
||||
|
||||
this.addUVLine( this.parseUVIndex( uvs[ uvi ], uvLen ) );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
state.startObject( '', false );
|
||||
|
||||
return state;
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
function OBJLoader( manager ) {
|
||||
|
||||
this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
|
||||
|
||||
this.materials = null;
|
||||
|
||||
}
|
||||
|
||||
OBJLoader.prototype = {
|
||||
|
||||
constructor: OBJLoader,
|
||||
|
||||
load: function ( url, onLoad, onProgress, onError ) {
|
||||
|
||||
var scope = this;
|
||||
|
||||
var loader = new THREE.FileLoader( scope.manager );
|
||||
loader.setPath( this.path );
|
||||
loader.load( url, function ( text ) {
|
||||
|
||||
onLoad( scope.parse( text ) );
|
||||
|
||||
}, onProgress, onError );
|
||||
|
||||
},
|
||||
|
||||
setPath: function ( value ) {
|
||||
|
||||
this.path = value;
|
||||
|
||||
},
|
||||
|
||||
setMaterials: function ( materials ) {
|
||||
|
||||
this.materials = materials;
|
||||
|
||||
return this;
|
||||
|
||||
},
|
||||
|
||||
parse: function ( text ) {
|
||||
|
||||
console.time( 'OBJLoader' );
|
||||
|
||||
var state = new ParserState();
|
||||
|
||||
if ( text.indexOf( '\r\n' ) !== - 1 ) {
|
||||
|
||||
// This is faster than String.split with regex that splits on both
|
||||
text = text.replace( /\r\n/g, '\n' );
|
||||
|
||||
}
|
||||
|
||||
if ( text.indexOf( '\\\n' ) !== - 1 ) {
|
||||
|
||||
// join lines separated by a line continuation character (\)
|
||||
text = text.replace( /\\\n/g, '' );
|
||||
|
||||
}
|
||||
|
||||
var lines = text.split( '\n' );
|
||||
var line = '', lineFirstChar = '';
|
||||
var lineLength = 0;
|
||||
var result = [];
|
||||
|
||||
// Faster to just trim left side of the line. Use if available.
|
||||
var trimLeft = ( typeof ''.trimLeft === 'function' );
|
||||
|
||||
for ( var i = 0, l = lines.length; i < l; i ++ ) {
|
||||
|
||||
line = lines[ i ];
|
||||
|
||||
line = trimLeft ? line.trimLeft() : line.trim();
|
||||
|
||||
lineLength = line.length;
|
||||
|
||||
if ( lineLength === 0 ) continue;
|
||||
|
||||
lineFirstChar = line.charAt( 0 );
|
||||
|
||||
// @todo invoke passed in handler if any
|
||||
if ( lineFirstChar === '#' ) continue;
|
||||
|
||||
if ( lineFirstChar === 'v' ) {
|
||||
|
||||
var data = line.split( /\s+/ );
|
||||
|
||||
switch ( data[ 0 ] ) {
|
||||
|
||||
case 'v':
|
||||
state.vertices.push(
|
||||
parseFloat( data[ 1 ] ),
|
||||
parseFloat( data[ 2 ] ),
|
||||
parseFloat( data[ 3 ] )
|
||||
);
|
||||
if ( data.length === 8 ) {
|
||||
|
||||
state.colors.push(
|
||||
parseFloat( data[ 4 ] ),
|
||||
parseFloat( data[ 5 ] ),
|
||||
parseFloat( data[ 6 ] )
|
||||
|
||||
);
|
||||
|
||||
}
|
||||
break;
|
||||
case 'vn':
|
||||
state.normals.push(
|
||||
parseFloat( data[ 1 ] ),
|
||||
parseFloat( data[ 2 ] ),
|
||||
parseFloat( data[ 3 ] )
|
||||
);
|
||||
break;
|
||||
case 'vt':
|
||||
state.uvs.push(
|
||||
parseFloat( data[ 1 ] ),
|
||||
parseFloat( data[ 2 ] )
|
||||
);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
} else if ( lineFirstChar === 'f' ) {
|
||||
|
||||
var lineData = line.substr( 1 ).trim();
|
||||
var vertexData = lineData.split( /\s+/ );
|
||||
var faceVertices = [];
|
||||
|
||||
// Parse the face vertex data into an easy to work with format
|
||||
|
||||
for ( var j = 0, jl = vertexData.length; j < jl; j ++ ) {
|
||||
|
||||
var vertex = vertexData[ j ];
|
||||
|
||||
if ( vertex.length > 0 ) {
|
||||
|
||||
var vertexParts = vertex.split( '/' );
|
||||
faceVertices.push( vertexParts );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Draw an edge between the first vertex and all subsequent vertices to form an n-gon
|
||||
|
||||
var v1 = faceVertices[ 0 ];
|
||||
|
||||
for ( var j = 1, jl = faceVertices.length - 1; j < jl; j ++ ) {
|
||||
|
||||
var v2 = faceVertices[ j ];
|
||||
var v3 = faceVertices[ j + 1 ];
|
||||
|
||||
state.addFace(
|
||||
v1[ 0 ], v2[ 0 ], v3[ 0 ],
|
||||
v1[ 1 ], v2[ 1 ], v3[ 1 ],
|
||||
v1[ 2 ], v2[ 2 ], v3[ 2 ]
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
} else if ( lineFirstChar === 'l' ) {
|
||||
|
||||
var lineParts = line.substring( 1 ).trim().split( " " );
|
||||
var lineVertices = [], lineUVs = [];
|
||||
|
||||
if ( line.indexOf( "/" ) === - 1 ) {
|
||||
|
||||
lineVertices = lineParts;
|
||||
|
||||
} else {
|
||||
|
||||
for ( var li = 0, llen = lineParts.length; li < llen; li ++ ) {
|
||||
|
||||
var parts = lineParts[ li ].split( "/" );
|
||||
|
||||
if ( parts[ 0 ] !== "" ) lineVertices.push( parts[ 0 ] );
|
||||
if ( parts[ 1 ] !== "" ) lineUVs.push( parts[ 1 ] );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
state.addLineGeometry( lineVertices, lineUVs );
|
||||
|
||||
} else if ( ( result = object_pattern.exec( line ) ) !== null ) {
|
||||
|
||||
// o object_name
|
||||
// or
|
||||
// g group_name
|
||||
|
||||
// WORKAROUND: https://bugs.chromium.org/p/v8/issues/detail?id=2869
|
||||
// var name = result[ 0 ].substr( 1 ).trim();
|
||||
var name = ( " " + result[ 0 ].substr( 1 ).trim() ).substr( 1 );
|
||||
|
||||
state.startObject( name );
|
||||
|
||||
} else if ( material_use_pattern.test( line ) ) {
|
||||
|
||||
// material
|
||||
|
||||
state.object.startMaterial( line.substring( 7 ).trim(), state.materialLibraries );
|
||||
|
||||
} else if ( material_library_pattern.test( line ) ) {
|
||||
|
||||
// mtl file
|
||||
|
||||
state.materialLibraries.push( line.substring( 7 ).trim() );
|
||||
|
||||
} else if ( lineFirstChar === 's' ) {
|
||||
|
||||
result = line.split( ' ' );
|
||||
|
||||
// smooth shading
|
||||
|
||||
// @todo Handle files that have varying smooth values for a set of faces inside one geometry,
|
||||
// but does not define a usemtl for each face set.
|
||||
// This should be detected and a dummy material created (later MultiMaterial and geometry groups).
|
||||
// This requires some care to not create extra material on each smooth value for "normal" obj files.
|
||||
// where explicit usemtl defines geometry groups.
|
||||
// Example asset: examples/models/obj/cerberus/Cerberus.obj
|
||||
|
||||
/*
|
||||
* http://paulbourke.net/dataformats/obj/
|
||||
* or
|
||||
* http://www.cs.utah.edu/~boulos/cs3505/obj_spec.pdf
|
||||
*
|
||||
* From chapter "Grouping" Syntax explanation "s group_number":
|
||||
* "group_number is the smoothing group number. To turn off smoothing groups, use a value of 0 or off.
|
||||
* Polygonal elements use group numbers to put elements in different smoothing groups. For free-form
|
||||
* surfaces, smoothing groups are either turned on or off; there is no difference between values greater
|
||||
* than 0."
|
||||
*/
|
||||
if ( result.length > 1 ) {
|
||||
|
||||
var value = result[ 1 ].trim().toLowerCase();
|
||||
state.object.smooth = ( value !== '0' && value !== 'off' );
|
||||
|
||||
} else {
|
||||
|
||||
// ZBrush can produce "s" lines #11707
|
||||
state.object.smooth = true;
|
||||
|
||||
}
|
||||
var material = state.object.currentMaterial();
|
||||
if ( material ) material.smooth = state.object.smooth;
|
||||
|
||||
} else {
|
||||
|
||||
// Handle null terminated files without exception
|
||||
if ( line === '\0' ) continue;
|
||||
|
||||
throw new Error( 'THREE.OBJLoader: Unexpected line: "' + line + '"' );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
state.finalize();
|
||||
|
||||
var container = new THREE.Group();
|
||||
container.materialLibraries = [].concat( state.materialLibraries );
|
||||
|
||||
for ( var i = 0, l = state.objects.length; i < l; i ++ ) {
|
||||
|
||||
var object = state.objects[ i ];
|
||||
var geometry = object.geometry;
|
||||
var materials = object.materials;
|
||||
var isLine = ( geometry.type === 'Line' );
|
||||
|
||||
// Skip o/g line declarations that did not follow with any faces
|
||||
if ( geometry.vertices.length === 0 ) continue;
|
||||
|
||||
var buffergeometry = new THREE.BufferGeometry();
|
||||
|
||||
buffergeometry.addAttribute( 'position', new THREE.Float32BufferAttribute( geometry.vertices, 3 ) );
|
||||
|
||||
if ( geometry.normals.length > 0 ) {
|
||||
|
||||
buffergeometry.addAttribute( 'normal', new THREE.Float32BufferAttribute( geometry.normals, 3 ) );
|
||||
|
||||
} else {
|
||||
|
||||
buffergeometry.computeVertexNormals();
|
||||
|
||||
}
|
||||
|
||||
if ( geometry.colors.length > 0 ) {
|
||||
|
||||
buffergeometry.addAttribute( 'color', new THREE.Float32BufferAttribute( geometry.colors, 3 ) );
|
||||
|
||||
}
|
||||
|
||||
if ( geometry.uvs.length > 0 ) {
|
||||
|
||||
buffergeometry.addAttribute( 'uv', new THREE.Float32BufferAttribute( geometry.uvs, 2 ) );
|
||||
|
||||
}
|
||||
|
||||
// Create materials
|
||||
|
||||
var createdMaterials = [];
|
||||
|
||||
for ( var mi = 0, miLen = materials.length; mi < miLen; mi ++ ) {
|
||||
|
||||
var sourceMaterial = materials[ mi ];
|
||||
var material = undefined;
|
||||
|
||||
if ( this.materials !== null ) {
|
||||
|
||||
material = this.materials.create( sourceMaterial.name );
|
||||
|
||||
// mtl etc. loaders probably can't create line materials correctly, copy properties to a line material.
|
||||
if ( isLine && material && ! ( material instanceof THREE.LineBasicMaterial ) ) {
|
||||
|
||||
var materialLine = new THREE.LineBasicMaterial();
|
||||
materialLine.copy( material );
|
||||
material = materialLine;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( ! material ) {
|
||||
|
||||
material = ( ! isLine ? new THREE.MeshPhongMaterial() : new THREE.LineBasicMaterial() );
|
||||
material.name = sourceMaterial.name;
|
||||
|
||||
}
|
||||
|
||||
material.flatShading = sourceMaterial.smooth ? false : true;
|
||||
|
||||
createdMaterials.push( material );
|
||||
|
||||
}
|
||||
|
||||
// Create mesh
|
||||
|
||||
var mesh;
|
||||
|
||||
if ( createdMaterials.length > 1 ) {
|
||||
|
||||
for ( var mi = 0, miLen = materials.length; mi < miLen; mi ++ ) {
|
||||
|
||||
var sourceMaterial = materials[ mi ];
|
||||
buffergeometry.addGroup( sourceMaterial.groupStart, sourceMaterial.groupCount, mi );
|
||||
|
||||
}
|
||||
|
||||
mesh = ( ! isLine ? new THREE.Mesh( buffergeometry, createdMaterials ) : new THREE.LineSegments( buffergeometry, createdMaterials ) );
|
||||
|
||||
} else {
|
||||
|
||||
mesh = ( ! isLine ? new THREE.Mesh( buffergeometry, createdMaterials[ 0 ] ) : new THREE.LineSegments( buffergeometry, createdMaterials[ 0 ] ) );
|
||||
|
||||
}
|
||||
|
||||
mesh.name = object.name;
|
||||
|
||||
container.add( mesh );
|
||||
|
||||
}
|
||||
|
||||
console.timeEnd( 'OBJLoader' );
|
||||
|
||||
return container;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
return OBJLoader;
|
||||
|
||||
} )();
|
||||
625
PCSurvey/libs/other/OrbitControls.js
Normal file
625
PCSurvey/libs/other/OrbitControls.js
Normal file
@@ -0,0 +1,625 @@
|
||||
/**
|
||||
* @author qiao / https://github.com/qiao
|
||||
* @author mrdoob / http://mrdoob.com
|
||||
* @author alteredq / http://alteredqualia.com/
|
||||
* @author WestLangley / http://github.com/WestLangley
|
||||
* @author erich666 / http://erichaines.com
|
||||
*/
|
||||
/*global THREE, console */
|
||||
|
||||
// This set of controls performs orbiting, dollying (zooming), and panning. It maintains
|
||||
// the "up" direction as +Y, unlike the TrackballControls. Touch on tablet and phones is
|
||||
// supported.
|
||||
//
|
||||
// Orbit - left mouse / touch: one finger move
|
||||
// Zoom - middle mouse, or mousewheel / touch: two finger spread or squish
|
||||
// Pan - right mouse, or arrow keys / touch: three finter swipe
|
||||
//
|
||||
// This is a drop-in replacement for (most) TrackballControls used in examples.
|
||||
// That is, include this js file and wherever you see:
|
||||
// controls = new THREE.TrackballControls( camera );
|
||||
// controls.target.z = 150;
|
||||
// Simple substitute "OrbitControls" and the control should work as-is.
|
||||
|
||||
THREE.OrbitControls = function ( object, domElement ) {
|
||||
|
||||
this.object = object;
|
||||
this.domElement = ( domElement !== undefined ) ? domElement : document;
|
||||
|
||||
// API
|
||||
|
||||
// Set to false to disable this control
|
||||
this.enabled = true;
|
||||
|
||||
// "target" sets the location of focus, where the control orbits around
|
||||
// and where it pans with respect to.
|
||||
this.target = new THREE.Vector3();
|
||||
|
||||
// center is old, deprecated; use "target" instead
|
||||
this.center = this.target;
|
||||
|
||||
// This option actually enables dollying in and out; left as "zoom" for
|
||||
// backwards compatibility
|
||||
this.noZoom = false;
|
||||
this.zoomSpeed = 1.0;
|
||||
|
||||
// Limits to how far you can dolly in and out
|
||||
this.minDistance = 0;
|
||||
this.maxDistance = Infinity;
|
||||
|
||||
// Set to true to disable this control
|
||||
this.noRotate = false;
|
||||
this.rotateSpeed = 1.0;
|
||||
|
||||
// Set to true to disable this control
|
||||
this.noPan = false;
|
||||
this.keyPanSpeed = 7.0; // pixels moved per arrow key push
|
||||
|
||||
// Set to true to automatically rotate around the target
|
||||
this.autoRotate = false;
|
||||
this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60
|
||||
|
||||
// How far you can orbit vertically, upper and lower limits.
|
||||
// Range is 0 to Math.PI radians.
|
||||
this.minPolarAngle = 0; // radians
|
||||
this.maxPolarAngle = Math.PI; // radians
|
||||
|
||||
// Set to true to disable use of the keys
|
||||
this.noKeys = false;
|
||||
|
||||
// The four arrow keys
|
||||
this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 };
|
||||
|
||||
////////////
|
||||
// internals
|
||||
|
||||
var scope = this;
|
||||
|
||||
var EPS = 0.000001;
|
||||
|
||||
var rotateStart = new THREE.Vector2();
|
||||
var rotateEnd = new THREE.Vector2();
|
||||
var rotateDelta = new THREE.Vector2();
|
||||
|
||||
var panStart = new THREE.Vector2();
|
||||
var panEnd = new THREE.Vector2();
|
||||
var panDelta = new THREE.Vector2();
|
||||
var panOffset = new THREE.Vector3();
|
||||
|
||||
var offset = new THREE.Vector3();
|
||||
|
||||
var dollyStart = new THREE.Vector2();
|
||||
var dollyEnd = new THREE.Vector2();
|
||||
var dollyDelta = new THREE.Vector2();
|
||||
|
||||
var phiDelta = 0;
|
||||
var thetaDelta = 0;
|
||||
var scale = 1;
|
||||
var pan = new THREE.Vector3();
|
||||
|
||||
var lastPosition = new THREE.Vector3();
|
||||
|
||||
var STATE = { NONE : -1, ROTATE : 0, DOLLY : 1, PAN : 2, TOUCH_ROTATE : 3, TOUCH_DOLLY : 4, TOUCH_PAN : 5 };
|
||||
|
||||
var state = STATE.NONE;
|
||||
|
||||
// for reset
|
||||
|
||||
this.target0 = this.target.clone();
|
||||
this.position0 = this.object.position.clone();
|
||||
|
||||
// events
|
||||
|
||||
var changeEvent = { type: 'change' };
|
||||
var startEvent = { type: 'start'};
|
||||
var endEvent = { type: 'end'};
|
||||
|
||||
this.rotateLeft = function ( angle ) {
|
||||
|
||||
if ( angle === undefined ) {
|
||||
|
||||
angle = getAutoRotationAngle();
|
||||
|
||||
}
|
||||
|
||||
thetaDelta -= angle;
|
||||
|
||||
};
|
||||
|
||||
this.rotateUp = function ( angle ) {
|
||||
|
||||
if ( angle === undefined ) {
|
||||
|
||||
angle = getAutoRotationAngle();
|
||||
|
||||
}
|
||||
|
||||
phiDelta -= angle;
|
||||
|
||||
};
|
||||
|
||||
// pass in distance in world space to move left
|
||||
this.panLeft = function ( distance ) {
|
||||
|
||||
var te = this.object.matrix.elements;
|
||||
|
||||
// get X column of matrix
|
||||
panOffset.set( te[ 0 ], te[ 1 ], te[ 2 ] );
|
||||
panOffset.multiplyScalar( - distance );
|
||||
|
||||
pan.add( panOffset );
|
||||
|
||||
};
|
||||
|
||||
// pass in distance in world space to move up
|
||||
this.panUp = function ( distance ) {
|
||||
|
||||
var te = this.object.matrix.elements;
|
||||
|
||||
// get Y column of matrix
|
||||
panOffset.set( te[ 4 ], te[ 5 ], te[ 6 ] );
|
||||
panOffset.multiplyScalar( distance );
|
||||
|
||||
pan.add( panOffset );
|
||||
|
||||
};
|
||||
|
||||
// pass in x,y of change desired in pixel space,
|
||||
// right and down are positive
|
||||
this.pan = function ( deltaX, deltaY ) {
|
||||
|
||||
var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
|
||||
|
||||
if ( scope.object.fov !== undefined ) {
|
||||
|
||||
// perspective
|
||||
var position = scope.object.position;
|
||||
var offset = position.clone().sub( scope.target );
|
||||
var targetDistance = offset.length();
|
||||
|
||||
// half of the fov is center to top of screen
|
||||
targetDistance *= Math.tan( ( scope.object.fov / 2 ) * Math.PI / 180.0 );
|
||||
|
||||
// we actually don't use screenWidth, since perspective camera is fixed to screen height
|
||||
scope.panLeft( 2 * deltaX * targetDistance / element.clientHeight );
|
||||
scope.panUp( 2 * deltaY * targetDistance / element.clientHeight );
|
||||
|
||||
} else if ( scope.object.top !== undefined ) {
|
||||
|
||||
// orthographic
|
||||
scope.panLeft( deltaX * (scope.object.right - scope.object.left) / element.clientWidth );
|
||||
scope.panUp( deltaY * (scope.object.top - scope.object.bottom) / element.clientHeight );
|
||||
|
||||
} else {
|
||||
|
||||
// camera neither orthographic or perspective
|
||||
console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' );
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
this.dollyIn = function ( dollyScale ) {
|
||||
|
||||
if ( dollyScale === undefined ) {
|
||||
|
||||
dollyScale = getZoomScale();
|
||||
|
||||
}
|
||||
|
||||
scale /= dollyScale;
|
||||
|
||||
};
|
||||
|
||||
this.dollyOut = function ( dollyScale ) {
|
||||
|
||||
if ( dollyScale === undefined ) {
|
||||
|
||||
dollyScale = getZoomScale();
|
||||
|
||||
}
|
||||
|
||||
scale *= dollyScale;
|
||||
|
||||
};
|
||||
|
||||
this.update = function () {
|
||||
|
||||
var position = this.object.position;
|
||||
|
||||
offset.copy( position ).sub( this.target );
|
||||
|
||||
// angle from z-axis around y-axis
|
||||
|
||||
var theta = Math.atan2( offset.x, offset.z );
|
||||
|
||||
// angle from y-axis
|
||||
|
||||
var phi = Math.atan2( Math.sqrt( offset.x * offset.x + offset.z * offset.z ), offset.y );
|
||||
|
||||
if ( this.autoRotate ) {
|
||||
|
||||
this.rotateLeft( getAutoRotationAngle() );
|
||||
|
||||
}
|
||||
|
||||
theta += thetaDelta;
|
||||
phi += phiDelta;
|
||||
|
||||
// restrict phi to be between desired limits
|
||||
phi = Math.max( this.minPolarAngle, Math.min( this.maxPolarAngle, phi ) );
|
||||
|
||||
// restrict phi to be betwee EPS and PI-EPS
|
||||
phi = Math.max( EPS, Math.min( Math.PI - EPS, phi ) );
|
||||
|
||||
var radius = offset.length() * scale;
|
||||
|
||||
// restrict radius to be between desired limits
|
||||
radius = Math.max( this.minDistance, Math.min( this.maxDistance, radius ) );
|
||||
|
||||
// move target to panned location
|
||||
this.target.add( pan );
|
||||
|
||||
offset.x = radius * Math.sin( phi ) * Math.sin( theta );
|
||||
offset.y = radius * Math.cos( phi );
|
||||
offset.z = radius * Math.sin( phi ) * Math.cos( theta );
|
||||
|
||||
position.copy( this.target ).add( offset );
|
||||
|
||||
this.object.lookAt( this.target );
|
||||
|
||||
thetaDelta = 0;
|
||||
phiDelta = 0;
|
||||
scale = 1;
|
||||
pan.set( 0, 0, 0 );
|
||||
|
||||
if ( lastPosition.distanceTo( this.object.position ) > 0 ) {
|
||||
|
||||
this.dispatchEvent( changeEvent );
|
||||
|
||||
lastPosition.copy( this.object.position );
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
this.reset = function () {
|
||||
|
||||
state = STATE.NONE;
|
||||
|
||||
this.target.copy( this.target0 );
|
||||
this.object.position.copy( this.position0 );
|
||||
|
||||
this.update();
|
||||
|
||||
};
|
||||
|
||||
function getAutoRotationAngle() {
|
||||
|
||||
return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed;
|
||||
|
||||
}
|
||||
|
||||
function getZoomScale() {
|
||||
|
||||
return Math.pow( 0.95, scope.zoomSpeed );
|
||||
|
||||
}
|
||||
|
||||
function onMouseDown( event ) {
|
||||
|
||||
if ( scope.enabled === false ) return;
|
||||
event.preventDefault();
|
||||
|
||||
if ( event.button === 0 ) {
|
||||
if ( scope.noRotate === true ) return;
|
||||
|
||||
state = STATE.ROTATE;
|
||||
|
||||
rotateStart.set( event.clientX, event.clientY );
|
||||
|
||||
} else if ( event.button === 1 ) {
|
||||
if ( scope.noZoom === true ) return;
|
||||
|
||||
state = STATE.DOLLY;
|
||||
|
||||
dollyStart.set( event.clientX, event.clientY );
|
||||
|
||||
} else if ( event.button === 2 ) {
|
||||
if ( scope.noPan === true ) return;
|
||||
|
||||
state = STATE.PAN;
|
||||
|
||||
panStart.set( event.clientX, event.clientY );
|
||||
|
||||
}
|
||||
|
||||
scope.domElement.addEventListener( 'mousemove', onMouseMove, false );
|
||||
scope.domElement.addEventListener( 'mouseup', onMouseUp, false );
|
||||
scope.dispatchEvent( startEvent );
|
||||
|
||||
}
|
||||
|
||||
function onMouseMove( event ) {
|
||||
|
||||
if ( scope.enabled === false ) return;
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
|
||||
|
||||
if ( state === STATE.ROTATE ) {
|
||||
|
||||
if ( scope.noRotate === true ) return;
|
||||
|
||||
rotateEnd.set( event.clientX, event.clientY );
|
||||
rotateDelta.subVectors( rotateEnd, rotateStart );
|
||||
|
||||
// rotating across whole screen goes 360 degrees around
|
||||
scope.rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed );
|
||||
|
||||
// rotating up and down along whole screen attempts to go 360, but limited to 180
|
||||
scope.rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed );
|
||||
|
||||
rotateStart.copy( rotateEnd );
|
||||
|
||||
} else if ( state === STATE.DOLLY ) {
|
||||
|
||||
if ( scope.noZoom === true ) return;
|
||||
|
||||
dollyEnd.set( event.clientX, event.clientY );
|
||||
dollyDelta.subVectors( dollyEnd, dollyStart );
|
||||
|
||||
if ( dollyDelta.y > 0 ) {
|
||||
|
||||
scope.dollyIn();
|
||||
|
||||
} else {
|
||||
|
||||
scope.dollyOut();
|
||||
|
||||
}
|
||||
|
||||
dollyStart.copy( dollyEnd );
|
||||
|
||||
} else if ( state === STATE.PAN ) {
|
||||
|
||||
if ( scope.noPan === true ) return;
|
||||
|
||||
panEnd.set( event.clientX, event.clientY );
|
||||
panDelta.subVectors( panEnd, panStart );
|
||||
|
||||
scope.pan( panDelta.x, panDelta.y );
|
||||
|
||||
panStart.copy( panEnd );
|
||||
|
||||
}
|
||||
|
||||
scope.update();
|
||||
|
||||
}
|
||||
|
||||
function onMouseUp( /* event */ ) {
|
||||
|
||||
if ( scope.enabled === false ) return;
|
||||
|
||||
scope.domElement.removeEventListener( 'mousemove', onMouseMove, false );
|
||||
scope.domElement.removeEventListener( 'mouseup', onMouseUp, false );
|
||||
scope.dispatchEvent( endEvent );
|
||||
state = STATE.NONE;
|
||||
|
||||
}
|
||||
|
||||
function onMouseWheel( event ) {
|
||||
|
||||
if ( scope.enabled === false || scope.noZoom === true ) return;
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
var delta = 0;
|
||||
|
||||
if ( event.wheelDelta !== undefined ) { // WebKit / Opera / Explorer 9
|
||||
|
||||
delta = event.wheelDelta;
|
||||
|
||||
} else if ( event.detail !== undefined ) { // Firefox
|
||||
|
||||
delta = - event.detail;
|
||||
|
||||
}
|
||||
|
||||
if ( delta > 0 ) {
|
||||
|
||||
scope.dollyOut();
|
||||
|
||||
} else {
|
||||
|
||||
scope.dollyIn();
|
||||
|
||||
}
|
||||
|
||||
scope.update();
|
||||
scope.dispatchEvent( startEvent );
|
||||
scope.dispatchEvent( endEvent );
|
||||
|
||||
}
|
||||
|
||||
function onKeyDown( event ) {
|
||||
|
||||
if ( scope.enabled === false || scope.noKeys === true || scope.noPan === true ) return;
|
||||
|
||||
switch ( event.keyCode ) {
|
||||
|
||||
case scope.keys.UP:
|
||||
scope.pan( 0, scope.keyPanSpeed );
|
||||
scope.update();
|
||||
break;
|
||||
|
||||
case scope.keys.BOTTOM:
|
||||
scope.pan( 0, - scope.keyPanSpeed );
|
||||
scope.update();
|
||||
break;
|
||||
|
||||
case scope.keys.LEFT:
|
||||
scope.pan( scope.keyPanSpeed, 0 );
|
||||
scope.update();
|
||||
break;
|
||||
|
||||
case scope.keys.RIGHT:
|
||||
scope.pan( - scope.keyPanSpeed, 0 );
|
||||
scope.update();
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function touchstart( event ) {
|
||||
|
||||
if ( scope.enabled === false ) return;
|
||||
|
||||
switch ( event.touches.length ) {
|
||||
|
||||
case 1: // one-fingered touch: rotate
|
||||
|
||||
if ( scope.noRotate === true ) return;
|
||||
|
||||
state = STATE.TOUCH_ROTATE;
|
||||
|
||||
rotateStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
|
||||
break;
|
||||
|
||||
case 2: // two-fingered touch: dolly
|
||||
|
||||
if ( scope.noZoom === true ) return;
|
||||
|
||||
state = STATE.TOUCH_DOLLY;
|
||||
|
||||
var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
|
||||
var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
|
||||
var distance = Math.sqrt( dx * dx + dy * dy );
|
||||
dollyStart.set( 0, distance );
|
||||
break;
|
||||
|
||||
case 3: // three-fingered touch: pan
|
||||
|
||||
if ( scope.noPan === true ) return;
|
||||
|
||||
state = STATE.TOUCH_PAN;
|
||||
|
||||
panStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
state = STATE.NONE;
|
||||
|
||||
}
|
||||
|
||||
scope.dispatchEvent( startEvent );
|
||||
|
||||
}
|
||||
|
||||
function touchmove( event ) {
|
||||
|
||||
if ( scope.enabled === false ) return;
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
|
||||
|
||||
switch ( event.touches.length ) {
|
||||
|
||||
case 1: // one-fingered touch: rotate
|
||||
|
||||
if ( scope.noRotate === true ) return;
|
||||
if ( state !== STATE.TOUCH_ROTATE ) return;
|
||||
|
||||
rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
|
||||
rotateDelta.subVectors( rotateEnd, rotateStart );
|
||||
|
||||
// rotating across whole screen goes 360 degrees around
|
||||
scope.rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed );
|
||||
// rotating up and down along whole screen attempts to go 360, but limited to 180
|
||||
scope.rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed );
|
||||
|
||||
rotateStart.copy( rotateEnd );
|
||||
|
||||
scope.update();
|
||||
break;
|
||||
|
||||
case 2: // two-fingered touch: dolly
|
||||
|
||||
if ( scope.noZoom === true ) return;
|
||||
if ( state !== STATE.TOUCH_DOLLY ) return;
|
||||
|
||||
var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
|
||||
var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
|
||||
var distance = Math.sqrt( dx * dx + dy * dy );
|
||||
|
||||
dollyEnd.set( 0, distance );
|
||||
dollyDelta.subVectors( dollyEnd, dollyStart );
|
||||
|
||||
if ( dollyDelta.y > 0 ) {
|
||||
|
||||
scope.dollyOut();
|
||||
|
||||
} else {
|
||||
|
||||
scope.dollyIn();
|
||||
|
||||
}
|
||||
|
||||
dollyStart.copy( dollyEnd );
|
||||
|
||||
scope.update();
|
||||
break;
|
||||
|
||||
case 3: // three-fingered touch: pan
|
||||
|
||||
if ( scope.noPan === true ) return;
|
||||
if ( state !== STATE.TOUCH_PAN ) return;
|
||||
|
||||
panEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
|
||||
panDelta.subVectors( panEnd, panStart );
|
||||
|
||||
scope.pan( panDelta.x, panDelta.y );
|
||||
|
||||
panStart.copy( panEnd );
|
||||
|
||||
scope.update();
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
state = STATE.NONE;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function touchend( /* event */ ) {
|
||||
|
||||
if ( scope.enabled === false ) return;
|
||||
|
||||
scope.dispatchEvent( endEvent );
|
||||
state = STATE.NONE;
|
||||
|
||||
}
|
||||
|
||||
this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false );
|
||||
this.domElement.addEventListener( 'mousedown', onMouseDown, false );
|
||||
this.domElement.addEventListener( 'mousewheel', onMouseWheel, false );
|
||||
this.domElement.addEventListener( 'DOMMouseScroll', onMouseWheel, false ); // firefox
|
||||
|
||||
this.domElement.addEventListener( 'touchstart', touchstart, false );
|
||||
this.domElement.addEventListener( 'touchend', touchend, false );
|
||||
this.domElement.addEventListener( 'touchmove', touchmove, false );
|
||||
|
||||
window.addEventListener( 'keydown', onKeyDown, false );
|
||||
|
||||
};
|
||||
|
||||
THREE.OrbitControls.prototype = Object.create( THREE.EventDispatcher.prototype );
|
||||
498
PCSurvey/libs/other/PLYLoader.js
Normal file
498
PCSurvey/libs/other/PLYLoader.js
Normal file
@@ -0,0 +1,498 @@
|
||||
/**
|
||||
* @author Wei Meng / http://about.me/menway
|
||||
*
|
||||
* Utilizes Three.js which is made available under the MIT license.
|
||||
* Copyright © 2010-2015 three.js authors Ricardo Cabello (mrdoob)
|
||||
* Description: A THREE loader for PLY ASCII files (known as the Polygon
|
||||
* File Format or the Stanford Triangle Format).
|
||||
*
|
||||
* Limitations: ASCII decoding assumes file is UTF-8.
|
||||
*
|
||||
* Usage:
|
||||
* var loader = new THREE.PLYLoader();
|
||||
* loader.load('./models/ply/ascii/dolphins.ply', function (geometry) {
|
||||
*
|
||||
* scene.add( new THREE.Mesh( geometry ) );
|
||||
*
|
||||
* } );
|
||||
*
|
||||
* If the PLY file uses non standard property names, they can be mapped while
|
||||
* loading. For example, the following maps the properties
|
||||
* “diffuse_(red|green|blue)” in the file to standard color names.
|
||||
*
|
||||
* loader.setPropertyNameMapping( {
|
||||
* diffuse_red: 'red',
|
||||
* diffuse_green: 'green',
|
||||
* diffuse_blue: 'blue'
|
||||
* } );
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
THREE.PLYLoader = function ( manager ) {
|
||||
|
||||
this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
|
||||
|
||||
this.propertyNameMapping = {};
|
||||
|
||||
};
|
||||
|
||||
THREE.PLYLoader.prototype = {
|
||||
|
||||
constructor: THREE.PLYLoader,
|
||||
|
||||
load: function ( url, onLoad, onProgress, onError ) {
|
||||
|
||||
var scope = this;
|
||||
|
||||
var loader = new THREE.FileLoader( this.manager );
|
||||
loader.setResponseType( 'arraybuffer' );
|
||||
loader.load( url, function ( text ) {
|
||||
|
||||
onLoad( scope.parse( text ) );
|
||||
|
||||
}, onProgress, onError );
|
||||
|
||||
},
|
||||
|
||||
setPropertyNameMapping: function ( mapping ) {
|
||||
|
||||
this.propertyNameMapping = mapping;
|
||||
|
||||
},
|
||||
|
||||
parse: function ( data ) {
|
||||
|
||||
function isASCII( data ) {
|
||||
|
||||
var header = parseHeader( bin2str( data ) );
|
||||
return header.format === 'ascii';
|
||||
|
||||
}
|
||||
|
||||
function bin2str( buf ) {
|
||||
|
||||
var array_buffer = new Uint8Array( buf );
|
||||
var str = '';
|
||||
|
||||
for ( var i = 0; i < buf.byteLength; i ++ ) {
|
||||
|
||||
str += String.fromCharCode( array_buffer[ i ] ); // implicitly assumes little-endian
|
||||
|
||||
}
|
||||
|
||||
return str;
|
||||
|
||||
}
|
||||
|
||||
function parseHeader( data ) {
|
||||
|
||||
var patternHeader = /ply([\s\S]*)end_header\s/;
|
||||
var headerText = '';
|
||||
var headerLength = 0;
|
||||
var result = patternHeader.exec( data );
|
||||
|
||||
if ( result !== null ) {
|
||||
|
||||
headerText = result [ 1 ];
|
||||
headerLength = result[ 0 ].length;
|
||||
|
||||
}
|
||||
|
||||
var header = {
|
||||
comments: [],
|
||||
elements: [],
|
||||
headerLength: headerLength
|
||||
};
|
||||
|
||||
var lines = headerText.split( '\n' );
|
||||
var currentElement;
|
||||
var lineType, lineValues;
|
||||
|
||||
function make_ply_element_property( propertValues, propertyNameMapping ) {
|
||||
|
||||
var property = { type: propertValues[ 0 ] };
|
||||
|
||||
if ( property.type === 'list' ) {
|
||||
|
||||
property.name = propertValues[ 3 ];
|
||||
property.countType = propertValues[ 1 ];
|
||||
property.itemType = propertValues[ 2 ];
|
||||
|
||||
} else {
|
||||
|
||||
property.name = propertValues[ 1 ];
|
||||
|
||||
}
|
||||
|
||||
if ( property.name in propertyNameMapping ) {
|
||||
|
||||
property.name = propertyNameMapping[ property.name ];
|
||||
|
||||
}
|
||||
|
||||
return property;
|
||||
|
||||
}
|
||||
|
||||
for ( var i = 0; i < lines.length; i ++ ) {
|
||||
|
||||
var line = lines[ i ];
|
||||
line = line.trim();
|
||||
|
||||
if ( line === '' ) continue;
|
||||
|
||||
lineValues = line.split( /\s+/ );
|
||||
lineType = lineValues.shift();
|
||||
line = lineValues.join( ' ' );
|
||||
|
||||
switch ( lineType ) {
|
||||
|
||||
case 'format':
|
||||
|
||||
header.format = lineValues[ 0 ];
|
||||
header.version = lineValues[ 1 ];
|
||||
|
||||
break;
|
||||
|
||||
case 'comment':
|
||||
|
||||
header.comments.push( line );
|
||||
|
||||
break;
|
||||
|
||||
case 'element':
|
||||
|
||||
if ( currentElement !== undefined ) {
|
||||
|
||||
header.elements.push( currentElement );
|
||||
|
||||
}
|
||||
|
||||
currentElement = {};
|
||||
currentElement.name = lineValues[ 0 ];
|
||||
currentElement.count = parseInt( lineValues[ 1 ] );
|
||||
currentElement.properties = [];
|
||||
|
||||
break;
|
||||
|
||||
case 'property':
|
||||
|
||||
currentElement.properties.push( make_ply_element_property( lineValues, scope.propertyNameMapping ) );
|
||||
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
|
||||
console.log( 'unhandled', lineType, lineValues );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( currentElement !== undefined ) {
|
||||
|
||||
header.elements.push( currentElement );
|
||||
|
||||
}
|
||||
|
||||
return header;
|
||||
|
||||
}
|
||||
|
||||
function parseASCIINumber( n, type ) {
|
||||
|
||||
switch ( type ) {
|
||||
|
||||
case 'char': case 'uchar': case 'short': case 'ushort': case 'int': case 'uint':
|
||||
case 'int8': case 'uint8': case 'int16': case 'uint16': case 'int32': case 'uint32':
|
||||
|
||||
return parseInt( n );
|
||||
|
||||
case 'float': case 'double': case 'float32': case 'float64':
|
||||
|
||||
return parseFloat( n );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function parseASCIIElement( properties, line ) {
|
||||
|
||||
var values = line.split( /\s+/ );
|
||||
|
||||
var element = {};
|
||||
|
||||
for ( var i = 0; i < properties.length; i ++ ) {
|
||||
|
||||
if ( properties[ i ].type === 'list' ) {
|
||||
|
||||
var list = [];
|
||||
var n = parseASCIINumber( values.shift(), properties[ i ].countType );
|
||||
|
||||
for ( var j = 0; j < n; j ++ ) {
|
||||
|
||||
list.push( parseASCIINumber( values.shift(), properties[ i ].itemType ) );
|
||||
|
||||
}
|
||||
|
||||
element[ properties[ i ].name ] = list;
|
||||
|
||||
} else {
|
||||
|
||||
element[ properties[ i ].name ] = parseASCIINumber( values.shift(), properties[ i ].type );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return element;
|
||||
|
||||
}
|
||||
|
||||
function parseASCII( data ) {
|
||||
|
||||
// PLY ascii format specification, as per http://en.wikipedia.org/wiki/PLY_(file_format)
|
||||
|
||||
var buffer = {
|
||||
indices : [],
|
||||
vertices : [],
|
||||
normals : [],
|
||||
uvs : [],
|
||||
colors : []
|
||||
};
|
||||
|
||||
var result;
|
||||
|
||||
var header = parseHeader( data );
|
||||
|
||||
var patternBody = /end_header\s([\s\S]*)$/;
|
||||
var body = '';
|
||||
if ( ( result = patternBody.exec( data ) ) !== null ) {
|
||||
|
||||
body = result [ 1 ];
|
||||
|
||||
}
|
||||
|
||||
var lines = body.split( '\n' );
|
||||
var currentElement = 0;
|
||||
var currentElementCount = 0;
|
||||
|
||||
for ( var i = 0; i < lines.length; i ++ ) {
|
||||
|
||||
var line = lines[ i ];
|
||||
line = line.trim();
|
||||
if ( line === '' ) {
|
||||
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
if ( currentElementCount >= header.elements[ currentElement ].count ) {
|
||||
|
||||
currentElement ++;
|
||||
currentElementCount = 0;
|
||||
|
||||
}
|
||||
|
||||
var element = parseASCIIElement( header.elements[ currentElement ].properties, line );
|
||||
|
||||
handleElement( buffer, header.elements[ currentElement ].name, element );
|
||||
|
||||
currentElementCount ++;
|
||||
|
||||
}
|
||||
|
||||
return postProcess( buffer );
|
||||
|
||||
}
|
||||
|
||||
function postProcess( buffer ) {
|
||||
|
||||
var geometry = new THREE.BufferGeometry();
|
||||
|
||||
// mandatory buffer data
|
||||
|
||||
geometry.setIndex( new ( buffer.indices.length > 65535 ? THREE.Uint32BufferAttribute : THREE.Uint16BufferAttribute )( buffer.indices, 1 ) );
|
||||
geometry.addAttribute( 'position', new THREE.Float32BufferAttribute( buffer.vertices, 3 ) );
|
||||
|
||||
// optional buffer data
|
||||
|
||||
if ( buffer.normals.length > 0 ) {
|
||||
|
||||
geometry.addAttribute( 'normal', new THREE.Float32BufferAttribute( buffer.normals, 3 ) );
|
||||
|
||||
}
|
||||
|
||||
if ( buffer.uvs.length > 0 ) {
|
||||
|
||||
geometry.addAttribute( 'uv', new THREE.Float32BufferAttribute( buffer.uvs, 2 ) );
|
||||
|
||||
}
|
||||
|
||||
if ( buffer.colors.length > 0 ) {
|
||||
|
||||
geometry.addAttribute( 'color', new THREE.Float32BufferAttribute( buffer.colors, 3 ) );
|
||||
|
||||
}
|
||||
|
||||
geometry.computeBoundingSphere();
|
||||
|
||||
return geometry;
|
||||
|
||||
}
|
||||
|
||||
function handleElement( buffer, elementName, element ) {
|
||||
|
||||
if ( elementName === 'vertex' ) {
|
||||
|
||||
buffer.vertices.push( element.x, element.y, element.z );
|
||||
|
||||
if ( 'nx' in element && 'ny' in element && 'nz' in element ) {
|
||||
|
||||
buffer.normals.push( element.nx, element.ny, element.nz );
|
||||
|
||||
}
|
||||
|
||||
if ( 's' in element && 't' in element ) {
|
||||
|
||||
buffer.uvs.push( element.s, element.t );
|
||||
|
||||
}
|
||||
|
||||
if ( 'red' in element && 'green' in element && 'blue' in element ) {
|
||||
|
||||
buffer.colors.push( element.red / 255.0, element.green / 255.0, element.blue / 255.0 );
|
||||
|
||||
}
|
||||
|
||||
} else if ( elementName === 'face' ) {
|
||||
|
||||
var vertex_indices = element.vertex_indices || element.vertex_index; // issue #9338
|
||||
|
||||
if ( vertex_indices.length === 3 ) {
|
||||
|
||||
buffer.indices.push( vertex_indices[ 0 ], vertex_indices[ 1 ], vertex_indices[ 2 ] );
|
||||
|
||||
} else if ( vertex_indices.length === 4 ) {
|
||||
|
||||
buffer.indices.push( vertex_indices[ 0 ], vertex_indices[ 1 ], vertex_indices[ 3 ] );
|
||||
buffer.indices.push( vertex_indices[ 1 ], vertex_indices[ 2 ], vertex_indices[ 3 ] );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function binaryRead( dataview, at, type, little_endian ) {
|
||||
|
||||
switch ( type ) {
|
||||
|
||||
// corespondences for non-specific length types here match rply:
|
||||
case 'int8': case 'char': return [ dataview.getInt8( at ), 1 ];
|
||||
case 'uint8': case 'uchar': return [ dataview.getUint8( at ), 1 ];
|
||||
case 'int16': case 'short': return [ dataview.getInt16( at, little_endian ), 2 ];
|
||||
case 'uint16': case 'ushort': return [ dataview.getUint16( at, little_endian ), 2 ];
|
||||
case 'int32': case 'int': return [ dataview.getInt32( at, little_endian ), 4 ];
|
||||
case 'uint32': case 'uint': return [ dataview.getUint32( at, little_endian ), 4 ];
|
||||
case 'float32': case 'float': return [ dataview.getFloat32( at, little_endian ), 4 ];
|
||||
case 'float64': case 'double': return [ dataview.getFloat64( at, little_endian ), 8 ];
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function binaryReadElement( dataview, at, properties, little_endian ) {
|
||||
|
||||
var element = {};
|
||||
var result, read = 0;
|
||||
|
||||
for ( var i = 0; i < properties.length; i ++ ) {
|
||||
|
||||
if ( properties[ i ].type === 'list' ) {
|
||||
|
||||
var list = [];
|
||||
|
||||
result = binaryRead( dataview, at + read, properties[ i ].countType, little_endian );
|
||||
var n = result[ 0 ];
|
||||
read += result[ 1 ];
|
||||
|
||||
for ( var j = 0; j < n; j ++ ) {
|
||||
|
||||
result = binaryRead( dataview, at + read, properties[ i ].itemType, little_endian );
|
||||
list.push( result[ 0 ] );
|
||||
read += result[ 1 ];
|
||||
|
||||
}
|
||||
|
||||
element[ properties[ i ].name ] = list;
|
||||
|
||||
} else {
|
||||
|
||||
result = binaryRead( dataview, at + read, properties[ i ].type, little_endian );
|
||||
element[ properties[ i ].name ] = result[ 0 ];
|
||||
read += result[ 1 ];
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return [ element, read ];
|
||||
|
||||
}
|
||||
|
||||
function parseBinary( data ) {
|
||||
|
||||
var buffer = {
|
||||
indices : [],
|
||||
vertices : [],
|
||||
normals : [],
|
||||
uvs : [],
|
||||
colors : []
|
||||
};
|
||||
|
||||
var header = parseHeader( bin2str( data ) );
|
||||
var little_endian = ( header.format === 'binary_little_endian' );
|
||||
var body = new DataView( data, header.headerLength );
|
||||
var result, loc = 0;
|
||||
|
||||
for ( var currentElement = 0; currentElement < header.elements.length; currentElement ++ ) {
|
||||
|
||||
for ( var currentElementCount = 0; currentElementCount < header.elements[ currentElement ].count; currentElementCount ++ ) {
|
||||
|
||||
result = binaryReadElement( body, loc, header.elements[ currentElement ].properties, little_endian );
|
||||
loc += result[ 1 ];
|
||||
var element = result[ 0 ];
|
||||
|
||||
handleElement( buffer, header.elements[ currentElement ].name, element );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return postProcess( buffer );
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
var geometry;
|
||||
var scope = this;
|
||||
|
||||
if ( data instanceof ArrayBuffer ) {
|
||||
|
||||
geometry = isASCII( data ) ? parseASCII( bin2str( data ) ) : parseBinary( data );
|
||||
|
||||
} else {
|
||||
|
||||
geometry = parseASCII( data );
|
||||
|
||||
}
|
||||
|
||||
return geometry;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
91
PCSurvey/libs/other/dat.gui.min.js
vendored
Normal file
91
PCSurvey/libs/other/dat.gui.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
200
PCSurvey/libs/other/stats.js
Normal file
200
PCSurvey/libs/other/stats.js
Normal file
@@ -0,0 +1,200 @@
|
||||
// stats.js -
|
||||
// @author mrdoob http://github.com/mrdoob/stats.js
|
||||
|
||||
// The MIT License
|
||||
//
|
||||
// Copyright (c) 2009-2016 stats.js author mrdoob / http://mrdoob.com/
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
*/
|
||||
|
||||
var Stats = function () {
|
||||
|
||||
this.fps = 0;
|
||||
var mode = 0;
|
||||
|
||||
var container = document.createElement( 'div' );
|
||||
container.style.cssText = 'position:fixed;top:0;left:0;cursor:pointer;opacity:0.9;z-index:10000';
|
||||
container.addEventListener( 'click', function ( event ) {
|
||||
|
||||
event.preventDefault();
|
||||
showPanel( ++ mode % container.children.length );
|
||||
|
||||
}, false );
|
||||
|
||||
//
|
||||
|
||||
function addPanel( panel ) {
|
||||
|
||||
container.appendChild( panel.dom );
|
||||
return panel;
|
||||
|
||||
}
|
||||
|
||||
function showPanel( id ) {
|
||||
|
||||
for ( var i = 0; i < container.children.length; i ++ ) {
|
||||
|
||||
container.children[ i ].style.display = i === id ? 'block' : 'none';
|
||||
|
||||
}
|
||||
|
||||
mode = id;
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
var beginTime = ( performance || Date ).now(), prevTime = beginTime, frames = 0;
|
||||
|
||||
var fpsPanel = addPanel( new Stats.Panel( 'FPS', '#0ff', '#002' ) );
|
||||
var msPanel = addPanel( new Stats.Panel( 'MS', '#0f0', '#020' ) );
|
||||
|
||||
if ( self.performance && self.performance.memory ) {
|
||||
|
||||
var memPanel = addPanel( new Stats.Panel( 'MB', '#f08', '#201' ) );
|
||||
|
||||
}
|
||||
|
||||
showPanel( 0 );
|
||||
|
||||
return {
|
||||
|
||||
REVISION: 16,
|
||||
|
||||
dom: container,
|
||||
|
||||
addPanel: addPanel,
|
||||
showPanel: showPanel,
|
||||
|
||||
begin: function () {
|
||||
|
||||
beginTime = ( performance || Date ).now();
|
||||
|
||||
},
|
||||
|
||||
end: function () {
|
||||
|
||||
frames ++;
|
||||
|
||||
var time = ( performance || Date ).now();
|
||||
|
||||
msPanel.update( time - beginTime, 200 );
|
||||
|
||||
if ( time > prevTime + 3000 ) {
|
||||
|
||||
this.fps = ( frames * 1000 ) / ( time - prevTime );
|
||||
fpsPanel.update( this.fps, 100 );
|
||||
|
||||
|
||||
prevTime = time;
|
||||
frames = 0;
|
||||
|
||||
if ( memPanel ) {
|
||||
|
||||
var memory = performance.memory;
|
||||
memPanel.update( memory.usedJSHeapSize / 1048576, memory.jsHeapSizeLimit / 1048576 );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return time;
|
||||
|
||||
},
|
||||
|
||||
update: function () {
|
||||
|
||||
beginTime = this.end();
|
||||
|
||||
},
|
||||
|
||||
// Backwards Compatibility
|
||||
|
||||
domElement: container,
|
||||
setMode: showPanel
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
Stats.Panel = function ( name, fg, bg ) {
|
||||
|
||||
var min = Infinity, max = 0, round = Math.round;
|
||||
var PR = round( window.devicePixelRatio || 1 );
|
||||
|
||||
var WIDTH = 80 * PR, HEIGHT = 48 * PR,
|
||||
TEXT_X = 3 * PR, TEXT_Y = 2 * PR,
|
||||
GRAPH_X = 3 * PR, GRAPH_Y = 15 * PR,
|
||||
GRAPH_WIDTH = 74 * PR, GRAPH_HEIGHT = 30 * PR;
|
||||
|
||||
var canvas = document.createElement( 'canvas' );
|
||||
canvas.width = WIDTH;
|
||||
canvas.height = HEIGHT;
|
||||
canvas.style.cssText = 'width:80px;height:48px';
|
||||
|
||||
var context = canvas.getContext( '2d' );
|
||||
context.font = 'bold ' + ( 9 * PR ) + 'px Helvetica,Arial,sans-serif';
|
||||
context.textBaseline = 'top';
|
||||
|
||||
context.fillStyle = bg;
|
||||
context.fillRect( 0, 0, WIDTH, HEIGHT );
|
||||
|
||||
context.fillStyle = fg;
|
||||
context.fillText( name, TEXT_X, TEXT_Y );
|
||||
context.fillRect( GRAPH_X, GRAPH_Y, GRAPH_WIDTH, GRAPH_HEIGHT );
|
||||
|
||||
context.fillStyle = bg;
|
||||
context.globalAlpha = 0.9;
|
||||
context.fillRect( GRAPH_X, GRAPH_Y, GRAPH_WIDTH, GRAPH_HEIGHT );
|
||||
|
||||
return {
|
||||
|
||||
dom: canvas,
|
||||
|
||||
update: function ( value, maxValue ) {
|
||||
|
||||
min = Math.min( min, value );
|
||||
max = Math.max( max, value );
|
||||
|
||||
context.fillStyle = bg;
|
||||
context.globalAlpha = 1;
|
||||
context.fillRect( 0, 0, WIDTH, GRAPH_Y );
|
||||
context.fillStyle = fg;
|
||||
context.fillText( round( value ) + ' ' + name + ' (' + round( min ) + '-' + round( max ) + ')', TEXT_X, TEXT_Y );
|
||||
|
||||
context.drawImage( canvas, GRAPH_X + PR, GRAPH_Y, GRAPH_WIDTH - PR, GRAPH_HEIGHT, GRAPH_X, GRAPH_Y, GRAPH_WIDTH - PR, GRAPH_HEIGHT );
|
||||
|
||||
context.fillRect( GRAPH_X + GRAPH_WIDTH - PR, GRAPH_Y, PR, GRAPH_HEIGHT );
|
||||
|
||||
context.fillStyle = bg;
|
||||
context.globalAlpha = 0.9;
|
||||
context.fillRect( GRAPH_X + GRAPH_WIDTH - PR, GRAPH_Y, PR, round( ( 1 - ( value / maxValue ) ) * GRAPH_HEIGHT ) );
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
if ( typeof module === 'object' ) {
|
||||
|
||||
module.exports = Stats;
|
||||
|
||||
}
|
||||
28
PCSurvey/libs/other/stats.min.js
vendored
Normal file
28
PCSurvey/libs/other/stats.min.js
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
// stats.js - http://github.com/mrdoob/stats.js
|
||||
|
||||
// The MIT License
|
||||
//
|
||||
// Copyright (c) 2009-2016 stats.js authors
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
var Stats=function(){function h(a){c.appendChild(a.dom);return a}function k(a){for(var d=0;d<c.children.length;d++)c.children[d].style.display=d===a?"block":"none";l=a}var l=0,c=document.createElement("div");c.style.cssText="position:fixed;top:0;left:0;cursor:pointer;opacity:0.9;z-index:10000";c.addEventListener("click",function(a){a.preventDefault();k(++l%c.children.length)},!1);var g=(performance||Date).now(),e=g,a=0,r=h(new Stats.Panel("FPS","#0ff","#002")),f=h(new Stats.Panel("MS","#0f0","#020"));
|
||||
if(self.performance&&self.performance.memory)var t=h(new Stats.Panel("MB","#f08","#201"));k(0);return{REVISION:16,dom:c,addPanel:h,showPanel:k,begin:function(){g=(performance||Date).now()},end:function(){a++;var c=(performance||Date).now();f.update(c-g,200);if(c>e+1E3&&(r.update(1E3*a/(c-e),100),e=c,a=0,t)){var d=performance.memory;t.update(d.usedJSHeapSize/1048576,d.jsHeapSizeLimit/1048576)}return c},update:function(){g=this.end()},domElement:c,setMode:k}};
|
||||
Stats.Panel=function(h,k,l){var c=Infinity,g=0,e=Math.round,a=e(window.devicePixelRatio||1),r=80*a,f=48*a,t=3*a,u=2*a,d=3*a,m=15*a,n=74*a,p=30*a,q=document.createElement("canvas");q.width=r;q.height=f;q.style.cssText="width:80px;height:48px";var b=q.getContext("2d");b.font="bold "+9*a+"px Helvetica,Arial,sans-serif";b.textBaseline="top";b.fillStyle=l;b.fillRect(0,0,r,f);b.fillStyle=k;b.fillText(h,t,u);b.fillRect(d,m,n,p);b.fillStyle=l;b.globalAlpha=.9;b.fillRect(d,m,n,p);return{dom:q,update:function(f,
|
||||
v){c=Math.min(c,f);g=Math.max(g,f);b.fillStyle=l;b.globalAlpha=1;b.fillRect(0,0,r,m);b.fillStyle=k;b.fillText(e(f)+" "+h+" ("+e(c)+"-"+e(g)+")",t,u);b.drawImage(q,d+a,m,n-a,p,d,m,n-a,p);b.fillRect(d+n-a,m,a,p);b.fillStyle=l;b.globalAlpha=.9;b.fillRect(d+n-a,m,a,e((1-f/v)*p))}}};"object"===typeof module&&(module.exports=Stats);
|
||||
945
PCSurvey/libs/other/webgl-debug.js
Normal file
945
PCSurvey/libs/other/webgl-debug.js
Normal file
@@ -0,0 +1,945 @@
|
||||
/*
|
||||
** Copyright (c) 2012 The Khronos Group Inc.
|
||||
**
|
||||
** Permission is hereby granted, free of charge, to any person obtaining a
|
||||
** copy of this software and/or associated documentation files (the
|
||||
** "Materials"), to deal in the Materials without restriction, including
|
||||
** without limitation the rights to use, copy, modify, merge, publish,
|
||||
** distribute, sublicense, and/or sell copies of the Materials, and to
|
||||
** permit persons to whom the Materials are furnished to do so, subject to
|
||||
** the following conditions:
|
||||
**
|
||||
** The above copyright notice and this permission notice shall be included
|
||||
** in all copies or substantial portions of the Materials.
|
||||
**
|
||||
** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
|
||||
*/
|
||||
|
||||
// Various functions for helping debug WebGL apps.
|
||||
|
||||
WebGLDebugUtils = function() {
|
||||
|
||||
/**
|
||||
* Wrapped logging function.
|
||||
* @param {string} msg Message to log.
|
||||
*/
|
||||
var log = function(msg) {
|
||||
if (window.console && window.console.log) {
|
||||
window.console.log(msg);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Wrapped error logging function.
|
||||
* @param {string} msg Message to log.
|
||||
*/
|
||||
var error = function(msg) {
|
||||
if (window.console && window.console.error) {
|
||||
window.console.error(msg);
|
||||
} else {
|
||||
log(msg);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Which arguments are enums based on the number of arguments to the function.
|
||||
* So
|
||||
* 'texImage2D': {
|
||||
* 9: { 0:true, 2:true, 6:true, 7:true },
|
||||
* 6: { 0:true, 2:true, 3:true, 4:true },
|
||||
* },
|
||||
*
|
||||
* means if there are 9 arguments then 6 and 7 are enums, if there are 6
|
||||
* arguments 3 and 4 are enums
|
||||
*
|
||||
* @type {!Object.<number, !Object.<number, string>}
|
||||
*/
|
||||
var glValidEnumContexts = {
|
||||
// Generic setters and getters
|
||||
|
||||
'enable': {1: { 0:true }},
|
||||
'disable': {1: { 0:true }},
|
||||
'getParameter': {1: { 0:true }},
|
||||
|
||||
// Rendering
|
||||
|
||||
'drawArrays': {3:{ 0:true }},
|
||||
'drawElements': {4:{ 0:true, 2:true }},
|
||||
|
||||
// Shaders
|
||||
|
||||
'createShader': {1: { 0:true }},
|
||||
'getShaderParameter': {2: { 1:true }},
|
||||
'getProgramParameter': {2: { 1:true }},
|
||||
'getShaderPrecisionFormat': {2: { 0: true, 1:true }},
|
||||
|
||||
// Vertex attributes
|
||||
|
||||
'getVertexAttrib': {2: { 1:true }},
|
||||
'vertexAttribPointer': {6: { 2:true }},
|
||||
|
||||
// Textures
|
||||
|
||||
'bindTexture': {2: { 0:true }},
|
||||
'activeTexture': {1: { 0:true }},
|
||||
'getTexParameter': {2: { 0:true, 1:true }},
|
||||
'texParameterf': {3: { 0:true, 1:true }},
|
||||
'texParameteri': {3: { 0:true, 1:true, 2:true }},
|
||||
'texImage2D': {
|
||||
9: { 0:true, 2:true, 6:true, 7:true },
|
||||
6: { 0:true, 2:true, 3:true, 4:true }
|
||||
},
|
||||
'texSubImage2D': {
|
||||
9: { 0:true, 6:true, 7:true },
|
||||
7: { 0:true, 4:true, 5:true }
|
||||
},
|
||||
'copyTexImage2D': {8: { 0:true, 2:true }},
|
||||
'copyTexSubImage2D': {8: { 0:true }},
|
||||
'generateMipmap': {1: { 0:true }},
|
||||
'compressedTexImage2D': {7: { 0: true, 2:true }},
|
||||
'compressedTexSubImage2D': {8: { 0: true, 6:true }},
|
||||
|
||||
// Buffer objects
|
||||
|
||||
'bindBuffer': {2: { 0:true }},
|
||||
'bufferData': {3: { 0:true, 2:true }},
|
||||
'bufferSubData': {3: { 0:true }},
|
||||
'getBufferParameter': {2: { 0:true, 1:true }},
|
||||
|
||||
// Renderbuffers and framebuffers
|
||||
|
||||
'pixelStorei': {2: { 0:true, 1:true }},
|
||||
'readPixels': {7: { 4:true, 5:true }},
|
||||
'bindRenderbuffer': {2: { 0:true }},
|
||||
'bindFramebuffer': {2: { 0:true }},
|
||||
'checkFramebufferStatus': {1: { 0:true }},
|
||||
'framebufferRenderbuffer': {4: { 0:true, 1:true, 2:true }},
|
||||
'framebufferTexture2D': {5: { 0:true, 1:true, 2:true }},
|
||||
'getFramebufferAttachmentParameter': {3: { 0:true, 1:true, 2:true }},
|
||||
'getRenderbufferParameter': {2: { 0:true, 1:true }},
|
||||
'renderbufferStorage': {4: { 0:true, 1:true }},
|
||||
|
||||
// Frame buffer operations (clear, blend, depth test, stencil)
|
||||
|
||||
'clear': {1: { 0: { 'enumBitwiseOr': ['COLOR_BUFFER_BIT', 'DEPTH_BUFFER_BIT', 'STENCIL_BUFFER_BIT'] }}},
|
||||
'depthFunc': {1: { 0:true }},
|
||||
'blendFunc': {2: { 0:true, 1:true }},
|
||||
'blendFuncSeparate': {4: { 0:true, 1:true, 2:true, 3:true }},
|
||||
'blendEquation': {1: { 0:true }},
|
||||
'blendEquationSeparate': {2: { 0:true, 1:true }},
|
||||
'stencilFunc': {3: { 0:true }},
|
||||
'stencilFuncSeparate': {4: { 0:true, 1:true }},
|
||||
'stencilMaskSeparate': {2: { 0:true }},
|
||||
'stencilOp': {3: { 0:true, 1:true, 2:true }},
|
||||
'stencilOpSeparate': {4: { 0:true, 1:true, 2:true, 3:true }},
|
||||
|
||||
// Culling
|
||||
|
||||
'cullFace': {1: { 0:true }},
|
||||
'frontFace': {1: { 0:true }},
|
||||
|
||||
// ANGLE_instanced_arrays extension
|
||||
|
||||
'drawArraysInstancedANGLE': {4: { 0:true }},
|
||||
'drawElementsInstancedANGLE': {5: { 0:true, 2:true }},
|
||||
|
||||
// EXT_blend_minmax extension
|
||||
|
||||
'blendEquationEXT': {1: { 0:true }}
|
||||
};
|
||||
|
||||
/**
|
||||
* Map of numbers to names.
|
||||
* @type {Object}
|
||||
*/
|
||||
var glEnums = null;
|
||||
|
||||
/**
|
||||
* Map of names to numbers.
|
||||
* @type {Object}
|
||||
*/
|
||||
var enumStringToValue = null;
|
||||
|
||||
/**
|
||||
* Initializes this module. Safe to call more than once.
|
||||
* @param {!WebGLRenderingContext} ctx A WebGL context. If
|
||||
* you have more than one context it doesn't matter which one
|
||||
* you pass in, it is only used to pull out constants.
|
||||
*/
|
||||
function init(ctx) {
|
||||
if (glEnums == null) {
|
||||
glEnums = { };
|
||||
enumStringToValue = { };
|
||||
for (var propertyName in ctx) {
|
||||
if (typeof ctx[propertyName] == 'number') {
|
||||
glEnums[ctx[propertyName]] = propertyName;
|
||||
enumStringToValue[propertyName] = ctx[propertyName];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the utils have been initialized.
|
||||
*/
|
||||
function checkInit() {
|
||||
if (glEnums == null) {
|
||||
throw 'WebGLDebugUtils.init(ctx) not called';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true or false if value matches any WebGL enum
|
||||
* @param {*} value Value to check if it might be an enum.
|
||||
* @return {boolean} True if value matches one of the WebGL defined enums
|
||||
*/
|
||||
function mightBeEnum(value) {
|
||||
checkInit();
|
||||
return (glEnums[value] !== undefined);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an string version of an WebGL enum.
|
||||
*
|
||||
* Example:
|
||||
* var str = WebGLDebugUtil.glEnumToString(ctx.getError());
|
||||
*
|
||||
* @param {number} value Value to return an enum for
|
||||
* @return {string} The string version of the enum.
|
||||
*/
|
||||
function glEnumToString(value) {
|
||||
checkInit();
|
||||
var name = glEnums[value];
|
||||
return (name !== undefined) ? ("gl." + name) :
|
||||
("/*UNKNOWN WebGL ENUM*/ 0x" + value.toString(16) + "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the string version of a WebGL argument.
|
||||
* Attempts to convert enum arguments to strings.
|
||||
* @param {string} functionName the name of the WebGL function.
|
||||
* @param {number} numArgs the number of arguments passed to the function.
|
||||
* @param {number} argumentIndx the index of the argument.
|
||||
* @param {*} value The value of the argument.
|
||||
* @return {string} The value as a string.
|
||||
*/
|
||||
function glFunctionArgToString(functionName, numArgs, argumentIndex, value) {
|
||||
var funcInfo = glValidEnumContexts[functionName];
|
||||
if (funcInfo !== undefined) {
|
||||
var funcInfo = funcInfo[numArgs];
|
||||
if (funcInfo !== undefined) {
|
||||
if (funcInfo[argumentIndex]) {
|
||||
if (typeof funcInfo[argumentIndex] === 'object' &&
|
||||
funcInfo[argumentIndex]['enumBitwiseOr'] !== undefined) {
|
||||
var enums = funcInfo[argumentIndex]['enumBitwiseOr'];
|
||||
var orResult = 0;
|
||||
var orEnums = [];
|
||||
for (var i = 0; i < enums.length; ++i) {
|
||||
var enumValue = enumStringToValue[enums[i]];
|
||||
if ((value & enumValue) !== 0) {
|
||||
orResult |= enumValue;
|
||||
orEnums.push(glEnumToString(enumValue));
|
||||
}
|
||||
}
|
||||
if (orResult === value) {
|
||||
return orEnums.join(' | ');
|
||||
} else {
|
||||
return glEnumToString(value);
|
||||
}
|
||||
} else {
|
||||
return glEnumToString(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (value === null) {
|
||||
return "null";
|
||||
} else if (value === undefined) {
|
||||
return "undefined";
|
||||
} else {
|
||||
return value.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the arguments of a WebGL function to a string.
|
||||
* Attempts to convert enum arguments to strings.
|
||||
*
|
||||
* @param {string} functionName the name of the WebGL function.
|
||||
* @param {number} args The arguments.
|
||||
* @return {string} The arguments as a string.
|
||||
*/
|
||||
function glFunctionArgsToString(functionName, args) {
|
||||
// apparently we can't do args.join(",");
|
||||
var argStr = "";
|
||||
var numArgs = args.length;
|
||||
for (var ii = 0; ii < numArgs; ++ii) {
|
||||
argStr += ((ii == 0) ? '' : ', ') +
|
||||
glFunctionArgToString(functionName, numArgs, ii, args[ii]);
|
||||
}
|
||||
return argStr;
|
||||
};
|
||||
|
||||
|
||||
function makePropertyWrapper(wrapper, original, propertyName) {
|
||||
//log("wrap prop: " + propertyName);
|
||||
wrapper.__defineGetter__(propertyName, function() {
|
||||
return original[propertyName];
|
||||
});
|
||||
// TODO(gmane): this needs to handle properties that take more than
|
||||
// one value?
|
||||
wrapper.__defineSetter__(propertyName, function(value) {
|
||||
//log("set: " + propertyName);
|
||||
original[propertyName] = value;
|
||||
});
|
||||
}
|
||||
|
||||
// Makes a function that calls a function on another object.
|
||||
function makeFunctionWrapper(original, functionName) {
|
||||
//log("wrap fn: " + functionName);
|
||||
var f = original[functionName];
|
||||
return function() {
|
||||
//log("call: " + functionName);
|
||||
var result = f.apply(original, arguments);
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a WebGL context returns a wrapped context that calls
|
||||
* gl.getError after every command and calls a function if the
|
||||
* result is not gl.NO_ERROR.
|
||||
*
|
||||
* @param {!WebGLRenderingContext} ctx The webgl context to
|
||||
* wrap.
|
||||
* @param {!function(err, funcName, args): void} opt_onErrorFunc
|
||||
* The function to call when gl.getError returns an
|
||||
* error. If not specified the default function calls
|
||||
* console.log with a message.
|
||||
* @param {!function(funcName, args): void} opt_onFunc The
|
||||
* function to call when each webgl function is called.
|
||||
* You can use this to log all calls for example.
|
||||
* @param {!WebGLRenderingContext} opt_err_ctx The webgl context
|
||||
* to call getError on if different than ctx.
|
||||
*/
|
||||
function makeDebugContext(ctx, opt_onErrorFunc, opt_onFunc, opt_err_ctx) {
|
||||
opt_err_ctx = opt_err_ctx || ctx;
|
||||
init(ctx);
|
||||
opt_onErrorFunc = opt_onErrorFunc || function(err, functionName, args) {
|
||||
// apparently we can't do args.join(",");
|
||||
var argStr = "";
|
||||
var numArgs = args.length;
|
||||
for (var ii = 0; ii < numArgs; ++ii) {
|
||||
argStr += ((ii == 0) ? '' : ', ') +
|
||||
glFunctionArgToString(functionName, numArgs, ii, args[ii]);
|
||||
}
|
||||
error("WebGL error "+ glEnumToString(err) + " in "+ functionName +
|
||||
"(" + argStr + ")");
|
||||
};
|
||||
|
||||
// Holds booleans for each GL error so after we get the error ourselves
|
||||
// we can still return it to the client app.
|
||||
var glErrorShadow = { };
|
||||
|
||||
// Makes a function that calls a WebGL function and then calls getError.
|
||||
function makeErrorWrapper(ctx, functionName) {
|
||||
return function() {
|
||||
if (opt_onFunc) {
|
||||
opt_onFunc(functionName, arguments);
|
||||
}
|
||||
var result = ctx[functionName].apply(ctx, arguments);
|
||||
var err = opt_err_ctx.getError();
|
||||
if (err != 0) {
|
||||
glErrorShadow[err] = true;
|
||||
opt_onErrorFunc(err, functionName, arguments);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
// Make a an object that has a copy of every property of the WebGL context
|
||||
// but wraps all functions.
|
||||
var wrapper = {};
|
||||
for (var propertyName in ctx) {
|
||||
if (typeof ctx[propertyName] == 'function') {
|
||||
if (propertyName != 'getExtension') {
|
||||
wrapper[propertyName] = makeErrorWrapper(ctx, propertyName);
|
||||
} else {
|
||||
var wrapped = makeErrorWrapper(ctx, propertyName);
|
||||
wrapper[propertyName] = function () {
|
||||
var result = wrapped.apply(ctx, arguments);
|
||||
return makeDebugContext(result, opt_onErrorFunc, opt_onFunc, opt_err_ctx);
|
||||
};
|
||||
}
|
||||
} else {
|
||||
makePropertyWrapper(wrapper, ctx, propertyName);
|
||||
}
|
||||
}
|
||||
|
||||
// Override the getError function with one that returns our saved results.
|
||||
wrapper.getError = function() {
|
||||
for (var err in glErrorShadow) {
|
||||
if (glErrorShadow.hasOwnProperty(err)) {
|
||||
if (glErrorShadow[err]) {
|
||||
glErrorShadow[err] = false;
|
||||
return err;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ctx.NO_ERROR;
|
||||
};
|
||||
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
function resetToInitialState(ctx) {
|
||||
var numAttribs = ctx.getParameter(ctx.MAX_VERTEX_ATTRIBS);
|
||||
var tmp = ctx.createBuffer();
|
||||
ctx.bindBuffer(ctx.ARRAY_BUFFER, tmp);
|
||||
for (var ii = 0; ii < numAttribs; ++ii) {
|
||||
ctx.disableVertexAttribArray(ii);
|
||||
ctx.vertexAttribPointer(ii, 4, ctx.FLOAT, false, 0, 0);
|
||||
ctx.vertexAttrib1f(ii, 0);
|
||||
}
|
||||
ctx.deleteBuffer(tmp);
|
||||
|
||||
var numTextureUnits = ctx.getParameter(ctx.MAX_TEXTURE_IMAGE_UNITS);
|
||||
for (var ii = 0; ii < numTextureUnits; ++ii) {
|
||||
ctx.activeTexture(ctx.TEXTURE0 + ii);
|
||||
ctx.bindTexture(ctx.TEXTURE_CUBE_MAP, null);
|
||||
ctx.bindTexture(ctx.TEXTURE_2D, null);
|
||||
}
|
||||
|
||||
ctx.activeTexture(ctx.TEXTURE0);
|
||||
ctx.useProgram(null);
|
||||
ctx.bindBuffer(ctx.ARRAY_BUFFER, null);
|
||||
ctx.bindBuffer(ctx.ELEMENT_ARRAY_BUFFER, null);
|
||||
ctx.bindFramebuffer(ctx.FRAMEBUFFER, null);
|
||||
ctx.bindRenderbuffer(ctx.RENDERBUFFER, null);
|
||||
ctx.disable(ctx.BLEND);
|
||||
ctx.disable(ctx.CULL_FACE);
|
||||
ctx.disable(ctx.DEPTH_TEST);
|
||||
ctx.disable(ctx.DITHER);
|
||||
ctx.disable(ctx.SCISSOR_TEST);
|
||||
ctx.blendColor(0, 0, 0, 0);
|
||||
ctx.blendEquation(ctx.FUNC_ADD);
|
||||
ctx.blendFunc(ctx.ONE, ctx.ZERO);
|
||||
ctx.clearColor(0, 0, 0, 0);
|
||||
ctx.clearDepth(1);
|
||||
ctx.clearStencil(-1);
|
||||
ctx.colorMask(true, true, true, true);
|
||||
ctx.cullFace(ctx.BACK);
|
||||
ctx.depthFunc(ctx.LESS);
|
||||
ctx.depthMask(true);
|
||||
ctx.depthRange(0, 1);
|
||||
ctx.frontFace(ctx.CCW);
|
||||
ctx.hint(ctx.GENERATE_MIPMAP_HINT, ctx.DONT_CARE);
|
||||
ctx.lineWidth(1);
|
||||
ctx.pixelStorei(ctx.PACK_ALIGNMENT, 4);
|
||||
ctx.pixelStorei(ctx.UNPACK_ALIGNMENT, 4);
|
||||
ctx.pixelStorei(ctx.UNPACK_FLIP_Y_WEBGL, false);
|
||||
ctx.pixelStorei(ctx.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
|
||||
// TODO: Delete this IF.
|
||||
if (ctx.UNPACK_COLORSPACE_CONVERSION_WEBGL) {
|
||||
ctx.pixelStorei(ctx.UNPACK_COLORSPACE_CONVERSION_WEBGL, ctx.BROWSER_DEFAULT_WEBGL);
|
||||
}
|
||||
ctx.polygonOffset(0, 0);
|
||||
ctx.sampleCoverage(1, false);
|
||||
ctx.scissor(0, 0, ctx.canvas.width, ctx.canvas.height);
|
||||
ctx.stencilFunc(ctx.ALWAYS, 0, 0xFFFFFFFF);
|
||||
ctx.stencilMask(0xFFFFFFFF);
|
||||
ctx.stencilOp(ctx.KEEP, ctx.KEEP, ctx.KEEP);
|
||||
ctx.viewport(0, 0, ctx.canvas.width, ctx.canvas.height);
|
||||
ctx.clear(ctx.COLOR_BUFFER_BIT | ctx.DEPTH_BUFFER_BIT | ctx.STENCIL_BUFFER_BIT);
|
||||
|
||||
// TODO: This should NOT be needed but Firefox fails with 'hint'
|
||||
while(ctx.getError());
|
||||
}
|
||||
|
||||
function makeLostContextSimulatingCanvas(canvas) {
|
||||
var unwrappedContext_;
|
||||
var wrappedContext_;
|
||||
var onLost_ = [];
|
||||
var onRestored_ = [];
|
||||
var wrappedContext_ = {};
|
||||
var contextId_ = 1;
|
||||
var contextLost_ = false;
|
||||
var resourceId_ = 0;
|
||||
var resourceDb_ = [];
|
||||
var numCallsToLoseContext_ = 0;
|
||||
var numCalls_ = 0;
|
||||
var canRestore_ = false;
|
||||
var restoreTimeout_ = 0;
|
||||
|
||||
// Holds booleans for each GL error so can simulate errors.
|
||||
var glErrorShadow_ = { };
|
||||
|
||||
canvas.getContext = function(f) {
|
||||
return function() {
|
||||
var ctx = f.apply(canvas, arguments);
|
||||
// Did we get a context and is it a WebGL context?
|
||||
if (ctx instanceof WebGLRenderingContext) {
|
||||
if (ctx != unwrappedContext_) {
|
||||
if (unwrappedContext_) {
|
||||
throw "got different context"
|
||||
}
|
||||
unwrappedContext_ = ctx;
|
||||
wrappedContext_ = makeLostContextSimulatingContext(unwrappedContext_);
|
||||
}
|
||||
return wrappedContext_;
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
}(canvas.getContext);
|
||||
|
||||
function wrapEvent(listener) {
|
||||
if (typeof(listener) == "function") {
|
||||
return listener;
|
||||
} else {
|
||||
return function(info) {
|
||||
listener.handleEvent(info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var addOnContextLostListener = function(listener) {
|
||||
onLost_.push(wrapEvent(listener));
|
||||
};
|
||||
|
||||
var addOnContextRestoredListener = function(listener) {
|
||||
onRestored_.push(wrapEvent(listener));
|
||||
};
|
||||
|
||||
|
||||
function wrapAddEventListener(canvas) {
|
||||
var f = canvas.addEventListener;
|
||||
canvas.addEventListener = function(type, listener, bubble) {
|
||||
switch (type) {
|
||||
case 'webglcontextlost':
|
||||
addOnContextLostListener(listener);
|
||||
break;
|
||||
case 'webglcontextrestored':
|
||||
addOnContextRestoredListener(listener);
|
||||
break;
|
||||
default:
|
||||
f.apply(canvas, arguments);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
wrapAddEventListener(canvas);
|
||||
|
||||
canvas.loseContext = function() {
|
||||
if (!contextLost_) {
|
||||
contextLost_ = true;
|
||||
numCallsToLoseContext_ = 0;
|
||||
++contextId_;
|
||||
while (unwrappedContext_.getError());
|
||||
clearErrors();
|
||||
glErrorShadow_[unwrappedContext_.CONTEXT_LOST_WEBGL] = true;
|
||||
var event = makeWebGLContextEvent("context lost");
|
||||
var callbacks = onLost_.slice();
|
||||
setTimeout(function() {
|
||||
//log("numCallbacks:" + callbacks.length);
|
||||
for (var ii = 0; ii < callbacks.length; ++ii) {
|
||||
//log("calling callback:" + ii);
|
||||
callbacks[ii](event);
|
||||
}
|
||||
if (restoreTimeout_ >= 0) {
|
||||
setTimeout(function() {
|
||||
canvas.restoreContext();
|
||||
}, restoreTimeout_);
|
||||
}
|
||||
}, 0);
|
||||
}
|
||||
};
|
||||
|
||||
canvas.restoreContext = function() {
|
||||
if (contextLost_) {
|
||||
if (onRestored_.length) {
|
||||
setTimeout(function() {
|
||||
if (!canRestore_) {
|
||||
throw "can not restore. webglcontestlost listener did not call event.preventDefault";
|
||||
}
|
||||
freeResources();
|
||||
resetToInitialState(unwrappedContext_);
|
||||
contextLost_ = false;
|
||||
numCalls_ = 0;
|
||||
canRestore_ = false;
|
||||
var callbacks = onRestored_.slice();
|
||||
var event = makeWebGLContextEvent("context restored");
|
||||
for (var ii = 0; ii < callbacks.length; ++ii) {
|
||||
callbacks[ii](event);
|
||||
}
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
canvas.loseContextInNCalls = function(numCalls) {
|
||||
if (contextLost_) {
|
||||
throw "You can not ask a lost contet to be lost";
|
||||
}
|
||||
numCallsToLoseContext_ = numCalls_ + numCalls;
|
||||
};
|
||||
|
||||
canvas.getNumCalls = function() {
|
||||
return numCalls_;
|
||||
};
|
||||
|
||||
canvas.setRestoreTimeout = function(timeout) {
|
||||
restoreTimeout_ = timeout;
|
||||
};
|
||||
|
||||
function isWebGLObject(obj) {
|
||||
//return false;
|
||||
return (obj instanceof WebGLBuffer ||
|
||||
obj instanceof WebGLFramebuffer ||
|
||||
obj instanceof WebGLProgram ||
|
||||
obj instanceof WebGLRenderbuffer ||
|
||||
obj instanceof WebGLShader ||
|
||||
obj instanceof WebGLTexture);
|
||||
}
|
||||
|
||||
function checkResources(args) {
|
||||
for (var ii = 0; ii < args.length; ++ii) {
|
||||
var arg = args[ii];
|
||||
if (isWebGLObject(arg)) {
|
||||
return arg.__webglDebugContextLostId__ == contextId_;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function clearErrors() {
|
||||
var k = Object.keys(glErrorShadow_);
|
||||
for (var ii = 0; ii < k.length; ++ii) {
|
||||
delete glErrorShadow_[k];
|
||||
}
|
||||
}
|
||||
|
||||
function loseContextIfTime() {
|
||||
++numCalls_;
|
||||
if (!contextLost_) {
|
||||
if (numCallsToLoseContext_ == numCalls_) {
|
||||
canvas.loseContext();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Makes a function that simulates WebGL when out of context.
|
||||
function makeLostContextFunctionWrapper(ctx, functionName) {
|
||||
var f = ctx[functionName];
|
||||
return function() {
|
||||
// log("calling:" + functionName);
|
||||
// Only call the functions if the context is not lost.
|
||||
loseContextIfTime();
|
||||
if (!contextLost_) {
|
||||
//if (!checkResources(arguments)) {
|
||||
// glErrorShadow_[wrappedContext_.INVALID_OPERATION] = true;
|
||||
// return;
|
||||
//}
|
||||
var result = f.apply(ctx, arguments);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function freeResources() {
|
||||
for (var ii = 0; ii < resourceDb_.length; ++ii) {
|
||||
var resource = resourceDb_[ii];
|
||||
if (resource instanceof WebGLBuffer) {
|
||||
unwrappedContext_.deleteBuffer(resource);
|
||||
} else if (resource instanceof WebGLFramebuffer) {
|
||||
unwrappedContext_.deleteFramebuffer(resource);
|
||||
} else if (resource instanceof WebGLProgram) {
|
||||
unwrappedContext_.deleteProgram(resource);
|
||||
} else if (resource instanceof WebGLRenderbuffer) {
|
||||
unwrappedContext_.deleteRenderbuffer(resource);
|
||||
} else if (resource instanceof WebGLShader) {
|
||||
unwrappedContext_.deleteShader(resource);
|
||||
} else if (resource instanceof WebGLTexture) {
|
||||
unwrappedContext_.deleteTexture(resource);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function makeWebGLContextEvent(statusMessage) {
|
||||
return {
|
||||
statusMessage: statusMessage,
|
||||
preventDefault: function() {
|
||||
canRestore_ = true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return canvas;
|
||||
|
||||
function makeLostContextSimulatingContext(ctx) {
|
||||
// copy all functions and properties to wrapper
|
||||
for (var propertyName in ctx) {
|
||||
if (typeof ctx[propertyName] == 'function') {
|
||||
wrappedContext_[propertyName] = makeLostContextFunctionWrapper(
|
||||
ctx, propertyName);
|
||||
} else {
|
||||
makePropertyWrapper(wrappedContext_, ctx, propertyName);
|
||||
}
|
||||
}
|
||||
|
||||
// Wrap a few functions specially.
|
||||
wrappedContext_.getError = function() {
|
||||
loseContextIfTime();
|
||||
if (!contextLost_) {
|
||||
var err;
|
||||
while (err = unwrappedContext_.getError()) {
|
||||
glErrorShadow_[err] = true;
|
||||
}
|
||||
}
|
||||
for (var err in glErrorShadow_) {
|
||||
if (glErrorShadow_[err]) {
|
||||
delete glErrorShadow_[err];
|
||||
return err;
|
||||
}
|
||||
}
|
||||
return wrappedContext_.NO_ERROR;
|
||||
};
|
||||
|
||||
var creationFunctions = [
|
||||
"createBuffer",
|
||||
"createFramebuffer",
|
||||
"createProgram",
|
||||
"createRenderbuffer",
|
||||
"createShader",
|
||||
"createTexture"
|
||||
];
|
||||
for (var ii = 0; ii < creationFunctions.length; ++ii) {
|
||||
var functionName = creationFunctions[ii];
|
||||
wrappedContext_[functionName] = function(f) {
|
||||
return function() {
|
||||
loseContextIfTime();
|
||||
if (contextLost_) {
|
||||
return null;
|
||||
}
|
||||
var obj = f.apply(ctx, arguments);
|
||||
obj.__webglDebugContextLostId__ = contextId_;
|
||||
resourceDb_.push(obj);
|
||||
return obj;
|
||||
};
|
||||
}(ctx[functionName]);
|
||||
}
|
||||
|
||||
var functionsThatShouldReturnNull = [
|
||||
"getActiveAttrib",
|
||||
"getActiveUniform",
|
||||
"getBufferParameter",
|
||||
"getContextAttributes",
|
||||
"getAttachedShaders",
|
||||
"getFramebufferAttachmentParameter",
|
||||
"getParameter",
|
||||
"getProgramParameter",
|
||||
"getProgramInfoLog",
|
||||
"getRenderbufferParameter",
|
||||
"getShaderParameter",
|
||||
"getShaderInfoLog",
|
||||
"getShaderSource",
|
||||
"getTexParameter",
|
||||
"getUniform",
|
||||
"getUniformLocation",
|
||||
"getVertexAttrib"
|
||||
];
|
||||
for (var ii = 0; ii < functionsThatShouldReturnNull.length; ++ii) {
|
||||
var functionName = functionsThatShouldReturnNull[ii];
|
||||
wrappedContext_[functionName] = function(f) {
|
||||
return function() {
|
||||
loseContextIfTime();
|
||||
if (contextLost_) {
|
||||
return null;
|
||||
}
|
||||
return f.apply(ctx, arguments);
|
||||
}
|
||||
}(wrappedContext_[functionName]);
|
||||
}
|
||||
|
||||
var isFunctions = [
|
||||
"isBuffer",
|
||||
"isEnabled",
|
||||
"isFramebuffer",
|
||||
"isProgram",
|
||||
"isRenderbuffer",
|
||||
"isShader",
|
||||
"isTexture"
|
||||
];
|
||||
for (var ii = 0; ii < isFunctions.length; ++ii) {
|
||||
var functionName = isFunctions[ii];
|
||||
wrappedContext_[functionName] = function(f) {
|
||||
return function() {
|
||||
loseContextIfTime();
|
||||
if (contextLost_) {
|
||||
return false;
|
||||
}
|
||||
return f.apply(ctx, arguments);
|
||||
}
|
||||
}(wrappedContext_[functionName]);
|
||||
}
|
||||
|
||||
wrappedContext_.checkFramebufferStatus = function(f) {
|
||||
return function() {
|
||||
loseContextIfTime();
|
||||
if (contextLost_) {
|
||||
return wrappedContext_.FRAMEBUFFER_UNSUPPORTED;
|
||||
}
|
||||
return f.apply(ctx, arguments);
|
||||
};
|
||||
}(wrappedContext_.checkFramebufferStatus);
|
||||
|
||||
wrappedContext_.getAttribLocation = function(f) {
|
||||
return function() {
|
||||
loseContextIfTime();
|
||||
if (contextLost_) {
|
||||
return -1;
|
||||
}
|
||||
return f.apply(ctx, arguments);
|
||||
};
|
||||
}(wrappedContext_.getAttribLocation);
|
||||
|
||||
wrappedContext_.getVertexAttribOffset = function(f) {
|
||||
return function() {
|
||||
loseContextIfTime();
|
||||
if (contextLost_) {
|
||||
return 0;
|
||||
}
|
||||
return f.apply(ctx, arguments);
|
||||
};
|
||||
}(wrappedContext_.getVertexAttribOffset);
|
||||
|
||||
wrappedContext_.isContextLost = function() {
|
||||
return contextLost_;
|
||||
};
|
||||
|
||||
return wrappedContext_;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
/**
|
||||
* Initializes this module. Safe to call more than once.
|
||||
* @param {!WebGLRenderingContext} ctx A WebGL context. If
|
||||
* you have more than one context it doesn't matter which one
|
||||
* you pass in, it is only used to pull out constants.
|
||||
*/
|
||||
'init': init,
|
||||
|
||||
/**
|
||||
* Returns true or false if value matches any WebGL enum
|
||||
* @param {*} value Value to check if it might be an enum.
|
||||
* @return {boolean} True if value matches one of the WebGL defined enums
|
||||
*/
|
||||
'mightBeEnum': mightBeEnum,
|
||||
|
||||
/**
|
||||
* Gets an string version of an WebGL enum.
|
||||
*
|
||||
* Example:
|
||||
* WebGLDebugUtil.init(ctx);
|
||||
* var str = WebGLDebugUtil.glEnumToString(ctx.getError());
|
||||
*
|
||||
* @param {number} value Value to return an enum for
|
||||
* @return {string} The string version of the enum.
|
||||
*/
|
||||
'glEnumToString': glEnumToString,
|
||||
|
||||
/**
|
||||
* Converts the argument of a WebGL function to a string.
|
||||
* Attempts to convert enum arguments to strings.
|
||||
*
|
||||
* Example:
|
||||
* WebGLDebugUtil.init(ctx);
|
||||
* var str = WebGLDebugUtil.glFunctionArgToString('bindTexture', 2, 0, gl.TEXTURE_2D);
|
||||
*
|
||||
* would return 'TEXTURE_2D'
|
||||
*
|
||||
* @param {string} functionName the name of the WebGL function.
|
||||
* @param {number} numArgs The number of arguments
|
||||
* @param {number} argumentIndx the index of the argument.
|
||||
* @param {*} value The value of the argument.
|
||||
* @return {string} The value as a string.
|
||||
*/
|
||||
'glFunctionArgToString': glFunctionArgToString,
|
||||
|
||||
/**
|
||||
* Converts the arguments of a WebGL function to a string.
|
||||
* Attempts to convert enum arguments to strings.
|
||||
*
|
||||
* @param {string} functionName the name of the WebGL function.
|
||||
* @param {number} args The arguments.
|
||||
* @return {string} The arguments as a string.
|
||||
*/
|
||||
'glFunctionArgsToString': glFunctionArgsToString,
|
||||
|
||||
/**
|
||||
* Given a WebGL context returns a wrapped context that calls
|
||||
* gl.getError after every command and calls a function if the
|
||||
* result is not NO_ERROR.
|
||||
*
|
||||
* You can supply your own function if you want. For example, if you'd like
|
||||
* an exception thrown on any GL error you could do this
|
||||
*
|
||||
* function throwOnGLError(err, funcName, args) {
|
||||
* throw WebGLDebugUtils.glEnumToString(err) +
|
||||
* " was caused by call to " + funcName;
|
||||
* };
|
||||
*
|
||||
* ctx = WebGLDebugUtils.makeDebugContext(
|
||||
* canvas.getContext("webgl"), throwOnGLError);
|
||||
*
|
||||
* @param {!WebGLRenderingContext} ctx The webgl context to wrap.
|
||||
* @param {!function(err, funcName, args): void} opt_onErrorFunc The function
|
||||
* to call when gl.getError returns an error. If not specified the default
|
||||
* function calls console.log with a message.
|
||||
* @param {!function(funcName, args): void} opt_onFunc The
|
||||
* function to call when each webgl function is called. You
|
||||
* can use this to log all calls for example.
|
||||
*/
|
||||
'makeDebugContext': makeDebugContext,
|
||||
|
||||
/**
|
||||
* Given a canvas element returns a wrapped canvas element that will
|
||||
* simulate lost context. The canvas returned adds the following functions.
|
||||
*
|
||||
* loseContext:
|
||||
* simulates a lost context event.
|
||||
*
|
||||
* restoreContext:
|
||||
* simulates the context being restored.
|
||||
*
|
||||
* lostContextInNCalls:
|
||||
* loses the context after N gl calls.
|
||||
*
|
||||
* getNumCalls:
|
||||
* tells you how many gl calls there have been so far.
|
||||
*
|
||||
* setRestoreTimeout:
|
||||
* sets the number of milliseconds until the context is restored
|
||||
* after it has been lost. Defaults to 0. Pass -1 to prevent
|
||||
* automatic restoring.
|
||||
*
|
||||
* @param {!Canvas} canvas The canvas element to wrap.
|
||||
*/
|
||||
'makeLostContextSimulatingCanvas': makeLostContextSimulatingCanvas,
|
||||
|
||||
/**
|
||||
* Resets a context to the initial state.
|
||||
* @param {!WebGLRenderingContext} ctx The webgl context to
|
||||
* reset.
|
||||
*/
|
||||
'resetToInitialState': resetToInitialState
|
||||
};
|
||||
|
||||
}();
|
||||
Reference in New Issue
Block a user