Created
February 29, 2016 13:35
-
-
Save superguigui/2b79b2b3bf26add4b6d2 to your computer and use it in GitHub Desktop.
THREE.Meshline from spite in ES6 with update method
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
#extension GL_OES_standard_derivatives : enable | |
precision mediump float; | |
uniform sampler2D map; | |
uniform float useMap; | |
varying vec2 vUV; | |
varying vec4 vColor; | |
void main() { | |
vec4 c = vColor; | |
if (useMap == 1.0) { | |
c *= texture2D(map, vUV); | |
} | |
gl_FragColor = c; | |
} |
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
import THREE from 'three'; | |
export default class MeshLine { | |
constructor() { | |
this.attributes = {}; | |
this.positions = []; | |
this.geometry = new THREE.BufferGeometry(); | |
this.widthCallback = null; | |
} | |
setGeometry(geometry, widthCallback) { | |
this.widthCallback = widthCallback; | |
this.setPositions(geometry); | |
this.process(); | |
} | |
setPositions(geometry) { | |
this.positions = []; | |
if (geometry instanceof THREE.Geometry) { | |
for (let j = 0, l = geometry.vertices.length, v; j < l; j++) { | |
v = geometry.vertices[ j ]; | |
this.positions.push(v.x, v.y, v.z); | |
this.positions.push(v.x, v.y, v.z); | |
} | |
} | |
if (geometry instanceof THREE.BufferGeometry) { | |
geometry = geometry.getAttribute('position').array; | |
} | |
if (geometry instanceof Float32Array || geometry instanceof Array) { | |
for (let j = 0, l = geometry.length; j < l; j += 3) { | |
this.positions.push(geometry[j + 0], geometry[j + 1], geometry[j + 2]); | |
this.positions.push(geometry[j + 0], geometry[j + 1], geometry[j + 2]); | |
} | |
} | |
} | |
compareV3(a, b) { | |
const aa = a * 6; | |
const ab = b * 6; | |
return (this.positions[aa + 0] === this.positions[ab + 0]) && (this.positions[aa + 1] === this.positions[ab + 1]) && (this.positions[aa + 2] === this.positions[ab + 2]); | |
} | |
copyV3(a) { | |
const aa = a * 6; | |
return [this.positions[aa + 0], this.positions[aa + 1], this.positions[aa + 2]]; | |
} | |
assignPosition(destination, source, index) { | |
destination[index + 0] = source[index + 0]; | |
destination[index + 1] = source[index + 1]; | |
destination[index + 2] = source[index + 2]; | |
destination[index + 3] = source[index + 3]; | |
destination[index + 4] = source[index + 4]; | |
destination[index + 5] = source[index + 5]; | |
} | |
assignPrevNext(destination, source, index) { | |
destination[index + 0] = source[0]; | |
destination[index + 1] = source[1]; | |
destination[index + 2] = source[2]; | |
destination[index + 3] = source[0]; | |
destination[index + 4] = source[1]; | |
destination[index + 5] = source[2]; | |
} | |
updateWidth(widthCallback) { | |
const widths = this.attributes.width.array; | |
this.widthCallback = widthCallback; | |
for (let j = 0, l = this.positions.length / 3, w; j < l; j += 2) { | |
w = this.widthCallback ? this.widthCallback(j / (l - 1)) : 1; | |
widths[j + 0] = w; | |
widths[j + 1] = w; | |
} | |
this.geometry.attributes.width.needsUpdate = true; | |
} | |
updatePositions(geometry) { | |
const l = this.positions.length / 6; | |
if (l !== geometry.vertices.length) { | |
throw new Error('THREE.MeshLine :: new geometry length should be the same as previously'); | |
} | |
this.setPositions(geometry); | |
const positions = this.attributes.position.array; | |
const previouses = this.attributes.previous.array; | |
const nexts = this.attributes.next.array; | |
let v; | |
for (let j = 0, m = l * 6; j < m; j += 6) { | |
this.assignPosition(positions, this.positions, j); | |
} | |
v = this.compareV3(0, l - 1) ? this.copyV3(l - 2) : this.copyV3(0); | |
this.assignPrevNext(previouses, v, 0); | |
for (let j = 0; j < l - 1; j++) { | |
v = this.copyV3(j); | |
this.assignPrevNext(previouses, v, j * 6); | |
v = this.copyV3(j + 1); | |
this.assignPrevNext(nexts, v, j * 6); | |
} | |
v = this.compareV3(l - 1, 0) ? this.copyV3( 1 ) : this.copyV3( l - 1 ); | |
this.assignPrevNext(nexts, v, (l - 1) * 6); | |
this.geometry.attributes.position.needsUpdate = true; | |
this.geometry.attributes.previous.needsUpdate = true; | |
this.geometry.attributes.next.needsUpdate = true; | |
} | |
process() { | |
const l = this.positions.length / 6; | |
const positions = new Float32Array(this.positions); | |
const previouses = new Float32Array(l * 6); | |
const nexts = new Float32Array(l * 6); | |
const uvs = new Float32Array(l * 4); | |
const widths = new Float32Array(l * 2); | |
const sides = new Float32Array(l * 2); | |
const indices = new Uint16Array((l - 1) * 6); | |
let w, v; | |
v = this.compareV3(0, l - 1) ? this.copyV3(l - 2) : this.copyV3(0); | |
this.assignPrevNext(previouses, v, 0); | |
for (let j = 0; j < l; j++) { | |
sides[j * 2 + 0] = 1; | |
sides[j * 2 + 1] = -1; | |
w = this.widthCallback ? this.widthCallback(j / (l - 1)) : 1; | |
widths[j * 2 + 0] = w; | |
widths[j * 2 + 1] = w; | |
uvs[j * 4 + 0] = j / (l - 1); | |
uvs[j * 4 + 1] = 0; | |
uvs[j * 4 + 2] = j / (l - 1); | |
uvs[j * 4 + 4] = 1; | |
if (j < l - 1) { | |
v = this.copyV3(j); | |
this.assignPrevNext(previouses, v, j * 6); | |
v = this.copyV3(j + 1); | |
this.assignPrevNext(nexts, v, j * 6); | |
indices[j * 6 + 0] = j * 2 + 0; | |
indices[j * 6 + 1] = j * 2 + 1; | |
indices[j * 6 + 2] = j * 2 + 2; | |
indices[j * 6 + 3] = j * 2 + 2; | |
indices[j * 6 + 4] = j * 2 + 1; | |
indices[j * 6 + 5] = j * 2 + 3; | |
} | |
} | |
v = this.compareV3(l - 1, 0) ? this.copyV3( 1 ) : this.copyV3( l - 1 ); | |
this.assignPrevNext(nexts, v, (l - 1) * 6); | |
this.attributes = { | |
position: new THREE.BufferAttribute(positions, 3).setDynamic(true), | |
previous: new THREE.BufferAttribute(previouses, 3).setDynamic(true), | |
next: new THREE.BufferAttribute(nexts, 3).setDynamic(true), | |
side: new THREE.BufferAttribute(sides, 1), | |
width: new THREE.BufferAttribute(widths, 1).setDynamic(true), | |
uv: new THREE.BufferAttribute(uvs, 2).setDynamic(true), | |
index: new THREE.BufferAttribute(indices, 1) | |
}; | |
this.geometry.addAttribute('position', this.attributes.position); | |
this.geometry.addAttribute('previous', this.attributes.previous); | |
this.geometry.addAttribute('next', this.attributes.next); | |
this.geometry.addAttribute('side', this.attributes.side); | |
this.geometry.addAttribute('width', this.attributes.width); | |
this.geometry.addAttribute('uv', this.attributes.uv); | |
this.geometry.setIndex(this.attributes.index); | |
} | |
} |
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
precision highp float; | |
attribute vec3 position; | |
attribute vec3 previous; | |
attribute vec3 next; | |
attribute float side; | |
attribute float width; | |
attribute vec2 uv; | |
uniform mat4 projectionMatrix; | |
uniform mat4 modelViewMatrix; | |
uniform vec2 resolution; | |
uniform float lineWidth; | |
uniform vec3 color; | |
uniform float opacity; | |
uniform float near; | |
uniform float far; | |
uniform float sizeAttenuation; | |
varying vec2 vUV; | |
varying vec4 vColor; | |
vec2 fix(vec4 i, float aspect) { | |
vec2 res = i.xy / i.w; | |
res.x *= aspect; | |
return res; | |
} | |
void main() { | |
float aspect = resolution.x / resolution.y; | |
float pixelWidthRatio = 1. / (resolution.x * projectionMatrix[0][0]); | |
vColor = vec4(color, opacity); | |
vUV = uv; | |
mat4 m = projectionMatrix * modelViewMatrix; | |
vec4 finalPosition = m * vec4(position, 1.0); | |
vec4 prevPos = m * vec4(previous, 1.0); | |
vec4 nextPos = m * vec4(next, 1.0); | |
vec2 currentP = fix(finalPosition, aspect); | |
vec2 prevP = fix(prevPos, aspect); | |
vec2 nextP = fix(nextPos, aspect); | |
float pixelWidth = finalPosition.w * pixelWidthRatio; | |
float w = 1.8 * pixelWidth * lineWidth * width; | |
if (sizeAttenuation == 1.) { | |
w = 1.8 * lineWidth * width; | |
} | |
vec2 dir; | |
if (nextP == currentP) { | |
dir = normalize(currentP - prevP); | |
} | |
else if (prevP == currentP) { | |
dir = normalize(nextP - currentP); | |
} | |
else { | |
vec2 dir1 = normalize(currentP - prevP); | |
vec2 dir2 = normalize(nextP - currentP); | |
dir = normalize(dir1 + dir2); | |
vec2 perp = vec2(-dir1.y, dir1.x); | |
vec2 miter = vec2(-dir.y, dir.x); | |
} | |
vec2 normal = vec2(-dir.y, dir.x); | |
normal.x /= aspect; | |
normal *= .5 * w; | |
vec4 offset = vec4(normal * side, 0.0, 1.0); | |
finalPosition.xy += offset.xy; | |
gl_Position = finalPosition; | |
} |
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
import THREE from 'three'; | |
var glslify = require('glslify'); | |
const vertexShader = glslify('./meshline.vert'); | |
const fragmentShader = glslify('./meshline.frag'); | |
export default class MeshLineMaterial extends THREE.Material { | |
constructor(parameters = {}) { | |
super(); | |
this.lineWidth = this.check(parameters.lineWidth, 1); | |
this.map = this.check(parameters.map, null); | |
this.useMap = this.check(parameters.useMap, 0); | |
this.color = this.check(parameters.color, new THREE.Color(0xffffff)); | |
this.opacity = this.check(parameters.opacity, 1); | |
this.resolution = this.check(parameters.resolution, new THREE.Vector2(1, 1)); | |
this.sizeAttenuation = this.check(parameters.sizeAttenuation, 1); | |
this.near = this.check(parameters.near, 1); | |
this.far = this.check(parameters.far, 1); | |
this.dashArray = this.check(parameters.dashArray, []); | |
this.useDash = (this.dashArray !== []) ? 1 : 0; | |
let material = new THREE.RawShaderMaterial({ | |
uniforms: { | |
lineWidth: {type: 'f', value: this.lineWidth}, | |
map: {type: 't', value: this.map}, | |
useMap: {type: 'f', value: this.useMap}, | |
color: {type: 'c', value: this.color}, | |
opacity: {type: 'f', value: this.opacity}, | |
resolution: {type: 'v2', value: this.resolution}, | |
sizeAttenuation: {type: 'f', value: this.sizeAttenuation}, | |
near: {type: 'f', value: this.near}, | |
far: {type: 'f', value: this.far}, | |
dashArray: {type: 'v2', value: new THREE.Vector2(this.dashArray[0], this.dashArray[1])}, | |
useDash: {type: 'f', value: this.useDash} | |
}, | |
vertexShader: vertexShader, | |
fragmentShader: fragmentShader | |
}); | |
delete parameters.lineWidth; | |
delete parameters.map; | |
delete parameters.useMap; | |
delete parameters.color; | |
delete parameters.opacity; | |
delete parameters.resolution; | |
delete parameters.sizeAttenuation; | |
delete parameters.near; | |
delete parameters.far; | |
delete parameters.dashArray; | |
material.type = 'MeshLineMaterial'; | |
material.setValues(parameters); | |
return material; | |
} | |
check(v, d) { | |
return v === undefined ? d : v; | |
} | |
copy(source) { | |
super.copy(this, source); | |
this.lineWidth = source.lineWidth; | |
this.map = source.map; | |
this.useMap = source.useMap; | |
this.color.copy( source.color ); | |
this.opacity = source.opacity; | |
this.resolution.copy( source.resolution ); | |
this.sizeAttenuation = source.sizeAttenuation; | |
this.near = source.near; | |
this.far = source.far; | |
return this; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment