diff --git a/demos/02_contour.html b/demos/02_contour.html
index b2782dde8c..3b177a5dbe 100644
--- a/demos/02_contour.html
+++ b/demos/02_contour.html
@@ -30,8 +30,7 @@ const scene = new L7.Scene({
zoom: 14.82,
});
scene.on('loaded', () => {
- $.get('./data/contour.geojson', data => {
- // data.features = data.features.slice(0,1);
+ $.get('https://gw.alipayobjects.com/os/rmsportal/ZVfOvhVCzwBkISNsuKCc.json', data => {
scene.LineLayer({
zIndex: 2
})
diff --git a/demos/dashline.html b/demos/dashline.html
new file mode 100644
index 0000000000..537388f68c
--- /dev/null
+++ b/demos/dashline.html
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+ dashline demo
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/package.json b/package.json
index 43486efeb6..5e4095f1c6 100755
--- a/package.json
+++ b/package.json
@@ -108,8 +108,9 @@
"earcut": "^2.1.3",
"fecha": "^2.3.3",
"gl-matrix": "^2.4.1",
+ "gl-vec2": "^1.3.0",
"lodash": "^4.17.5",
- "polyline-normals": "^2.0.2",
+ "polyline-miter-util": "^1.0.1",
"rbush": "^2.0.2",
"simple-statistics": "^7.0.1",
"supercluster": "^6.0.1",
diff --git a/src/geom/buffer/line.js b/src/geom/buffer/line.js
index b86dac7895..e5b3ed7932 100644
--- a/src/geom/buffer/line.js
+++ b/src/geom/buffer/line.js
@@ -75,7 +75,7 @@ export default class LineBuffer extends BufferBase {
}
_getMeshLineAttributes() {
const layerData = this.get('layerData');
- const { lineType } = this.get('style');
+ const { dashArray } = this.get('style');
const positions = [];
const pickingIds = [];
const normal = [];
@@ -84,10 +84,11 @@ export default class LineBuffer extends BufferBase {
const indexArray = [];
const sizes = [];
const attrDistance = [];
+ const attrDashArray = [];
layerData.forEach(item => {
const props = item;
const positionCount = positions.length / 3;
- const attr = lineShape.Line(item.coordinates, props, positionCount, (lineType !== 'soild'));
+ const attr = lineShape.Line(item.coordinates, props, positionCount, dashArray);
positions.push(...attr.positions);
normal.push(...attr.normal);
miter.push(...attr.miter);
@@ -96,6 +97,7 @@ export default class LineBuffer extends BufferBase {
sizes.push(...attr.sizes);
attrDistance.push(...attr.attrDistance);
pickingIds.push(...attr.pickingIds);
+ attrDashArray.push(...attr.dashArray);
});
return {
positions,
@@ -105,7 +107,8 @@ export default class LineBuffer extends BufferBase {
indexArray,
pickingIds,
sizes,
- attrDistance
+ attrDistance,
+ attrDashArray
};
}
diff --git a/src/geom/buffer/point/fillBuffer.js b/src/geom/buffer/point/fillBuffer.js
index c2e861eaf6..a61a5a8ce4 100644
--- a/src/geom/buffer/point/fillBuffer.js
+++ b/src/geom/buffer/point/fillBuffer.js
@@ -29,6 +29,12 @@ export default function fillBuffer(layerData) {
throw new Error('Invalid shape type: ' + shape);
}
toPointShapeAttributes(polygon, coordinates, { size, shape, color, id }, attribute);
+ // toPointShapeAttributes(polygon, null, {}, attribute);
+ // instanced attributes
+ // attribute.vertices.push(...coordinates);
+ // attribute.a_size.push(...size);
+ // attribute.colors.push(...color);
+ // attribute.pickingIds.push(id);
});
return attribute;
@@ -78,5 +84,8 @@ function toPointShapeAttributes(polygon, geo, style, attribute) {
attribute.colors.push(...color, ...color, ...color);
attribute.pickingIds.push(id, id, id);
+ // attribute.shapePositions.push(ax, ay, az, bx, by, bz, cx, cy, cz);
+ // attribute.normals.push(nx, ny, nz, nx, ny, nz, nx, ny, nz);
+
}
}
diff --git a/src/geom/material/lineMaterial.js b/src/geom/material/lineMaterial.js
index 91a1c4d204..858a4b93e7 100644
--- a/src/geom/material/lineMaterial.js
+++ b/src/geom/material/lineMaterial.js
@@ -1,9 +1,9 @@
import * as THREE from '../../core/three';
import Material from './material';
-import { getModule } from '../../util/shaderModule';
+import { getModule, wrapUniforms } from '../../util/shaderModule';
import arcline_frag from '../shader/arcline_frag.glsl';
import arcline_vert from '../shader/arcline_vert.glsl';
-
+import merge from '@antv/util/lib/deep-mix';
export function LineMaterial(options) {
const { vs, fs } = getModule('line');
@@ -40,19 +40,14 @@ export function ArcLineMaterial(options) {
return material;
}
-export function MeshLineMaterial(options) {
- const { vs, fs } = getModule('meshline');
+export function MeshLineMaterial(options, defines) {
+ const { vs, fs, uniforms } = getModule('meshline');
const material = new Material({
- uniforms: {
- u_opacity: { value: options.u_opacity || 1.0 },
- u_time: { value: options.u_time || 0 },
- u_zoom: { value: options.u_zoom },
- u_duration: { value: options.u_duration || 2.0 },
- u_interval: { value: options.u_interval || 1.0 },
- u_trailLength: { value: options.u_trailLength || 0.2 },
- u_activeId: { value: options.activeId || 0 },
- u_activeColor: { value: options.activeColor || [ 1.0, 0, 0, 1.0 ] }
- },
+ uniforms: wrapUniforms(merge(uniforms, options, {
+ u_activeId: options.activeId,
+ u_activeColor: options.activeColor
+ })),
+ defines,
vertexShader: vs,
fragmentShader: fs,
transparent: true,
@@ -60,23 +55,3 @@ export function MeshLineMaterial(options) {
});
return material;
}
-export function DashLineMaterial(options) {
- const { vs, fs } = getModule('meshline');
- const material = new Material({
- uniforms: {
- u_opacity: { value: options.u_opacity || 1.0 },
- u_time: { value: options.u_time || 0 },
- u_zoom: { value: options.u_zoom },
- u_dashSteps: { value: options.u_dashSteps || 12 },
- u_dashSmooth: { value: options.u_dashSmooth || 0.01 },
- u_dashDistance: { value: options.u_dashDistance || 0.2 },
- u_activeId: { value: options.activeId || 0 },
- u_activeColor: { value: options.activeColor || [ 1.0, 0, 0, 1.0 ] }
- },
- vertexShader: vs,
- fragmentShader: fs,
- transparent: true
- });
- return material;
-}
-
diff --git a/src/geom/shader/dashline_frag.glsl b/src/geom/shader/dashline_frag.glsl
deleted file mode 100644
index 1cc8f95356..0000000000
--- a/src/geom/shader/dashline_frag.glsl
+++ /dev/null
@@ -1,10 +0,0 @@
-varying float v_lineU;
-uniform float u_dashSteps;
-uniform float u_dashSmooth;
-uniform float u_dashDistance;
-varying vec4 v_color;
-void main() {
- float lineUMod = mod(v_lineU, 1.0/ u_dashSteps) * u_dashSteps;
- float dash = smoothstep(u_dashDistance, u_dashDistance+u_dashSmooth, length(lineUMod-0.5));
- gl_FragColor = vec4(v_color.xyz * vec3(dash), v_color.a * dash);
-}
\ No newline at end of file
diff --git a/src/geom/shader/dashline_vert.glsl b/src/geom/shader/dashline_vert.glsl
deleted file mode 100644
index d5492bd848..0000000000
--- a/src/geom/shader/dashline_vert.glsl
+++ /dev/null
@@ -1,23 +0,0 @@
-precision highp float;
-attribute float a_miter;
-attribute vec4 a_color;
-attribute float a_size;
-uniform float u_zoom;
-uniform float u_opacity;
-varying vec4 v_color;
-attribute float a_distance;
-varying float v_lineU;
-uniform float u_activeId;
-uniform vec4 u_activeColor;
-void main() {
- v_lineU = a_distance;
- mat4 matModelViewProjection = projectionMatrix * modelViewMatrix;
- vec3 pointPos = position.xyz + vec3(normal * a_size * pow(2.0,20.0-u_zoom) / 2.0 * a_miter);
- v_color = a_color;
- v_color.a *= u_opacity;
- if(pickingId == u_activeId) {
- v_color = u_activeColor;
- }
- gl_Position = matModelViewProjection * vec4(pointPos, 1.0);
- worldId = id_toPickColor(pickingId);
-}
\ No newline at end of file
diff --git a/src/geom/shader/index.js b/src/geom/shader/index.js
index 0962214754..23e47bf8df 100644
--- a/src/geom/shader/index.js
+++ b/src/geom/shader/index.js
@@ -19,10 +19,6 @@ import mesh_line_vert from '../shader/meshline_vert.glsl';
import line_frag from '../shader/line_frag.glsl';
import line_vert from '../shader/line_vert.glsl';
-// 虚线
-import dash_vert from '../shader/dashline_vert.glsl';
-import dash_frag from '../shader/dashline_frag.glsl';
-
// 热力图
import heatmap_color_vert from '../shader/heatmap_colorize_vert.glsl';
import heatmap_color_frag from '../shader/heatmap_colorize_frag.glsl';
@@ -58,7 +54,6 @@ export function compileBuiltinModules() {
registerModule('pointline', { vs: point_line_vert, fs: point_line_frag });
registerModule('meshline', { vs: mesh_line_vert, fs: mesh_line_frag });
registerModule('line', { vs: line_vert, fs: line_frag });
- registerModule('dashline', { vs: dash_vert, fs: dash_frag });
registerModule('heatmap_color', { vs: heatmap_color_vert, fs: heatmap_color_frag });
registerModule('heatmap_intensity', { vs: heatmap_intensity_vert, fs: heatmap_intensity_frag });
registerModule('text', { vs: text_vert, fs: text_frag });
diff --git a/src/geom/shader/meshline_frag.glsl b/src/geom/shader/meshline_frag.glsl
index 3372d3c248..bb96ae0092 100644
--- a/src/geom/shader/meshline_frag.glsl
+++ b/src/geom/shader/meshline_frag.glsl
@@ -1,11 +1,28 @@
precision highp float;
-uniform float u_opacity;
+
+uniform float u_opacity : 1.0;
+uniform float u_dash_offset : 0.0;
+uniform float u_dash_ratio : 0.0;
+uniform float u_blur : 0.9;
+
varying vec4 v_color;
-varying float vTime;
+varying float v_distance;
+varying float v_dash_array;
+varying float v_time;
+varying vec2 v_normal;
+
void main() {
- gl_FragColor = v_color;
+ gl_FragColor = v_color;
+ #ifdef DASHLINE
+ gl_FragColor.a *= u_opacity * ceil(mod(v_distance + u_dash_offset, v_dash_array) - (v_dash_array * u_dash_ratio));
+ #else
gl_FragColor.a = v_color.a * u_opacity;
- #ifdef ANIMATE
- gl_FragColor.a *= vTime;
- #endif
+ #endif
+ #ifdef ANIMATE
+ gl_FragColor.a *= v_time;
+ #endif
+
+ // anti-alias
+ float blur = 1. - smoothstep(u_blur, 1., length(v_normal));
+ gl_FragColor.a *= blur;
}
diff --git a/src/geom/shader/meshline_vert.glsl b/src/geom/shader/meshline_vert.glsl
index 76c5d8b73a..2a5a87aefe 100644
--- a/src/geom/shader/meshline_vert.glsl
+++ b/src/geom/shader/meshline_vert.glsl
@@ -1,38 +1,48 @@
-precision highp float;
attribute float a_miter;
attribute vec4 a_color;
attribute float a_size;
attribute float a_distance;
+attribute float a_dash_array;
+
uniform float u_zoom;
+uniform float u_time : 0;
+uniform float u_activeId : 1;
+uniform vec4 u_activeColor : [ 1.0, 0, 0, 1.0 ];
+
+varying float v_time;
varying vec4 v_color;
-uniform float u_time;
-varying float vTime;
-uniform float u_activeId;
-uniform vec4 u_activeColor;
-// animate
+varying float v_distance;
+varying float v_dash_array;
+varying vec2 v_normal;
+
#ifdef ANIMATE
-uniform float u_duration; // 动画持续时间
-uniform float u_interval;
-uniform float u_repeat;
-uniform float u_trailLength;
+uniform float u_duration : 2.0;
+uniform float u_interval : 1.0;
+uniform float u_trailLength : 0.2;
#endif
-
void main() {
- mat4 matModelViewProjection = projectionMatrix * modelViewMatrix;
- vec3 pointPos = vec3(position.xy,0.) + vec3(normal * a_size * pow(2.0,20.0-u_zoom) / 2.0 * a_miter);
- v_color = a_color;
- if(pickingId == u_activeId) {
+ v_color = a_color;
+ v_distance = a_distance;
+ v_dash_array = a_dash_array;
+
+ // anti-alias
+ v_normal = vec2(normal * sign(a_miter));
+
+ // extrude along normal
+ float extrude_scale = pow(2.0, 20.0 - u_zoom);
+ vec3 offset = vec3(normal * a_size * extrude_scale / 2.0 * a_miter);
+ gl_Position = projectionMatrix * modelViewMatrix * vec4(position.xy + offset.xy, 0., 1.0);
+
+ #ifdef ANIMATE
+ float alpha =1.0 - fract( mod(1.0- a_distance,u_interval)* (1.0/u_interval) + u_time / u_duration);
+ alpha = (alpha + u_trailLength -1.0) / u_trailLength;
+ v_time = clamp(alpha,0.,1.);
+ #endif
+
+ // picking
+ if(pickingId == u_activeId) {
v_color = u_activeColor;
}
- #ifdef ANIMATE
- //mod(a_distance,0.2) * 5.
- float alpa =1.0 - fract( mod(1.0- a_distance,u_interval)* (1.0/u_interval) + u_time / u_duration);
- alpa = (alpa + u_trailLength -1.0) / u_trailLength;
- vTime = clamp(alpa,0.,1.);
- // vTime = (28800. + mod(u_time* 1000.,28800.)- position.z) / 100.;
- #endif
- worldId = id_toPickColor(pickingId);
- gl_Position = matModelViewProjection * vec4(pointPos.xy, 0., 1.0);
-
+ worldId = id_toPickColor(pickingId);
}
\ No newline at end of file
diff --git a/src/geom/shape/line.js b/src/geom/shape/line.js
index f171c1f86c..c3849a8f8d 100644
--- a/src/geom/shape/line.js
+++ b/src/geom/shape/line.js
@@ -1,4 +1,5 @@
-import getNormal from 'polyline-normals';
+import getNormals from '../../util/polyline-normals';
+import flatten from '@antv/util/lib/flatten';
/**
* shape arc
@@ -64,69 +65,61 @@ export function defaultLine(geo, index) {
return { positions, indexes: indexArray };
}
// mesh line
-export function Line(path, props, positionsIndex) {
+export function Line(path, props, positionsIndex, lengthPerDashSegment = 200) {
if (path.length === 1) path = path[0];// 面坐标转线坐标
const positions = [];
const pickingIds = [];
const normal = [];
const miter = [];
const colors = [];
- const indexArray = [];
- const normals = getNormal(path);
+ const dashArray = [];
+
+ const { normals, attrIndex, attrPos } = getNormals(path, false, positionsIndex);
+
let attrDistance = [];
const sizes = [];
- let c = 0;
- let index = positionsIndex;
const { size, color, id } = props;
- path.forEach((point, pointIndex, list) => {
- const i = index;
- colors.push(...color);
+ attrPos.forEach((point, pointIndex) => {
colors.push(...color);
pickingIds.push(id);
- pickingIds.push(id);
sizes.push(size[0]);
- sizes.push(size[0]);
- if (pointIndex !== list.length - 1) {
- indexArray[c++] = i + 0;
- indexArray[c++] = i + 3;
- indexArray[c++] = i + 1;
- indexArray[c++] = i + 0;
- indexArray[c++] = i + 2;
- indexArray[c++] = i + 3;
- }
- // point[2] = size[1];
- positions.push(...point);
+ point[2] = size[1];
positions.push(...point);
- if (pointIndex === 0) {
- attrDistance.push(0, 0);
+ if (pointIndex === 0 || pointIndex === 1) {
+ attrDistance.push(0);
+ } else if (pointIndex % 2 === 0) {
+ attrDistance.push(attrDistance[pointIndex - 2]
+ + lineSegmentDistance(attrPos[pointIndex - 2], attrPos[pointIndex]));
} else {
- const d = attrDistance[pointIndex * 2 - 1] + lineSegmentDistance(path[pointIndex - 1], path[pointIndex]);
- attrDistance.push(d, d);
+ attrDistance.push(attrDistance[pointIndex - 1]);
}
-
- index += 2;
});
+
+ const totalLength = attrDistance[attrDistance.length - 1];
+ const ratio = lengthPerDashSegment / totalLength;
normals.forEach(n => {
const norm = n[0];
const m = n[1];
normal.push(norm[0], norm[1], 0);
- normal.push(norm[0], norm[1], 0);
- miter.push(-m);
miter.push(m);
+ dashArray.push(ratio);
});
+
attrDistance = attrDistance.map(d => {
- return d / attrDistance[attrDistance.length - 1];
+ return d / totalLength;
});
+
return {
positions,
normal,
- indexArray,
+ indexArray: flatten(attrIndex),
miter,
colors,
sizes,
pickingIds,
- attrDistance
+ attrDistance,
+ dashArray
};
}
diff --git a/src/layer/render/line/drawDashLine.js b/src/layer/render/line/drawDashLine.js
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/src/layer/render/line/drawMeshLine.js b/src/layer/render/line/drawMeshLine.js
index 37eaf2ad25..5709c5ae77 100644
--- a/src/layer/render/line/drawMeshLine.js
+++ b/src/layer/render/line/drawMeshLine.js
@@ -11,15 +11,19 @@ export default function DrawLine(attributes, cfg, layer) {
geometry.addAttribute('normal', new THREE.Float32BufferAttribute(attributes.normal, 3));
geometry.addAttribute('a_miter', new THREE.Float32BufferAttribute(attributes.miter, 1));
geometry.addAttribute('a_distance', new THREE.Float32BufferAttribute(attributes.attrDistance, 1));
+ geometry.addAttribute('a_dash_array', new THREE.Float32BufferAttribute(attributes.attrDashArray, 1));
const lineMaterial = new MeshLineMaterial({
u_opacity: style.opacity,
u_zoom: zoom,
u_time: 0,
+ u_dash_offset: style.dashOffset,
+ u_dash_ratio: style.dashRatio,
activeColor: activeOption.fill
}, {
SHAPE: false,
- ANIMATE: false
+ ANIMATE: false,
+ DASHLINE: style.lineType === 'dash'
});
const lineMesh = new THREE.Mesh(geometry, lineMaterial);
diff --git a/src/layer/render/point/drawFill.js b/src/layer/render/point/drawFill.js
index 883d762107..008d47dd35 100644
--- a/src/layer/render/point/drawFill.js
+++ b/src/layer/render/point/drawFill.js
@@ -16,6 +16,17 @@ export default function DrawFill(attributes, style) {
geometry.addAttribute('normal', new THREE.Float32BufferAttribute(attributes.normals, 3));
geometry.addAttribute('a_shape', new THREE.Float32BufferAttribute(attributes.shapePositions, 3));
geometry.addAttribute('a_size', new THREE.Float32BufferAttribute(attributes.a_size, 3));
+
+ // const instancedGeometry = new THREE.InstancedBufferGeometry();
+
+ // instancedGeometry.addAttribute('normal', new THREE.Float32BufferAttribute(attributes.normals, 3));
+ // instancedGeometry.addAttribute('a_shape', new THREE.Float32BufferAttribute(attributes.shapePositions, 3));
+ // // instanced attributes
+ // instancedGeometry.addAttribute('position', new THREE.InstancedBufferAttribute(new Float32Array(attributes.vertices), 3));
+ // instancedGeometry.addAttribute('a_color', new THREE.InstancedBufferAttribute(new Float32Array(attributes.colors), 4));
+ // instancedGeometry.addAttribute('pickingId', new THREE.InstancedBufferAttribute(new Float32Array(attributes.pickingIds), 1));
+ // instancedGeometry.addAttribute('a_size', new THREE.InstancedBufferAttribute(new Float32Array(attributes.a_size), 3));
+
const material = new PolygonMaterial({
u_opacity: opacity,
u_activeColor: activeColor
@@ -25,6 +36,7 @@ export default function DrawFill(attributes, style) {
material.setDefinesvalue('SHAPE', true);
material.depthTest = false;
const fillMesh = new THREE.Mesh(geometry, material);
+ // const fillMesh = new THREE.Mesh(instancedGeometry, material);
return fillMesh;
}
diff --git a/src/util/polyline-normals.js b/src/util/polyline-normals.js
new file mode 100644
index 0000000000..01b649903c
--- /dev/null
+++ b/src/util/polyline-normals.js
@@ -0,0 +1,131 @@
+/**
+ * 对于 polyline-normal 的改进
+ * 超过阈值,miter 转成 bevel 接头,
+ * 要注意 Three.js 中默认 THREE.FrontFaceDirectionCCW
+ * @see https://zhuanlan.zhihu.com/p/59541559
+ */
+import { direction, normal, computeMiter } from 'polyline-miter-util';
+import { create, copy, dot } from 'gl-vec2';
+
+function extrusions(positions, out, point, normal, scale) {
+ addNext(out, normal, -scale);
+ addNext(out, normal, scale);
+ positions.push(point);
+ positions.push(point);
+}
+
+function addNext(out, normal, length) {
+ out.push([[ normal[0], normal[1] ], length ]);
+}
+
+export default function(points, closed, indexOffset) {
+ const lineA = [ 0, 0 ];
+ const lineB = [ 0, 0 ];
+ const tangent = [ 0, 0 ];
+ const miter = [ 0, 0 ];
+ let _lastFlip = -1;
+ let _started = false;
+ let _normal = null;
+ const tmp = create();
+ let count = indexOffset || 0;
+ const miterLimit = 3;
+
+ const out = [];
+ const attrPos = [];
+ const attrIndex = [];
+ const attrCounters = [ 0, 0 ];
+ if (closed) {
+ points = points.slice();
+ points.push(points[0]);
+ }
+
+ const total = points.length;
+
+ for (let i = 1; i < total; i++) {
+ const index = count;
+ const last = points[i - 1];
+ const cur = points[i];
+ const next = i < points.length - 1 ? points[i + 1] : null;
+
+ attrCounters.push(i / total, i / total);
+
+ direction(lineA, cur, last);
+
+ if (!_normal) {
+ _normal = [ 0, 0 ];
+ normal(_normal, lineA);
+ }
+
+ if (!_started) {
+ _started = true;
+ extrusions(attrPos, out, last, _normal, 1);
+ }
+
+ attrIndex.push([ index + 0, index + 3, index + 1 ]);
+
+ // no miter, simple segment
+ if (!next) {
+ // reset normal
+ normal(_normal, lineA);
+ extrusions(attrPos, out, cur, _normal, 1);
+ attrIndex.push(
+ _lastFlip === 1 ? [ index + 1, index + 3, index + 2 ] : [ index, index + 2, index + 3 ]);
+
+ count += 2;
+ } else {
+ // get unit dir of next line
+ direction(lineB, next, cur);
+
+ // stores tangent & miter
+ let miterLen = computeMiter(tangent, miter, lineA, lineB, 1);
+
+ // get orientation
+ let flip = (dot(tangent, _normal) < 0) ? -1 : 1;
+ const bevel = miterLen > miterLimit;
+ if (bevel) {
+ miterLen = miterLimit;
+ attrCounters.push(i / total);
+
+ // next two points in our first segment
+ addNext(out, _normal, -flip);
+ attrPos.push(cur);
+ addNext(out, miter, miterLen * flip);
+ attrPos.push(cur);
+
+ attrIndex.push(_lastFlip !== -flip
+ ? [ index + 1, index + 3, index + 2 ] : [ index, index + 2, index + 3 ]);
+
+ // now add the bevel triangle
+ attrIndex.push([ index + 2, index + 3, index + 4 ]);
+
+ normal(tmp, lineB);
+ copy(_normal, tmp); // store normal for next round
+
+ addNext(out, _normal, -flip);
+ attrPos.push(cur);
+
+ // the miter is now the normal for our next join
+ count += 3;
+ } else {
+ // next two points for our miter join
+ extrusions(attrPos, out, cur, miter, miterLen);
+ attrIndex.push(_lastFlip === 1
+ ? [ index + 1, index + 3, index + 2 ] : [ index, index + 2, index + 3 ]);
+
+ flip = -1;
+
+ // the miter is now the normal for our next join
+ copy(_normal, miter);
+ count += 2;
+ }
+ _lastFlip = flip;
+ }
+ }
+
+ return {
+ normals: out,
+ attrIndex,
+ attrPos,
+ attrCounters
+ };
+}
diff --git a/src/util/shaderModule.js b/src/util/shaderModule.js
index b73abed272..2c6c775d85 100644
--- a/src/util/shaderModule.js
+++ b/src/util/shaderModule.js
@@ -1,3 +1,7 @@
+import uniq from '@antv/util/lib/uniq';
+import isString from '@antv/util/lib/is-string';
+import ColorUtil from '../attr/color-util';
+
const SHADER_TYPE = {
VS: 'vs',
FS: 'fs'
@@ -9,9 +13,10 @@ const globalDefaultprecision = '#ifdef GL_FRAGMENT_PRECISION_HIGH\n precision hi
const globalDefaultAttribute = 'attribute float pickingId;\n varying vec4 worldId;\n';
const globalDefaultInclude = '#pragma include "pick_color"\n';
const includeRegExp = /#pragma include (["^+"]?["\ "[a-zA-Z_0-9](.*)"]*?)/g;
+const uniformRegExp = /uniform\s+(bool|float|int|vec2|vec3|vec4|ivec2|ivec3|ivec4|mat2|mat3|mat4|sampler2D|samplerCube)\s+([\s\S]*?);/g;
function processModule(rawContent, includeList, type) {
- return rawContent.replace(includeRegExp, (_, strMatch) => {
+ const compiled = rawContent.replace(includeRegExp, (_, strMatch) => {
const includeOpt = strMatch.split(' ');
const includeName = includeOpt[0].replace(/"/g, '');
@@ -19,18 +24,110 @@ function processModule(rawContent, includeList, type) {
return '';
}
- let txt = rawContentCache[includeName][type];
+ const txt = rawContentCache[includeName][type];
includeList.push(includeName);
- txt = processModule(txt, includeList, type);
- return txt;
+ const { content } = processModule(txt, includeList, type);
+ return content;
});
+
+ return {
+ content: compiled,
+ includeList
+ };
}
-export function registerModule(moduleName, { vs, fs }) {
+function getUniformLengthByType(type) {
+ let arrayLength = 0;
+ switch (type) {
+ case 'vec2':
+ case 'ivec2':
+ arrayLength = 2;
+ break;
+ case 'vec3':
+ case 'ivec3':
+ arrayLength = 3;
+ break;
+ case 'vec4':
+ case 'ivec4':
+ case 'mat2':
+ arrayLength = 4;
+ break;
+ case 'mat3':
+ arrayLength = 9;
+ break;
+ case 'mat4':
+ arrayLength = 16;
+ break;
+ default:
+ }
+ return arrayLength;
+}
+
+function extractUniforms(content) {
+ const uniforms = {};
+ content = content.replace(uniformRegExp, (_, type, c) => {
+ const defaultValues = c.split(':');
+ const uniformName = defaultValues[0].trim();
+ let defaultValue = '';
+ if (defaultValues.length > 1) {
+ defaultValue = defaultValues[1].trim();
+ }
+
+ // set default value for uniform according to its type
+ // eg. vec2 u -> [0.0, 0.0]
+ switch (type) {
+ case 'bool':
+ defaultValue = defaultValue === 'true';
+ break;
+ case 'float':
+ case 'int':
+ defaultValue = Number(defaultValue);
+ break;
+ case 'vec2':
+ case 'vec3':
+ case 'vec4':
+ case 'ivec2':
+ case 'ivec3':
+ case 'ivec4':
+ case 'mat2':
+ case 'mat3':
+ case 'mat4':
+ if (defaultValue) {
+ defaultValue = defaultValue.replace('[', '').replace(']', '')
+ .split(',')
+ .reduce((prev, cur) => {
+ prev.push(Number(cur.trim()));
+ return prev;
+ }, []);
+ } else {
+ defaultValue = new Array(getUniformLengthByType(type)).fill(0);
+ }
+ break;
+ default:
+ }
+
+ uniforms[uniformName] = defaultValue;
+ return `uniform ${type} ${uniformName};\n`;
+ });
+ return {
+ content,
+ uniforms
+ };
+}
+
+export function registerModule(moduleName, { vs, fs, uniforms: declaredUniforms }) {
+ const { content: extractedVS, uniforms: vsUniforms } = extractUniforms(vs);
+ const { content: extractedFS, uniforms: fsUniforms } = extractUniforms(fs);
+
rawContentCache[moduleName] = {
- [SHADER_TYPE.VS]: vs,
- [SHADER_TYPE.FS]: fs
+ [SHADER_TYPE.VS]: extractedVS,
+ [SHADER_TYPE.FS]: extractedFS,
+ uniforms: {
+ ...vsUniforms,
+ ...fsUniforms,
+ ...declaredUniforms
+ }
};
}
@@ -39,11 +136,20 @@ export function getModule(moduleName) {
return moduleCache[moduleName];
}
- let vs = rawContentCache[moduleName][SHADER_TYPE.VS];
- let fs = rawContentCache[moduleName][SHADER_TYPE.FS];
- vs = globalDefaultAttribute + globalDefaultInclude + vs;
- vs = processModule(vs, [], SHADER_TYPE.VS);
- fs = processModule(fs, [], SHADER_TYPE.FS);
+ let rawVS = rawContentCache[moduleName][SHADER_TYPE.VS];
+ const rawFS = rawContentCache[moduleName][SHADER_TYPE.FS];
+
+ rawVS = globalDefaultAttribute + globalDefaultInclude + rawVS;
+
+ const { content: vs, includeList: vsIncludeList } = processModule(rawVS, [], SHADER_TYPE.VS);
+ let { content: fs, includeList: fsIncludeList } = processModule(rawFS, [], SHADER_TYPE.FS);
+ // TODO: extract uniforms and their default values from GLSL
+ const uniforms = uniq(vsIncludeList.concat(fsIncludeList).concat(moduleName)).reduce((prev, cur) => {
+ return {
+ ...prev,
+ ...rawContentCache[cur].uniforms
+ };
+ }, {});
/**
* set default precision for fragment shader
@@ -55,7 +161,76 @@ export function getModule(moduleName) {
moduleCache[moduleName] = {
[SHADER_TYPE.VS]: vs.trim(),
- [SHADER_TYPE.FS]: fs.trim()
+ [SHADER_TYPE.FS]: fs.trim(),
+ uniforms
};
return moduleCache[moduleName];
}
+
+export function wrapUniforms(uniforms) {
+ return Object.keys(uniforms).reduce((prev, cur) => {
+ prev[cur] = {
+ value: uniforms[cur]
+ };
+ return prev;
+ }, {});
+}
+
+const DEFAULT_LIGHT = {
+ type: 'directional',
+ direction: [ 1, 10.5, 12 ],
+ ambient: [ 0.2, 0.2, 0.2 ],
+ diffuse: [ 0.6, 0.6, 0.6 ],
+ specular: [ 0.1, 0.1, 0.1 ]
+};
+
+const DEFAULT_DIRECTIONAL_LIGHT = {
+ direction: [ 0, 0, 0 ],
+ ambient: [ 0, 0, 0 ],
+ diffuse: [ 0, 0, 0 ],
+ specular: [ 0, 0, 0 ]
+};
+
+const DEFAULT_SPOT_LIGHT = {
+ position: [ 0, 0, 0 ],
+ direction: [ 0, 0, 0 ],
+ ambient: [ 0, 0, 0 ],
+ diffuse: [ 0, 0, 0 ],
+ specular: [ 0, 0, 0 ],
+ constant: 1,
+ linear: 0,
+ quadratic: 0,
+ angle: 14,
+ exponent: 40,
+ blur: 5
+};
+
+const COLOR_ATTRIBUTES = [
+ 'ambient', 'diffuse', 'specular'
+];
+
+export function generateLightingUniforms(lights) {
+ const lightsMap = {
+ u_directional_lights: new Array(3).fill({ ...DEFAULT_DIRECTIONAL_LIGHT }),
+ u_num_of_directional_lights: 0,
+ u_spot_lights: new Array(3).fill({ ...DEFAULT_SPOT_LIGHT }),
+ u_num_of_spot_lights: 0
+ };
+ if (!lights || !lights.length) {
+ lights = [ DEFAULT_LIGHT ];
+ }
+ lights.forEach(({ type, ...rest }, i) => {
+ const lightsUniformName = `u_${type}_lights`;
+ const lightsNumUniformName = `u_num_of_${type}_lights`;
+
+ Object.keys(rest).forEach(key => {
+ if (isString(rest[key]) && COLOR_ATTRIBUTES.indexOf(key) > -1) {
+ rest[key] = ColorUtil.color2RGBA(rest[key]).slice(0, 3);
+ }
+ });
+
+ lightsMap[lightsUniformName][i] = { ...lightsMap[lightsUniformName][i], ...rest };
+ lightsMap[lightsNumUniformName]++;
+ });
+ return lightsMap;
+}