Skip to content
Snippets Groups Projects
Commit 04acddf8 authored by 표 영종's avatar 표 영종
Browse files

Initial commit

parents
No related branches found
No related tags found
No related merge requests found
//////////////////////////////////////////////////////////////////////////////
//
// Angel.js
//
//////////////////////////////////////////////////////////////////////////////
//----------------------------------------------------------------------------
//
// Helper functions
//
function _argumentsToArray( args )
{
return [].concat.apply( [], Array.prototype.slice.apply(args) );
}
//----------------------------------------------------------------------------
function radians( degrees ) {
return degrees * Math.PI / 180.0;
}
//----------------------------------------------------------------------------
//
// Vector Constructors
//
function vec2()
{
var result = _argumentsToArray( arguments );
switch ( result.length ) {
case 0: result.push( 0.0 );
case 1: result.push( 0.0 );
}
return result.splice( 0, 2 );
}
function vec3()
{
var result = _argumentsToArray( arguments );
switch ( result.length ) {
case 0: result.push( 0.0 );
case 1: result.push( 0.0 );
case 2: result.push( 0.0 );
}
return result.splice( 0, 3 );
}
function vec4()
{
var result = _argumentsToArray( arguments );
switch ( result.length ) {
case 0: result.push( 0.0 );
case 1: result.push( 0.0 );
case 2: result.push( 0.0 );
case 3: result.push( 1.0 );
}
return result.splice( 0, 4 );
}
//----------------------------------------------------------------------------
//
// Matrix Constructors
//
function mat2()
{
var v = _argumentsToArray( arguments );
var m = [];
switch ( v.length ) {
case 0:
v[0] = 1;
case 1:
m = [
vec2( v[0], 0.0 ),
vec2( 0.0, v[0] )
];
break;
default:
m.push( vec2(v) ); v.splice( 0, 2 );
m.push( vec2(v) );
break;
}
m.matrix = true;
return m;
}
//----------------------------------------------------------------------------
function mat3()
{
var v = _argumentsToArray( arguments );
var m = [];
switch ( v.length ) {
case 0:
v[0] = 1;
case 1:
m = [
vec3( v[0], 0.0, 0.0 ),
vec3( 0.0, v[0], 0.0 ),
vec3( 0.0, 0.0, v[0] )
];
break;
default:
m.push( vec3(v) ); v.splice( 0, 3 );
m.push( vec3(v) ); v.splice( 0, 3 );
m.push( vec3(v) );
break;
}
m.matrix = true;
return m;
}
//----------------------------------------------------------------------------
function mat4()
{
var v = _argumentsToArray( arguments );
var m = [];
switch ( v.length ) {
case 0:
v[0] = 1;
case 1:
m = [
vec4( v[0], 0.0, 0.0, 0.0 ),
vec4( 0.0, v[0], 0.0, 0.0 ),
vec4( 0.0, 0.0, v[0], 0.0 ),
vec4( 0.0, 0.0, 0.0, v[0] )
];
break;
default:
m.push( vec4(v) ); v.splice( 0, 4 );
m.push( vec4(v) ); v.splice( 0, 4 );
m.push( vec4(v) ); v.splice( 0, 4 );
m.push( vec4(v) );
break;
}
m.matrix = true;
return m;
}
//----------------------------------------------------------------------------
//
// Generic Mathematical Operations for Vectors and Matrices
//
function equal( u, v )
{
if ( u.length != v.length ) { return false; }
if ( u.matrix && v.matrix ) {
for ( var i = 0; i < u.length; ++i ) {
if ( u[i].length != v[i].length ) { return false; }
for ( var j = 0; j < u[i].length; ++j ) {
if ( u[i][j] !== v[i][j] ) { return false; }
}
}
}
else if ( u.matrix && !v.matrix || !u.matrix && v.matrix ) {
return false;
}
else {
for ( var i = 0; i < u.length; ++i ) {
if ( u[i] !== v[i] ) { return false; }
}
}
return true;
}
//----------------------------------------------------------------------------
function add( u, v )
{
var result = [];
if ( u.matrix && v.matrix ) {
if ( u.length != v.length ) {
throw "add(): trying to add matrices of different dimensions";
}
for ( var i = 0; i < u.length; ++i ) {
if ( u[i].length != v[i].length ) {
throw "add(): trying to add matrices of different dimensions";
}
result.push( [] );
for ( var j = 0; j < u[i].length; ++j ) {
result[i].push( u[i][j] + v[i][j] );
}
}
result.matrix = true;
return result;
}
else if ( u.matrix && !v.matrix || !u.matrix && v.matrix ) {
throw "add(): trying to add matrix and non-matrix variables";
}
else {
if ( u.length != v.length ) {
throw "add(): vectors are not the same dimension";
}
for ( var i = 0; i < u.length; ++i ) {
result.push( u[i] + v[i] );
}
return result;
}
}
//----------------------------------------------------------------------------
function subtract( u, v )
{
var result = [];
if ( u.matrix && v.matrix ) {
if ( u.length != v.length ) {
throw "subtract(): trying to subtract matrices" +
" of different dimensions";
}
for ( var i = 0; i < u.length; ++i ) {
if ( u[i].length != v[i].length ) {
throw "subtract(): trying to subtact matrices" +
" of different dimensions";
}
result.push( [] );
for ( var j = 0; j < u[i].length; ++j ) {
result[i].push( u[i][j] - v[i][j] );
}
}
result.matrix = true;
return result;
}
else if ( u.matrix && !v.matrix || !u.matrix && v.matrix ) {
throw "subtact(): trying to subtact matrix and non-matrix variables";
}
else {
if ( u.length != v.length ) {
throw "subtract(): vectors are not the same length";
}
for ( var i = 0; i < u.length; ++i ) {
result.push( u[i] - v[i] );
}
return result;
}
}
//----------------------------------------------------------------------------
function mult( u, v )
{
var result = [];
if ( u.matrix && v.matrix ) {
if ( u.length != v.length ) {
throw "mult(): trying to add matrices of different dimensions";
}
for ( var i = 0; i < u.length; ++i ) {
if ( u[i].length != v[i].length ) {
throw "mult(): trying to add matrices of different dimensions";
}
}
for ( var i = 0; i < u.length; ++i ) {
result.push( [] );
for ( var j = 0; j < v.length; ++j ) {
var sum = 0.0;
for ( var k = 0; k < u.length; ++k ) {
sum += u[i][k] * v[k][j];
}
result[i].push( sum );
}
}
result.matrix = true;
return result;
}
else {
if ( u.length != v.length ) {
throw "mult(): vectors are not the same dimension";
}
for ( var i = 0; i < u.length; ++i ) {
result.push( u[i] * v[i] );
}
return result;
}
}
//----------------------------------------------------------------------------
//
// Basic Transformation Matrix Generators
//
function translate( x, y, z )
{
if ( Array.isArray(x) && x.length == 3 ) {
z = x[2];
y = x[1];
x = x[0];
}
var result = mat4();
result[0][3] = x;
result[1][3] = y;
result[2][3] = z;
return result;
}
//----------------------------------------------------------------------------
function rotate( angle, axis )
{
if ( !Array.isArray(axis) ) {
axis = [ arguments[1], arguments[2], arguments[3] ];
}
var v = normalize( axis );
var x = v[0];
var y = v[1];
var z = v[2];
var c = Math.cos( radians(angle) );
var omc = 1.0 - c;
var s = Math.sin( radians(angle) );
var result = mat4(
vec4( x*x*omc + c, x*y*omc - z*s, x*z*omc + y*s, 0.0 ),
vec4( x*y*omc + z*s, y*y*omc + c, y*z*omc - x*s, 0.0 ),
vec4( x*z*omc - y*s, y*z*omc + x*s, z*z*omc + c, 0.0 ),
vec4()
);
return result;
}
//----------------------------------------------------------------------------
function scale( x, y, z )
{
if ( Array.isArray(x) && x.length == 3 ) {
z = x[2];
y = x[1];
x = x[0];
}
var result = mat4();
result[0][0] = x;
result[1][1] = y;
result[2][2] = z;
return result;
}
//----------------------------------------------------------------------------
//
// ModelView Matrix Generators
//
function lookAt( eye, at, up )
{
if ( !Array.isArray(eye) || eye.length != 3) {
throw "lookAt(): first parameter [eye] must be an a vec3";
}
if ( !Array.isArray(at) || at.length != 3) {
throw "lookAt(): first parameter [at] must be an a vec3";
}
if ( !Array.isArray(up) || up.length != 3) {
throw "lookAt(): first parameter [up] must be an a vec3";
}
if ( equal(eye, at) ) {
return mat4();
}
var v = normalize( subtract(at, eye) ); // view direction vector
var n = normalize( cross(v, up) ); // perpendicular vector
var u = normalize( cross(n, v) ); // "new" up vector
v = negate( v );
var result = mat4(
vec4( n, -dot(n, eye) ),
vec4( u, -dot(u, eye) ),
vec4( v, -dot(v, eye) ),
vec4()
);
return result;
}
//----------------------------------------------------------------------------
//
// Projection Matrix Generators
//
function ortho( left, right, bottom, top, near, far )
{
if ( left == right ) { throw "ortho(): left and right are equal"; }
if ( bottom == top ) { throw "ortho(): bottom and top are equal"; }
if ( near == far ) { throw "ortho(): near and far are equal"; }
var w = right - left;
var h = top - bottom;
var d = far - near;
var result = mat4();
result[0][0] = 2.0 / w;
result[1][1] = 2.0 / h;
result[2][2] = -2.0 / d;
result[0][3] = -(left + right) / w;
result[1][3] = -(top + bottom) / h;
result[2][3] = -(near + far) / d;
return result;
}
//----------------------------------------------------------------------------
function perspective( fovy, aspect, near, far )
{
var f = 1.0 / Math.tan( radians(fovy) / 2 );
var d = far - near;
var result = mat4();
result[0][0] = f / aspect;
result[1][1] = f;
result[2][2] = -(near + far) / d;
result[2][3] = -2 * near * far / d;
result[3][2] = -1;
result[3][3] = 0.0;
return result;
}
//----------------------------------------------------------------------------
//
// Matrix Functions
//
function transpose( m )
{
if ( !m.matrix ) {
return "transpose(): trying to transpose a non-matrix";
}
var result = [];
for ( var i = 0; i < m.length; ++i ) {
result.push( [] );
for ( var j = 0; j < m[i].length; ++j ) {
result[i].push( m[j][i] );
}
}
result.matrix = true;
return result;
}
//----------------------------------------------------------------------------
//
// Vector Functions
//
function dot( u, v )
{
if ( u.length != v.length ) {
throw "dot(): vectors are not the same dimension";
}
var sum = 0.0;
for ( var i = 0; i < u.length; ++i ) {
sum += u[i] * v[i];
}
return sum;
}
//----------------------------------------------------------------------------
function negate( u )
{
var result = [];
for ( var i = 0; i < u.length; ++i ) {
result.push( -u[i] );
}
return result;
}
//----------------------------------------------------------------------------
function cross( u, v )
{
if ( !Array.isArray(u) || u.length < 3 ) {
throw "cross(): first argument is not a vector of at least 3";
}
if ( !Array.isArray(v) || v.length < 3 ) {
throw "cross(): second argument is not a vector of at least 3";
}
var result = [
u[1]*v[2] - u[2]*v[1],
u[2]*v[0] - u[0]*v[2],
u[0]*v[1] - u[1]*v[0]
];
return result;
}
//----------------------------------------------------------------------------
function length( u )
{
return Math.sqrt( dot(u, u) );
}
//----------------------------------------------------------------------------
function normalize( u, excludeLastComponent )
{
if ( excludeLastComponent ) {
var last = u.pop();
}
var len = length( u );
if ( !isFinite(len) ) {
throw "normalize: vector " + u + " has zero length";
}
for ( var i = 0; i < u.length; ++i ) {
u[i] /= len;
}
if ( excludeLastComponent ) {
u.push( last );
}
return u;
}
//----------------------------------------------------------------------------
function mix( u, v, s )
{
if ( typeof s !== "number" ) {
throw "mix: the last paramter " + s + " must be a number";
}
if ( u.length != v.length ) {
throw "vector dimension mismatch";
}
var result = [];
for ( var i = 0; i < u.length; ++i ) {
result.push( (1.0 - s) * u[i] + s * v[i] );
}
return result;
}
//----------------------------------------------------------------------------
//
// Vector and Matrix functions
//
function scale( s, u )
{
if ( !Array.isArray(u) ) {
throw "scale: second parameter " + u + " is not a vector";
}
var result = [];
for ( var i = 0; i < u.length; ++i ) {
result.push( s * u[i] );
}
return result;
}
//----------------------------------------------------------------------------
//
//
//
function flatten( v )
{
if ( v.matrix === true ) {
v = transpose( v );
}
var n = v.length;
var elemsAreArrays = false;
if ( Array.isArray(v[0]) ) {
elemsAreArrays = true;
n *= v[0].length;
}
var floats = new Float32Array( n );
if ( elemsAreArrays ) {
var idx = 0;
for ( var i = 0; i < v.length; ++i ) {
for ( var j = 0; j < v[i].length; ++j ) {
floats[idx++] = v[i][j];
}
}
}
else {
for ( var i = 0; i < v.length; ++i ) {
floats[i] = v[i];
}
}
return floats;
}
//----------------------------------------------------------------------------
var sizeof = {
'vec2' : new Float32Array( flatten(vec2()) ).byteLength,
'vec3' : new Float32Array( flatten(vec3()) ).byteLength,
'vec4' : new Float32Array( flatten(vec4()) ).byteLength,
'mat2' : new Float32Array( flatten(mat2()) ).byteLength,
'mat3' : new Float32Array( flatten(mat3()) ).byteLength,
'mat4' : new Float32Array( flatten(mat4()) ).byteLength
};
This diff is collapsed.
//////////////////////////////////////////////////////////////////////////////
//
// MV2.js
//
//////////////////////////////////////////////////////////////////////////////
//-------------------------------------------------------------------------
// printing
function printm(m)
{
if(m.length == 2)
for(var i=0; i<m.length; i++)
console.log(m[i][0], m[i][1]);
else if(m.length == 3)
for(var i=0; i<m.length; i++)
console.log(m[i][0], m[i][1], m[i][2]);
else if(m.length == 4)
for(var i=0; i<m.length; i++)
console.log(m[i][0], m[i][1], m[i][2], m[i][3]);
}
// determinants
function det2(m)
{
return m[0][0]*m[1][1]-m[0][1]*m[1][0];
}
function det3(m)
{
var d = m[0][0]*m[1][1]*m[2][2]
+ m[0][1]*m[1][2]*m[2][0]
+ m[0][2]*m[2][1]*m[1][0]
- m[2][0]*m[1][1]*m[0][2]
- m[1][0]*m[0][1]*m[2][2]
- m[0][0]*m[1][2]*m[2][1]
;
return d;
}
function det4(m)
{
var m0 = [
vec3(m[1][1], m[1][2], m[1][3]),
vec3(m[2][1], m[2][2], m[2][3]),
vec3(m[3][1], m[3][2], m[3][3])
];
var m1 = [
vec3(m[1][0], m[1][2], m[1][3]),
vec3(m[2][0], m[2][2], m[2][3]),
vec3(m[3][0], m[3][2], m[3][3])
];
var m2 = [
vec3(m[1][0], m[1][1], m[1][3]),
vec3(m[2][0], m[2][1], m[2][3]),
vec3(m[3][0], m[3][1], m[3][3])
];
var m3 = [
vec3(m[1][0], m[1][1], m[1][2]),
vec3(m[2][0], m[2][1], m[2][2]),
vec3(m[3][0], m[3][1], m[3][2])
];
return m[0][0]*det3(m0) - m[0][1]*det3(m1)
+ m[0][2]*det3(m2) - m[0][3]*det3(m3);
}
function det(m)
{
if(m.matrix != true) console.log("not a matrix");
if(m.length == 2) return det2(m);
if(m.length == 3) return det3(m);
if(m.length == 4) return det4(m);
}
//---------------------------------------------------------
// inverses
function inverse2(m)
{
var a = mat2();
var d = det2(m);
a[0][0] = m[1][1]/d;
a[0][1] = -m[0][1]/d;
a[1][0] = -m[1][0]/d;
a[1][1] = m[0][0]/d;
a.matrix = true;
return a;
}
function inverse3(m)
{
var a = mat3();
var d = det3(m);
var a00 = [
vec2(m[1][1], m[1][2]),
vec2(m[2][1], m[2][2])
];
var a01 = [
vec2(m[1][0], m[1][2]),
vec2(m[2][0], m[2][2])
];
var a02 = [
vec2(m[1][0], m[1][1]),
vec2(m[2][0], m[2][1])
];
var a10 = [
vec2(m[0][1], m[0][2]),
vec2(m[2][1], m[2][2])
];
var a11 = [
vec2(m[0][0], m[0][2]),
vec2(m[2][0], m[2][2])
];
var a12 = [
vec2(m[0][0], m[0][1]),
vec2(m[2][0], m[2][1])
];
var a20 = [
vec2(m[0][1], m[0][2]),
vec2(m[1][1], m[1][2])
];
var a21 = [
vec2(m[0][0], m[0][2]),
vec2(m[1][0], m[1][2])
];
var a22 = [
vec2(m[0][0], m[0][1]),
vec2(m[1][0], m[1][1])
];
a[0][0] = det2(a00)/d;
a[0][1] = -det2(a10)/d;
a[0][2] = det2(a20)/d;
a[1][0] = -det2(a01)/d;
a[1][1] = det2(a11)/d;
a[1][2] = -det2(a21)/d;
a[2][0] = det2(a02)/d;
a[2][1] = -det2(a12)/d;
a[2][2] = det2(a22)/d;
return a;
}
function inverse4(m)
{
var a = mat4();
var d = det4(m);
var a00 = [
vec3(m[1][1], m[1][2], m[1][3]),
vec3(m[2][1], m[2][2], m[2][3]),
vec3(m[3][1], m[3][2], m[3][3])
];
var a01 = [
vec3(m[1][0], m[1][2], m[1][3]),
vec3(m[2][0], m[2][2], m[2][3]),
vec3(m[3][0], m[3][2], m[3][3])
];
var a02 = [
vec3(m[1][0], m[1][1], m[1][3]),
vec3(m[2][0], m[2][1], m[2][3]),
vec3(m[3][0], m[3][1], m[3][3])
];
var a03 = [
vec3(m[1][0], m[1][1], m[1][2]),
vec3(m[2][0], m[2][1], m[2][2]),
vec3(m[3][0], m[3][1], m[3][2])
];
var a10 = [
vec3(m[0][1], m[0][2], m[0][3]),
vec3(m[2][1], m[2][2], m[2][3]),
vec3(m[3][1], m[3][2], m[3][3])
];
var a11 = [
vec3(m[0][0], m[0][2], m[0][3]),
vec3(m[2][0], m[2][2], m[2][3]),
vec3(m[3][0], m[3][2], m[3][3])
];
var a12 = [
vec3(m[0][0], m[0][1], m[0][3]),
vec3(m[2][0], m[2][1], m[2][3]),
vec3(m[3][0], m[3][1], m[3][3])
];
var a13 = [
vec3(m[0][0], m[0][1], m[0][2]),
vec3(m[2][0], m[2][1], m[2][2]),
vec3(m[3][0], m[3][1], m[3][2])
];
var a20 = [
vec3(m[0][1], m[0][2], m[0][3]),
vec3(m[1][1], m[1][2], m[1][3]),
vec3(m[3][1], m[3][2], m[3][3])
];
var a21 = [
vec3(m[0][0], m[0][2], m[0][3]),
vec3(m[1][0], m[1][2], m[1][3]),
vec3(m[3][0], m[3][2], m[3][3])
];
var a22 = [
vec3(m[0][0], m[0][1], m[0][3]),
vec3(m[1][0], m[1][1], m[1][3]),
vec3(m[3][0], m[3][1], m[3][3])
];
var a23 = [
vec3(m[0][0], m[0][1], m[0][2]),
vec3(m[1][0], m[1][1], m[1][2]),
vec3(m[3][0], m[3][1], m[3][2])
];
var a30 = [
vec3(m[0][1], m[0][2], m[0][3]),
vec3(m[1][1], m[1][2], m[1][3]),
vec3(m[2][1], m[2][2], m[2][3])
];
var a31 = [
vec3(m[0][0], m[0][2], m[0][3]),
vec3(m[1][0], m[1][2], m[1][3]),
vec3(m[2][0], m[2][2], m[2][3])
];
var a32 = [
vec3(m[0][0], m[0][1], m[0][3]),
vec3(m[1][0], m[1][1], m[1][3]),
vec3(m[2][0], m[2][1], m[2][3])
];
var a33 = [
vec3(m[0][0], m[0][1], m[0][2]),
vec3(m[1][0], m[1][1], m[1][2]),
vec3(m[2][0], m[2][1], m[2][2])
];
a[0][0] = det3(a00)/d;
a[0][1] = -det3(a10)/d;
a[0][2] = det3(a20)/d;
a[0][3] = -det3(a30)/d;
a[1][0] = -det3(a01)/d;
a[1][1] = det3(a11)/d;
a[1][2] = -det3(a21)/d;
a[1][3] = det3(a31)/d;
a[2][0] = det3(a02)/d;
a[2][1] = -det3(a12)/d;
a[2][2] = det3(a22)/d;
a[2][3] = -det3(a32)/d;
a[3][0] = -det3(a03)/d;
a[3][1] = det3(a13)/d;
a[3][2] = -det3(a23)/d;
a[3][3] = det3(a33)/d;
return a;
}
function inverse(m)
{
if(m.matrix != true) console.log("not a matrix");
if(m.length == 2) return inverse2(m);
if(m.length == 3) return inverse3(m);
if(m.length == 4) return inverse4(m);
}
function normalMatrix(m, flag)
{
var a = mat4();
a = inverse(transpose(m));
if(flag != true) return a;
else {
var b = mat3();
for(var i=0;i<3;i++) for(var j=0; j<3; j++) b[i][j] = a[i][j];
return b;
}
}
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
Common files:
webgl-utils.js: standard utilities from google to set up a webgl context
MV.js: our matrix/vector package. Documentation on website
initShaders.js: functions to initialize shaders in the html file
initShaders2.js: functions to initialize shaders that are in separate files
\ No newline at end of file
//
// initShaders.js
//
function initShaders( gl, vertexShaderId, fragmentShaderId )
{
var vertShdr;
var fragShdr;
var vertElem = document.getElementById( vertexShaderId );
if ( !vertElem ) {
alert( "Unable to load vertex shader " + vertexShaderId );
return -1;
}
else {
vertShdr = gl.createShader( gl.VERTEX_SHADER );
gl.shaderSource( vertShdr, vertElem.text );
gl.compileShader( vertShdr );
if ( !gl.getShaderParameter(vertShdr, gl.COMPILE_STATUS) ) {
var msg = "Vertex shader failed to compile. The error log is:"
+ "<pre>" + gl.getShaderInfoLog( vertShdr ) + "</pre>";
alert( msg );
return -1;
}
}
var fragElem = document.getElementById( fragmentShaderId );
if ( !fragElem ) {
alert( "Unable to load vertex shader " + fragmentShaderId );
return -1;
}
else {
fragShdr = gl.createShader( gl.FRAGMENT_SHADER );
gl.shaderSource( fragShdr, fragElem.text );
gl.compileShader( fragShdr );
if ( !gl.getShaderParameter(fragShdr, gl.COMPILE_STATUS) ) {
var msg = "Fragment shader failed to compile. The error log is:"
+ "<pre>" + gl.getShaderInfoLog( fragShdr ) + "</pre>";
alert( msg );
return -1;
}
}
var program = gl.createProgram();
gl.attachShader( program, vertShdr );
gl.attachShader( program, fragShdr );
gl.linkProgram( program );
if ( !gl.getProgramParameter(program, gl.LINK_STATUS) ) {
var msg = "Shader program failed to link. The error log is:"
+ "<pre>" + gl.getProgramInfoLog( program ) + "</pre>";
alert( msg );
return -1;
}
return program;
}
// Get a file as a string using AJAX
function loadFileAJAX(name) {
var xhr = new XMLHttpRequest(),
okStatus = document.location.protocol === "file:" ? 0 : 200;
xhr.open('GET', name, false);
xhr.send(null);
return xhr.status == okStatus ? xhr.responseText : null;
};
function initShaders(gl, vShaderName, fShaderName) {
function getShader(gl, shaderName, type) {
var shader = gl.createShader(type),
shaderScript = loadFileAJAX(shaderName);
if (!shaderScript) {
alert("Could not find shader source: "+shaderName);
}
gl.shaderSource(shader, shaderScript);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
alert(gl.getShaderInfoLog(shader));
return null;
}
return shader;
}
var vertexShader = getShader(gl, vShaderName, gl.VERTEX_SHADER),
fragmentShader = getShader(gl, fShaderName, gl.FRAGMENT_SHADER),
program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
alert("Could not initialise shaders");
return null;
}
return program;
};
/*
* Copyright 2010, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @fileoverview This file contains functions every webgl program will need
* a version of one way or another.
*
* Instead of setting up a context manually it is recommended to
* use. This will check for success or failure. On failure it
* will attempt to present an approriate message to the user.
*
* gl = WebGLUtils.setupWebGL(canvas);
*
* For animated WebGL apps use of setTimeout or setInterval are
* discouraged. It is recommended you structure your rendering
* loop like this.
*
* function render() {
* window.requestAnimFrame(render, canvas);
*
* // do rendering
* ...
* }
* render();
*
* This will call your rendering function up to the refresh rate
* of your display but will stop rendering if your app is not
* visible.
*/
WebGLUtils = function() {
/**
* Creates the HTLM for a failure message
* @param {string} canvasContainerId id of container of th
* canvas.
* @return {string} The html.
*/
var makeFailHTML = function(msg) {
return '' +
'<table style="background-color: #8CE; width: 100%; height: 100%;"><tr>' +
'<td align="center">' +
'<div style="display: table-cell; vertical-align: middle;">' +
'<div style="">' + msg + '</div>' +
'</div>' +
'</td></tr></table>';
};
/**
* Mesasge for getting a webgl browser
* @type {string}
*/
var GET_A_WEBGL_BROWSER = '' +
'This page requires a browser that supports WebGL.<br/>' +
'<a href="http://get.webgl.org">Click here to upgrade your browser.</a>';
/**
* Mesasge for need better hardware
* @type {string}
*/
var OTHER_PROBLEM = '' +
"It doesn't appear your computer can support WebGL.<br/>" +
'<a href="http://get.webgl.org/troubleshooting/">Click here for more information.</a>';
/**
* Creates a webgl context. If creation fails it will
* change the contents of the container of the <canvas>
* tag to an error message with the correct links for WebGL.
* @param {Element} canvas. The canvas element to create a
* context from.
* @param {WebGLContextCreationAttirbutes} opt_attribs Any
* creation attributes you want to pass in.
* @return {WebGLRenderingContext} The created context.
*/
var setupWebGL = function(canvas, opt_attribs) {
function showLink(str) {
var container = canvas.parentNode;
if (container) {
container.innerHTML = makeFailHTML(str);
}
};
if (!window.WebGLRenderingContext) {
showLink(GET_A_WEBGL_BROWSER);
return null;
}
var context = create3DContext(canvas, opt_attribs);
if (!context) {
showLink(OTHER_PROBLEM);
}
return context;
};
/**
* Creates a webgl context.
* @param {!Canvas} canvas The canvas tag to get context
* from. If one is not passed in one will be created.
* @return {!WebGLContext} The created context.
*/
var create3DContext = function(canvas, opt_attribs) {
var names = ["webgl", "experimental-webgl", "webkit-3d", "moz-webgl"];
var context = null;
for (var ii = 0; ii < names.length; ++ii) {
try {
context = canvas.getContext(names[ii], opt_attribs);
} catch(e) {}
if (context) {
break;
}
}
return context;
}
return {
create3DContext: create3DContext,
setupWebGL: setupWebGL
};
}();
/**
* Provides requestAnimationFrame in a cross browser way.
*/
window.requestAnimFrame = (function() {
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(/* function FrameRequestCallback */ callback, /* DOMElement Element */ element) {
window.setTimeout(callback, 1000/60);
};
})();
<html>
<script id="vertex-shader" type="x-shader/x-vertex">
attribute vec4 vPosition;
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;
void main()
{
gl_Position = projectionMatrix * modelViewMatrix * vPosition;
}
</script>
<script id="fragment-shader" type="x-shader/x-fragment">
precision mediump float;
void main()
{
gl_FragColor = vec4(0.3, 0.4, 0.5, 1.0);
}
</script>
<script type="text/javascript" src="../Common/webgl-utils.js"></script>
<script type="text/javascript" src="../Common/initShaders.js"></script>
<script type="text/javascript" src="../Common/MV.js"></script>
<script type="text/javascript" src="project.js"></script>
<div>
torso angle -180 <input id="slider0" type="range"
min="-180" max="180" step="10" value="0"
/>
180
</div><br/>
<div>
head2 angle -180 <input id="slider10" type="range"
min="-180" max="180" step="10" value="0"
/>
180
</div><br/>
<div>
head1 angle -180 <input id="slider1" type="range"
min="-180" max="180" step="10" value="0"
/>
180
</div><br/>
<div>
left upper arm angle -180 <input id="slider2" type="range"
min="-180" max="180" step="10" value="0"
/>
180
</div><br/>
<div>
left lower arm angle -180 <input id="slider3" type="range"
min="-180" max="180" step="10" value="0"
/>
180
</div><br/>
<div>
right upper arm angle -180 <input id="slider4" type="range"
min="-180" max="180" step="10" value="0"
/>
180
</div><br/>
<div>
right lower arm angle -180 <input id="slider5" type="range"
min="-180" max="180" step="10" value="0"
/>
180
</div><br/>
<div>
left upper leg angle -180 <input id="slider6" type="range"
min="-180" max="180" step="10" value="0"
/>
180
</div><br/>
<div>
left lower leg angle -180 <input id="slider7" type="range"
min="-180" max="180" step="10" value="0"
/>
180
</div><br/>
<div>
right upper leg angle -180 <input id="slider8" type="range"
min="-180" max="180" step="10" value="0"
/>
180
</div><br/>
<div>
right lower leg angle -180 <input id="slider9" type="range"
min="-180" max="180" step="10" value="0"
/>
180
</div><br/>
<body>
<canvas id="gl-canvas" width="512"" height="512"
Oops ... your browser doesn't support the HTML5 canvas element
</canvas>
</body>
</html>
"use strict";
var canvas;
var gl;
var program;
var projectionMatrix;
var modelViewMatrix;
var instanceMatrix;
var modelViewMatrixLoc;
var vertices = [
vec4( -0.5, -0.5, 0.5, 1.0 ),
vec4( -0.5, 0.5, 0.5, 1.0 ),
vec4( 0.5, 0.5, 0.5, 1.0 ),
vec4( 0.5, -0.5, 0.5, 1.0 ),
vec4( -0.5, -0.5, -0.5, 1.0 ),
vec4( -0.5, 0.5, -0.5, 1.0 ),
vec4( 0.5, 0.5, -0.5, 1.0 ),
vec4( 0.5, -0.5, -0.5, 1.0 )
];
var torsoId = 0;
var headId = 1;
var head1Id = 1;
var head2Id = 13;
var leftUpperArmId = 2;
var leftLowerArmId = 3;
var rightUpperArmId = 4;
var rightLowerArmId = 5;
var leftUpperLegId = 6;
var leftLowerLegId = 7;
var rightUpperLegId = 8;
var rightLowerLegId = 9;
var headUpperArmId = 10;
var headLowerArmId = 11;
var headArmHandId = 12;
var torsoHeight = 8;
var torsoWidth = 1.6;
var upperArmHeight = 3.0;
var lowerArmHeight = 3.0;
var upperArmWidth = 0.5;
var lowerArmWidth = 0.5;
var upperLegWidth = 0.5;
var lowerLegWidth = 0.5;
var lowerLegHeight = 3.0;
var upperLegHeight = 3.0;
var headHeight = 0.5;
var headWidth = 1.5;
var headUpperArmHeight = 5.0;
var headUpperArmWidth = 0.5;
var headLowerArmHeight = 5.0;
var headLowerArmWidth = 0.5;
var headArmHandHeight = 1.0;
var headArmHandWidth = 0.5;
var numNodes = 13;
var numAngles = 14;
var angle = 0;
var theta = [0, 0, 210, -60, 210, -60, 210, -60, 210, -60, 30, 120, -60, 0];
var numVertices = 24;
var stack = [];
var figure = [];
for( var i=0; i<numNodes; i++) figure[i] = createNode(null, null, null, null);
var vBuffer;
var modelViewLoc;
var pointsArray = [];
//-------------------------------------------
function scale4(a, b, c) {
var result = mat4();
result[0][0] = a;
result[1][1] = b;
result[2][2] = c;
return result;
}
//--------------------------------------------
function createNode(transform, render, sibling, child){
var node = {
transform: transform,
render: render,
sibling: sibling,
child: child,
}
return node;
}
function initNodes(Id) {
var m = mat4();
switch(Id) {
case torsoId:
m = rotate(theta[torsoId], 0, 1, 0 );
figure[torsoId] = createNode( m, torso, null, headId );
break;
case headId:
case head1Id:
case head2Id:
m = translate(0.0, torsoWidth+0.5*headHeight, torsoHeight*0.25);
m = mult(m, rotate(theta[head1Id], 1, 0, 0))
m = mult(m, rotate(theta[head2Id], 0, 1, 0));
m = mult(m, translate(0.0, -0.5*headHeight, 0.0));
figure[headId] = createNode( m, head, leftUpperArmId, headUpperArmId);
break;
case headUpperArmId:
m = translate(0.0, headHeight, 0);
m = mult(m, rotate(theta[headUpperArmId], 1, 0, 0));
figure[headUpperArmId] = createNode( m, headUpperArm, null, headLowerArmId);
break;
case headLowerArmId:
m = translate(0.0, headUpperArmHeight, 0);
m = mult(m, rotate(theta[headLowerArmId], 1, 0, 0));
figure[headLowerArmId] = createNode( m, headLowerArm, null, headArmHandId );
break;
case headArmHandId:
m = translate(0.0, headLowerArmHeight, 0);
m = mult(m, rotate(theta[headArmHandId], 1, 0, 0));
figure[headArmHandId] = createNode( m, headArmHand, null, null);
break;
case leftUpperArmId:
m = translate(-(torsoWidth*0.5), 0.5*torsoWidth, torsoHeight*0.4);
m = mult(m, rotate(theta[leftUpperArmId], 1, 0, 0));
figure[leftUpperArmId] = createNode( m, leftUpperArm, rightUpperArmId, leftLowerArmId );
break;
case rightUpperArmId:
m = translate(torsoWidth*0.5, 0.5*torsoWidth, torsoHeight*0.4);
m = mult(m, rotate(theta[rightUpperArmId], 1, 0, 0));
figure[rightUpperArmId] = createNode( m, rightUpperArm, leftUpperLegId, rightLowerArmId );
break;
case leftUpperLegId:
m = translate(-(torsoWidth*0.5), 0.5*torsoWidth, -torsoHeight*0.4);
m = mult(m , rotate(theta[leftUpperLegId], 1, 0, 0));
figure[leftUpperLegId] = createNode( m, leftUpperLeg, rightUpperLegId, leftLowerLegId );
break;
case rightUpperLegId:
m = translate(torsoWidth*0.5, 0.5*torsoWidth, -torsoHeight*0.4);
m = mult(m, rotate(theta[rightUpperLegId], 1, 0, 0));
figure[rightUpperLegId] = createNode( m, rightUpperLeg, null, rightLowerLegId );
break;
case leftLowerArmId:
m = translate(0.0, upperArmHeight*0.9, 0.0);
m = mult(m, rotate(theta[leftLowerArmId], 1, 0, 0));
figure[leftLowerArmId] = createNode( m, leftLowerArm, null, null );
break;
case rightLowerArmId:
m = translate(0.0, upperArmHeight*0.9, 0.0);
m = mult(m, rotate(theta[rightLowerArmId], 1, 0, 0));
figure[rightLowerArmId] = createNode( m, rightLowerArm, null, null );
break;
case leftLowerLegId:
m = translate(0.0, upperLegHeight*0.9, 0.0);
m = mult(m, rotate(theta[leftLowerLegId], 1, 0, 0));
figure[leftLowerLegId] = createNode( m, leftLowerLeg, null, null );
break;
case rightLowerLegId:
m = translate(0.0, upperLegHeight*0.9, 0.0);
m = mult(m, rotate(theta[rightLowerLegId], 1, 0, 0));
figure[rightLowerLegId] = createNode( m, rightLowerLeg, null, null );
break;
}
}
function traverse(Id) {
if(Id == null) return;
stack.push(modelViewMatrix);
// 원래 modelViewMatrix에서 옮기고 rotate까지
modelViewMatrix = mult(modelViewMatrix, figure[Id].transform);
// 중심점에서 조금 올리고 scaling 하기
figure[Id].render();
// traverse
if(figure[Id].child != null) traverse(figure[Id].child);
modelViewMatrix = stack.pop();
if(figure[Id].sibling != null) traverse(figure[Id].sibling);
}
function torso() {
instanceMatrix = mult(modelViewMatrix, translate(0.0, 0.5*torsoWidth, 0.0) );
instanceMatrix = mult(instanceMatrix, scale4( torsoWidth, torsoWidth, torsoHeight));
gl.uniformMatrix4fv(modelViewMatrixLoc, false, flatten(instanceMatrix));
for(var i =0; i<6; i++) gl.drawArrays(gl.TRIANGLE_FAN, 4*i, 4);
}
function head() {
instanceMatrix = mult(modelViewMatrix, translate(0.0, 0.5 * headHeight, 0.0 ));
instanceMatrix = mult(instanceMatrix, scale4(headWidth, headHeight, headWidth) );
gl.uniformMatrix4fv(modelViewMatrixLoc, false, flatten(instanceMatrix));
for(var i =0; i<6; i++) gl.drawArrays(gl.TRIANGLE_FAN, 4*i, 4);
}
function headUpperArm() {
instanceMatrix = mult(modelViewMatrix, translate(0.0, 0.5 * headUpperArmHeight, 0.0 ));
instanceMatrix = mult(instanceMatrix, scale4(headUpperArmWidth, headUpperArmHeight, headUpperArmWidth) );
gl.uniformMatrix4fv(modelViewMatrixLoc, false, flatten(instanceMatrix));
for(var i =0; i<6; i++) gl.drawArrays(gl.TRIANGLE_FAN, 4*i, 4);
}
function headLowerArm() {
instanceMatrix = mult(modelViewMatrix, translate(0.0, 0.5 * headLowerArmHeight, 0.0 ));
instanceMatrix = mult(instanceMatrix, scale4(headLowerArmWidth, headLowerArmHeight, headLowerArmWidth) );
gl.uniformMatrix4fv(modelViewMatrixLoc, false, flatten(instanceMatrix));
for(var i =0; i<6; i++) gl.drawArrays(gl.TRIANGLE_FAN, 4*i, 4);
}
function headArmHand() {
instanceMatrix = mult(modelViewMatrix, translate(0.0, 0.5 * headArmHandHeight, 0.0 ));
instanceMatrix = mult(instanceMatrix, scale4(headArmHandWidth, headArmHandHeight, headArmHandWidth) );
gl.uniformMatrix4fv(modelViewMatrixLoc, false, flatten(instanceMatrix));
for(var i =0; i<6; i++) gl.drawArrays(gl.TRIANGLE_FAN, 4*i, 4);
}
function leftUpperArm() {
instanceMatrix = mult(modelViewMatrix, translate(0.0, 0.5 * upperArmHeight, 0.0) );
instanceMatrix = mult(instanceMatrix, scale4(upperArmWidth, upperArmHeight, upperArmWidth) );
gl.uniformMatrix4fv(modelViewMatrixLoc, false, flatten(instanceMatrix));
for(var i =0; i<6; i++) gl.drawArrays(gl.TRIANGLE_FAN, 4*i, 4);
}
function leftLowerArm() {
instanceMatrix = mult(modelViewMatrix, translate(0.0, 0.5 * lowerArmHeight, 0.0) );
instanceMatrix = mult(instanceMatrix, scale4(lowerArmWidth, lowerArmHeight, lowerArmWidth) );
gl.uniformMatrix4fv(modelViewMatrixLoc, false, flatten(instanceMatrix));
for(var i =0; i<6; i++) gl.drawArrays(gl.TRIANGLE_FAN, 4*i, 4);
}
function rightUpperArm() {
instanceMatrix = mult(modelViewMatrix, translate(0.0, 0.5 * upperArmHeight, 0.0) );
instanceMatrix = mult(instanceMatrix, scale4(upperArmWidth, upperArmHeight, upperArmWidth) );
gl.uniformMatrix4fv(modelViewMatrixLoc, false, flatten(instanceMatrix));
for(var i =0; i<6; i++) gl.drawArrays(gl.TRIANGLE_FAN, 4*i, 4);
}
function rightLowerArm() {
instanceMatrix = mult(modelViewMatrix, translate(0.0, 0.5 * lowerArmHeight, 0.0) );
instanceMatrix = mult(instanceMatrix, scale4(lowerArmWidth, lowerArmHeight, lowerArmWidth) );
gl.uniformMatrix4fv(modelViewMatrixLoc, false, flatten(instanceMatrix));
for(var i =0; i<6; i++) gl.drawArrays(gl.TRIANGLE_FAN, 4*i, 4);
}
function leftUpperLeg() {
instanceMatrix = mult(modelViewMatrix, translate(0.0, 0.5 * upperLegHeight, 0.0) );
instanceMatrix = mult(instanceMatrix, scale4(upperLegWidth, upperLegHeight, upperLegWidth) );
gl.uniformMatrix4fv(modelViewMatrixLoc, false, flatten(instanceMatrix));
for(var i =0; i<6; i++) gl.drawArrays(gl.TRIANGLE_FAN, 4*i, 4);
}
function leftLowerLeg() {
instanceMatrix = mult(modelViewMatrix, translate( 0.0, 0.5 * lowerLegHeight, 0.0) );
instanceMatrix = mult(instanceMatrix, scale4(lowerLegWidth, lowerLegHeight, lowerLegWidth) );
gl.uniformMatrix4fv(modelViewMatrixLoc, false, flatten(instanceMatrix));
for(var i =0; i<6; i++) gl.drawArrays(gl.TRIANGLE_FAN, 4*i, 4);
}
function rightUpperLeg() {
instanceMatrix = mult(modelViewMatrix, translate(0.0, 0.5 * upperLegHeight, 0.0) );
instanceMatrix = mult(instanceMatrix, scale4(upperLegWidth, upperLegHeight, upperLegWidth) );
gl.uniformMatrix4fv(modelViewMatrixLoc, false, flatten(instanceMatrix));
for(var i =0; i<6; i++) gl.drawArrays(gl.TRIANGLE_FAN, 4*i, 4);
}
function rightLowerLeg() {
instanceMatrix = mult(modelViewMatrix, translate(0.0, 0.5 * lowerLegHeight, 0.0) );
instanceMatrix = mult(instanceMatrix, scale4(lowerLegWidth, lowerLegHeight, lowerLegWidth) )
gl.uniformMatrix4fv(modelViewMatrixLoc, false, flatten(instanceMatrix));
for(var i =0; i<6; i++) gl.drawArrays(gl.TRIANGLE_FAN, 4*i, 4);
}
function quad(a, b, c, d) {
pointsArray.push(vertices[a]);
pointsArray.push(vertices[b]);
pointsArray.push(vertices[c]);
pointsArray.push(vertices[d]);
}
function cube()
{
quad( 1, 0, 3, 2 );
quad( 2, 3, 7, 6 );
quad( 3, 0, 4, 7 );
quad( 6, 5, 1, 2 );
quad( 4, 5, 6, 7 );
quad( 5, 4, 0, 1 );
}
window.onload = function init() {
canvas = document.getElementById( "gl-canvas" );
gl = WebGLUtils.setupWebGL( canvas );
if ( !gl ) { alert( "WebGL isn't available" ); }
gl.viewport( 0, 0, canvas.width, canvas.height );
gl.clearColor( 1.0, 1.0, 1.0, 1.0 );
//
// Load shaders and initialize attribute buffers
//
program = initShaders( gl, "vertex-shader", "fragment-shader");
gl.useProgram( program);
instanceMatrix = mat4();
projectionMatrix = ortho(-10.0,10.0,-10.0, 10.0,-10.0,10.0);
modelViewMatrix = mat4();
gl.uniformMatrix4fv(gl.getUniformLocation( program, "modelViewMatrix"), false, flatten(modelViewMatrix) );
gl.uniformMatrix4fv( gl.getUniformLocation( program, "projectionMatrix"), false, flatten(projectionMatrix) );
modelViewMatrixLoc = gl.getUniformLocation(program, "modelViewMatrix")
cube();
vBuffer = gl.createBuffer();
gl.bindBuffer( gl.ARRAY_BUFFER, vBuffer );
gl.bufferData(gl.ARRAY_BUFFER, flatten(pointsArray), gl.STATIC_DRAW);
var vPosition = gl.getAttribLocation( program, "vPosition" );
gl.vertexAttribPointer( vPosition, 4, gl.FLOAT, false, 0, 0 );
gl.enableVertexAttribArray( vPosition );
document.getElementById("slider0").onchange = function(event) {
theta[torsoId ] = event.target.value;
initNodes(torsoId);
};
document.getElementById("slider1").onchange = function(event) {
theta[head1Id] = event.target.value;
initNodes(head1Id);
};
document.getElementById("slider2").onchange = function(event) {
theta[leftUpperArmId] = event.target.value;
initNodes(leftUpperArmId);
};
document.getElementById("slider3").onchange = function(event) {
theta[leftLowerArmId] = event.target.value;
initNodes(leftLowerArmId);
};
document.getElementById("slider4").onchange = function(event) {
theta[rightUpperArmId] = event.target.value;
initNodes(rightUpperArmId);
};
document.getElementById("slider5").onchange = function(event) {
theta[rightLowerArmId] = event.target.value;
initNodes(rightLowerArmId);
};
document.getElementById("slider6").onchange = function(event) {
theta[leftUpperLegId] = event.target.value;
initNodes(leftUpperLegId);
};
document.getElementById("slider7").onchange = function(event) {
theta[leftLowerLegId] = event.target.value;
initNodes(leftLowerLegId);
};
document.getElementById("slider8").onchange = function(event) {
theta[rightUpperLegId] = event.target.value;
initNodes(rightUpperLegId);
};
document.getElementById("slider9").onchange = function(event) {
theta[rightLowerLegId] = event.target.value;
initNodes(rightLowerLegId);
};
document.getElementById("slider10").onchange = function(event) {
theta[head2Id] = event.target.value;
initNodes(head2Id);
};
for(i=0; i<numNodes; i++) initNodes(i);
render();
}
var render = function() {
gl.clear( gl.COLOR_BUFFER_BIT );
theta[head2Id] += 1;
initNodes(head2Id);
traverse(torsoId);
requestAnimFrame(render);
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment