mirror of https://gitee.com/antv-l7/antv-l7
feat: support bevel joint, dashline & anti-alias
This commit is contained in:
parent
83df28b246
commit
c0ea754522
|
@ -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
|
||||
})
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<meta name="geometry" content="diagram">
|
||||
<link rel="stylesheet" href="./assets/common.css">
|
||||
<title>dashline demo</title>
|
||||
<style>
|
||||
#map { position:absolute; top:0; bottom:0; width:100%; }
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="map"></div>
|
||||
<script src="https://webapi.amap.com/maps?v=1.4.8&key=15cd8a57710d40c9b7c0e3cc120f1200&plugin=Map3D"></script>
|
||||
<script src="./assets/jquery-3.2.1.min.js"></script>
|
||||
<script src="./assets/dat.gui.min.js"></script>
|
||||
<script src="../build/L7.js"></script>
|
||||
<script>
|
||||
|
||||
const color1 = [ 'rgba(37, 140, 249, 0.8)', 'rgba(14, 241, 242, 0.8)', 'rgba(255, 255, 255, 0.8)' ];
|
||||
const scene = new L7.Scene({
|
||||
id: 'map',
|
||||
mapStyle: 'dark', // 样式URL
|
||||
center: [ 102.602992, 23.107329],
|
||||
pitch: 15,
|
||||
zoom: 14.82,
|
||||
});
|
||||
scene.on('loaded', () => {
|
||||
$.get('https://gw.alipayobjects.com/os/rmsportal/ZVfOvhVCzwBkISNsuKCc.json', data => {
|
||||
scene.LineLayer({
|
||||
zIndex: 2
|
||||
})
|
||||
.source(data)
|
||||
.size('ELEV',(value)=>{
|
||||
return [2,(value-1000)*7];
|
||||
})
|
||||
.active(true)
|
||||
.shape('line')
|
||||
.style({
|
||||
lineType: 'dash',
|
||||
dashArray: 200,
|
||||
dashOffset: 0.2,
|
||||
dashRatio: 0.5
|
||||
})
|
||||
.color('ELEV',["#E8FCFF", "#CFF6FF", "#A1E9ff", "#65CEF7", "#3CB1F0", "#2894E0", "#1772c2", "#105CB3", "#0D408C", "#002466"].reverse())
|
||||
.render();
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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 });
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
};
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue