load-collada-dae
Version:
Load the WebGL graphics buffer data from a collada .dae model and return a draw command that accepts options
135 lines (112 loc) • 4.51 kB
JavaScript
module.exports = generateVertexShader
// TODO: Add comments
// TODO: Add documentation
// - dual quaternion linear blending
// - conditional texturing
function generateVertexShader (opts) {
var textureVars = ''
var varyingStatement = ''
if (opts.texture) {
textureVars = `
attribute vec2 aTextureCoord;
varying vec2 vTextureCoord;
`
varyingStatement = `
vTextureCoord = aTextureCoord;
`
}
// TODO: Optimize default shader after benchmarks are in place
var vertexShader = `
attribute vec3 aVertexPosition;
attribute vec3 aVertexNormal;
uniform bool uUseLighting;
uniform vec3 uAmbientColor;
uniform vec3 uLightingDirection;
uniform vec3 uDirectionalColor;
varying vec3 vLightWeighting;
${textureVars}
attribute vec4 aJointIndex;
attribute vec4 aJointWeight;
uniform vec4 boneRotQuaternions[${opts.numJoints}];
uniform vec4 boneTransQuaternions[${opts.numJoints}];
uniform mat4 uMVMatrix;
uniform mat4 uPMatrix;
uniform mat3 uNMatrix;
void main (void) {
// Blend our dual quaternion
vec4 weightedRotQuat =
boneRotQuaternions[int(aJointIndex.x)] * aJointWeight.x +
boneRotQuaternions[int(aJointIndex.y)] * aJointWeight.y +
boneRotQuaternions[int(aJointIndex.z)] * aJointWeight.z +
boneRotQuaternions[int(aJointIndex.w)] * aJointWeight.w;
vec4 weightedTransQuat =
boneTransQuaternions[int(aJointIndex.x)] * aJointWeight.x +
boneTransQuaternions[int(aJointIndex.y)] * aJointWeight.y +
boneTransQuaternions[int(aJointIndex.z)] * aJointWeight.z +
boneTransQuaternions[int(aJointIndex.w)] * aJointWeight.w;
// Normalize our dual quaternion
float xRot = weightedRotQuat[0];
float yRot = weightedRotQuat[1];
float zRot = weightedRotQuat[2];
float wRot = weightedRotQuat[3];
float rotQuatMagnitude = sqrt(xRot * xRot + yRot * yRot + zRot * zRot + wRot * wRot);
weightedRotQuat = weightedRotQuat / rotQuatMagnitude;
weightedTransQuat = weightedTransQuat / rotQuatMagnitude;
float xR = weightedRotQuat[0];
float yR = weightedRotQuat[1];
float zR = weightedRotQuat[2];
float wR = weightedRotQuat[3];
float xT = weightedTransQuat[0];
float yT = weightedTransQuat[1];
float zT = weightedTransQuat[2];
float wT = weightedTransQuat[3];
float t0 = 2.0 * (-wT * xR + xT * wR - yT * zR + zT * yR);
float t1 = 2.0 * (-wT * yR + xT * zR + yT * wR - zT * xR);
float t2 = 2.0 * (-wT * zR - xT * yR + yT * xR + zT * wR);
mat4 weightedMatrix = mat4(
1.0 - (2.0 * yR * yR) - (2.0 * zR * zR),
(2.0 * xR * yR) + (2.0 * wR * zR),
(2.0 * xR * zR) - (2.0 * wR * yR),
0,
(2.0 * xR * yR) - (2.0 * wR * zR),
1.0 - (2.0 * xR * xR) - (2.0 * zR * zR),
(2.0 * yR * zR) + (2.0 * wR * xR),
0,
(2.0 * xR * zR) + (2.0 * wR * yR),
(2.0 * yR * zR) - (2.0 * wR * xR),
1.0 - (2.0 * xR * xR) - (2.0 * yR * yR),
0,
t0,
t1,
t2,
1
);
vec4 leftWorldSpace = weightedMatrix * vec4(aVertexPosition, 1.0);
float y = leftWorldSpace.z;
float z = -leftWorldSpace.y;
leftWorldSpace.y = y;
leftWorldSpace.z = z;
if (uUseLighting) {
// Dual Quaternion skinning always leads to rigid transformation matrices so
// we only pass in the top left 3x3 of the modelMatrix without worrying
// about transposing it
// @see https://www.cs.utah.edu/~ladislav/kavan07skinning/kavan07skinning.pdf for
// vertex normal transformation equation
vec3 transformedNormal = (weightedMatrix * vec4(aVertexNormal, 0.0)).xyz;
y = transformedNormal.z;
z = -transformedNormal.y;
transformedNormal.y = y;
transformedNormal.z = z;
// We convert our normal into column major before multiplying it with our normal matrix
transformedNormal = uNMatrix * transformedNormal;
float directionalLightWeighting = max(dot(transformedNormal, uLightingDirection), 0.0);
vLightWeighting = uAmbientColor + uDirectionalColor * directionalLightWeighting;
} else {
vLightWeighting = vec3(1.0, 1.0, 1.0);
}
${varyingStatement}
gl_Position = uPMatrix * uMVMatrix * leftWorldSpace;
}
`
return vertexShader
}