Skip to content

Instantly share code, notes, and snippets.

@superguigui
Created February 29, 2016 13:35
Show Gist options
  • Save superguigui/2b79b2b3bf26add4b6d2 to your computer and use it in GitHub Desktop.
Save superguigui/2b79b2b3bf26add4b6d2 to your computer and use it in GitHub Desktop.
THREE.Meshline from spite in ES6 with update method
#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;
}
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);
}
}
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;
}
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