mirror of https://gitee.com/antv-l7/antv-l7
fix(layer): fix merge conflict
This commit is contained in:
commit
453adb09d6
|
@ -65,3 +65,5 @@ jspm_packages/
|
||||||
# End of https://www.gitignore.io/api/node
|
# End of https://www.gitignore.io/api/node
|
||||||
|
|
||||||
lib/
|
lib/
|
||||||
|
|
||||||
|
.DS_Store
|
||||||
|
|
Binary file not shown.
|
@ -0,0 +1,150 @@
|
||||||
|
# PixelPickingEngine 设计
|
||||||
|
|
||||||
|
在地图交互中,除了地图底图本身提供的平移、旋转、缩放、flyTo 等相机动作,最常用的就是信息要素的拾取以及后续的高亮了。
|
||||||
|
|
||||||
|
3D 引擎常用的拾取技术通常有两种:RayPicking 和 PixelPicking。前者从鼠标点击处沿着投影方向发射一根射线,通过包围盒碰撞检测获取到接触到的第一个对象,后续就可以进行选中对象的高亮甚至是跟随移动了,以上运算均在 CPU 侧完成。
|
||||||
|
但是在 L7 的场景中,海量数据在同一个 Geometry 中,无法计算每个要素的包围盒,因此在 GPU 侧完成的 PixelPicking 更加适合。
|
||||||
|
|
||||||
|
作为拾取引擎 PixelPickingEngine,除了实现内置基本的拾取 Pass,最重要的是提供友好易用的 API,覆盖以下常见场景:
|
||||||
|
* 基本的拾取场景,用户只需要开启 Layer 拾取功能并设置高亮颜色即可。
|
||||||
|
* 拾取后展示特定 UI 组件的场景,用户需要监听事件,在回调中使用上述拾取对象完成组件展示。
|
||||||
|
* 更灵活的联动场景,用户可以不依赖 L7 内置的事件监听机制,直接拾取并高亮指定点/区域包含的要素。
|
||||||
|
|
||||||
|
本文会依次介绍:
|
||||||
|
* PixelPicking 原理
|
||||||
|
* 使用方法
|
||||||
|
* 拾取对象结构
|
||||||
|
* 拾取 API 的使用方法
|
||||||
|
* 开启/关闭拾取
|
||||||
|
* 设置高亮颜色
|
||||||
|
* 展示自定义 UI 组件
|
||||||
|
* 在自定义 Layer 中使用
|
||||||
|
|
||||||
|
## PixelPicking 原理
|
||||||
|
|
||||||
|
在执行时机方面,基于 [MultiPassRenderer](./MultiPassRenderer.md) 的设计,拾取发生在实际渲染之前:
|
||||||
|
```
|
||||||
|
ClearPass -> PixelPickingPass -> RenderPass -> [ ...其他后处理 Pass ] -> CopyPass
|
||||||
|
```
|
||||||
|
|
||||||
|
PixelPickingPass 分解步骤如下:
|
||||||
|
1. 逐要素编码(idx -> color),传入 attributes 渲染 Layer 到纹理。
|
||||||
|
2. 获取鼠标在视口中的位置。由于目前 L7 与地图结合的方案为双 Canvas 而非共享 WebGL Context,事件监听注册在地图底图上。
|
||||||
|
3. 读取纹理在指定位置的颜色,进行解码(color -> idx),查找对应要素,作为 Layer `onHover/onClick` 回调参数传入。
|
||||||
|
4. (可选)将待高亮要素对应的颜色传入 Vertex Shader 用于每个 Vertex 判断自身是否被选中,如果被选中,在 Fragment Shader 中将高亮颜色与计算颜色混合。
|
||||||
|
|
||||||
|
## 使用方法
|
||||||
|
|
||||||
|
### 拾取对象结构定义
|
||||||
|
|
||||||
|
拾取对象结构定义如下:
|
||||||
|
|
||||||
|
| 参数名 | 类型 | 说明 |
|
||||||
|
| -------- | --- | ------------- |
|
||||||
|
| x | `number` | 鼠标位置在视口空间 x 坐标,取值范围 `[0, viewportWidth]` |
|
||||||
|
| y | `number` | 鼠标位置在视口空间 y 坐标,取值范围 `[0, viewportHeight]` |
|
||||||
|
| lnglat | `{ lng: number; lat: number; }` | 鼠标位置经纬度坐标 |
|
||||||
|
| feature | `object` | GeoJSON feature 属性 |
|
||||||
|
|
||||||
|
### API
|
||||||
|
|
||||||
|
对于基本的拾取场景,用户只需要开启 Layer 拾取功能并设置高亮颜色即可。
|
||||||
|
而对于拾取后展示特定 UI 组件的场景,用户需要监听事件,在回调中使用上述拾取对象完成组件展示。
|
||||||
|
最后,对于更灵活的联动场景,用户可以不依赖 L7 内置的事件监听机制,直接拾取并高亮指定点/区域包含的要素。
|
||||||
|
|
||||||
|
#### 禁用/开启拾取
|
||||||
|
|
||||||
|
并不是所有 Layer 都需要拾取(例如文本渲染 Layer),通过 `enablePicking` 关闭可以跳过该阶段,减少不必要的渲染开销:
|
||||||
|
```typescript
|
||||||
|
const layer = new PolygonLayer({
|
||||||
|
enablePicking: false, // 关闭拾取
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
⚠️L7 默认开启拾取。
|
||||||
|
|
||||||
|
#### 设置高亮颜色
|
||||||
|
|
||||||
|
如果一个 Layer 开启了拾取,我们可以通过 `highlightColor` 设置高亮颜色:
|
||||||
|
```typescript
|
||||||
|
const layer = new PolygonLayer({
|
||||||
|
enablePicking: true, // 开启拾取
|
||||||
|
highlightColor: 'red', // 设置高亮颜色
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 展示自定义 UI 组件
|
||||||
|
|
||||||
|
监听 Layer 上的 `hover/mousemove` 事件就可以得到拾取对象,然后通过对象中包含的位置以及原始数据信息,就可以使用 L7 内置或者自定义 UI 组件展示:
|
||||||
|
```typescript
|
||||||
|
layer.on('hover', ({ x, y, lnglat, feature }) => {
|
||||||
|
// 展示 UI 组件
|
||||||
|
});
|
||||||
|
layer.on('mousemove', ({ x, y, lnglat, feature }) => {
|
||||||
|
// 同上
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
除了基于事件监听,还可以通过 Layer 的构造函数传入 `onHover` 回调,在后续 Layer 对应的 react 组件中也可以以这种方式使用:
|
||||||
|
```typescript
|
||||||
|
const layer = new PolygonLayer({
|
||||||
|
enablePicking: true,
|
||||||
|
onHover: ({ x, y, lnglat, feature }) => {
|
||||||
|
// 展示 UI 组件
|
||||||
|
},
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 直接调用拾取引擎方法
|
||||||
|
|
||||||
|
除了默认在地图上交互完成拾取,在与其他系统进行联动时,脱离了地图交互,仍需要具备拾取指定点/区域内包含要素的能力。
|
||||||
|
```typescript
|
||||||
|
anotherSystem.on('hover', ({ x, y }) => {
|
||||||
|
layer.pick({
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
⚠️目前只支持拾取视口中一个点所在的要素,未来可以实现拾取指定区域内的全部要素。
|
||||||
|
|
||||||
|
### 自定义 Layer 中的拾取
|
||||||
|
|
||||||
|
用户实现自定义 Layer 时,必然需要实现 Vertex/Fragment Shader。如果也想使用拾取功能,就需要在 Shader 中引入拾取模块,方法如下。
|
||||||
|
|
||||||
|
在 Vertex Shader 中引入 `picking` 模块。关于 L7 Shader 的模块化设计,[详见]()。
|
||||||
|
```glsl
|
||||||
|
// mylayer.vert.glsl
|
||||||
|
|
||||||
|
#pragma include "picking"
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
setPickingColor(customPickingColors);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
在 Fragment Shader 中
|
||||||
|
```glsl
|
||||||
|
// mylayer.frag.glsl
|
||||||
|
|
||||||
|
#pragma include "picking"
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
// 必须在末尾,保证后续不会再对 gl_FragColor 进行修改
|
||||||
|
gl_FragColor = highlightPickingColor(gl_FragColor);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
其中涉及 `picking` 模块方法说明如下:
|
||||||
|
|
||||||
|
| 方法名 | 应用 shader | 说明 |
|
||||||
|
| -------- | --- | ------------- |
|
||||||
|
| `setPickingColor` | `vertex` | 比较自身颜色编码与高亮颜色,判断是否被选中,传递结果给 fragment |
|
||||||
|
| `highlightPickingColor` | `fragment` | 当前 fragment 被选中则使用高亮颜色混合,否则直接输出原始计算结果 |
|
||||||
|
|
||||||
|
## 参考资料
|
||||||
|
|
||||||
|
* [Deck.gl 交互文档](https://deck.gl/#/documentation/developer-guide/adding-interactivity)
|
||||||
|
* [Deck.gl Picking 实现](https://deck.gl/#/documentation/developer-guide/writing-custom-layers/picking)
|
||||||
|
* 「Interactive.Computer.Graphics.Top.Down.Approach - 3.9 Picking」
|
|
@ -22,6 +22,7 @@
|
||||||
"@l7/source": "0.0.1",
|
"@l7/source": "0.0.1",
|
||||||
"eventemitter3": "^3.1.0",
|
"eventemitter3": "^3.1.0",
|
||||||
"gl-matrix": "^3.1.0",
|
"gl-matrix": "^3.1.0",
|
||||||
|
"hammerjs": "^2.0.8",
|
||||||
"inversify": "^5.0.1",
|
"inversify": "^5.0.1",
|
||||||
"inversify-inject-decorators": "^3.1.0",
|
"inversify-inject-decorators": "^3.1.0",
|
||||||
"lodash": "^4.17.15",
|
"lodash": "^4.17.15",
|
||||||
|
@ -32,6 +33,7 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/gl-matrix": "^2.4.5",
|
"@types/gl-matrix": "^2.4.5",
|
||||||
|
"@types/hammerjs": "^2.0.36",
|
||||||
"@types/lodash": "^4.14.138",
|
"@types/lodash": "^4.14.138",
|
||||||
"@types/viewport-mercator-project": "^6.1.0"
|
"@types/viewport-mercator-project": "^6.1.0"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import container, { lazyInject } from './inversify.config';
|
import container, { lazyInject } from './inversify.config';
|
||||||
import ClearPass from './services/renderer/passes/ClearPass';
|
import ClearPass from './services/renderer/passes/ClearPass';
|
||||||
|
import MultiPassRenderer from './services/renderer/passes/MultiPassRenderer';
|
||||||
|
import PixelPickingPass from './services/renderer/passes/PixelPickingPass';
|
||||||
import BlurHPass from './services/renderer/passes/post-processing/BlurHPass';
|
import BlurHPass from './services/renderer/passes/post-processing/BlurHPass';
|
||||||
import BlurVPass from './services/renderer/passes/post-processing/BlurVPass';
|
import BlurVPass from './services/renderer/passes/post-processing/BlurVPass';
|
||||||
import CopyPass from './services/renderer/passes/post-processing/CopyPass';
|
import CopyPass from './services/renderer/passes/post-processing/CopyPass';
|
||||||
|
@ -27,8 +29,10 @@ export {
|
||||||
SceneService,
|
SceneService,
|
||||||
packCircleVertex,
|
packCircleVertex,
|
||||||
/** pass */
|
/** pass */
|
||||||
|
MultiPassRenderer,
|
||||||
ClearPass,
|
ClearPass,
|
||||||
RenderPass,
|
RenderPass,
|
||||||
|
PixelPickingPass,
|
||||||
BlurHPass,
|
BlurHPass,
|
||||||
BlurVPass,
|
BlurVPass,
|
||||||
CopyPass,
|
CopyPass,
|
||||||
|
|
|
@ -10,6 +10,7 @@ import { IIconService} from './services/asset/IIconService';
|
||||||
import { ICameraService } from './services/camera/ICameraService';
|
import { ICameraService } from './services/camera/ICameraService';
|
||||||
import { IGlobalConfigService } from './services/config/IConfigService';
|
import { IGlobalConfigService } from './services/config/IConfigService';
|
||||||
import { ICoordinateSystemService } from './services/coordinate/ICoordinateSystemService';
|
import { ICoordinateSystemService } from './services/coordinate/ICoordinateSystemService';
|
||||||
|
import { IInteractionService } from './services/interaction/IInteractionService';
|
||||||
import { ILayerService } from './services/layer/ILayerService';
|
import { ILayerService } from './services/layer/ILayerService';
|
||||||
import { ILogService } from './services/log/ILogService';
|
import { ILogService } from './services/log/ILogService';
|
||||||
import { IShaderModuleService } from './services/shader/IShaderModuleService';
|
import { IShaderModuleService } from './services/shader/IShaderModuleService';
|
||||||
|
@ -19,6 +20,7 @@ import IconService from './services/asset/IconService';
|
||||||
import CameraService from './services/camera/CameraService';
|
import CameraService from './services/camera/CameraService';
|
||||||
import GlobalConfigService from './services/config/ConfigService';
|
import GlobalConfigService from './services/config/ConfigService';
|
||||||
import CoordinateSystemService from './services/coordinate/CoordinateSystemService';
|
import CoordinateSystemService from './services/coordinate/CoordinateSystemService';
|
||||||
|
import InteractionService from './services/interaction/InteractionService';
|
||||||
import LayerService from './services/layer/LayerService';
|
import LayerService from './services/layer/LayerService';
|
||||||
import LayerStyleService from './services/layer/LayerStyleService';
|
import LayerStyleService from './services/layer/LayerStyleService';
|
||||||
import LogService from './services/log/LogService';
|
import LogService from './services/log/LogService';
|
||||||
|
@ -58,6 +60,10 @@ container
|
||||||
.bind<ILogService>(TYPES.ILogService)
|
.bind<ILogService>(TYPES.ILogService)
|
||||||
.to(LogService)
|
.to(LogService)
|
||||||
.inSingletonScope();
|
.inSingletonScope();
|
||||||
|
container
|
||||||
|
.bind<IInteractionService>(TYPES.IInteractionService)
|
||||||
|
.to(InteractionService)
|
||||||
|
.inSingletonScope();
|
||||||
|
|
||||||
// @see https://github.com/inversify/InversifyJS/blob/master/wiki/inheritance.md#what-can-i-do-when-my-base-class-is-provided-by-a-third-party-module
|
// @see https://github.com/inversify/InversifyJS/blob/master/wiki/inheritance.md#what-can-i-do-when-my-base-class-is-provided-by-a-third-party-module
|
||||||
decorate(injectable(), EventEmitter);
|
decorate(injectable(), EventEmitter);
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
export interface IInteractionService {
|
||||||
|
init(): void;
|
||||||
|
destroy(): void;
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
import Hammer from 'hammerjs';
|
||||||
|
import { inject, injectable } from 'inversify';
|
||||||
|
import { TYPES } from '../../types';
|
||||||
|
import { ILogService } from '../log/ILogService';
|
||||||
|
import { IRendererService } from '../renderer/IRendererService';
|
||||||
|
import { IInteractionService } from './IInteractionService';
|
||||||
|
|
||||||
|
@injectable()
|
||||||
|
export default class InteractionService implements IInteractionService {
|
||||||
|
@inject(TYPES.IRendererService)
|
||||||
|
private readonly rendererService: IRendererService;
|
||||||
|
|
||||||
|
@inject(TYPES.ILogService)
|
||||||
|
private readonly logger: ILogService;
|
||||||
|
|
||||||
|
private hammertime: HammerManager;
|
||||||
|
|
||||||
|
public init() {
|
||||||
|
const $containter = this.rendererService.getContainer();
|
||||||
|
if ($containter) {
|
||||||
|
const hammertime = new Hammer($containter);
|
||||||
|
hammertime.get('pan').set({ direction: Hammer.DIRECTION_ALL });
|
||||||
|
hammertime.get('pinch').set({ enable: true });
|
||||||
|
|
||||||
|
// hammertime.on('panstart', this.onPanstart);
|
||||||
|
hammertime.on('panmove', this.onPanmove);
|
||||||
|
// hammertime.on('panend', this.onPanend);
|
||||||
|
// hammertime.on('pinch', this.onPinch);
|
||||||
|
|
||||||
|
// $containter.addEventListener('wheel', this.onMousewheel);
|
||||||
|
this.hammertime = hammertime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public destroy() {
|
||||||
|
if (this.hammertime) {
|
||||||
|
this.hammertime.destroy();
|
||||||
|
}
|
||||||
|
const $containter = this.rendererService.getContainer();
|
||||||
|
if ($containter) {
|
||||||
|
// $containter.removeEventListener('wheel', this.onMousewheel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private onPanmove = (e: HammerInput) => {
|
||||||
|
// @ts-ignore
|
||||||
|
// this.logger.info(e);
|
||||||
|
// if (this.isMoving) {
|
||||||
|
// this.deltaX = e.center.x - this.lastX;
|
||||||
|
// this.deltaY = e.center.y - this.lastY;
|
||||||
|
// this.lastX = e.center.x;
|
||||||
|
// this.lastY = e.center.y;
|
||||||
|
// this.emit(Mouse.MOVE_EVENT, {
|
||||||
|
// deltaX: this.deltaX,
|
||||||
|
// deltaY: this.deltaY,
|
||||||
|
// deltaZ: this.deltaZ
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
};
|
||||||
|
}
|
|
@ -109,6 +109,7 @@ export interface ILayerPlugin {
|
||||||
*/
|
*/
|
||||||
export interface ILayerInitializationOptions {
|
export interface ILayerInitializationOptions {
|
||||||
enableMultiPassRenderer: boolean;
|
enableMultiPassRenderer: boolean;
|
||||||
|
enablePicking: boolean;
|
||||||
passes: Array<string | [string, { [key: string]: unknown }]>;
|
passes: Array<string | [string, { [key: string]: unknown }]>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,9 +31,6 @@ export interface IPostProcessingPass extends IPass {
|
||||||
export interface IPostProcessor {
|
export interface IPostProcessor {
|
||||||
getReadFBO(): IFramebuffer;
|
getReadFBO(): IFramebuffer;
|
||||||
getWriteFBO(): IFramebuffer;
|
getWriteFBO(): IFramebuffer;
|
||||||
useScreenRenderTarget(renderCommand: () => void): void;
|
|
||||||
useOffscreenRenderTarget(renderCommand: () => void): void;
|
|
||||||
renderToPostProcessor(renderCommand: () => void): void;
|
|
||||||
resize(viewportWidth: number, viewportHeight: number): void;
|
resize(viewportWidth: number, viewportHeight: number): void;
|
||||||
add(pass: IPostProcessingPass, layer: ILayer): void;
|
add(pass: IPostProcessingPass, layer: ILayer): void;
|
||||||
render(layer: ILayer): Promise<unknown>;
|
render(layer: ILayer): Promise<unknown>;
|
||||||
|
|
|
@ -2,9 +2,13 @@ import { ILayer } from '../layer/ILayerService';
|
||||||
import { IAttribute, IAttributeInitializationOptions } from './IAttribute';
|
import { IAttribute, IAttributeInitializationOptions } from './IAttribute';
|
||||||
import { IBuffer, IBufferInitializationOptions } from './IBuffer';
|
import { IBuffer, IBufferInitializationOptions } from './IBuffer';
|
||||||
import { IElements, IElementsInitializationOptions } from './IElements';
|
import { IElements, IElementsInitializationOptions } from './IElements';
|
||||||
import { IFramebuffer } from './IFramebuffer';
|
import {
|
||||||
|
IFramebuffer,
|
||||||
|
IFramebufferInitializationOptions,
|
||||||
|
} from './IFramebuffer';
|
||||||
import { IModel, IModelInitializationOptions } from './IModel';
|
import { IModel, IModelInitializationOptions } from './IModel';
|
||||||
import { IMultiPassRenderer, IPass } from './IMultiPassRenderer';
|
import { IMultiPassRenderer, IPass } from './IMultiPassRenderer';
|
||||||
|
import { ITexture2D, ITexture2DInitializationOptions } from './ITexture2D';
|
||||||
|
|
||||||
export interface IRenderConfig {
|
export interface IRenderConfig {
|
||||||
/**
|
/**
|
||||||
|
@ -32,7 +36,13 @@ export interface IRendererService {
|
||||||
createAttribute(options: IAttributeInitializationOptions): IAttribute;
|
createAttribute(options: IAttributeInitializationOptions): IAttribute;
|
||||||
createBuffer(options: IBufferInitializationOptions): IBuffer;
|
createBuffer(options: IBufferInitializationOptions): IBuffer;
|
||||||
createElements(options: IElementsInitializationOptions): IElements;
|
createElements(options: IElementsInitializationOptions): IElements;
|
||||||
createMultiPassRenderer(layer: ILayer): IMultiPassRenderer;
|
createTexture2D(options: ITexture2DInitializationOptions): ITexture2D;
|
||||||
|
createFramebuffer(options: IFramebufferInitializationOptions): IFramebuffer;
|
||||||
|
renderToFramebuffer(
|
||||||
|
framebuffer: IFramebuffer | null,
|
||||||
|
drawCommands: () => void,
|
||||||
|
): void;
|
||||||
getViewportSize(): { width: number; height: number };
|
getViewportSize(): { width: number; height: number };
|
||||||
|
getContainer(): HTMLElement | null;
|
||||||
viewport(size: { x: number; y: number; width: number; height: number }): void;
|
viewport(size: { x: number; y: number; width: number; height: number }): void;
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ export default class BasePostProcessingPass<InitializationOptions = {}>
|
||||||
protected readonly shaderModule: IShaderModuleService;
|
protected readonly shaderModule: IShaderModuleService;
|
||||||
|
|
||||||
@lazyInject(TYPES.IRendererService)
|
@lazyInject(TYPES.IRendererService)
|
||||||
protected readonly renderer: IRendererService;
|
protected readonly rendererService: IRendererService;
|
||||||
|
|
||||||
protected config: Partial<InitializationOptions> | undefined;
|
protected config: Partial<InitializationOptions> | undefined;
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ export default class BasePostProcessingPass<InitializationOptions = {}>
|
||||||
}
|
}
|
||||||
|
|
||||||
public init() {
|
public init() {
|
||||||
const { createAttribute, createBuffer, createModel } = this.renderer;
|
const { createAttribute, createBuffer, createModel } = this.rendererService;
|
||||||
const { vs, fs, uniforms } = this.setupShaders();
|
const { vs, fs, uniforms } = this.setupShaders();
|
||||||
|
|
||||||
this.model = createModel({
|
this.model = createModel({
|
||||||
|
@ -81,19 +81,31 @@ export default class BasePostProcessingPass<InitializationOptions = {}>
|
||||||
|
|
||||||
public render(layer: ILayer) {
|
public render(layer: ILayer) {
|
||||||
const postProcessor = layer.multiPassRenderer.getPostProcessor();
|
const postProcessor = layer.multiPassRenderer.getPostProcessor();
|
||||||
|
const { renderToFramebuffer } = this.rendererService;
|
||||||
|
|
||||||
const useRenderTarget = (this.renderToScreen
|
renderToFramebuffer(
|
||||||
? postProcessor.useScreenRenderTarget
|
this.renderToScreen ? null : postProcessor.getWriteFBO(),
|
||||||
: postProcessor.useOffscreenRenderTarget
|
() => {
|
||||||
).bind(postProcessor);
|
|
||||||
|
|
||||||
useRenderTarget(async () => {
|
|
||||||
this.model.draw({
|
this.model.draw({
|
||||||
uniforms: {
|
uniforms: {
|
||||||
u_Texture: postProcessor.getReadFBO(),
|
u_Texture: postProcessor.getReadFBO(),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// const useRenderTarget = (this.renderToScreen
|
||||||
|
// ? postProcessor.useScreenRenderTarget
|
||||||
|
// : postProcessor.useOffscreenRenderTarget
|
||||||
|
// ).bind(postProcessor);
|
||||||
|
|
||||||
|
// useRenderTarget(async () => {
|
||||||
|
// this.model.draw({
|
||||||
|
// uniforms: {
|
||||||
|
// u_Texture: postProcessor.getReadFBO(),
|
||||||
|
// },
|
||||||
|
// });
|
||||||
|
// });
|
||||||
}
|
}
|
||||||
|
|
||||||
public isEnabled() {
|
public isEnabled() {
|
||||||
|
|
|
@ -1,45 +1,44 @@
|
||||||
|
import { injectable } from 'inversify';
|
||||||
|
import { ILayer } from '../../layer/ILayerService';
|
||||||
import {
|
import {
|
||||||
ILayer,
|
|
||||||
IMultiPassRenderer,
|
IMultiPassRenderer,
|
||||||
IPass,
|
IPass,
|
||||||
IPostProcessingPass,
|
IPostProcessingPass,
|
||||||
IPostProcessor,
|
IPostProcessor,
|
||||||
PassType,
|
PassType,
|
||||||
} from '@l7/core';
|
} from '../IMultiPassRenderer';
|
||||||
import regl from 'regl';
|
import PostProcessor from './PostProcessor';
|
||||||
import ReglPostProcessor from './ReglPostProcessor';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ported from Three.js EffectComposer
|
* ported from Three.js EffectComposer
|
||||||
* @example
|
* @example
|
||||||
* const renderer = new MultiPassRenderer(gl, [
|
* const renderer = new MultiPassRenderer([
|
||||||
* new ClearPass(gl),
|
* new ClearPass(),
|
||||||
* new RenderPass(gl, {
|
* new RenderPass({
|
||||||
* models: [
|
* models: [
|
||||||
* new Model(),
|
* new Model(),
|
||||||
* new Model(),
|
* new Model(),
|
||||||
* ],
|
* ],
|
||||||
* }),
|
* }),
|
||||||
* new CopyPass(gl, {
|
* new CopyPass({
|
||||||
* renderToScreen: true,
|
* renderToScreen: true,
|
||||||
* }),
|
* }),
|
||||||
* new TAAPass(gl),
|
* new TAAPass(),
|
||||||
* ]);
|
* ]);
|
||||||
* renderer.render();
|
* renderer.render();
|
||||||
* @see https://yuque.antfin-inc.com/yuqi.pyq/fgetpa/apuvbf#dRM8W
|
* @see https://yuque.antfin-inc.com/yuqi.pyq/fgetpa/apuvbf#dRM8W
|
||||||
*/
|
*/
|
||||||
export default class ReglMultiPassRenderer implements IMultiPassRenderer {
|
@injectable()
|
||||||
|
export default class MultiPassRenderer implements IMultiPassRenderer {
|
||||||
private passes: IPass[] = [];
|
private passes: IPass[] = [];
|
||||||
private postProcessor: IPostProcessor;
|
private postProcessor: IPostProcessor;
|
||||||
|
|
||||||
private reGl: regl.Regl;
|
|
||||||
private layer: ILayer;
|
private layer: ILayer;
|
||||||
private renderFlag: boolean;
|
private renderFlag: boolean;
|
||||||
|
|
||||||
constructor(reGl: regl.Regl, layer: ILayer) {
|
constructor(layer: ILayer) {
|
||||||
this.reGl = reGl;
|
|
||||||
this.layer = layer;
|
this.layer = layer;
|
||||||
this.postProcessor = new ReglPostProcessor(reGl);
|
this.postProcessor = new PostProcessor();
|
||||||
}
|
}
|
||||||
|
|
||||||
public setRenderFlag(renderFlag: boolean) {
|
public setRenderFlag(renderFlag: boolean) {
|
|
@ -0,0 +1,46 @@
|
||||||
|
import { inject, injectable } from 'inversify';
|
||||||
|
import { lazyInject } from '../../../index';
|
||||||
|
import { TYPES } from '../../../types';
|
||||||
|
import { ILayer, ILayerService } from '../../layer/ILayerService';
|
||||||
|
import { gl } from '../gl';
|
||||||
|
import { IFramebuffer } from '../IFramebuffer';
|
||||||
|
import { IPass, PassType } from '../IMultiPassRenderer';
|
||||||
|
import { IRendererService } from '../IRendererService';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PixelPickingPass based on
|
||||||
|
*/
|
||||||
|
@injectable()
|
||||||
|
export default class PixelPickingPass implements IPass {
|
||||||
|
@lazyInject(TYPES.IRendererService)
|
||||||
|
protected readonly rendererService: IRendererService;
|
||||||
|
|
||||||
|
private pickingFBO: IFramebuffer;
|
||||||
|
|
||||||
|
public getType() {
|
||||||
|
return PassType.Normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(layer: ILayer) {
|
||||||
|
const { createTexture2D, createFramebuffer } = this.rendererService;
|
||||||
|
this.pickingFBO = createFramebuffer({
|
||||||
|
color: createTexture2D({
|
||||||
|
width: 1,
|
||||||
|
height: 1,
|
||||||
|
wrapS: gl.CLAMP_TO_EDGE,
|
||||||
|
wrapT: gl.CLAMP_TO_EDGE,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public render(layer: ILayer) {
|
||||||
|
const { getViewportSize, renderToFramebuffer } = this.rendererService;
|
||||||
|
this.pickingFBO.resize(getViewportSize());
|
||||||
|
|
||||||
|
renderToFramebuffer(this.pickingFBO, () => {
|
||||||
|
layer.multiPassRenderer.setRenderFlag(false);
|
||||||
|
layer.render();
|
||||||
|
layer.multiPassRenderer.setRenderFlag(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,32 +1,29 @@
|
||||||
import {
|
import { injectable } from 'inversify';
|
||||||
gl,
|
import { lazyInject } from '../../../index';
|
||||||
IFramebuffer,
|
import { TYPES } from '../../../types';
|
||||||
ILayer,
|
import { ILayer } from '../../layer/ILayerService';
|
||||||
IPostProcessingPass,
|
import { gl } from '../gl';
|
||||||
IPostProcessor,
|
import { IFramebuffer } from '../IFramebuffer';
|
||||||
} from '@l7/core';
|
import { IPostProcessingPass, IPostProcessor } from '../IMultiPassRenderer';
|
||||||
import regl from 'regl';
|
import { IRendererService } from '../IRendererService';
|
||||||
import ReglFramebuffer from './ReglFramebuffer';
|
|
||||||
import ReglTexture2D from './ReglTexture2D';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ported from Three.js EffectComposer
|
* ported from Three.js EffectComposer
|
||||||
|
* 后处理负责 pingpong read/write framebuffer,最后一个 pass 默认输出到屏幕
|
||||||
*/
|
*/
|
||||||
export default class ReglPostProcessor implements IPostProcessor {
|
@injectable()
|
||||||
|
export default class PostProcessor implements IPostProcessor {
|
||||||
|
@lazyInject(TYPES.IRendererService)
|
||||||
|
protected readonly rendererService: IRendererService;
|
||||||
|
|
||||||
private passes: IPostProcessingPass[] = [];
|
private passes: IPostProcessingPass[] = [];
|
||||||
private readFBO: IFramebuffer;
|
private readFBO: IFramebuffer;
|
||||||
private writeFBO: IFramebuffer;
|
private writeFBO: IFramebuffer;
|
||||||
|
|
||||||
private screenRenderTarget: regl.DrawCommand;
|
constructor() {
|
||||||
private offscreenRenderTarget: regl.DrawCommand;
|
const { createFramebuffer, createTexture2D } = this.rendererService;
|
||||||
private inputRenderTarget: regl.DrawCommand;
|
this.readFBO = createFramebuffer({
|
||||||
|
color: createTexture2D({
|
||||||
private reGl: regl.Regl;
|
|
||||||
|
|
||||||
constructor(reGl: regl.Regl) {
|
|
||||||
this.reGl = reGl;
|
|
||||||
this.readFBO = new ReglFramebuffer(reGl, {
|
|
||||||
color: new ReglTexture2D(reGl, {
|
|
||||||
width: 1,
|
width: 1,
|
||||||
height: 1,
|
height: 1,
|
||||||
wrapS: gl.CLAMP_TO_EDGE,
|
wrapS: gl.CLAMP_TO_EDGE,
|
||||||
|
@ -34,27 +31,14 @@ export default class ReglPostProcessor implements IPostProcessor {
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
this.writeFBO = new ReglFramebuffer(reGl, {
|
this.writeFBO = createFramebuffer({
|
||||||
color: new ReglTexture2D(reGl, {
|
color: createTexture2D({
|
||||||
width: 1,
|
width: 1,
|
||||||
height: 1,
|
height: 1,
|
||||||
wrapS: gl.CLAMP_TO_EDGE,
|
wrapS: gl.CLAMP_TO_EDGE,
|
||||||
wrapT: gl.CLAMP_TO_EDGE,
|
wrapT: gl.CLAMP_TO_EDGE,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
this.screenRenderTarget = reGl({
|
|
||||||
framebuffer: null,
|
|
||||||
});
|
|
||||||
|
|
||||||
this.offscreenRenderTarget = reGl({
|
|
||||||
// since post-processor will swap read/write fbos, we must retrieve it dynamically
|
|
||||||
framebuffer: () => (this.writeFBO as ReglFramebuffer).get(),
|
|
||||||
});
|
|
||||||
|
|
||||||
this.inputRenderTarget = reGl({
|
|
||||||
framebuffer: () => (this.readFBO as ReglFramebuffer).get(),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public getReadFBO() {
|
public getReadFBO() {
|
||||||
|
@ -65,26 +49,6 @@ export default class ReglPostProcessor implements IPostProcessor {
|
||||||
return this.writeFBO;
|
return this.writeFBO;
|
||||||
}
|
}
|
||||||
|
|
||||||
public renderToPostProcessor(renderCommand: () => void) {
|
|
||||||
this.inputRenderTarget(() => {
|
|
||||||
this.reGl.clear({
|
|
||||||
color: [0, 0, 0, 0],
|
|
||||||
depth: 1,
|
|
||||||
stencil: 0,
|
|
||||||
framebuffer: (this.getReadFBO() as ReglFramebuffer).get(),
|
|
||||||
});
|
|
||||||
renderCommand();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public useScreenRenderTarget(callback: () => void) {
|
|
||||||
this.screenRenderTarget({}, callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
public useOffscreenRenderTarget(callback: () => void) {
|
|
||||||
this.offscreenRenderTarget({}, callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async render(layer: ILayer) {
|
public async render(layer: ILayer) {
|
||||||
for (let i = 0; i < this.passes.length; i++) {
|
for (let i = 0; i < this.passes.length; i++) {
|
||||||
const pass = this.passes[i];
|
const pass = this.passes[i];
|
|
@ -1,12 +1,18 @@
|
||||||
import { inject, injectable } from 'inversify';
|
import { injectable } from 'inversify';
|
||||||
import { ILayer, ILayerService } from '../../layer/ILayerService';
|
import { lazyInject } from '../../../index';
|
||||||
|
import { TYPES } from '../../../types';
|
||||||
|
import { ILayer } from '../../layer/ILayerService';
|
||||||
import { IPass, PassType } from '../IMultiPassRenderer';
|
import { IPass, PassType } from '../IMultiPassRenderer';
|
||||||
|
import { IRendererService } from '../IRendererService';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* RenderPass,负责输出到后续 PostProcessor 的 readFBO 中
|
* RenderPass,负责输出到后续 PostProcessor 的 readFBO 中
|
||||||
*/
|
*/
|
||||||
@injectable()
|
@injectable()
|
||||||
export default class RenderPass implements IPass {
|
export default class RenderPass implements IPass {
|
||||||
|
@lazyInject(TYPES.IRendererService)
|
||||||
|
protected readonly rendererService: IRendererService;
|
||||||
|
|
||||||
public getType() {
|
public getType() {
|
||||||
return PassType.Normal;
|
return PassType.Normal;
|
||||||
}
|
}
|
||||||
|
@ -16,7 +22,16 @@ export default class RenderPass implements IPass {
|
||||||
}
|
}
|
||||||
|
|
||||||
public render(layer: ILayer) {
|
public render(layer: ILayer) {
|
||||||
layer.multiPassRenderer.getPostProcessor().renderToPostProcessor(() => {
|
const { renderToFramebuffer, clear } = this.rendererService;
|
||||||
|
const readFBO = layer.multiPassRenderer.getPostProcessor().getReadFBO();
|
||||||
|
renderToFramebuffer(readFBO, () => {
|
||||||
|
clear({
|
||||||
|
color: [0, 0, 0, 0],
|
||||||
|
depth: 1,
|
||||||
|
stencil: 0,
|
||||||
|
framebuffer: readFBO,
|
||||||
|
});
|
||||||
|
|
||||||
// render to post processor
|
// render to post processor
|
||||||
layer.multiPassRenderer.setRenderFlag(false);
|
layer.multiPassRenderer.setRenderFlag(false);
|
||||||
layer.render();
|
layer.render();
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
import { injectable } from 'inversify';
|
import { injectable } from 'inversify';
|
||||||
|
import { lazyInject } from '../../../../index';
|
||||||
import blur from '../../../../shaders/post-processing/blur.glsl';
|
import blur from '../../../../shaders/post-processing/blur.glsl';
|
||||||
import quad from '../../../../shaders/post-processing/quad.glsl';
|
import quad from '../../../../shaders/post-processing/quad.glsl';
|
||||||
|
import { TYPES } from '../../../../types';
|
||||||
|
import { IRendererService } from '../../IRendererService';
|
||||||
import BasePostProcessingPass from '../BasePostProcessingPass';
|
import BasePostProcessingPass from '../BasePostProcessingPass';
|
||||||
|
|
||||||
export interface IBlurHPassConfig {
|
export interface IBlurHPassConfig {
|
||||||
|
@ -15,6 +18,9 @@ const defaultConfig: IBlurHPassConfig = {
|
||||||
export default class BlurHPass extends BasePostProcessingPass<
|
export default class BlurHPass extends BasePostProcessingPass<
|
||||||
IBlurHPassConfig
|
IBlurHPassConfig
|
||||||
> {
|
> {
|
||||||
|
@lazyInject(TYPES.IRendererService)
|
||||||
|
protected readonly rendererService: IRendererService;
|
||||||
|
|
||||||
public setupShaders() {
|
public setupShaders() {
|
||||||
this.shaderModule.registerModule('blur-pass', {
|
this.shaderModule.registerModule('blur-pass', {
|
||||||
vs: quad,
|
vs: quad,
|
||||||
|
@ -22,7 +28,7 @@ export default class BlurHPass extends BasePostProcessingPass<
|
||||||
});
|
});
|
||||||
|
|
||||||
const { vs, fs, uniforms } = this.shaderModule.getModule('blur-pass');
|
const { vs, fs, uniforms } = this.shaderModule.getModule('blur-pass');
|
||||||
const { width, height } = this.renderer.getViewportSize();
|
const { width, height } = this.rendererService.getViewportSize();
|
||||||
|
|
||||||
const { blurRadius } = {
|
const { blurRadius } = {
|
||||||
...defaultConfig,
|
...defaultConfig,
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
import { injectable } from 'inversify';
|
import { injectable } from 'inversify';
|
||||||
|
import { lazyInject } from '../../../../index';
|
||||||
import blur from '../../../../shaders/post-processing/blur.glsl';
|
import blur from '../../../../shaders/post-processing/blur.glsl';
|
||||||
import quad from '../../../../shaders/post-processing/quad.glsl';
|
import quad from '../../../../shaders/post-processing/quad.glsl';
|
||||||
|
import { TYPES } from '../../../../types';
|
||||||
|
import { IRendererService } from '../../IRendererService';
|
||||||
import BasePostProcessingPass from '../BasePostProcessingPass';
|
import BasePostProcessingPass from '../BasePostProcessingPass';
|
||||||
|
|
||||||
export interface IBlurVPassConfig {
|
export interface IBlurVPassConfig {
|
||||||
|
@ -15,6 +18,9 @@ const defaultConfig: IBlurVPassConfig = {
|
||||||
export default class BlurVPass extends BasePostProcessingPass<
|
export default class BlurVPass extends BasePostProcessingPass<
|
||||||
IBlurVPassConfig
|
IBlurVPassConfig
|
||||||
> {
|
> {
|
||||||
|
@lazyInject(TYPES.IRendererService)
|
||||||
|
protected readonly rendererService: IRendererService;
|
||||||
|
|
||||||
public setupShaders() {
|
public setupShaders() {
|
||||||
this.shaderModule.registerModule('blur-pass', {
|
this.shaderModule.registerModule('blur-pass', {
|
||||||
vs: quad,
|
vs: quad,
|
||||||
|
@ -22,7 +28,7 @@ export default class BlurVPass extends BasePostProcessingPass<
|
||||||
});
|
});
|
||||||
|
|
||||||
const { vs, fs, uniforms } = this.shaderModule.getModule('blur-pass');
|
const { vs, fs, uniforms } = this.shaderModule.getModule('blur-pass');
|
||||||
const { width, height } = this.renderer.getViewportSize();
|
const { width, height } = this.rendererService.getViewportSize();
|
||||||
|
|
||||||
const { blurRadius } = {
|
const { blurRadius } = {
|
||||||
...defaultConfig,
|
...defaultConfig,
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { TYPES } from '../../types';
|
||||||
import { createRendererContainer } from '../../utils/dom';
|
import { createRendererContainer } from '../../utils/dom';
|
||||||
import { ICameraService, IViewport } from '../camera/ICameraService';
|
import { ICameraService, IViewport } from '../camera/ICameraService';
|
||||||
import { IGlobalConfig, IGlobalConfigService } from '../config/IConfigService';
|
import { IGlobalConfig, IGlobalConfigService } from '../config/IConfigService';
|
||||||
|
import { IInteractionService } from '../interaction/IInteractionService';
|
||||||
import { ILayer, ILayerService } from '../layer/ILayerService';
|
import { ILayer, ILayerService } from '../layer/ILayerService';
|
||||||
import { ILogService } from '../log/ILogService';
|
import { ILogService } from '../log/ILogService';
|
||||||
import { IMapCamera, IMapService } from '../map/IMapService';
|
import { IMapCamera, IMapService } from '../map/IMapService';
|
||||||
|
@ -38,6 +39,9 @@ export default class Scene extends EventEmitter implements ISceneService {
|
||||||
@inject(TYPES.ICameraService)
|
@inject(TYPES.ICameraService)
|
||||||
private readonly cameraService: ICameraService;
|
private readonly cameraService: ICameraService;
|
||||||
|
|
||||||
|
@inject(TYPES.IInteractionService)
|
||||||
|
private readonly interactionService: IInteractionService;
|
||||||
|
|
||||||
@inject(TYPES.IShaderModuleService)
|
@inject(TYPES.IShaderModuleService)
|
||||||
private readonly shaderModule: IShaderModuleService;
|
private readonly shaderModule: IShaderModuleService;
|
||||||
|
|
||||||
|
@ -110,6 +114,9 @@ export default class Scene extends EventEmitter implements ISceneService {
|
||||||
// 初始化 ShaderModule
|
// 初始化 ShaderModule
|
||||||
this.shaderModule.registerBuiltinModules();
|
this.shaderModule.registerBuiltinModules();
|
||||||
|
|
||||||
|
// 初始化 container 上的交互
|
||||||
|
this.interactionService.init();
|
||||||
|
|
||||||
// TODO:init renderer
|
// TODO:init renderer
|
||||||
this.logger.info('renderer loaded');
|
this.logger.info('renderer loaded');
|
||||||
});
|
});
|
||||||
|
@ -141,6 +148,7 @@ export default class Scene extends EventEmitter implements ISceneService {
|
||||||
this.inited = false;
|
this.inited = false;
|
||||||
this.layerService.clean();
|
this.layerService.clean();
|
||||||
this.configService.reset();
|
this.configService.reset();
|
||||||
|
this.interactionService.destroy();
|
||||||
window.removeEventListener('resize', this.handleWindowResized, false);
|
window.removeEventListener('resize', this.handleWindowResized, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ const TYPES = {
|
||||||
IRendererService: Symbol.for('IRendererService'),
|
IRendererService: Symbol.for('IRendererService'),
|
||||||
IShaderModuleService: Symbol.for('IShaderModuleService'),
|
IShaderModuleService: Symbol.for('IShaderModuleService'),
|
||||||
IIconService: Symbol.for('IIconService'),
|
IIconService: Symbol.for('IIconService'),
|
||||||
|
IInteractionService: Symbol.for('IInteractionService'),
|
||||||
|
|
||||||
/** multi-pass */
|
/** multi-pass */
|
||||||
ClearPass: Symbol.for('ClearPass'),
|
ClearPass: Symbol.for('ClearPass'),
|
||||||
|
|
|
@ -0,0 +1,103 @@
|
||||||
|
import {
|
||||||
|
gl,
|
||||||
|
IRendererService,
|
||||||
|
IShaderModuleService,
|
||||||
|
lazyInject,
|
||||||
|
TYPES,
|
||||||
|
} from '@l7/core';
|
||||||
|
import BaseLayer from '../core/BaseLayer';
|
||||||
|
import LineBuffer from './buffers/line';
|
||||||
|
import line_frag from './shaders/line_frag.glsl';
|
||||||
|
import line_vert from './shaders/line_vert.glsl';
|
||||||
|
export default class LineLayer extends BaseLayer {
|
||||||
|
public name: string = 'LineLayer';
|
||||||
|
@lazyInject(TYPES.IShaderModuleService)
|
||||||
|
private readonly shaderModule: IShaderModuleService;
|
||||||
|
|
||||||
|
@lazyInject(TYPES.IRendererService)
|
||||||
|
private readonly renderer: IRendererService;
|
||||||
|
|
||||||
|
protected renderModels() {
|
||||||
|
this.models.forEach((model) =>
|
||||||
|
model.draw({
|
||||||
|
uniforms: {
|
||||||
|
u_ModelMatrix: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1],
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
protected buildModels(): void {
|
||||||
|
this.shaderModule.registerModule('line', {
|
||||||
|
vs: line_vert,
|
||||||
|
fs: line_frag,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.models = [];
|
||||||
|
const { vs, fs, uniforms } = this.shaderModule.getModule('line');
|
||||||
|
const buffer = new LineBuffer({
|
||||||
|
data: this.getEncodedData(),
|
||||||
|
style: this.styleOption,
|
||||||
|
});
|
||||||
|
console.log(buffer);
|
||||||
|
const {
|
||||||
|
createAttribute,
|
||||||
|
createBuffer,
|
||||||
|
createElements,
|
||||||
|
createModel,
|
||||||
|
} = this.renderer;
|
||||||
|
|
||||||
|
this.models.push(
|
||||||
|
createModel({
|
||||||
|
attributes: {
|
||||||
|
a_Position: createAttribute({
|
||||||
|
buffer: createBuffer({
|
||||||
|
data: buffer.attributes.positions,
|
||||||
|
type: gl.FLOAT,
|
||||||
|
}),
|
||||||
|
size: 3,
|
||||||
|
}),
|
||||||
|
a_normal: createAttribute({
|
||||||
|
buffer: createBuffer({
|
||||||
|
data: buffer.attributes.normals,
|
||||||
|
type: gl.FLOAT,
|
||||||
|
}),
|
||||||
|
size: 3,
|
||||||
|
}),
|
||||||
|
a_color: createAttribute({
|
||||||
|
buffer: createBuffer({
|
||||||
|
data: buffer.attributes.colors,
|
||||||
|
type: gl.FLOAT,
|
||||||
|
}),
|
||||||
|
size: 4,
|
||||||
|
}),
|
||||||
|
a_size: createAttribute({
|
||||||
|
buffer: createBuffer({
|
||||||
|
data: buffer.attributes.sizes,
|
||||||
|
type: gl.FLOAT,
|
||||||
|
}),
|
||||||
|
size: 1,
|
||||||
|
}),
|
||||||
|
a_miter: createAttribute({
|
||||||
|
buffer: createBuffer({
|
||||||
|
data: buffer.attributes.miters,
|
||||||
|
type: gl.FLOAT,
|
||||||
|
}),
|
||||||
|
size: 1,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
uniforms: {
|
||||||
|
...uniforms,
|
||||||
|
u_opacity: this.styleOption.opacity as number,
|
||||||
|
},
|
||||||
|
fs,
|
||||||
|
vs,
|
||||||
|
count: buffer.indexArray.length,
|
||||||
|
elements: createElements({
|
||||||
|
data: buffer.indexArray,
|
||||||
|
type: gl.UNSIGNED_INT,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
uniform float u_blur : 0.9;
|
||||||
|
varying vec4 v_color;
|
||||||
|
varying vec3 v_normal;
|
||||||
|
void main() {
|
||||||
|
gl_FragColor = v_color;
|
||||||
|
// anti-alias
|
||||||
|
// float blur = 1. - smoothstep(u_blur, 1., length(v_normal));
|
||||||
|
// gl_FragColor.a *= blur;
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
|
||||||
|
attribute float a_miter;
|
||||||
|
attribute vec4 a_color;
|
||||||
|
attribute float a_size;
|
||||||
|
attribute float a_distance;
|
||||||
|
attribute float a_dash_array;
|
||||||
|
attribute float a_total_distance;
|
||||||
|
attribute vec3 a_normal;
|
||||||
|
attribute vec3 a_Position;
|
||||||
|
uniform mat4 u_ModelMatrix;
|
||||||
|
|
||||||
|
varying vec4 v_color;
|
||||||
|
varying float v_dash_array;
|
||||||
|
varying vec3 v_normal;
|
||||||
|
#pragma include "projection"
|
||||||
|
void main() {
|
||||||
|
v_normal = a_normal;
|
||||||
|
v_color = a_color;
|
||||||
|
vec3 size = a_miter * a_size * v_normal;
|
||||||
|
vec2 offset = project_pixel(size.xy);
|
||||||
|
vec4 project_pos = project_position(vec4(a_Position.xy, 0, 1.0));
|
||||||
|
gl_Position = project_common_position_to_clipspace(vec4(project_pos.xy + offset, 0, 1.0));
|
||||||
|
}
|
|
@ -9,6 +9,8 @@ import {
|
||||||
IPostProcessingPass,
|
IPostProcessingPass,
|
||||||
IRendererService,
|
IRendererService,
|
||||||
lazyInject,
|
lazyInject,
|
||||||
|
MultiPassRenderer,
|
||||||
|
PixelPickingPass,
|
||||||
RenderPass,
|
RenderPass,
|
||||||
TYPES,
|
TYPES,
|
||||||
} from '@l7/core';
|
} from '@l7/core';
|
||||||
|
@ -20,6 +22,20 @@ const builtinPostProcessingPassMap: {
|
||||||
blurV: BlurVPass,
|
blurV: BlurVPass,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 'blurH' -> ['blurH', {}]
|
||||||
|
*/
|
||||||
|
function normalizePasses(
|
||||||
|
passes: Array<string | [string, { [key: string]: unknown }]>,
|
||||||
|
) {
|
||||||
|
return passes.map((pass: string | [string, { [key: string]: unknown }]) => {
|
||||||
|
if (typeof pass === 'string') {
|
||||||
|
pass = [pass, {}];
|
||||||
|
}
|
||||||
|
return pass;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据 Layer 配置的 passes 创建 MultiPassRenderer 并渲染
|
* 根据 Layer 配置的 passes 创建 MultiPassRenderer 并渲染
|
||||||
* @example
|
* @example
|
||||||
|
@ -83,28 +99,23 @@ export default class MultiPassRendererPlugin implements ILayerPlugin {
|
||||||
layer: ILayer,
|
layer: ILayer,
|
||||||
passes: Array<string | [string, { [key: string]: unknown }]>,
|
passes: Array<string | [string, { [key: string]: unknown }]>,
|
||||||
) {
|
) {
|
||||||
const multiPassRenderer = this.rendererService.createMultiPassRenderer(
|
const multiPassRenderer = new MultiPassRenderer(layer);
|
||||||
layer,
|
|
||||||
);
|
|
||||||
// TODO: PickingPass
|
|
||||||
multiPassRenderer.add(new ClearPass());
|
|
||||||
multiPassRenderer.add(new RenderPass());
|
|
||||||
|
|
||||||
const normalizedPasses: Array<
|
multiPassRenderer.add(new ClearPass());
|
||||||
[string, { [key: string]: unknown }]
|
|
||||||
> = passes.map((pass: string | [string, { [key: string]: unknown }]) => {
|
if (layer.getInitializationOptions().enablePicking) {
|
||||||
if (typeof pass === 'string') {
|
multiPassRenderer.add(new PixelPickingPass());
|
||||||
pass = [pass, {}];
|
|
||||||
}
|
}
|
||||||
return pass;
|
multiPassRenderer.add(new RenderPass());
|
||||||
});
|
|
||||||
|
|
||||||
// post processing
|
// post processing
|
||||||
// TODO: pass initialization params
|
// TODO: pass initialization params
|
||||||
normalizedPasses.forEach((pass: [string, { [key: string]: unknown }]) => {
|
normalizePasses(passes).forEach(
|
||||||
|
(pass: [string, { [key: string]: unknown }]) => {
|
||||||
const PostProcessingPassClazz = builtinPostProcessingPassMap[pass[0]];
|
const PostProcessingPassClazz = builtinPostProcessingPassMap[pass[0]];
|
||||||
multiPassRenderer.add(new PostProcessingPassClazz(pass[1]));
|
multiPassRenderer.add(new PostProcessingPassClazz(pass[1]));
|
||||||
});
|
},
|
||||||
|
);
|
||||||
// 末尾为固定的 CopyPass
|
// 末尾为固定的 CopyPass
|
||||||
multiPassRenderer.add(new CopyPass());
|
multiPassRenderer.add(new CopyPass());
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,195 @@
|
||||||
|
/**
|
||||||
|
* 对于 polyline-normal 的改进
|
||||||
|
* 超过阈值,miter 转成 bevel 接头,
|
||||||
|
* 要注意 Three.js 中默认 THREE.FrontFaceDirectionCCW
|
||||||
|
* @see https://zhuanlan.zhihu.com/p/59541559
|
||||||
|
*/
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
import { copy, create, dot } from 'gl-vec2';
|
||||||
|
// @ts-ignore
|
||||||
|
import { computeMiter, direction, normal } from 'polyline-miter-util';
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
function extrusions(positions, out, miters, point, normal1, scale) {
|
||||||
|
addNext(out, miters, normal1, -scale);
|
||||||
|
addNext(out, miters, normal1, scale);
|
||||||
|
positions.push(...point, 0);
|
||||||
|
positions.push(...point, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
// tslint:disable-next-line:no-shadowed-variable
|
||||||
|
function addNext(out, miters, normal, length) {
|
||||||
|
out.push(normal[0], normal[1], 0);
|
||||||
|
miters.push(length);
|
||||||
|
}
|
||||||
|
// @ts-ignore
|
||||||
|
function lineSegmentDistance(end, start) {
|
||||||
|
const dx = start[0] - end[0];
|
||||||
|
const dy = start[1] - end[1];
|
||||||
|
const dz = start[2] - end[2];
|
||||||
|
return Math.sqrt(dx * dx + dy * dy + dz * dz);
|
||||||
|
}
|
||||||
|
// @ts-ignore
|
||||||
|
function isPointEqual(a, b) {
|
||||||
|
return a[0] === b[0] && a[1] === b[1];
|
||||||
|
}
|
||||||
|
// @ts-ignore
|
||||||
|
export default function(points, closed, indexOffset) {
|
||||||
|
const lineA = [0, 0];
|
||||||
|
const lineB = [0, 0];
|
||||||
|
const tangent = [0, 0];
|
||||||
|
const miter = [0, 0];
|
||||||
|
// tslint:disable-next-line:variable-name
|
||||||
|
let _started = false;
|
||||||
|
// tslint:disable-next-line:variable-name
|
||||||
|
let _normal = null;
|
||||||
|
const tmp = create();
|
||||||
|
let count = indexOffset || 0;
|
||||||
|
const miterLimit = 3;
|
||||||
|
// @ts-ignore
|
||||||
|
const out = [];
|
||||||
|
const attrPos = [];
|
||||||
|
const attrIndex = [];
|
||||||
|
// @ts-ignore
|
||||||
|
const miters = [];
|
||||||
|
const attrDistance = [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];
|
||||||
|
let next = i < points.length - 1 ? points[i + 1] : null;
|
||||||
|
// 如果当前点和前一点相同,跳过
|
||||||
|
if (isPointEqual(last, cur)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (next) {
|
||||||
|
let nextIndex = i + 1;
|
||||||
|
// 找到不相同的下一点
|
||||||
|
while (next && isPointEqual(cur, next)) {
|
||||||
|
next = nextIndex < points.length - 1 ? points[++nextIndex] : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const lineDistance = lineSegmentDistance(cur, last);
|
||||||
|
const d = lineDistance + attrDistance[attrDistance.length - 1];
|
||||||
|
|
||||||
|
direction(lineA, cur, last);
|
||||||
|
|
||||||
|
if (!_normal) {
|
||||||
|
_normal = [0, 0];
|
||||||
|
normal(_normal, lineA);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_started) {
|
||||||
|
_started = true;
|
||||||
|
// @ts-ignore
|
||||||
|
extrusions(attrPos, out, miters, last, _normal, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
attrIndex.push(index + 0, index + 2, index + 1);
|
||||||
|
|
||||||
|
// no miter, simple segment
|
||||||
|
if (!next) {
|
||||||
|
// reset normal
|
||||||
|
normal(_normal, lineA);
|
||||||
|
// @ts-ignore
|
||||||
|
extrusions(attrPos, out, miters, cur, _normal, 1);
|
||||||
|
attrDistance.push(d, d);
|
||||||
|
attrIndex.push(index + 1, 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
|
||||||
|
const flip = dot(tangent, _normal) < 0 ? -1 : 1;
|
||||||
|
const bevel = Math.abs(miterLen) > miterLimit;
|
||||||
|
|
||||||
|
// 处理前后两条线段重合的情况,这种情况不需要使用任何接头(miter/bevel)。
|
||||||
|
// 理论上这种情况下 miterLen = Infinity,本应通过 isFinite(miterLen) 判断,
|
||||||
|
// 但是 AMap 投影变换后丢失精度,只能通过一个阈值(1000)判断。
|
||||||
|
if (Math.abs(miterLen) > 1000) {
|
||||||
|
// @ts-ignore
|
||||||
|
extrusions(attrPos, out, miters, cur, _normal, 1);
|
||||||
|
attrIndex.push(index + 1, index + 2, index + 3);
|
||||||
|
attrIndex.push(index + 2, index + 4, index + 3);
|
||||||
|
normal(tmp, lineB);
|
||||||
|
copy(_normal, tmp); // store normal for next round
|
||||||
|
// @ts-ignore
|
||||||
|
extrusions(attrPos, out, miters, cur, _normal, 1);
|
||||||
|
attrDistance.push(d, d, d, d);
|
||||||
|
|
||||||
|
// the miter is now the normal for our next join
|
||||||
|
count += 4;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bevel) {
|
||||||
|
miterLen = miterLimit;
|
||||||
|
|
||||||
|
// next two points in our first segment
|
||||||
|
// @ts-ignore
|
||||||
|
extrusions(attrPos, out, miters, cur, _normal, 1);
|
||||||
|
|
||||||
|
attrIndex.push(index + 1, index + 2, index + 3);
|
||||||
|
|
||||||
|
// now add the bevel triangle
|
||||||
|
attrIndex.push(
|
||||||
|
...(flip === 1
|
||||||
|
? [index + 2, index + 4, index + 5]
|
||||||
|
: [index + 4, index + 5, index + 3]),
|
||||||
|
);
|
||||||
|
|
||||||
|
normal(tmp, lineB);
|
||||||
|
copy(_normal, tmp); // store normal for next round
|
||||||
|
// @ts-ignore
|
||||||
|
extrusions(attrPos, out, miters, cur, _normal, 1);
|
||||||
|
attrDistance.push(d, d, d, d);
|
||||||
|
|
||||||
|
// the miter is now the normal for our next join
|
||||||
|
count += 4;
|
||||||
|
} else {
|
||||||
|
// next two points in our first segment
|
||||||
|
// @ts-ignore
|
||||||
|
extrusions(attrPos, out, miters, cur, _normal, 1);
|
||||||
|
attrIndex.push(index + 1, index + 2, index + 3);
|
||||||
|
|
||||||
|
// now add the miter triangles
|
||||||
|
// @ts-ignore
|
||||||
|
addNext(out, miters, miter, miterLen * -flip);
|
||||||
|
attrPos.push(...cur, 0);
|
||||||
|
attrIndex.push(index + 2, index + 4, index + 3);
|
||||||
|
attrIndex.push(index + 4, index + 5, index + 6);
|
||||||
|
normal(tmp, lineB);
|
||||||
|
copy(_normal, tmp); // store normal for next round
|
||||||
|
// @ts-ignore
|
||||||
|
extrusions(attrPos, out, miters, cur, _normal, 1);
|
||||||
|
attrDistance.push(d, d, d, d, d);
|
||||||
|
|
||||||
|
// the miter is now the normal for our next join
|
||||||
|
count += 5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// @ts-ignore
|
||||||
|
return {
|
||||||
|
// @ts-ignore
|
||||||
|
normals: out,
|
||||||
|
attrIndex,
|
||||||
|
attrPos,
|
||||||
|
attrDistance,
|
||||||
|
// @ts-ignore
|
||||||
|
miters,
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,7 +1,4 @@
|
||||||
import {
|
import { IRenderbuffer, IRenderbufferInitializationOptions } from '@l7/core';
|
||||||
IRenderbuffer,
|
|
||||||
IRenderbufferInitializationOptions,
|
|
||||||
} from '@l7/core';
|
|
||||||
import regl from 'regl';
|
import regl from 'regl';
|
||||||
import { formatMap } from './constants';
|
import { formatMap } from './constants';
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
* @see https://github.com/regl-project/regl/blob/gh-pages/API.md
|
* @see https://github.com/regl-project/regl/blob/gh-pages/API.md
|
||||||
*/
|
*/
|
||||||
import {
|
import {
|
||||||
gl,
|
|
||||||
IAttribute,
|
IAttribute,
|
||||||
IAttributeInitializationOptions,
|
IAttributeInitializationOptions,
|
||||||
IBuffer,
|
IBuffer,
|
||||||
|
@ -11,20 +10,22 @@ import {
|
||||||
IClearOptions,
|
IClearOptions,
|
||||||
IElements,
|
IElements,
|
||||||
IElementsInitializationOptions,
|
IElementsInitializationOptions,
|
||||||
ILayer,
|
IFramebuffer,
|
||||||
|
IFramebufferInitializationOptions,
|
||||||
IModel,
|
IModel,
|
||||||
IModelInitializationOptions,
|
IModelInitializationOptions,
|
||||||
IMultiPassRenderer,
|
|
||||||
IRendererService,
|
IRendererService,
|
||||||
|
ITexture2D,
|
||||||
|
ITexture2DInitializationOptions,
|
||||||
} from '@l7/core';
|
} from '@l7/core';
|
||||||
import { inject, injectable } from 'inversify';
|
import { injectable } from 'inversify';
|
||||||
import regl from 'regl';
|
import regl from 'regl';
|
||||||
import ReglAttribute from './ReglAttribute';
|
import ReglAttribute from './ReglAttribute';
|
||||||
import ReglBuffer from './ReglBuffer';
|
import ReglBuffer from './ReglBuffer';
|
||||||
import ReglElements from './ReglElements';
|
import ReglElements from './ReglElements';
|
||||||
import ReglFramebuffer from './ReglFramebuffer';
|
import ReglFramebuffer from './ReglFramebuffer';
|
||||||
import ReglModel from './ReglModel';
|
import ReglModel from './ReglModel';
|
||||||
import ReglMultiPassRenderer from './ReglMultiPassRenderer';
|
import ReglTexture2D from './ReglTexture2D';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* regl renderer
|
* regl renderer
|
||||||
|
@ -32,8 +33,10 @@ import ReglMultiPassRenderer from './ReglMultiPassRenderer';
|
||||||
@injectable()
|
@injectable()
|
||||||
export default class ReglRendererService implements IRendererService {
|
export default class ReglRendererService implements IRendererService {
|
||||||
private gl: regl.Regl;
|
private gl: regl.Regl;
|
||||||
|
private $container: HTMLDivElement | null;
|
||||||
|
|
||||||
public async init($container: HTMLDivElement): Promise<void> {
|
public async init($container: HTMLDivElement): Promise<void> {
|
||||||
|
this.$container = $container;
|
||||||
// tslint:disable-next-line:typedef
|
// tslint:disable-next-line:typedef
|
||||||
this.gl = await new Promise((resolve, reject) => {
|
this.gl = await new Promise((resolve, reject) => {
|
||||||
regl({
|
regl({
|
||||||
|
@ -67,31 +70,43 @@ export default class ReglRendererService implements IRendererService {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public createModel = (options: IModelInitializationOptions): IModel => {
|
public createModel = (options: IModelInitializationOptions): IModel =>
|
||||||
return new ReglModel(this.gl, options);
|
new ReglModel(this.gl, options);
|
||||||
};
|
|
||||||
|
|
||||||
public createAttribute = (
|
public createAttribute = (
|
||||||
options: IAttributeInitializationOptions,
|
options: IAttributeInitializationOptions,
|
||||||
): IAttribute => {
|
): IAttribute => new ReglAttribute(this.gl, options);
|
||||||
return new ReglAttribute(this.gl, options);
|
|
||||||
};
|
|
||||||
|
|
||||||
public createBuffer = (options: IBufferInitializationOptions): IBuffer => {
|
public createBuffer = (options: IBufferInitializationOptions): IBuffer =>
|
||||||
return new ReglBuffer(this.gl, options);
|
new ReglBuffer(this.gl, options);
|
||||||
};
|
|
||||||
|
|
||||||
public createElements = (
|
public createElements = (
|
||||||
options: IElementsInitializationOptions,
|
options: IElementsInitializationOptions,
|
||||||
): IElements => {
|
): IElements => new ReglElements(this.gl, options);
|
||||||
return new ReglElements(this.gl, options);
|
|
||||||
|
public createTexture2D = (
|
||||||
|
options: ITexture2DInitializationOptions,
|
||||||
|
): ITexture2D => new ReglTexture2D(this.gl, options);
|
||||||
|
|
||||||
|
public createFramebuffer = (options: IFramebufferInitializationOptions) =>
|
||||||
|
new ReglFramebuffer(this.gl, options);
|
||||||
|
|
||||||
|
public renderToFramebuffer = (
|
||||||
|
framebuffer: IFramebuffer | null,
|
||||||
|
drawCommands: () => void,
|
||||||
|
) => {
|
||||||
|
const useFramebuffer = this.gl({
|
||||||
|
// since post-processor will swap read/write fbos, we must retrieve it dynamically
|
||||||
|
framebuffer: framebuffer
|
||||||
|
? () => (framebuffer as ReglFramebuffer).get()
|
||||||
|
: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO: pass other options
|
||||||
|
useFramebuffer({}, drawCommands);
|
||||||
};
|
};
|
||||||
|
|
||||||
public createMultiPassRenderer = (layer: ILayer): IMultiPassRenderer => {
|
public clear = (options: IClearOptions) => {
|
||||||
return new ReglMultiPassRenderer(this.gl, layer);
|
|
||||||
};
|
|
||||||
|
|
||||||
public clear(options: IClearOptions) {
|
|
||||||
// @see https://github.com/regl-project/regl/blob/gh-pages/API.md#clear-the-draw-buffer
|
// @see https://github.com/regl-project/regl/blob/gh-pages/API.md#clear-the-draw-buffer
|
||||||
const { color, depth, stencil, framebuffer = null } = options;
|
const { color, depth, stencil, framebuffer = null } = options;
|
||||||
const reglClearOptions: regl.ClearOptions = {
|
const reglClearOptions: regl.ClearOptions = {
|
||||||
|
@ -106,9 +121,9 @@ export default class ReglRendererService implements IRendererService {
|
||||||
: (framebuffer as ReglFramebuffer).get();
|
: (framebuffer as ReglFramebuffer).get();
|
||||||
|
|
||||||
this.gl.clear(reglClearOptions);
|
this.gl.clear(reglClearOptions);
|
||||||
}
|
};
|
||||||
|
|
||||||
public viewport({
|
public viewport = ({
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
width,
|
width,
|
||||||
|
@ -118,17 +133,21 @@ export default class ReglRendererService implements IRendererService {
|
||||||
y: number;
|
y: number;
|
||||||
width: number;
|
width: number;
|
||||||
height: number;
|
height: number;
|
||||||
}) {
|
}) => {
|
||||||
// use WebGL context directly
|
// use WebGL context directly
|
||||||
// @see https://github.com/regl-project/regl/blob/gh-pages/API.md#unsafe-escape-hatch
|
// @see https://github.com/regl-project/regl/blob/gh-pages/API.md#unsafe-escape-hatch
|
||||||
this.gl._gl.viewport(x, y, width, height);
|
this.gl._gl.viewport(x, y, width, height);
|
||||||
this.gl._refresh();
|
this.gl._refresh();
|
||||||
}
|
};
|
||||||
|
|
||||||
public getViewportSize() {
|
public getViewportSize = () => {
|
||||||
return {
|
return {
|
||||||
width: this.gl._gl.drawingBufferWidth,
|
width: this.gl._gl.drawingBufferWidth,
|
||||||
height: this.gl._gl.drawingBufferHeight,
|
height: this.gl._gl.drawingBufferHeight,
|
||||||
};
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
|
public getContainer = () => {
|
||||||
|
return this.$container;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
import { Line } from '@l7/layers';
|
||||||
|
import { Scene } from '@l7/scene';
|
||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
export default class Point3D extends React.Component {
|
||||||
|
private scene: Scene;
|
||||||
|
|
||||||
|
public componentWillUnmount() {
|
||||||
|
this.scene.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async componentDidMount() {
|
||||||
|
const response = await fetch(
|
||||||
|
'https://gw.alipayobjects.com/os/rmsportal/ZVfOvhVCzwBkISNsuKCc.json',
|
||||||
|
);
|
||||||
|
const testdata = {
|
||||||
|
type: 'FeatureCollection',
|
||||||
|
features: [
|
||||||
|
{
|
||||||
|
type: 'Feature',
|
||||||
|
properties: {},
|
||||||
|
geometry: {
|
||||||
|
type: 'LineString',
|
||||||
|
coordinates: [
|
||||||
|
[91.58203125, 34.95799531086792],
|
||||||
|
[96.767578125, 34.379712580462204],
|
||||||
|
[99.228515625, 33.7243396617476],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
const scene = new Scene({
|
||||||
|
center: [102.602992, 23.107329],
|
||||||
|
id: 'map',
|
||||||
|
pitch: 0,
|
||||||
|
type: 'mapbox',
|
||||||
|
style: 'mapbox://styles/mapbox/dark-v9',
|
||||||
|
zoom: 2,
|
||||||
|
});
|
||||||
|
const LineLayer = new Line({});
|
||||||
|
|
||||||
|
LineLayer.source(testdata)
|
||||||
|
.size(5)
|
||||||
|
.color('red')
|
||||||
|
.shape('line')
|
||||||
|
.size(10);
|
||||||
|
scene.addLayer(LineLayer);
|
||||||
|
// function run() {
|
||||||
|
// scene.render();
|
||||||
|
// requestAnimationFrame(run);
|
||||||
|
// }
|
||||||
|
// requestAnimationFrame(run);
|
||||||
|
scene.render();
|
||||||
|
this.scene = scene;
|
||||||
|
console.log(LineLayer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public render() {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
id="map"
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
bottom: 0,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -83,6 +83,17 @@ export default class Mapbox extends React.Component {
|
||||||
this.scene = scene;
|
this.scene = scene;
|
||||||
console.log(layer);
|
console.log(layer);
|
||||||
/*** 运行时修改样式属性 ***/
|
/*** 运行时修改样式属性 ***/
|
||||||
|
// const gui = new dat.GUI();
|
||||||
|
// this.gui = gui;
|
||||||
|
// const pointFolder = gui.addFolder('Polygon 样式属性');
|
||||||
|
// pointFolder
|
||||||
|
// .add(layer.styleOptions, 'opacity')
|
||||||
|
// .onChange((opacity: number) => {
|
||||||
|
// layer.style({
|
||||||
|
// opacity,
|
||||||
|
// });
|
||||||
|
// scene.render();
|
||||||
|
// });
|
||||||
}
|
}
|
||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
|
|
10
yarn.lock
10
yarn.lock
|
@ -2825,6 +2825,11 @@
|
||||||
"@types/minimatch" "*"
|
"@types/minimatch" "*"
|
||||||
"@types/node" "*"
|
"@types/node" "*"
|
||||||
|
|
||||||
|
"@types/hammerjs@^2.0.36":
|
||||||
|
version "2.0.36"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/hammerjs/-/hammerjs-2.0.36.tgz#17ce0a235e9ffbcdcdf5095646b374c2bf615a4c"
|
||||||
|
integrity sha512-7TUK/k2/QGpEAv/BCwSHlYu3NXZhQ9ZwBYpzr9tjlPIL2C5BeGhH3DmVavRx3ZNyELX5TLC91JTz/cen6AAtIQ==
|
||||||
|
|
||||||
"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0":
|
"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0":
|
||||||
version "2.0.1"
|
version "2.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz#42995b446db9a48a11a07ec083499a860e9138ff"
|
resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz#42995b446db9a48a11a07ec083499a860e9138ff"
|
||||||
|
@ -7507,6 +7512,11 @@ gzip-size@5.1.1:
|
||||||
duplexer "^0.1.1"
|
duplexer "^0.1.1"
|
||||||
pify "^4.0.1"
|
pify "^4.0.1"
|
||||||
|
|
||||||
|
hammerjs@^2.0.8:
|
||||||
|
version "2.0.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/hammerjs/-/hammerjs-2.0.8.tgz#04ef77862cff2bb79d30f7692095930222bf60f1"
|
||||||
|
integrity sha1-BO93hiz/K7edMPdpIJWTAiK/YPE=
|
||||||
|
|
||||||
handle-thing@^2.0.0:
|
handle-thing@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.0.tgz#0e039695ff50c93fc288557d696f3c1dc6776754"
|
resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.0.tgz#0e039695ff50c93fc288557d696f3c1dc6776754"
|
||||||
|
|
Loading…
Reference in New Issue