Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
71ed1aa
commit d9ad285
Showing
8 changed files
with
496 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
{ | ||
"name": "@luma.gl/debug", | ||
"private": true, | ||
"version": "0.0.1", | ||
"description": "Debug utilities for luma.gl", | ||
"license": "MIT", | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/uber/luma.gl" | ||
}, | ||
"keywords": [ | ||
"webgl", | ||
"glsl", | ||
"debug", | ||
"3d" | ||
], | ||
"main": "dist/es5/index.js", | ||
"module": "dist/esm/index.js", | ||
"esnext": "dist/es6/index.js", | ||
"files": [ | ||
"src", | ||
"dist" | ||
], | ||
"sideEffects": false, | ||
"browser": { | ||
"fs": false | ||
}, | ||
"scripts": { | ||
"clean": "rm -fr dist && mkdir -p dist/es5 dist/esm dist/es6", | ||
"build": "npm run clean && npm run build-es6 && npm run build-esm && npm run build-es5", | ||
"build-es6": "BABEL_ENV=es6 babel src --config-file ../../babel.config.js --out-dir dist/es6 --source-maps --ignore 'node_modules/'", | ||
"build-esm": "BABEL_ENV=esm babel src --config-file ../../babel.config.js --out-dir dist/esm --source-maps --ignore 'node_modules/'", | ||
"build-es5": "BABEL_ENV=es5 babel src --config-file ../../babel.config.js --out-dir dist/es5 --source-maps --ignore 'node_modules/'" | ||
}, | ||
"dependencies": { | ||
"luma.gl": "^6.1.0-alpha.3", | ||
"glsl-transpiler": "1.5.9" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
import Compiler from 'glsl-transpiler'; | ||
|
||
const normalize = source => | ||
source | ||
// prepr does not like #define without value | ||
.replace(/^(#define \w+) *$/gm, ($0, $1) => `${$1} 1`); | ||
|
||
const compileVS = Compiler({ | ||
uniform: name => `uniforms.${name}`, | ||
attribute: name => `attributes.${name}` | ||
}); | ||
|
||
const compileFS = Compiler({ | ||
uniform: name => `uniforms.${name}`, | ||
attribute: name => `attributes.${name}`, | ||
varying: name => `varyings.${name}` | ||
}); | ||
|
||
// @returns JavaScript function of the transpiled shader | ||
export function compileVertexShader(name, source) { | ||
source = normalize(source); | ||
|
||
const compiledSource = compileVS(source); | ||
const {compiler} = compileVS; | ||
|
||
// TODO - input validation? | ||
const stats = { | ||
attributes: compiler.attributes, | ||
uniforms: compiler.uniforms, | ||
varyings: compiler.varyings, | ||
functions: compiler.functions | ||
}; | ||
compiler.reset(); | ||
|
||
return evalScript( | ||
`function vs(uniforms, attributes) { | ||
var gl_Position; | ||
${compiledSource} | ||
/* End of shader code */ | ||
main(); | ||
return { | ||
gl_Position, | ||
varyings: {${Object.keys(stats.varyings).join(', ')}} | ||
}; | ||
}`, | ||
name | ||
); | ||
} | ||
|
||
// @returns JavaScript function of the transpiled shader | ||
export function compileFragmentShader(name, source) { | ||
source = normalize(source); | ||
|
||
const compiledSource = compileFS(source); | ||
const {compiler} = compileFS; | ||
|
||
compiler.reset(); | ||
|
||
return evalScript( | ||
`function fs(uniforms, varyings) { | ||
var gl_FragColor; | ||
var isDiscarded = false; | ||
function discard() { | ||
isDiscarded = true; | ||
} | ||
${compiledSource} | ||
/* End of shader code */ | ||
main(); | ||
return { | ||
gl_FragColor, | ||
isDiscarded | ||
}; | ||
}`, | ||
name | ||
); | ||
} | ||
|
||
/* eslint-disable no-eval */ | ||
function evalScript(value, name) { | ||
const script = `(function() { return ${value}; })() | ||
//# sourceURL=${name}.js`; | ||
return eval(script); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,185 @@ | ||
import GL from 'luma.gl/constants'; | ||
import drawModel from './draw-model'; | ||
|
||
/* Draws WebGL style wireframe in a 2d canvas */ | ||
export default class DebugContext { | ||
constructor(sourceCanvas) { | ||
this.sourceCanvas = sourceCanvas; | ||
|
||
const container = sourceCanvas.offsetParent; | ||
/* global window */ | ||
const containerStyle = window.getComputedStyle(container); | ||
if (containerStyle.position === 'static') { | ||
container.style.position = 'relative'; | ||
} | ||
|
||
this.canvas = this._createCanvas(container); | ||
this.context = this.canvas.getContext('2d'); | ||
|
||
this._draw = this._draw.bind(this); | ||
} | ||
|
||
clear(opts = {}) { | ||
const {canvas, context} = this; | ||
const {clientWidth: width, clientHeight: height} = this.sourceCanvas; | ||
|
||
canvas.width = width; | ||
canvas.height = height; | ||
context.clearRect(0, 0, width, height); | ||
|
||
for (const name in opts) { | ||
context[name] = opts[name]; | ||
} | ||
} | ||
|
||
drawModel(model, opts) { | ||
drawModel(Object.assign({}, opts, { | ||
model, | ||
draw: this._draw | ||
})); | ||
} | ||
|
||
/* eslint-disable complexity */ | ||
_draw({drawMode, indices, positions, colors}) { | ||
this.positions = positions.map(this._clipspaceToScreen, this); | ||
this.colors = colors.map(this._rgbaToColor, this); | ||
|
||
switch (drawMode) { | ||
case GL.POINTS: { | ||
for (let i = 0; i < indices.length; i++) { | ||
this._drawPoint(indices[i]); | ||
} | ||
break; | ||
} | ||
|
||
case GL.LINES: { | ||
for (let i = 0; i < indices.length - 1; i += 2) { | ||
this._drawLine(indices[i], indices[i] + 1); | ||
} | ||
break; | ||
} | ||
|
||
case GL.LINE_STRIP: { | ||
for (let i = 0; i < indices.length - 1; i++) { | ||
this._drawLine(indices[i], indices[i + 1]); | ||
} | ||
break; | ||
} | ||
|
||
case GL.LINE_LOOP: { | ||
for (let i = 0; i < indices.length; i++) { | ||
this._drawLine(i === 0 ? indices[indices.length - 1] : indices[i - 1], indices[i]); | ||
} | ||
break; | ||
} | ||
|
||
case GL.TRIANGLES: { | ||
for (let i = 0; i < indices.length - 2; i += 3) { | ||
this._drawTriangle(indices[i], indices[i + 1], indices[i + 2]); | ||
} | ||
break; | ||
} | ||
|
||
case GL.TRIANGLE_STRIP: { | ||
for (let i = 0; i < indices.length - 2; i++) { | ||
this._drawTriangle(indices[i], indices[i + 1], indices[i + 2]); | ||
} | ||
break; | ||
} | ||
|
||
case GL.TRIANGLE_FAN: { | ||
for (let i = 1; i < indices.length - 1; i++) { | ||
this._drawTriangle(indices[0], indices[i], indices[i + 1]); | ||
} | ||
break; | ||
} | ||
|
||
default: | ||
throw new Error('unknown draw mode'); | ||
} | ||
} | ||
/* eslint-enable complexity */ | ||
|
||
_createCanvas(container) { | ||
const canvas = document.createElement('canvas'); | ||
container.append(canvas); | ||
Object.assign(canvas.style, { | ||
position: 'absolute', | ||
left: '0px', | ||
top: '0px', | ||
pointerEvents: 'none' | ||
}); | ||
return canvas; | ||
} | ||
|
||
_clipspaceToScreen(position) { | ||
const {width, height} = this.canvas; | ||
const [x, y, z, w] = position; | ||
|
||
const depth = z / w; | ||
|
||
if (depth < -1 || depth > 1) { | ||
return null; | ||
} | ||
|
||
return [(x / w + 1) * width / 2, (1 - y / w) * height / 2]; | ||
} | ||
|
||
_rgbaToColor(color) { | ||
if (Array.isArray(color)) { | ||
const rgb = color.slice(0, 3).map(x => (x * 255) | 0); | ||
const a = Number.isFinite(color[3]) ? color[3] : 1; | ||
return `rgba(${rgb.join(',')},${a.toFixed(2)})`; | ||
} | ||
return color; | ||
} | ||
|
||
_drawPoint(i) { | ||
const {context, positions, colors} = this; | ||
const p = positions[i]; | ||
const color = colors[i]; | ||
|
||
if (!p) { | ||
return; | ||
} | ||
if (color) { | ||
context.fillStyle = color; | ||
} | ||
context.fillRect(p[0] - 0.5, p[1] - 0.5, 1, 1); | ||
} | ||
|
||
_drawLine(i0, i1) { | ||
const {context, positions, colors} = this; | ||
const p0 = positions[i0]; | ||
const p1 = positions[i1]; | ||
const color0 = colors[i0]; | ||
const color1 = colors[i1]; | ||
|
||
if (!p0 || !p1) { | ||
return; | ||
} | ||
|
||
context.beginPath(); | ||
if (color0 !== color1) { | ||
// Use gradient | ||
const gradient = context.createLinearGradient(p0[0], p0[1], p1[0], p1[1]); | ||
gradient.addColorStop(0, color0); | ||
gradient.addColorStop(1, color1); | ||
|
||
context.strokeStyle = gradient; | ||
} else if (color0) { | ||
// Single color | ||
context.strokeStyle = color0; | ||
} | ||
|
||
context.moveTo(p0[0], p0[1]); | ||
context.lineTo(p1[0], p1[1]); | ||
context.stroke(); | ||
} | ||
|
||
_drawTriangle(i0, i1, i2) { | ||
this._drawLine(i0, i1); | ||
this._drawLine(i1, i2); | ||
this._drawLine(i2, i0); | ||
} | ||
} |
Oops, something went wrong.