Skip to content

Instantly share code, notes, and snippets.

@Maciek416
Forked from cjcliffe/CubicVR.js
Created April 20, 2010 02:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save Maciek416/371960 to your computer and use it in GitHub Desktop.
Save Maciek416/371960 to your computer and use it in GitHub Desktop.
/*
Javascript port of CubicVR 3D engine for WebGL
by Charles J. Cliffe
http://www.cubicvr.org/
May be used under the terms of LGPL v3.0 or greater.
*/
var CubicVR = null;
var CubicVR_GLCore = new Object();
var CubicVR_Materials = Array();
var CubicVR_Material_ref = Array();
var M_PI = 3.1415926535897932384626433832795028841968;
var M_TWO_PI = 2.0*M_PI;
/* Transform Controller */
cubicvr_transform = function(init_mat)
{
this.m_stack = new Array();
this.m_cache = new Array();
this.c_stack = 0;
this.valid = 0;
this.result = null;
if (typeof(init_mat)!='undefined')
{
this.m_stack[0] = init_mat;
}
else
{
this.setIdentity();
}
}
CubicVR_GLCore.init = function(gl_in)
{
CubicVR_GLCore.gl = gl_in;
}
cubicvr_transform.prototype.setIdentity = function()
{
this.m_stack[this.c_stack] = this.getIdentity();
if (this.valid == this.c_stack && this.c_stack) this.valid--;
}
cubicvr_transform.prototype.getIdentity = function()
{
return [ 1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0 ];
}
cubicvr_transform.prototype.invalidate = function()
{
this.valid = 0;
this.result = null;
}
cubicvr_transform.prototype.getResult = function()
{
if (!this.c_stack) return this.m_stack[0];
if (this.valid != this.c_stack)
{
if (this.valid > this.c_stack)
{
while (this.valid > this.c_stack+1)
{
this.valid--;
this.m_cache.pop();
}
}
else
{
for (var i = this.valid; i <= this.c_stack; i++)
{
this.m_cache[i] = (i==0)?this.m_stack[0]:this.multiply4_4by4_4(this.m_cache[i-1],this.m_stack[i]);
this.valid++;
}
}
this.result = this.m_cache[this.valid-1];
}
return this.result;
}
cubicvr_transform.prototype.pushMatrix = function(m)
{
this.c_stack++;
this.m_stack.push(m?m:this.getIdentity());
}
cubicvr_transform.prototype.popMatrix = function()
{
if (this.c_stack==0) return;
this.c_stack--;
return this.m_stack.pop();
}
cubicvr_transform.prototype.multiply4_4by4_4 = function(m1, m2)
{
var mOut = new Array();
mOut[0] = m2[0]* m1[0]+m2[4]* m1[1]+ m2[8]*m1[2]+ m2[12]*m1[3];
mOut[1] = m2[1]* m1[0]+m2[5]* m1[1]+ m2[9]*m1[2]+ m2[13]*m1[3];
mOut[2] = m2[2]* m1[0]+m2[6]* m1[1]+m2[10]*m1[2]+ m2[14]*m1[3];
mOut[3] = m2[3]* m1[0]+m2[7]* m1[1]+m2[11]*m1[2]+ m2[15]*m1[3];
mOut[4] = m2[0]* m1[4]+m2[4]* m1[5]+ m2[8]*m1[6]+ m2[12]*m1[7];
mOut[5] = m2[1]* m1[4]+m2[5]* m1[5]+ m2[9]*m1[6]+ m2[13]*m1[7];
mOut[6] = m2[2]* m1[4]+m2[6]* m1[5]+m2[10]*m1[6]+ m2[14]*m1[7];
mOut[7] = m2[3]* m1[4]+m2[7]* m1[5]+m2[11]*m1[6]+ m2[15]*m1[7];
mOut[8] = m2[0]* m1[8]+m2[4]* m1[9]+ m2[8]*m1[10]+m2[12]*m1[11];
mOut[9] = m2[1]* m1[8]+m2[5]* m1[9]+ m2[9]*m1[10]+m2[13]*m1[11];
mOut[10] = m2[2]* m1[8]+m2[6]* m1[9]+m2[10]*m1[10]+m2[14]*m1[11];
mOut[11] = m2[3]* m1[8]+m2[7]* m1[9]+m2[11]*m1[10]+m2[15]*m1[11];
mOut[12] = m2[0]*m1[12]+m2[4]*m1[13]+ m2[8]*m1[14]+m2[12]*m1[15];
mOut[13] = m2[1]*m1[12]+m2[5]*m1[13]+ m2[9]*m1[14]+m2[13]*m1[15];
mOut[14] = m2[2]*m1[12]+m2[6]*m1[13]+m2[10]*m1[14]+m2[14]*m1[15];
mOut[15] = m2[3]*m1[12]+m2[7]*m1[13]+m2[11]*m1[14]+m2[15]*m1[15];
return mOut;
}
cubicvr_transform.prototype.m_mat = cubicvr_transform.prototype.multiply4_4by4_4;
cubicvr_transform.prototype.multiply1_4by4_4 = function(m1, m2)
{
var mOut = new Array();
mOut[0] = m2[0]* m1[0]+ m2[4]* m1[1]+ m2[8]* m1[2]+ m2[12]*m1[3];
mOut[1] = m2[1]* m1[0]+ m2[5]* m1[1]+ m2[9]* m1[2]+ m2[13]*m1[3];
mOut[2] = m2[2]* m1[0]+ m2[6]* m1[1]+ m2[10]*m1[2]+ m2[14]*m1[3];
mOut[3] = m2[3]* m1[0]+ m2[7]* m1[1]+ m2[11]*m1[2]+ m2[15]*m1[3];
return mOut;
}
cubicvr_transform.prototype.m_vector = cubicvr_transform.prototype.multiply1_4by4_4;
cubicvr_transform.prototype.multiply1_3by4_4 = function(m1, m2)
{
var mOut = new Array();
mOut[0] = m2[0]* m1[0]+ m2[4]* m1[1]+ m2[8]* m1[2]+ m2[12];
mOut[1] = m2[1]* m1[0]+ m2[5]* m1[1]+ m2[9]* m1[2]+ m2[13];
mOut[2] = m2[2]* m1[0]+ m2[6]* m1[1]+ m2[10]*m1[2]+ m2[14];
return mOut;
}
cubicvr_transform.prototype.m_point = cubicvr_transform.prototype.multiply1_3by4_4;
cubicvr_transform.prototype.translate = function(x, y, z)
{
var m = this.getIdentity();
m[12] = x;
m[13] = y;
m[14] = z;
this.m_stack[this.c_stack] = this.multiply4_4by4_4(this.m_stack[this.c_stack],m);
if (this.valid == this.c_stack && this.c_stack) this.valid--;
}
cubicvr_transform.prototype.scale = function(x, y, z)
{
var m = this.getIdentity();
m[0] = x;
m[5] = y;
m[10] = z;
this.m_stack[this.c_stack] = this.multiply4_4by4_4(this.m_stack[this.c_stack],m);
if (this.valid == this.c_stack && this.c_stack) this.valid--;
}
cubicvr_transform.prototype.rotate = function(ang, x, y, z)
{
var sAng,cAng
if (x||y||z)
{
sAng = Math.sin(ang*(M_PI/180.0));
cAng = Math.cos(ang*(M_PI/180.0));
}
if (x)
{
var X_ROT = this.getIdentity();
X_ROT[5] = cAng*x;
X_ROT[9] = sAng*x;
X_ROT[6] = -sAng*x;
X_ROT[10] = cAng*x;
this.m_stack[this.c_stack] = this.multiply4_4by4_4(this.m_stack[this.c_stack],X_ROT);
}
if (y)
{
var Y_ROT = this.getIdentity();
Y_ROT[0] = cAng*y;
Y_ROT[8] = -sAng*y;
Y_ROT[2] = sAng*y;
Y_ROT[10] = cAng*y;
this.m_stack[this.c_stack] = this.multiply4_4by4_4(this.m_stack[this.c_stack],Y_ROT);
}
if (z)
{
var Z_ROT = this.getIdentity();
Z_ROT[0] = cAng*z;
Z_ROT[4] = sAng*z;
Z_ROT[1] = -sAng*z;
Z_ROT[5] = cAng*z;
this.m_stack[this.c_stack] = this.multiply4_4by4_4(this.m_stack[this.c_stack],Z_ROT);
}
if (this.valid == this.c_stack && this.c_stack) this.valid--;
}
var cubicvr_xyz = function(x,y,z) { return [x?x:0,y?y:0,z?z:0]; }
var cubicvr_rgb = function(r,g,b) { return [r?r:0,g?g:0,b?b:0]; }
var cubicvr_rgba = function(r,g,b,a) { return [r?r:0,g?g:0,b?b:0,a?a:0]; }
var cubicvr_calcNormal = function(pt1,pt2,pt3)
{
var v1 = cubicvr_xyz(pt1[0] - pt2[0],
pt1[1] - pt2[1],
pt1[2] - pt2[2]);
var v2 = cubicvr_xyz(pt2[0] - pt3[0],
pt2[1] - pt3[1],
pt2[2] - pt3[2]);
return cubicvr_xyz(v1[1]*v2[2] - v1[2]*v2[1],
v1[2]*v2[0] - v1[0]*v2[2],
v1[0]*v2[1] - v1[1]*v2[0]);
}
var cubicvr_normalize = function(pt)
{
var d = Math.sqrt(pt[0]*pt[0]+pt[1]*pt[1]+pt[2]*pt[2]);
return cubicvr_xyz(pt[0]/d,pt[1]/d,pt[2]/d);
}
var cubicvr_length = function(pt)
{
return Math.sqrt(pt[0]*pt[0]+pt[1]*pt[1]+pt[2]*pt[2]);
}
var cubicvr_dp = function(v1,v2)
{
return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2];
}
var cubicvr_angle = function(v1,v2)
{
return Math.acos( cubicvr_dp(v1,v2) / (cubicvr_length(v1)*cubicvr_length(v2)) );
};
var cubicvr_crossProduct = function(vectA, vectB)
{
return cubicvr_xyz(vectA[1] * vectB[2] - vectB[1] * vectA[2],
vectA[2] * vectB[0] - vectB[2] * vectA[0],
vectA[0] * vectB[1] - vectB[0] * vectA[1]);
};
/* Projection / Modelview matrix manipulation */
var cubicvr_perspective = function(fovy, aspect, near, far)
{
var yFac = Math.tan(fovy * M_PI / 360.0);
var xFac = yFac*aspect;
return [
1.0 / xFac, 0, 0, 0,
0, 1.0 / yFac, 0, 0,
0, 0, -(far+near)/(far-near), -1,
0, 0, -(2.0*far*near)/(far-near), 0
];
}
var cubicvr_lookat = function(eyeX, eyeY, eyeZ, lookAtX, lookAtY, lookAtZ, upX, upY, upZ)
{
var view_vec = cubicvr_normalize([lookAtX - eyeX, lookAtY - eyeY, lookAtZ - eyeZ]);
var up_vec = cubicvr_normalize([upX, upY, upZ]);
var s = cubicvr_crossProduct(view_vec,up_vec);
var u = cubicvr_crossProduct(s,view_vec);
var mat = [
s[0], u[0], -view_vec[0], 0,
s[1], u[1], -view_vec[1], 0,
s[2], u[2], -view_vec[2], 0,
0, 0, 0, 1
];
trans = new cubicvr_transform();
trans.translate(-eyeX,-eyeY,-eyeZ);
trans.pushMatrix(mat);
mat = trans.getResult();
return mat;
}
var cubicvr_getShader = function(gl, id)
{
var shaderScript = document.getElementById(id);
if (!shaderScript) {
return null;
}
var str = "";
var k = shaderScript.firstChild;
while (k) {
if (k.nodeType == 3) {
str += k.textContent;
}
k = k.nextSibling;
}
var shader;
if (shaderScript.type == "x-shader/x-fragment") {
shader = gl.createShader(gl.FRAGMENT_SHADER);
} else if (shaderScript.type == "x-shader/x-vertex") {
shader = gl.createShader(gl.VERTEX_SHADER);
} else {
return null;
}
gl.shaderSource(shader, str);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
alert(gl.getShaderInfoLog(shader));
return null;
}
return shader;
}
/* Faces */
cubicvr_face = function()
{
this.points = new Array();
this.point_normals = new Array();
this.normal = null;
this.material = 0;
this.segment = 0;
}
cubicvr_face.prototype.setUV = function(uvs,point_num)
{
if (typeof(this.uvs) == 'undefined') this.uvs = new Array();
if (typeof(point_num)!='undefined')
{
this.uvs[point_num] = uvs;
}
else
{
if (uvs.length!=2) this.uvs = uvs;
else this.uvs.push(uvs);
}
}
cubicvr_object = function()
{
this.points = new Array(); // point list
this.faces = new Array(); // faces with point references
this.currentFace = -1; // start with no faces
this.currentMaterial = 0; // null material
this.compiled = null; // VBO data
}
cubicvr_object.prototype.addPoint = function(p)
{
if (p.length != 3)
{
for (var i = 0; i < p.length; i++)
{
this.points.push(p[i]);
}
}
else
{
this.points.push(p);
}
return this.points.length-1;
}
cubicvr_object.prototype.setFaceMaterial = function(mat)
{
this.currentMaterial = (typeof(mat)=='object')?mat.material_id:mat;
if (this.currentFace > -1)
{
faces[this.currentFace].material = this.currentMaterial;
}
}
cubicvr_object.prototype.addFace = function(p_list,face_num,face_mat)
{
if (typeof(face_num)=='undefined')
{
this.currentFace = this.faces.length;
this.faces.push(new cubicvr_face());
}
else
{
if (typeof(this.faces[face_num])=='undefined')
{
this.faces[face_num] = new cubicvr_face();
}
this.currentFace = face_num;
}
if (typeof(p_list)=='object')
{
this.faces[this.currentFace].points = p_list;
}
if (typeof(face_mat)!='undefined')
{
this.faces[this.currentFace].material = (typeof(face_mat)=='object')?face_mat.material_id:face_mat;
}
else
{
this.faces[this.currentFace].material = this.currentMaterial;
}
return this.currentFace;
}
cubicvr_object.prototype.triangulateQuads = function()
{
for (var i = 0; i < this.faces.length; i++)
{
if (this.faces[i].points.length == 4)
{
var p = this.faces.length;
this.addFace([this.faces[i].points[2],this.faces[i].points[3],this.faces[i].points[0]],this.faces.length,this.faces[i].material);
this.faces[i].points.pop();
this.faces[p].normal = this.faces[i].normal;
if (typeof(this.faces[i].uvs) != 'undefined')
{
if (this.faces[i].uvs.length == 4)
{
this.faces[p].setUV(this.faces[i].uvs[2],0);
this.faces[p].setUV(this.faces[i].uvs[3],1);
this.faces[p].setUV(this.faces[i].uvs[0],2);
this.faces[i].uvs.pop();
}
}
if (this.faces[i].point_normals.length == 4)
{
this.faces[p].point_normals[0] = this.faces[i].point_normals[2];
this.faces[p].point_normals[1] = this.faces[i].point_normals[3];
this.faces[p].point_normals[2] = this.faces[i].point_normals[0];
this.faces[i].point_normals.pop();
}
}
}
}
cubicvr_object.prototype.calcFaceNormals = function()
{
for (var i = 0; i < this.faces.length; i++)
{
if (this.faces[i].points.length < 3)
{
this.faces[i].normal = cubicvr_xyz(0,0,0);
continue;
}
this.faces[i].normal = cubicvr_normalize(cubicvr_calcNormal(this.points[this.faces[i].points[0]],
this.points[this.faces[i].points[1]],
this.points[this.faces[i].points[2]]));
}
}
cubicvr_object.prototype.calcNormals = function()
{
this.calcFaceNormals();
point_smoothRef = new Array();
// build a quick list of point/face sharing
for (var i = 0; i < this.faces.length; i++)
{
for (var j = 0; j < this.faces[i].points.length; j++)
{
var idx = this.faces[i].points[j];
if (typeof(point_smoothRef[idx])=='undefined') point_smoothRef[idx] = new Array();
point_smoothRef[idx].push([i,j]);
}
}
// step through smoothing references and compute normals
for (var i in point_smoothRef)
{
var numPts = point_smoothRef[i].length;
for (var j = 0; j < numPts; j++)
{
var ptCount = 1;
var faceNum = point_smoothRef[i][j][0];
var pointNum = point_smoothRef[i][j][1];
var max_smooth = CubicVR_Materials[this.faces[faceNum].material].max_smooth;
// set point to it's face's normal
this.faces[faceNum].point_normals[pointNum] = this.faces[faceNum].normal;
// step through all other faces which share this point
for (var k = 0; k < numPts; k++)
{
if (j==k) continue; // don't include self in comparison
var faceRefNum = point_smoothRef[i][k][0];
var ang = cubicvr_angle(this.faces[faceRefNum].normal, this.faces[faceNum].normal);
// note that ang != ang tests for infinity case where the normals are the same
if (ang != ang || ang*(180.0/M_PI) <= max_smooth)
{
this.faces[faceNum].point_normals[pointNum][0] += this.faces[faceRefNum].normal[0];
this.faces[faceNum].point_normals[pointNum][1] += this.faces[faceRefNum].normal[1];
this.faces[faceNum].point_normals[pointNum][2] += this.faces[faceRefNum].normal[2];
ptCount++;
}
}
this.faces[faceNum].point_normals[pointNum][0] /= ptCount;
this.faces[faceNum].point_normals[pointNum][1] /= ptCount;
this.faces[faceNum].point_normals[pointNum][2] /= ptCount;
this.faces[faceNum].point_normals[pointNum] = cubicvr_normalize(this.faces[faceNum].point_normals[pointNum]);
}
}
}
var cubicvr_vtx_eq = function(a,b)
{
if ((typeof(a)=='undefined' && typeof(a)!='undefined') ||
(typeof(b)=='undefined' && typeof(b)!='undefined'))
return false;
if (typeof(a)=='undefined'&&typeof(b)=='undefined') return true;
return (a[0]==b[0]&&a[1]==b[1]&&a[2]==b[2]);
}
var cubicvr_uv_eq = function(a,b)
{
if ((typeof(a)=='undefined' && typeof(a)!='undefined') ||
(typeof(b)=='undefined' && typeof(b)!='undefined'))
return false;
if (typeof(a)=='undefined'&&typeof(b)=='undefined') return true;
return (a[0]==b[0]&&a[1]==b[1]);
}
cubicvr_object.prototype.compile = function()
{
this.compiled = new Object();
var compileRef = new Array();
for (var i = 0; i < this.faces.length; i++)
{
if (this.faces[i].points.length==3)
{
var matId = this.faces[i].material; // cj sucks
var segId = this.faces[i].segment;
if (typeof(compileRef[matId])=='undefined') compileRef[matId] = new Array();
if (typeof(compileRef[matId][segId])=='undefined') compileRef[matId][segId] = new Array();
compileRef[matId][segId].push(i);
}
}
var vtxRef = new Array();
this.compiled.vbo_normals = new Array();
this.compiled.vbo_points = new Array();
this.compiled.vbo_uvs = new Array();
for (var i in compileRef)
{
for (var j in compileRef[i])
{
var idxCount = 0;
for (var k = 0; k < compileRef[i][j].length; k++)
{
var faceNum = compileRef[i][j][k];
var found = false;
var hasUV = false;
var hasNorm = false;
for (var x = 0; x < 3; x++)
{
var ptNum = this.faces[faceNum].points[x];
hasUV = (this.faces[faceNum].uvs.length != 0);
hasNorm = (this.faces[faceNum].point_normals.length != 0);
var foundPt = -1;
if (typeof(vtxRef[ptNum])!='undefined')
{
for (var y = 0; y < vtxRef[ptNum].length; y++)
{
// face / point
var oFace = vtxRef[ptNum][y][0] // faceNum
var oPoint = vtxRef[ptNum][y][1]; // pointNum
var oIndex = vtxRef[ptNum][y][2]; // index
foundPt = oIndex;
if (hasNorm)
{
foundPt = (cubicvr_vtx_eq(
this.faces[oFace].point_normals[oPoint],
this.faces[faceNum].point_normals[x])
)?oIndex:-1;
}
if (hasUV)
{
foundPt = (cubicvr_uv_eq(
this.faces[oFace].uvs[oPoint],
this.faces[faceNum].uvs[x])
)?oIndex:-1;
}
}
}
if (foundPt!=-1)
{
if (typeof(this.compiled.elements)=='undefined') this.compiled.elements = new Array();
if (typeof(this.compiled.elements[i])=='undefined') this.compiled.elements[i] = new Array();
if (typeof(this.compiled.elements[i][j])=='undefined') this.compiled.elements[i][j] = new Array();
this.compiled.elements[i][j].push(foundPt);
}
else
{
this.compiled.vbo_points.push(this.points[ptNum][0]);
this.compiled.vbo_points.push(this.points[ptNum][1]);
this.compiled.vbo_points.push(this.points[ptNum][2]);
if (hasNorm)
{
this.compiled.vbo_normals.push(this.faces[faceNum].point_normals[x][0]);
this.compiled.vbo_normals.push(this.faces[faceNum].point_normals[x][1]);
this.compiled.vbo_normals.push(this.faces[faceNum].point_normals[x][2]);
}
if (hasUV)
{
this.compiled.vbo_uvs.push(this.faces[faceNum].uvs[x][0]);
this.compiled.vbo_uvs.push(this.faces[faceNum].uvs[x][1]);
}
if (typeof(this.compiled.elements)=='undefined') this.compiled.elements = new Array();
if (typeof(this.compiled.elements[i])=='undefined') this.compiled.elements[i] = new Array();
if (typeof(this.compiled.elements[i][j])=='undefined') this.compiled.elements[i][j] = new Array();
this.compiled.elements[i][j].push(idxCount);
if (typeof(vtxRef[ptNum])=='undefined') vtxRef[ptNum] = new Array();
vtxRef[ptNum].push([faceNum,x,idxCount]);
idxCount++;
}
}
}
}
}
this.compiled.gl_points = CubicVR_GLCore.gl.createBuffer();
CubicVR_GLCore.gl.bindBuffer(CubicVR_GLCore.gl.ARRAY_BUFFER, this.compiled.gl_points);
CubicVR_GLCore.gl.bufferData(CubicVR_GLCore.gl.ARRAY_BUFFER, new WebGLFloatArray(this.compiled.vbo_points), CubicVR_GLCore.gl.STATIC_DRAW);
this.compiled.gl_normals = CubicVR_GLCore.gl.createBuffer();
CubicVR_GLCore.gl.bindBuffer(CubicVR_GLCore.gl.ARRAY_BUFFER, this.compiled.gl_normals);
CubicVR_GLCore.gl.bufferData(CubicVR_GLCore.gl.ARRAY_BUFFER, new WebGLFloatArray(this.compiled.vbo_normals), CubicVR_GLCore.gl.STATIC_DRAW);
this.compiled.gl_uvs = CubicVR_GLCore.gl.createBuffer();
CubicVR_GLCore.gl.bindBuffer(CubicVR_GLCore.gl.ARRAY_BUFFER, this.compiled.gl_uvs);
CubicVR_GLCore.gl.bufferData(CubicVR_GLCore.gl.ARRAY_BUFFER, new WebGLFloatArray(this.compiled.vbo_uvs), CubicVR_GLCore.gl.STATIC_DRAW);
var gl_elements = new Array();
for (var i in this.compiled.elements)
{
for (var j in this.compiled.elements[i])
{
for (var k in this.compiled.elements[i][j])
{
gl_elements.push(this.compiled.elements[i][j][k]);
}
}
}
this.compiled.gl_elements = CubicVR_GLCore.gl.createBuffer();
CubicVR_GLCore.gl.bindBuffer(CubicVR_GLCore.gl.ELEMENT_ARRAY_BUFFER, this.compiled.gl_elements);
CubicVR_GLCore.gl.bufferData(CubicVR_GLCore.gl.ELEMENT_ARRAY_BUFFER, new WebGLUnsignedShortArray(gl_elements), CubicVR_GLCore.gl.STATIC_DRAW);
// console.log("vtx Ref: ",vtxRef);
// console.log("vtx Compiled Elems: ",this.compiled);
}
/* Projection enums */
var UV_PROJECTION_UV = 0;
var UV_PROJECTION_PLANAR = 1;
var UV_PROJECTION_CYLINDRICAL = 2;
var UV_PROJECTION_SPHERICAL = 3;
var UV_PROJECTION_CUBIC = 4;
/* Axis enums */
var UV_AXIS_X = 0;
var UV_AXIS_Y = 1;
var UV_AXIS_Z = 2;
cubicvr_uvmapper = function()
{
this.rotation = cubicvr_xyz(0,0,0);
this.scale = cubicvr_xyz(1,1,1);
this.center = cubicvr_xyz(0,0,0);
this.projection_mode = UV_PROJECTION_PLANAR;
this.projection_axis = UV_AXIS_X;
this.wrap_w_count = 1;
this.wrap_h_count = 1;
}
// convert XYZ space to longitude
var xyz_to_h = function(x, y, z)
{
var h
if (x == 0 && z == 0)
{
h = 0;
}
else
{
if (z == 0)
{
h = (x < 0) ? M_HALF_PI : -M_HALF_PI;
}
else if (z < 0)
{
h = -Math.atan(x / z) + M_PI;
}
else
{
h = -Math.atan(x / z);
}
}
return h;
};
// convert XYZ space to latitude and longitude
var xyz_to_hp = function(x,y,z)
{
var h,p;
if (x == 0 && z == 0)
{
h = 0;
if (y != 0)
{
p = (y < 0) ? -M_HALF_PI : M_HALF_PI;
}
else
{
p = 0;
}
}
else
{
if (z == 0)
{
h = (x < 0) ? M_HALF_PI : -M_HALF_PI;
}
else if (z < 0)
{
h = -Math.atan(x / z) + M_PI;
}
else
{
h = -Math.atan(x / z);
}
x = Math.sqrt(x * x + z * z);
if (x == 0)
{
p = (y < 0) ? -M_HALF_PI : M_HALF_PI;
}
else
{
p = Math.atan(y / x);
}
}
return [h,p];
};
cubicvr_uvmapper.prototype.apply = function(obj,mat_num,seg_num)
{
var u,v,s,t,lat,lon;
var trans = new cubicvr_transform();
var transformed = false;
var t_result = null;
if (this.center[0]||this.center[1]||this.center[2])
{
trans.translate(-this.center[0],-this.center[1],-this.center[2]);
transformed = true;
}
if (this.rotation[0]||this.rotation[1]||this.rotation[2])
{
if (this.rotation[0]) trans.rotate(this.rotation[2],0,0,1);
if (this.rotation[1]) trans.rotate(this.rotation[1],0,1,0);
if (this.rotation[2]) trans.rotate(this.rotation[0],1,0,0);
transformed = true;
}
if (transformed) t_result = trans.getResult();
if (typeof(mat_num)=='object') mat_num = mat_num.material_id;
for (var i = 0; i < obj.faces.length; i++)
{
if (obj.faces[i].material != mat_num) continue;
if (typeof(seg_num) != 'undefined') if (obj.faces[i].segment != seg_num) continue;
var nx, ny, nz;
if (this.projection_mode == UV_PROJECTION_CUBIC)
{
nx = Math.abs(obj.faces[i].normal[0]);
ny = Math.abs(obj.faces[i].normal[1]);
nz = Math.abs(obj.faces[i].normal[2]);
}
for (j = 0; j < obj.faces[i].points.length; j++)
{
var uvpoint = obj.points[obj.faces[i].points[j]];
if (transformed) uvpoint = trans.m_point(uvpoint,t_result);
/* calculate the uv for the points referenced by this face's pointref vector */
switch (this.projection_mode)
{
case UV_PROJECTION_CUBIC: /* cubic projection needs to know the surface normal */
/* x portion of vector is dominant, we're mapping in the Y/Z plane */
if (nx >= ny && nx >= nz)
{
/* we use a .5 offset because texture coordinates range from 0->1, so to center it we need to offset by .5 */
s = uvpoint[2] / this.scale[2] + 0.5; /* account for scale here */
t = uvpoint[1] / this.scale[1] + 0.5;
}
/* y portion of vector is dominant, we're mapping in the X/Z plane */
if (ny >= nx && ny >= nz)
{
s = -uvpoint[0] / this.scale[0] + 0.5;
t = uvpoint[2] / this.scale[2] + 0.5;
}
/* z portion of vector is dominant, we're mapping in the X/Y plane */
if (nz >= nx && nz >= ny)
{
s = -uvpoint[0] / this.scale[0] + 0.5;
t = uvpoint[1] / this.scale[1] + 0.5;
}
if (obj.faces[i].normal[0] > 0) { s = -s; }
if (obj.faces[i].normal[1] < 0) { s = -s; }
if (obj.faces[i].normal[2] > 0) { s = -s; }
obj.faces[i].setUV([s,t],j);
break;
case UV_PROJECTION_PLANAR:
s = ((this.projection_axis == UV_AXIS_X) ? uvpoint[2] / scale[2] + 0.5 : -uvpoint[0] / scale[0] + 0.5);
t = ((this.projection_axis == UV_AXIS_Y) ? uvpoint[2] / scale[2] + 0.5 : uvpoint[1] / scale[1] + 0.5);
obj.faces[i].setUV([s,t],j);
break;
case UV_PROJECTION_CYLINDRICAL:
// Cylindrical is a little more tricky, we map based on the degree around the center point
switch (this.projection_axis)
{
case UV_AXIS_X:
// xyz_to_h takes the point and returns a value representing the 'unwrapped' height position of this point
lon = xyz_to_h(uvpoint[2],uvpoint[0],-uvpoint[1]);
t = -uvpoint[0] / scale[0] + 0.5;
break;
case UV_AXIS_Y:
lon = xyz_to_h(-uvpoint[0],uvpoint[1],uvpoint[2]);
t = -uvpoint[1] / scale[1] + 0.5;
break;
case UV_AXIS_Z:
lon = xyz_to_h(-uvpoint[0],uvpoint[2],-uvpoint[1]);
t = -uvpoint[2] / scale[2] + 0.5;
break;
}
// convert it from radian space to texture space 0 to 1 * wrap, TWO_PI = 360 degrees
lon = 1.0 - lon / (M_TWO_PI);
if (this.wrap_w_count != 1.0) lon = lon * this.wrap_w_count;
u = lon;
v = t;
obj.faces[i].setUV([u,v],j);
break;
case UV_PROJECTION_SPHERICAL:
var latlon;
// spherical is similar to cylindrical except we also unwrap the 'width'
switch(this.projection_axis)
{
case UV_AXIS_X:
// xyz to hp takes the point value and 'unwraps' the latitude and longitude that projects to that point
latlon = xyz_to_hp(uvpoint[2],uvpoint[0],-uvpoint[1]);
break;
case UV_AXIS_Y:
latlon = xyz_to_hp(uvpoint[0],-uvpoint[1],uvpoint[2]);
break;
case UV_AXIS_Z:
latlon = xyz_to_hp(-uvpoint[0],uvpoint[2],-uvpoint[1]);
break;
}
// convert longitude and latitude to texture space coordinates, multiply by wrap height and width
lon = 1.0 - latlon[0] / M_TWO_PI;
lat = 0.5 - latlon[1] / M_PI;
if (this.wrap_w_count != 1.0) lon = lon * this.wrap_w_count;
if (this.wrap_h_count != 1.0) lat = lat * this.wrap_h_count;
u = lon;
v = lat;
obj.faces[i].setUV([u,v],j);
break;
// case UV_PROJECTION_UV:
// // not handled here..
// break;
default: // else mapping cannot be handled here, this shouldn't have happened :P
u = 0;
v = 0;
obj.faces[i].setUV([u,v],j);
break;
}
}
}
}
cubicvr_material = function(mat_name)
{
if (typeof(mat_name)!='undefined')
{
CubicVR_Material_ref[mat_name] = this;
}
this.material_id = CubicVR_Materials.length;
CubicVR_Materials.push(this);
this.diffuse = new cubicvr_rgb(0.5,0.5,0.5);
this.specular = new cubicvr_rgb(0.5,0.5,0.5);
this.shininess = 1.0;
this.max_smooth = 60.0;
}
var UNIFORM_TYPE_MATRIX = 0;
var UNIFORM_TYPE_VECTOR = 1;
var UNIFORM_TYPE_FLOAT = 2;
var UNIFORM_TYPE_ARRAY_VERTEX = 3;
var UNIFORM_TYPE_ARRAY_UV = 4;
var UNIFORM_TYPE_ARRAY_FLOAT = 5;
cubicvr_shader = function(vs_id,fs_id)
{
this.uniforms = new Array();
this.uniform_type = new Array();
vertexShader = cubicvr_getShader(gl, vs_id);
fragmentShader = cubicvr_getShader(gl, fs_id);
this.shader = CubicVR_GLCore.gl.createProgram();
CubicVR_GLCore.gl.attachShader(this.shader, vertexShader);
CubicVR_GLCore.gl.attachShader(this.shader, fragmentShader);
CubicVR_GLCore.gl.linkProgram(this.shader);
if (!CubicVR_GLCore.gl.getProgramParameter(this.shader, CubicVR_GLCore.gl.LINK_STATUS))
{
alert("Could not initialise shader vert("+vs_id+"), frag("+fs_id+")");
return;
}
}
cubicvr_shader.prototype.addMatrix = function(uniform_id)
{
this.use();
this.uniforms[uniform_id] = CubicVR_GLCore.gl.getUniformLocation(this.shader, uniform_id);
this.uniform_type[uniform_id] = UNIFORM_TYPE_MATRIX;
}
cubicvr_shader.prototype.addVector = function(uniform_id)
{
this.use();
this.uniforms[uniform_id] = CubicVR_GLCore.gl.getUniformLocation(this.shader, uniform_id);
this.uniform_type[uniform_id] = UNIFORM_TYPE_VECTOR;
}
cubicvr_shader.prototype.addVertexArray = function(uniform_id)
{
this.use();
this.uniforms[uniform_id] = CubicVR_GLCore.gl.getAttribLocation(this.shader, uniform_id);
this.uniform_type[uniform_id] = UNIFORM_TYPE_ARRAY_VERTEX;
}
cubicvr_shader.prototype.addUVArray = function(uniform_id)
{
this.use();
this.uniforms[uniform_id] = CubicVR_GLCore.gl.getAttribLocation(this.shader, uniform_id);
this.uniform_type[uniform_id] = UNIFORM_TYPE_ARRAY_UV;
}
cubicvr_shader.prototype.addFloatArray = function(uniform_id)
{
this.use();
this.uniforms[uniform_id] = CubicVR_GLCore.gl.getAttribLocation(this.shader, uniform_id);
this.uniform_type[uniform_id] = UNIFORM_TYPE_ARRAY_FLOAT;
}
cubicvr_shader.prototype.use = function()
{
CubicVR_GLCore.gl.useProgram(this.shader);
}
cubicvr_shader.prototype.init = function(istate)
{
if (typeof(istate)=='undefined') istate = true;
for (var i in this.uniforms)
{
switch (this.uniform_type[i])
{
case UNIFORM_TYPE_MATRIX:
break;
case UNIFORM_TYPE_VECTOR:
break;
case UNIFORM_TYPE_FLOAT:
break;
case UNIFORM_TYPE_ARRAY_VERTEX:
case UNIFORM_TYPE_ARRAY_UV:
case UNIFORM_TYPE_ARRAY_FLOAT:
if (istate) CubicVR_GLCore.gl.enableVertexAttribArray(this.uniforms[i]);
else CubicVR_GLCore.gl.disableVertexAttribArray(this.uniforms[i]);
break;
}
}
}
cubicvr_shader.prototype.setMatrix = function(uniform_id,mat)
{
CubicVR_GLCore.gl.uniformMatrix4fv(this.uniforms[uniform_id], false, new WebGLFloatArray(mat));
}
cubicvr_shader.prototype.setArray = function(uniform_id, buf)
{
switch (this.uniform_type[uniform_id])
{
case UNIFORM_TYPE_ARRAY_VERTEX:
CubicVR_GLCore.gl.bindBuffer(CubicVR_GLCore.gl.ARRAY_BUFFER, buf);
CubicVR_GLCore.gl.vertexAttribPointer(this.uniforms[uniform_id], 3, CubicVR_GLCore.gl.FLOAT, false, 0, 0);
case UNIFORM_TYPE_ARRAY_UV:
CubicVR_GLCore.gl.bindBuffer(CubicVR_GLCore.gl.ARRAY_BUFFER, buf);
CubicVR_GLCore.gl.vertexAttribPointer(this.uniforms[uniform_id], 2, CubicVR_GLCore.gl.FLOAT, false, 0, 0);
case UNIFORM_TYPE_ARRAY_FLOAT:
CubicVR_GLCore.gl.bindBuffer(CubicVR_GLCore.gl.ARRAY_BUFFER, buf);
CubicVR_GLCore.gl.vertexAttribPointer(this.uniforms[uniform_id], 1, CubicVR_GLCore.gl.FLOAT, false, 0, 0);
break;
}
}
var CubicVR = {
core: CubicVR_GLCore,
transform: cubicvr_transform,
object: cubicvr_object,
face: cubicvr_face,
material: cubicvr_material,
uvmapper: cubicvr_uvmapper,
xyz: cubicvr_xyz,
rgb: cubicvr_rgb,
rgba: cubicvr_rgba,
shader: cubicvr_shader,
perspective: cubicvr_perspective,
lookat: cubicvr_lookat
};
CubicVR_Materials.push(new cubicvr_material("(null)"));
/*
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html>
<head>
<title></title>
<script src="CubicVR.js" type="text/javascript"></script>
<script id="shader-fs" type="x-shader/x-fragment">
void main(void) {
gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
}
</script>
<script id="shader-vs" type="x-shader/x-vertex">
attribute vec3 aVertexPosition;
uniform mat4 uMVMatrix;
uniform mat4 uPMatrix;
void main(void) {
gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
}
</script>
<script type='text/javascript'>
function makeBox(boxObj, box_size, box_mat)
{
var half_box = box_size/2.0;
boxObj.setFaceMaterial(box_mat);
boxObj.addPoint([
CubicVR.xyz( half_box,-half_box, half_box),
CubicVR.xyz( half_box, half_box, half_box),
CubicVR.xyz(-half_box, half_box, half_box),
CubicVR.xyz(-half_box,-half_box, half_box),
CubicVR.xyz( half_box,-half_box, -half_box),
CubicVR.xyz( half_box, half_box, -half_box),
CubicVR.xyz(-half_box, half_box, -half_box),
CubicVR.xyz(-half_box,-half_box, -half_box)
]);
boxObj.addFace([0,1,2,3]);
boxObj.addFace([7,6,5,4]);
boxObj.addFace([4,5,1,0]);
boxObj.addFace([5,6,2,1]);
boxObj.addFace([6,7,3,2]);
boxObj.addFace([7,4,0,3]);
boxObj.calcNormals();
}
var obj_test;
var gl;
function initGL(canvas) {
try {
gl = canvas.getContext("experimental-webgl");
gl.viewport(0, 0, canvas.width, canvas.height);
} catch(e) {
}
if (!gl) {
alert("Could not initialise WebGL, sorry :-(");
}
CubicVR.core.init(gl);
}
var shaderProgram = null;
function webGLStart()
{
var canvas = document.getElementById("cubicvr-canvas");
initGL(canvas);
shaderProgram = new CubicVR.shader("shader-vs","shader-fs");
shaderProgram.addMatrix("uMVMatrix");
shaderProgram.addMatrix("uPMatrix");
shaderProgram.addVertexArray("aVertexPosition");
shaderProgram.init();
obj_test = new CubicVR.object();
// Make a material named test
boxMaterial = new CubicVR.material("test");
// Build a box, set face material to boxMaterial
makeBox(obj_test,1.0,boxMaterial);
// Create a UV Mapper and apply it to boxMaterial
boxMaterialMap = new CubicVR.uvmapper();
boxMaterialMap.projection_mode = UV_PROJECTION_CUBIC;
boxMaterialMap.apply(obj_test,boxMaterial);
obj_test.triangulateQuads();
obj_test.compile();
shaderProgram.setArray("aVertexPosition",obj_test.compiled.gl_points);
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clearDepth(1.0);
gl.enable(gl.DEPTH_TEST);
gl.depthFunc(gl.LEQUAL);
setInterval(drawScene, 15);
}
var xp = 0;
function drawScene()
{
xp += 0.02;
shaderProgram.use();
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
modelViewMat = CubicVR.lookat(1.3*Math.sin(xp), 1.0*Math.sin(xp), 1.3*Math.cos(xp), 0, 0, 0, 0, 1, 0);
projectionMat = CubicVR.perspective(90.0, 1.0, 0.1, 100.0);
shaderProgram.use();
shaderProgram.setMatrix("uMVMatrix",modelViewMat);
shaderProgram.setMatrix("uPMatrix",projectionMat);
// start of a renderer...
// shaderProgram.setArray("aVertexPosition",obj_test.compiled.gl_points);
gl.bindBuffer(gl.ARRAY_BUFFER, obj_test.compiled.gl_points);
gl.vertexAttribPointer(shaderProgram.uniforms["aVertexPosition"], 3, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, obj_test.compiled.gl_elements);
gl.drawElements(gl.TRIANGLES, obj_test.compiled.elements[obj_test.currentMaterial][0].length, gl.UNSIGNED_SHORT, 0);
}
</script>
</head>
<body onLoad="webGLStart();">
<canvas id="cubicvr-canvas" style="border: none;" width="512" height="512"></canvas>
</body>
</html>
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment