From ae4a594a9383443d0ade5eb84105f3c8f2c6c47c Mon Sep 17 00:00:00 2001 From: thinkinggis Date: Wed, 16 Oct 2019 19:31:09 +0800 Subject: [PATCH] feat(point image): add point image --- packages/core/src/inversify.config.ts | 2 +- .../core/src/services/asset/IIconService.ts | 3 + .../core/src/services/asset/IconService.ts | 38 ++++++++---- .../core/src/services/renderer/ITexture2D.ts | 1 + .../core/src/services/scene/ISceneService.ts | 3 + .../core/src/services/scene/SceneService.ts | 13 +++- packages/layers/src/core/BaseLayer.ts | 5 +- .../layers/src/point/buffers/ImageBuffer.ts | 5 +- packages/layers/src/point/point.ts | 56 ++++++++++++----- .../layers/src/point/shaders/image_frag.glsl | 5 +- .../layers/src/point/shaders/image_vert.glsl | 7 ++- packages/scene/src/index.ts | 7 ++- packages/utils/src/fetchData.ts | 1 + stories/MapAdaptor/Map.stories.tsx | 4 +- stories/MapAdaptor/components/Image.tsx | 4 +- stories/MapAdaptor/components/Point3D.tsx | 11 ++-- stories/MapAdaptor/components/pointImage.tsx | 62 +++++++++++++++++++ 17 files changed, 177 insertions(+), 50 deletions(-) create mode 100644 stories/MapAdaptor/components/pointImage.tsx diff --git a/packages/core/src/inversify.config.ts b/packages/core/src/inversify.config.ts index c6b2509d12..30e69bcc00 100644 --- a/packages/core/src/inversify.config.ts +++ b/packages/core/src/inversify.config.ts @@ -6,7 +6,7 @@ import getDecorators from 'inversify-inject-decorators'; import { TYPES } from './types'; /** Service interfaces */ -import { IIconService} from './services/asset/IIconService'; +import { IIconService } from './services/asset/IIconService'; import { ICameraService } from './services/camera/ICameraService'; import { IGlobalConfigService } from './services/config/IConfigService'; import { ICoordinateSystemService } from './services/coordinate/ICoordinateSystemService'; diff --git a/packages/core/src/services/asset/IIconService.ts b/packages/core/src/services/asset/IIconService.ts index 81003ee678..a673e13c8b 100644 --- a/packages/core/src/services/asset/IIconService.ts +++ b/packages/core/src/services/asset/IIconService.ts @@ -15,7 +15,10 @@ export interface IICONMap { [key: string]: IIconValue; } export interface IIconService { + canvasHeight: number; + init(): void; addImage(id: string, image: IImage): void; getTexture(): ITexture2D; getIconMap(): IICONMap; + getCanvas(): HTMLCanvasElement; } diff --git a/packages/core/src/services/asset/IconService.ts b/packages/core/src/services/asset/IconService.ts index 71875a6f2f..68546ea1eb 100644 --- a/packages/core/src/services/asset/IconService.ts +++ b/packages/core/src/services/asset/IconService.ts @@ -1,5 +1,8 @@ import { inject, injectable } from 'inversify'; +import { TYPES } from '../../types'; import { buildIconMaping } from '../../utils/font_util'; +import { gl } from '../renderer/gl'; +import { IRendererService } from '../renderer/IRendererService'; import { ITexture2D } from '../renderer/ITexture2D'; import { IIcon, @@ -13,23 +16,24 @@ const MAX_CANVAS_WIDTH = 1024; const imageSize = 64; @injectable() export default class IconService implements IIconService { + public canvasHeight: number; + private textrure: ITexture2D; private canvas: HTMLCanvasElement; private iconData: IIcon[]; private iconMap: IICONMap; - private canvasHeigth: number; - private textrure: ITexture2D; private ctx: CanvasRenderingContext2D; - - constructor() { + public init() { this.iconData = []; this.iconMap = {}; this.canvas = document.createElement('canvas'); - // this.texture = this.ctx = this.canvas.getContext('2d') as CanvasRenderingContext2D; } - public async addImage(id: string, image: IImage) { - const imagedata = (await this.loadImage(image)) as HTMLImageElement; + public addImage(id: string, image: IImage) { + let imagedata = new Image(); + this.loadImage(image).then((img) => { + imagedata = img as HTMLImageElement; + }); this.iconData.push({ id, image: imagedata, @@ -42,28 +46,35 @@ export default class IconService implements IIconService { MAX_CANVAS_WIDTH, ); this.iconMap = mapping; - this.canvasHeigth = canvasHeight; + this.canvasHeight = canvasHeight; this.updateIconAtlas(); } public getTexture(): ITexture2D { - throw new Error('Method not implemented.'); + return this.textrure; } public getIconMap() { return this.iconMap; } + public getCanvas() { + return this.canvas; + } private updateIconAtlas() { this.canvas.width = MAX_CANVAS_WIDTH; - this.canvas.height = this.canvasHeigth; + this.canvas.height = this.canvasHeight; Object.keys(this.iconMap).forEach((item: string) => { const { x, y, image } = this.iconMap[item]; this.ctx.drawImage(image, x, y, imageSize, imageSize); }); - // this.texture.magFilter = THREE.LinearFilter; - // this.texture.minFilter = THREE.LinearFilter; - // this.texture.needsUpdate = true; + // const { createTexture2D } = this.rendererService; + // this.textrure = createTexture2D({ + // data: this.canvas, + // width: this.canvas.width, + // height: this.canvasHeight, + // mag: gl.LINEAR, + // }); } private loadImage(url: IImage) { @@ -73,6 +84,7 @@ export default class IconService implements IIconService { return; } const image = new Image(); + image.crossOrigin = 'anonymous'; image.onload = () => { resolve(image); }; diff --git a/packages/core/src/services/renderer/ITexture2D.ts b/packages/core/src/services/renderer/ITexture2D.ts index 5a77c70d21..31295eafa9 100644 --- a/packages/core/src/services/renderer/ITexture2D.ts +++ b/packages/core/src/services/renderer/ITexture2D.ts @@ -33,6 +33,7 @@ export interface ITexture2DInitializationOptions { */ data?: | undefined + | HTMLCanvasElement | HTMLImageElement | number[] | number[][] diff --git a/packages/core/src/services/scene/ISceneService.ts b/packages/core/src/services/scene/ISceneService.ts index cd9c7a664a..c84050d616 100644 --- a/packages/core/src/services/scene/ISceneService.ts +++ b/packages/core/src/services/scene/ISceneService.ts @@ -1,10 +1,13 @@ +import { IImage } from '../asset/IIconService'; import { ILayer } from '../layer/ILayerService'; import { IMapConfig } from '../map/IMapService'; import { IRenderConfig } from '../renderer/IRendererService'; + export interface ISceneService { init(config: IMapConfig & IRenderConfig): void; addLayer(layer: ILayer): void; + addImage(id: string, image: IImage): void; render(): void; destroy(): void; } diff --git a/packages/core/src/services/scene/SceneService.ts b/packages/core/src/services/scene/SceneService.ts index 4a7206f067..b41d86dfcc 100644 --- a/packages/core/src/services/scene/SceneService.ts +++ b/packages/core/src/services/scene/SceneService.ts @@ -3,6 +3,7 @@ import { inject, injectable } from 'inversify'; import { AsyncParallelHook, AsyncSeriesHook } from 'tapable'; import { TYPES } from '../../types'; import { createRendererContainer } from '../../utils/dom'; +import { IIconService, IImage } from '../asset/IIconService'; import { ICameraService, IViewport } from '../camera/ICameraService'; import { IGlobalConfig, IGlobalConfigService } from '../config/IConfigService'; import { IInteractionService } from '../interaction/IInteractionService'; @@ -12,7 +13,6 @@ import { IMapCamera, IMapService } from '../map/IMapService'; import { IRendererService } from '../renderer/IRendererService'; import { IShaderModuleService } from '../shader/IShaderModuleService'; import { ISceneService } from './ISceneService'; - /** * will emit `loaded` `resize` `destroy` event */ @@ -45,6 +45,9 @@ export default class Scene extends EventEmitter implements ISceneService { @inject(TYPES.IShaderModuleService) private readonly shaderModule: IShaderModuleService; + @inject(TYPES.IIconService) + private readonly iconService: IIconService; + /** * 是否首次渲染 */ @@ -76,6 +79,10 @@ export default class Scene extends EventEmitter implements ISceneService { public init(globalConfig: IGlobalConfig) { this.configService.setAndCheckConfig(globalConfig); + + // 初始化资源管理 字体,图片 + this.iconService.init(); + /** * 初始化底图 */ @@ -151,6 +158,9 @@ export default class Scene extends EventEmitter implements ISceneService { this.interactionService.destroy(); window.removeEventListener('resize', this.handleWindowResized, false); } + public addImage(id: string, img: IImage) { + this.iconService.addImage(id, img); + } private handleWindowResized = () => { this.emit('resize'); @@ -174,7 +184,6 @@ export default class Scene extends EventEmitter implements ISceneService { this.render(); } }; - private handleMapCameraChanged = (viewport: IViewport) => { this.cameraService.update(viewport); this.render(); diff --git a/packages/layers/src/core/BaseLayer.ts b/packages/layers/src/core/BaseLayer.ts index 8b756fdf79..19c17ca64c 100644 --- a/packages/layers/src/core/BaseLayer.ts +++ b/packages/layers/src/core/BaseLayer.ts @@ -59,6 +59,8 @@ export default class BaseLayer implements ILayer { public styleAttributes: { [key: string]: Required; } = {}; + @lazyInject(TYPES.IIconService) + protected readonly iconService: IIconService; protected layerSource: Source; @@ -71,9 +73,6 @@ export default class BaseLayer implements ILayer { @lazyInject(TYPES.IRendererService) private readonly rendererService: IRendererService; - @lazyInject(TYPES.IIconService) - private readonly iconService: IIconService; - constructor(initializationOptions: Partial) { this.initializationOptions = initializationOptions; } diff --git a/packages/layers/src/point/buffers/ImageBuffer.ts b/packages/layers/src/point/buffers/ImageBuffer.ts index c4494dcb7b..510a77bdd8 100644 --- a/packages/layers/src/point/buffers/ImageBuffer.ts +++ b/packages/layers/src/point/buffers/ImageBuffer.ts @@ -7,11 +7,12 @@ export default class ImageBuffer extends BaseBuffer { } protected buildFeatures() { const layerData = this.data as IEncodeFeature[]; + this.attributes.uv = new Float32Array(this.verticesCount * 2); layerData.forEach((item: IEncodeFeature, index: number) => { const { color = [0, 0, 0, 0], size, id, shape, coordinates } = item; - const { x, y } = this.iconMap[shape as string]; + const { x, y } = this.iconMap[shape as string] || { x: 0, y: 0 }; const coor = coordinates as Position; - this.attributes.vertices.set([coor[0], coor[1], coor[2] || 0], index * 3); + this.attributes.positions.set(coor, index * 3); this.attributes.colors.set(color, index * 4); this.attributes.pickingIds.set([id as number], index); this.attributes.sizes.set([size as number], index); // diff --git a/packages/layers/src/point/point.ts b/packages/layers/src/point/point.ts index 7e7ad2a7ba..e7f7e10e9a 100644 --- a/packages/layers/src/point/point.ts +++ b/packages/layers/src/point/point.ts @@ -1,5 +1,6 @@ import { gl, + IIconService, IRendererService, IShaderModuleService, lazyInject, @@ -7,8 +8,11 @@ import { } from '@l7/core'; import BaseLayer from '../core/BaseLayer'; import ExtrudeBuffer from './buffers/ExtrudeBuffer'; +import ImageBuffer from './buffers/ImageBuffer'; import extrude_frag from './shaders/extrude_frag.glsl'; import extrude_vert from './shaders/extrude_vert.glsl'; +import image_frag from './shaders/image_frag.glsl'; +import image_vert from './shaders/image_vert.glsl'; export default class PointLayer extends BaseLayer { public name: string = 'PointLayer'; @@ -19,6 +23,7 @@ export default class PointLayer extends BaseLayer { @lazyInject(TYPES.IRendererService) private readonly renderer: IRendererService; + protected renderModels() { this.models.forEach((model) => model.draw({ @@ -35,20 +40,28 @@ export default class PointLayer extends BaseLayer { vs: extrude_vert, fs: extrude_frag, }); + this.shaderModule.registerModule('pointImage', { + vs: image_vert, + fs: image_frag, + }); this.models = []; - const { vs, fs, uniforms } = this.shaderModule.getModule('point'); - const buffer = new ExtrudeBuffer({ - data: this.getEncodedData(), - }); - buffer.computeVertexNormals('miters', false); + const { vs, fs, uniforms } = this.shaderModule.getModule('pointImage'); + // const buffer = new ExtrudeBuffer({ + // data: this.getEncodedData(), + // }); + // buffer.computeVertexNormals('miters', false); const { createAttribute, createBuffer, createElements, + createTexture2D, createModel, } = this.renderer; - + const buffer = new ImageBuffer({ + data: this.getEncodedData(), + iconMap: this.iconService.getIconMap(), + }); this.models.push( createModel({ attributes: { @@ -78,27 +91,40 @@ export default class PointLayer extends BaseLayer { data: buffer.attributes.sizes, type: gl.FLOAT, }), - size: 3, + size: 1, }), - a_shape: createAttribute({ + a_uv: createAttribute({ buffer: createBuffer({ - data: buffer.attributes.miters, + data: buffer.attributes.uv, type: gl.FLOAT, }), - size: 3, + size: 2, }), + // a_shape: createAttribute({ + // buffer: createBuffer({ + // data: buffer.attributes.miters, + // type: gl.FLOAT, + // }), + // size: 3, + // }), }, uniforms: { ...uniforms, u_opacity: this.styleOption.opacity as number, + u_texture: createTexture2D({ + data: this.iconService.getCanvas(), + width: 1024, + height: this.iconService.canvasHeight, + }), }, fs, vs, - count: buffer.indexArray.length, - elements: createElements({ - data: buffer.indexArray, - type: gl.UNSIGNED_INT, - }), + primitive: gl.POINTS, + count: buffer.verticesCount, + // elements: createElements({ + // data: buffer.indexArray, + // type: gl.UNSIGNED_INT, + // }), }), ); } diff --git a/packages/layers/src/point/shaders/image_frag.glsl b/packages/layers/src/point/shaders/image_frag.glsl index 7f472a1e29..c492ed2a4c 100644 --- a/packages/layers/src/point/shaders/image_frag.glsl +++ b/packages/layers/src/point/shaders/image_frag.glsl @@ -1,8 +1,9 @@ uniform sampler2D u_texture; varying vec4 v_color; +varying vec2 v_uv; void main(){ - vec2 pos=v_uv+gl_PointCoord / 512.*64.; - pos.y=1.-pos.y; + vec2 pos= v_uv + gl_PointCoord / vec2(1024.,128.)*64.; + pos.y= 1.- pos.y; vec4 textureColor=texture2D(u_texture,pos); if(v_color == vec4(0.)){ gl_FragColor= textureColor; diff --git a/packages/layers/src/point/shaders/image_vert.glsl b/packages/layers/src/point/shaders/image_vert.glsl index c62ac0a641..bb0d424b45 100644 --- a/packages/layers/src/point/shaders/image_vert.glsl +++ b/packages/layers/src/point/shaders/image_vert.glsl @@ -1,15 +1,18 @@ precision highp float; attribute vec3 a_Position; attribute vec4 a_color; +attribute vec2 a_uv; attribute float a_size; attribute float a_shape; varying vec4 v_color; varying vec2 v_uv; +uniform mat4 u_ModelMatrix; #pragma include "projection" void main() { v_color = a_color; + v_uv = a_uv; vec4 project_pos = project_position(vec4(a_Position, 1.0)); - gl_Position = project_common_position_to_clipspace(vec4(project_pos, 1.0)); + gl_Position = project_common_position_to_clipspace(vec4(project_pos.xyz, 1.0)); gl_PointSize = a_size; - v_uv = uv; + } \ No newline at end of file diff --git a/packages/scene/src/index.ts b/packages/scene/src/index.ts index 40ce761aec..08c6792fd9 100644 --- a/packages/scene/src/index.ts +++ b/packages/scene/src/index.ts @@ -1,5 +1,6 @@ import { container, + IImage, ILayer, IMapConfig, IMapService, @@ -73,10 +74,14 @@ class Scene { public render(): void { this.sceneService.render(); } - + public addImage(id: string, img: IImage) { + this.sceneService.addImage(id, img); + } public destroy() { this.sceneService.destroy(); } + + // 资源管理 } export { Scene }; diff --git a/packages/utils/src/fetchData.ts b/packages/utils/src/fetchData.ts index 8430c34b4b..a4e874fee6 100644 --- a/packages/utils/src/fetchData.ts +++ b/packages/utils/src/fetchData.ts @@ -108,6 +108,7 @@ export const getImage = (requestParameters: any, callback: any) => { callback(err); } else if (imgData) { const img = new window.Image(); + img.crossOrigin = 'anonymous'; const URL = window.URL || window.webkitURL; img.onload = () => { callback(null, img); diff --git a/stories/MapAdaptor/Map.stories.tsx b/stories/MapAdaptor/Map.stories.tsx index 0aca4641a4..677f177358 100644 --- a/stories/MapAdaptor/Map.stories.tsx +++ b/stories/MapAdaptor/Map.stories.tsx @@ -7,6 +7,7 @@ import Point3D from './components/Point3D'; import Line from './components/Line'; import ImageLayer from './components/Image'; import GridHeatMap from './components/GridHeatmap'; +import PointImage from './components/pointImage'; // @ts-ignore import notes from './Map.md'; @@ -21,4 +22,5 @@ storiesOf('地图底图测试', module) .add('Point3D', () => ) .add('Line', () => ) .add('GridHeatMap', () => ) - .add('Image', () => ); + .add('Image', () => ) + .add('pointImage', () => ); diff --git a/stories/MapAdaptor/components/Image.tsx b/stories/MapAdaptor/components/Image.tsx index 77791ba5bb..abe2bdb95c 100644 --- a/stories/MapAdaptor/components/Image.tsx +++ b/stories/MapAdaptor/components/Image.tsx @@ -28,9 +28,9 @@ export default class ImageLayerDemo extends React.Component { }, }, ); - scene.addLayer(layer); + // scene.addLayer(layer); scene.render(); - this.scene = scene; + console.log(scene); } public render() { diff --git a/stories/MapAdaptor/components/Point3D.tsx b/stories/MapAdaptor/components/Point3D.tsx index 80c66eef1d..bbe2a407e7 100644 --- a/stories/MapAdaptor/components/Point3D.tsx +++ b/stories/MapAdaptor/components/Point3D.tsx @@ -19,6 +19,10 @@ export default class Point3D extends React.Component { style: 'mapbox://styles/mapbox/streets-v9', zoom: 1, }); + scene.addImage( + '00', + 'https://gw.alipayobjects.com/mdn/antv_site/afts/img/A*kzTMQqS2QdUAAAAAAAAAAABkARQnAQ', + ); const pointLayer = new Point({}); const p1 = { type: 'FeatureCollection', @@ -39,13 +43,8 @@ export default class Point3D extends React.Component { .shape('scalerank', [ 'triangleColumn', 'squareColumn', 'hexagonColumn' ,'cylinder' ]) .size([25, 10]); scene.addLayer(pointLayer); - // function run() { - // scene.render(); - // requestAnimationFrame(run); - // } - // requestAnimationFrame(run); scene.render(); - this.scene = scene; + } public render() { diff --git a/stories/MapAdaptor/components/pointImage.tsx b/stories/MapAdaptor/components/pointImage.tsx new file mode 100644 index 0000000000..2e63242142 --- /dev/null +++ b/stories/MapAdaptor/components/pointImage.tsx @@ -0,0 +1,62 @@ +import { Point } from '@l7/layers'; +import { Scene } from '@l7/scene'; +import * as React from 'react'; +import data from './data.json'; +export default class PointImage extends React.Component { + private scene: Scene; + + public componentWillUnmount() { + this.scene.destroy(); + } + + public componentDidMount() { + const scene = new Scene({ + center: [120.19382669582967, 30.258134], + id: 'map', + pitch: 0, + type: 'mapbox', + style: 'mapbox://styles/mapbox/streets-v9', + zoom: 1, + }); + scene.addImage( + '00', + 'https://gw.alipayobjects.com/mdn/antv_site/afts/img/A*kzTMQqS2QdUAAAAAAAAAAABkARQnAQ', + ); + const pointLayer = new Point({}); + const p1 = { + type: 'FeatureCollection', + features: [ + { + type: 'Feature', + properties: {}, + geometry: { + type: 'Point', + coordinates: [83.671875, 44.84029065139799], + }, + }, + ], + }; + pointLayer + .source(data) + // .color('blue') + .shape('00') + .size(14); + scene.addLayer(pointLayer); + scene.render(); + } + + public render() { + return ( +
+ ); + } +}