glo-demo-primitive
Version:
a quick mesh viewer using glo modules
154 lines (131 loc) • 3.86 kB
JavaScript
var createMesh = require('glo-mesh')
var createCamera = require('perspective-camera')
var createApp = require('canvas-loop')
var createShader = require('glo-shader')
var createTexture = require('glo-texture/2d')
var createContext = require('get-canvas-context')
var identity = require('gl-mat4/identity')
var assign = require('object-assign')
var injectDefines = require('glsl-inject-defines')
var defined = require('defined')
var material = require('./shader')
var toWireframe = require('gl-wireframe')
var orbitControls = require('orbit-controls')
module.exports = function meshViewer (primitive, opt) {
opt = opt || {}
var color = defined(opt.color, [ 1, 1, 1, 1 ])
var gl = opt.gl || createContext('webgl', { antialias: true })
var canvas = gl.canvas
document.body.appendChild(canvas)
canvas.oncontextmenu = function () {
return false
}
canvas.addEventListener('touchstart', function (ev) {
ev.preventDefault()
})
var wireframe = opt.wireframe
var culling = opt.culling !== false
var useTexture = primitive.uvs && opt.texture !== false
var shaderDefines = {}
if (opt.showNormals && primitive.normals) {
shaderDefines.USE_NORMALS = true
}
if (useTexture) {
shaderDefines.USE_TEXTURE = true
}
var shader = createShader(gl, assign(material, {
vertex: material.vertex,
fragment: injectDefines(material.fragment, shaderDefines),
uniforms: [
{ type: 'vec2', name: 'repeat' },
{ type: 'sampler2D', name: 'iChannel0' }
]
}))
// defaults
shader.bind()
shader.uniforms.repeat(opt.repeat || [1, 1])
shader.uniforms.iChannel0(0)
var model = identity([])
var camera = createCamera({
near: opt.near,
far: opt.far,
position: [0, 0, 1]
})
var mesh = createMesh(gl)
.attribute('position', primitive.positions)
.elements(wireframe ? toWireframe(primitive.cells) : primitive.cells)
var controls = orbitControls(assign({
distanceBounds: [0, 100],
distance: 4,
rotationSpeed: 1,
pinchSpeed: 0.025
}, opt, {
element: canvas
}))
// optional attributes
if (primitive.uvs) {
mesh.attribute('uv', primitive.uvs)
}
if (primitive.normals) {
mesh.attribute('normal', primitive.normals)
}
var time = 0
var tex
var app = createApp(canvas, { scale: window.devicePixelRatio })
.on('tick', render)
app.render = render
app.gl = gl
// create a default repeating texture
if (useTexture) {
if (opt.texture && typeof opt.texture.bind === 'function') {
tex = opt.texture
} else {
tex = createCheckerTexture(gl)
}
}
return app
function render (dt) {
dt = dt || 0
time += dt / 1000
var width = gl.drawingBufferWidth
var height = gl.drawingBufferHeight
gl.viewport(0, 0, width, height)
gl.clearColor(0, 0, 0, 1)
gl.clearDepth(1)
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
gl.enable(gl.DEPTH_TEST)
gl.depthFunc(gl.LEQUAL)
if (culling) {
gl.cullFace(gl.BACK)
gl.frontFace(gl.CCW)
gl.enable(gl.CULL_FACE)
} else {
gl.disable(gl.CULL_FACE)
}
camera.viewport = [0, 0, width, height]
controls.update(camera.position, camera.direction, camera.up)
camera.update()
shader.bind()
shader.uniforms.projection(camera.projection)
shader.uniforms.view(camera.view)
shader.uniforms.model(model)
shader.uniforms.color(color)
if (tex) {
tex.bind()
}
mesh.bind(shader)
mesh.draw(wireframe ? gl.LINES : gl.TRIANGLES)
mesh.unbind(shader)
}
}
function createCheckerTexture (gl) {
var pixels = [
[0xff, 0xff, 0xff, 0xff], [0xcc, 0xcc, 0xcc, 0xff],
[0xcc, 0xcc, 0xcc, 0xff], [0xff, 0xff, 0xff, 0xff]
]
return createTexture(gl, pixels, [ 2, 2 ], {
wrap: gl.REPEAT,
minFilter: gl.NEAREST,
magFilter: gl.NEAREST
})
}