function invert(out, M) { switch(M.length) { case 0: break case 1: out[0] = 1.0 / M[0] break case 4: invert2(out, M) break case 9: invert3(out, M) break case 16: invert4(out, M) break default: throw new Error('currently supports matrices up to 4x4') break } return out }
loadGLTF: function() { var gl = this.gl; var commands = this.commands; var showNormalsProgram = this.showNormalsProgram; var projectionMatrix = this.camProjectionMatrix; var viewMatrix = this.viewMatrix; var modelMatrix = this.boxModelMatrix; var modelViewMatrix = mult44(createMat4(), viewMatrix, modelMatrix); var normalMatrix = createMat4(); invert(normalMatrix, modelViewMatrix); transpose(normalMatrix, normalMatrix); normalMatrix = [ normalMatrix[0], normalMatrix[1], normalMatrix[2], normalMatrix[4], normalMatrix[5], normalMatrix[6], normalMatrix[8], normalMatrix[9], normalMatrix[10] ]; //loadGLTF(gl, __dirname + '/assets/model/box/box.gltf', function(err, json) { //loadGLTF(gl, __dirname + '/assets/model/duck/duck.gltf', function(err, json) { //loadGLTF(gl, __dirname + '/assets/model/wine/wine.gltf', function(err, json) { //loadGLTF(gl, __dirname + '/assets/model/SuperMurdoch/SuperMurdoch.gltf', function(err, json) { loadGLTF(gl, __dirname + '/assets/model/rambler/Rambler.gltf', function(err, json) { if (err) { log('load done', 'err:', err); } else { log('load done', '' + json); } var bbox = { min: [Infinity, Infinity, Infinity], max: [-Infinity, -Infinity, -Infinity] }; log('buildMesh'); forEachKeyValue(json.meshes, function(meshName, meshInfo, meshIndex) { meshInfo.primitives.forEach(function(primitiveInfo, primitiveIndex) { if (!primitiveInfo.vertexArray) return; //FIXME: TEMP! var materialInfo = json.materials[primitiveInfo.material]; var instanceValues = materialInfo.instanceTechnique.values; var techniqueInfo = json.techniques[materialInfo.instanceTechnique.technique]; var parameters = techniqueInfo.parameters; var defaultPass = techniqueInfo.passes.defaultPass; var instanceProgramInfo = defaultPass.instanceProgram; var program = json.programs[instanceProgramInfo.program]._program; var uniforms = {}; var localModelViewMatrix = createMat4(); //projectionMatrix: projectionMatrix, //viewMatrix: viewMatrix, //modelMatrix: modelMatrix forEachKeyValue(instanceProgramInfo.uniforms, function(uniformName, valueName) { var parameter = parameters[valueName]; var value = instanceValues[valueName] || parameter.value; uniforms[valueName] = null; //log('uniform', uniformName, valueName, value, parameter); if (!value) { if (parameter.source) { log('uniform source found', parameter.source); //TODO: uniform source //TODO: is source refering only to node matrices? value = json.nodes[parameter.source].matrix; } else if (parameter.semantic) { switch(parameter.semantic) { case 'MODELVIEW': copy44(localModelViewMatrix, modelViewMatrix); value = localModelViewMatrix; break; case 'PROJECTION': value = projectionMatrix; break; case 'MODELVIEWINVERSETRANSPOSE': value = normalMatrix; break; default: throw new Error('Unknown uniform semantic found ' + parameter.semantic); } } else { throw new Error('No value for uniform:' + valueName); } } if (instanceValues[valueName]) { switch(parameters[valueName].type) { case gl.SAMPLER_2D: value = json.textures[value]._texture;//TODO: check if this is not null (eg. texture loading failed) break; case gl.FLOAT_VEC2: break; case gl.FLOAT_VEC3: break; case gl.FLOAT_VEC4: break; case gl.FLOAT_MAT4: break; case gl.FLOAT: break; default: throw new Error('Unknown uniform type:' + parameters[valueName].type); } } if (value === null || value === undefined) { throw new Error('Uniform ' + valueName + ' is missing'); } uniforms[valueName] = value; }); //program = showNormalsProgram; forEachKeyValue(uniforms, function(name, value) { uniforms['u_' + name] = value; delete uniforms[name]; }) var positions = primitiveInfo.vertexArray.attributes['position'].dataBuf; for(var i=0; i<positions.length; i+=3) { for(var j=0; j<3; j++) { bbox.min[j] = Math.min(bbox.min[j], positions[i+j]); bbox.max[j] = Math.max(bbox.max[j], positions[i+j]); } } meshInfo._cmd = { vertexArray : primitiveInfo.vertexArray, program : program, uniforms : uniforms, renderState : { depthTest: true }, bbox: bbox } //var cmd = new DrawCommand({ // vertexArray : primitiveInfo.vertexArray, // program : program, // uniforms : uniforms, // renderState : { // depthTest: true // } //}); //commands.push(cmd); }); }); forEachKeyValue(json.nodes, function(nodeName, nodeInfo, nodeIndex) { var localMatrix = createMat4(); scale44(localMatrix, localMatrix, [2, -2, 2]) translate(localMatrix, localMatrix, [-2, 0, 0]) var matrixStack = [nodeInfo.matrix]; var parent = nodeInfo.parent; while(parent) { if (parent.matrix) { matrixStack.unshift(parent.matrix); } parent = parent.parent; } matrixStack.forEach(function(mat) { mult44(localMatrix, localMatrix, mat); }) if (nodeInfo.meshes) { nodeInfo.meshes.forEach(function(meshId) { var mesh = json.meshes[meshId]; var cmd = new DrawCommand(mesh._cmd); mult44(cmd.uniforms.u_modelViewMatrix, cmd.uniforms.u_modelViewMatrix, localMatrix); commands.push(cmd); }) } }); var size = sub3([0,0,0], bbox.max, bbox.min); var maxScale = Math.max(size[0], Math.max(size[1], size[2])) //console.log(maxScale) //maxScale = 0.05; var tmp = createMat4(); commands.forEach(function(cmd, cmdIndex) { if (!cmd.uniforms) return; //translate(cmd.uniforms.u_modelViewMatrix, cmd.uniforms.u_modelViewMatrix, [-size[0]/2, -size[1]/2, -size[2]/2]); //translate(cmd.uniforms.u_modelViewMatrix, cmd.uniforms.u_modelViewMatrix, [-0.1, 0, 0]); //scale44(cmd.uniforms.u_modelViewMatrix, cmd.uniforms.u_modelViewMatrix, [1/maxScale, 1/maxScale,1/maxScale]); //console.log('cmd', cmdIndex, cmd.vertexArray.indexBuffer.dataBuf.length) }) }); },
function drawCore(params, transparent) { params = params || {} var gl = this.gl gl.disable(gl.CULL_FACE) this._colorMap.bind(0) var uniforms = UNIFORMS uniforms.model = params.model || IDENTITY uniforms.view = params.view || IDENTITY uniforms.projection = params.projection || IDENTITY uniforms.lowerBound = this.bounds[0] uniforms.upperBound = this.bounds[1] uniforms.contourColor = this.contourColor[0] uniforms.inverseModel = invert(uniforms.inverseModel, uniforms.model) for(var i=0; i<2; ++i) { var clipClamped = uniforms.clipBounds[i] for(var j=0; j<3; ++j) { clipClamped[j] = Math.min(Math.max(this.clipBounds[i][j], -1e8), 1e8) } } uniforms.kambient = this.ambientLight uniforms.kdiffuse = this.diffuseLight uniforms.kspecular = this.specularLight uniforms.shape = uniforms.roughness = this.roughness uniforms.fresnel = this.fresnel uniforms.opacity = this.opacity uniforms.height = 0.0 uniforms.permutation = DEFAULT_PERM //Compute camera matrix inverse var invCameraMatrix = MATRIX_INVERSE multiply(invCameraMatrix, uniforms.view, uniforms.model) multiply(invCameraMatrix, uniforms.projection, invCameraMatrix) invert(invCameraMatrix, invCameraMatrix) for(var i=0; i<3; ++i) { uniforms.eyePosition[i] = invCameraMatrix[12+i] / invCameraMatrix[15] } var w = invCameraMatrix[15] for(var i=0; i<3; ++i) { w += this.lightPosition[i] * invCameraMatrix[4*i+3] } for(var i=0; i<3; ++i) { var s = invCameraMatrix[12+i] for(var j=0; j<3; ++j) { s += invCameraMatrix[4*j+i] * this.lightPosition[j] } uniforms.lightPosition[i] = s / w } var projectData = computeProjectionData(uniforms, this) if(projectData.showSurface && (transparent === (this.opacity < 1))) { //Set up uniforms this._shader.bind() this._shader.uniforms = uniforms //Draw it this._vao.bind() if(this.showSurface) { this._vao.draw(gl.TRIANGLES, this._vertexCount) } //Draw projections of surface for(var i=0; i<3; ++i) { if(!this.surfaceProject[i]) { continue } this._shader.uniforms.model = projectData.projections[i] this._shader.uniforms.clipBounds = projectData.clipBounds[i] this._vao.draw(gl.TRIANGLES, this._vertexCount) } this._vao.unbind() } if(projectData.showContour && !transparent) { var shader = this._contourShader //Don't apply lighting to contours uniforms.kambient = 1.0 uniforms.kdiffuse = 0.0 uniforms.kspecular = 0.0 uniforms.opacity = 1.0 shader.bind() shader.uniforms = uniforms //Draw contour lines var vao = this._contourVAO vao.bind() //Draw contour levels for(var i=0; i<3; ++i) { shader.uniforms.permutation = PERMUTATIONS[i] gl.lineWidth(this.contourWidth[i]) for(var j=0; j<this.contourLevels[i].length; ++j) { if(j === this.highlightLevel[i]) { shader.uniforms.contourColor = this.highlightColor[i] shader.uniforms.contourTint = this.highlightTint[i] } else if(j === 0 || (j-1) === this.highlightLevel[i]) { shader.uniforms.contourColor = this.contourColor[i] shader.uniforms.contourTint = this.contourTint[i] } shader.uniforms.height = this.contourLevels[i][j] vao.draw(gl.LINES, this._contourCounts[i][j], this._contourOffsets[i][j]) } } //Draw projections of surface for(var i=0; i<3; ++i) { shader.uniforms.model = projectData.projections[i] shader.uniforms.clipBounds = projectData.clipBounds[i] for(var j=0; j<3; ++j) { if(!this.contourProject[i][j]) { continue } shader.uniforms.permutation = PERMUTATIONS[j] gl.lineWidth(this.contourWidth[j]) for(var k=0; k<this.contourLevels[j].length; ++k) { if(k === this.highlightLevel[j]) { shader.uniforms.contourColor = this.highlightColor[j] shader.uniforms.contourTint = this.highlightTint[j] } else if(k === 0 || (k-1) === this.highlightLevel[j]) { shader.uniforms.contourColor = this.contourColor[j] shader.uniforms.contourTint = this.contourTint[j] } shader.uniforms.height = this.contourLevels[j][k] vao.draw(gl.LINES, this._contourCounts[j][k], this._contourOffsets[j][k]) } } } //Draw dynamic contours vao = this._dynamicVAO vao.bind() //Draw contour levels for(var i=0; i<3; ++i) { if(this._dynamicCounts[i] === 0) { continue } shader.uniforms.model = uniforms.model shader.uniforms.clipBounds = uniforms.clipBounds shader.uniforms.permutation = PERMUTATIONS[i] gl.lineWidth(this.dynamicWidth[i]) shader.uniforms.contourColor = this.dynamicColor[i] shader.uniforms.contourTint = this.dynamicTint[i] shader.uniforms.height = this.dynamicLevel[i] vao.draw(gl.LINES, this._dynamicCounts[i], this._dynamicOffsets[i]) for(var j=0; j<3; ++j) { if(!this.contourProject[j][i]) { continue } shader.uniforms.model = projectData.projections[j] shader.uniforms.clipBounds = projectData.clipBounds[j] vao.draw(gl.LINES, this._dynamicCounts[i], this._dynamicOffsets[i]) } } vao.unbind() } }
module.exports = function decomposeMat4(matrix, translation, scale, skew, perspective, quaternion) { if (!translation) translation = [0,0,0] if (!scale) scale = [0,0,0] if (!skew) skew = [0,0,0] if (!perspective) perspective = [0,0,0,1] if (!quaternion) quaternion = [0,0,0,1] //normalize, if not possible then bail out early if (!normalize(tmp, matrix)) return false // perspectiveMatrix is used to solve for perspective, but it also provides // an easy way to test for singularity of the upper 3x3 component. clone(perspectiveMatrix, tmp) perspectiveMatrix[3] = 0 perspectiveMatrix[7] = 0 perspectiveMatrix[11] = 0 perspectiveMatrix[15] = 1 // If the perspectiveMatrix is not invertible, we are also unable to // decompose, so we'll bail early. Constant taken from SkMatrix44::invert. if (Math.abs(determinant(perspectiveMatrix) < 1e-8)) return false var a03 = tmp[3], a13 = tmp[7], a23 = tmp[11], a30 = tmp[12], a31 = tmp[13], a32 = tmp[14], a33 = tmp[15] // First, isolate perspective. if (a03 !== 0 || a13 !== 0 || a23 !== 0) { tmpVec4[0] = a03 tmpVec4[1] = a13 tmpVec4[2] = a23 tmpVec4[3] = a33 // Solve the equation by inverting perspectiveMatrix and multiplying // rightHandSide by the inverse. // resuing the perspectiveMatrix here since it's no longer needed var ret = invert(perspectiveMatrix, perspectiveMatrix) if (!ret) return false transpose(perspectiveMatrix, perspectiveMatrix) //multiply by transposed inverse perspective matrix, into perspective vec4 vec4multMat4(perspective, tmpVec4, perspectiveMatrix) } else { //no perspective perspective[0] = perspective[1] = perspective[2] = 0 perspective[3] = 1 } // Next take care of translation translation[0] = a30 translation[1] = a31 translation[2] = a32 // Now get scale and shear. 'row' is a 3 element array of 3 component vectors mat3from4(row, tmp) // Compute X scale factor and normalize first row. scale[0] = vec3.length(row[0]) vec3.normalize(row[0], row[0]) // Compute XY shear factor and make 2nd row orthogonal to 1st. skew[0] = vec3.dot(row[0], row[1]) combine(row[1], row[1], row[0], 1.0, -skew[0]) // Now, compute Y scale and normalize 2nd row. scale[1] = vec3.length(row[1]) vec3.normalize(row[1], row[1]) skew[0] /= scale[1] // Compute XZ and YZ shears, orthogonalize 3rd row skew[1] = vec3.dot(row[0], row[2]) combine(row[2], row[2], row[0], 1.0, -skew[1]) skew[2] = vec3.dot(row[1], row[2]) combine(row[2], row[2], row[1], 1.0, -skew[2]) // Next, get Z scale and normalize 3rd row. scale[2] = vec3.length(row[2]) vec3.normalize(row[2], row[2]) skew[1] /= scale[2] skew[2] /= scale[2] // At this point, the matrix (in rows) is orthonormal. // Check for a coordinate system flip. If the determinant // is -1, then negate the matrix and the scaling factors. vec3.cross(pdum3, row[1], row[2]) if (vec3.dot(row[0], pdum3) < 0) { for (var i = 0; i < 3; i++) { scale[i] *= -1; row[i][0] *= -1 row[i][1] *= -1 row[i][2] *= -1 } } // Now, get the rotations out quaternion[0] = 0.5 * Math.sqrt(Math.max(1 + row[0][0] - row[1][1] - row[2][2], 0)) quaternion[1] = 0.5 * Math.sqrt(Math.max(1 - row[0][0] + row[1][1] - row[2][2], 0)) quaternion[2] = 0.5 * Math.sqrt(Math.max(1 - row[0][0] - row[1][1] + row[2][2], 0)) quaternion[3] = 0.5 * Math.sqrt(Math.max(1 + row[0][0] + row[1][1] + row[2][2], 0)) if (row[2][1] > row[1][2]) quaternion[0] = -quaternion[0] if (row[0][2] > row[2][0]) quaternion[1] = -quaternion[1] if (row[1][0] > row[0][1]) quaternion[2] = -quaternion[2] return true }
proto.drawTransparent = proto.draw = function(params) { params = params || {} var gl = this.gl var model = params.model || IDENTITY var view = params.view || IDENTITY var projection = params.projection || IDENTITY var clipBounds = [[-1e6,-1e6,-1e6],[1e6,1e6,1e6]] for(var i=0; i<3; ++i) { clipBounds[0][i] = Math.max(clipBounds[0][i], this.clipBounds[0][i]) clipBounds[1][i] = Math.min(clipBounds[1][i], this.clipBounds[1][i]) } var uniforms = { model: model, view: view, projection: projection, inverseModel: IDENTITY.slice(), clipBounds: clipBounds, kambient: this.ambientLight, kdiffuse: this.diffuseLight, kspecular: this.specularLight, roughness: this.roughness, fresnel: this.fresnel, eyePosition: [0,0,0], lightPosition: [0,0,0], contourColor: this.contourColor, texture: 0 } uniforms.inverseModel = invert(uniforms.inverseModel, uniforms.model) gl.disable(gl.CULL_FACE) this.texture.bind(0) var invCameraMatrix = new Array(16) multiply(invCameraMatrix, uniforms.view, uniforms.model) multiply(invCameraMatrix, uniforms.projection, invCameraMatrix) invert(invCameraMatrix, invCameraMatrix) for(var i=0; i<3; ++i) { uniforms.eyePosition[i] = invCameraMatrix[12+i] / invCameraMatrix[15] } var w = invCameraMatrix[15] for(var i=0; i<3; ++i) { w += this.lightPosition[i] * invCameraMatrix[4*i+3] } for(var i=0; i<3; ++i) { var s = invCameraMatrix[12+i] for(var j=0; j<3; ++j) { s += invCameraMatrix[4*j+i] * this.lightPosition[j] } uniforms.lightPosition[i] = s / w } if(this.triangleCount > 0) { var shader = this.triShader shader.bind() shader.uniforms = uniforms this.triangleVAO.bind() gl.drawArrays(gl.TRIANGLES, 0, this.triangleCount*3) this.triangleVAO.unbind() } if(this.edgeCount > 0 && this.lineWidth > 0) { var shader = this.lineShader shader.bind() shader.uniforms = uniforms this.edgeVAO.bind() gl.lineWidth(this.lineWidth * this.pixelRatio) gl.drawArrays(gl.LINES, 0, this.edgeCount*2) this.edgeVAO.unbind() } if(this.pointCount > 0) { var shader = this.pointShader shader.bind() shader.uniforms = uniforms this.pointVAO.bind() gl.drawArrays(gl.POINTS, 0, this.pointCount) this.pointVAO.unbind() } if(this.contourEnable && this.contourCount > 0 && this.contourLineWidth > 0) { var shader = this.contourShader shader.bind() shader.uniforms = uniforms this.contourVAO.bind() gl.drawArrays(gl.LINES, 0, this.contourCount) this.contourVAO.unbind() } }