refactor(layer): text layer

This commit is contained in:
thinkinggis 2019-03-25 00:57:09 +08:00
parent b8883b047b
commit c8e8814879
19 changed files with 514 additions and 198 deletions

View File

@ -25,22 +25,24 @@ const scene = new L7.Scene({
mapStyle: 'dark', // 样式URL
center: [ 120.19382669582967, 30.258134 ],
pitch: 0,
zoom: 3
zoom: 1
});
window.scene = scene;
scene.on('loaded', () => {
$.get('./data/provincePoint.geojson', data => {
// data.features = data.features.slice(0,1);
scene.PointLayer({
zIndex: 2
})
.source(data)
.shape('name', 'text')
.size(12) // default 1
.color('#fff')
.active(true)
.size(48) // default 1
.color('name')
.style({
stroke: '#999',
strokeWidth: 2,
opacity: 0.85
strokeWidth: 0,
opacity: 1.0
})
.render();
});

View File

@ -1,3 +0,0 @@
export class layerControl {
}

View File

@ -1,24 +0,0 @@
import { getMap } from '../../map';
import Base from '../base';
export default class MapContorller extends Base {
constructor(cfg, engine, scene) {
super(cfg);
this._engine = engine;
this.scene = scene;
}
_init() {
const mapType = this.get('mapType');
const mapCfg = this.get('mapCfg');
this.map = new getMap(mapType)(mapCfg);
this.map('mapLoad', this._mapload.bind(this));
}
_mapload() {
this.map.asyncCamera(this._engine);
this.emit('loaded');
}
_bindMapMethod() {
}
}

View File

@ -2,6 +2,7 @@ import Engine from './engine';
import { LAYER_MAP } from '../layer';
import Base from './base';
import LoadImage from './image';
import FontAtlasManager from '../geom/buffer/point/text/font-manager';
// import WorkerPool from './worker';
// import { MapProvider } from '../map/AMap';
import { getMap } from '../map/index';
@ -16,6 +17,7 @@ export default class Scene extends Base {
this._initMap();
// this._initAttribution(); // 暂时取消,后面作为组件去加载
this.addImage();
this.fontAtlasManager = new FontAtlasManager();
this._layers = [];
}

View File

@ -14,7 +14,9 @@ export { WebGLRenderTarget } from 'three/src/renderers/WebGLRenderTarget.js';
export { PerspectiveCamera } from 'three/src/cameras/PerspectiveCamera.js';
export { OrthographicCamera } from 'three/src/cameras/OrthographicCamera.js';
export { BufferGeometry } from 'three/src/core/BufferGeometry.js';
export { InstancedBufferGeometry } from 'three/src/core/InstancedBufferGeometry';
export { PlaneBufferGeometry } from 'three/src/geometries/PlaneGeometry.js';
export { BoxBufferGeometry } from 'three/src/geometries/BoxGeometry.js';
export { Raycaster } from 'three/src/core/Raycaster.js';
export { Matrix4 } from 'three/src/math/Matrix4.js';
export { Matrix3 } from 'three/src/math/Matrix3.js';
@ -38,4 +40,5 @@ export {
BufferAttribute
} from 'three/src/core/BufferAttribute.js';
export { InstancedBufferAttribute } from 'three/src/core/InstancedBufferAttribute'
// export * from '../../build/three.js';

View File

@ -0,0 +1,50 @@
export default function TextBuffer2(layerData, fontAtlasManager) {
const characterSet = [];
layerData.forEach(element => {
let text = element.shape || '';
text = text.toString();
for (let j = 0; j < text.length; j++) {
if (characterSet.indexOf(text[j]) === -1) {
characterSet.push(text[j]);
}
}
});
fontAtlasManager.setProps({
characterSet
});
const attr = generateTextBuffer(layerData, fontAtlasManager);
return attr;
}
function generateTextBuffer(layerData, fontAtlasManager) {
const attributes = {
vertices: [],
pickingIds: [],
textSizes: [], // 文字大小 // 长宽
textOffsets: [], // 文字偏移量
colors: [],
textUv: [] // 纹理坐标
};
const { texture, fontAtlas, mapping } = fontAtlasManager;
layerData.forEach(element => {
const size = element.size;
const pos = element.coordinates;
let text = element.shape || '';
const pen = { x: -text.length * size / 2, y: 0 };
text = text.toString();
for (let i = 0; i < text.length; i++) {
const metric = mapping[text[i]];
const { x, y, width, height } = metric;
const color = element.color;
attributes.vertices.push(...pos);
attributes.colors.push(...color);
attributes.textUv.push(x, y, width, height);
attributes.textOffsets.push(pen.x, pen.y);
attributes.pickingIds.push(element.id);
attributes.textSizes.push(size, size);
pen.x = pen.x + size;
}
});
attributes.texture = texture;
attributes.fontAtlas = fontAtlas;
return attributes;
}

View File

@ -0,0 +1,228 @@
import TinySDF from '@mapbox/tiny-sdf';
import { buildMapping } from '../../../../util/font-util';
import * as THREE from '../../../../core/three';
import * as logImage from 'console-image';
import LRUCache from './lru-cache';
export const DEFAULT_CHAR_SET = getDefaultCharacterSet();
export const DEFAULT_FONT_FAMILY = 'sans-serif';
export const DEFAULT_FONT_WEIGHT = 'normal';
export const DEFAULT_FONT_SIZE = 24;
export const DEFAULT_BUFFER = 3;
export const DEFAULT_CUTOFF = 0.25;
export const DEFAULT_RADIUS = 8;
const MAX_CANVAS_WIDTH = 1024;
const BASELINE_SCALE = 0.9;
const HEIGHT_SCALE = 1.2;
const CACHE_LIMIT = 3;
const cache = new LRUCache(CACHE_LIMIT);
const VALID_PROPS = [
'fontFamily',
'fontWeight',
'characterSet',
'fontSize',
'sdf',
'buffer',
'cutoff',
'radius'
];
function getDefaultCharacterSet() {
const charSet = [];
for (let i = 32; i < 128; i++) {
charSet.push(String.fromCharCode(i));
}
return charSet;
}
function setTextStyle(ctx, fontFamily, fontSize, fontWeight) {
ctx.font = `${fontWeight} ${fontSize}px ${fontFamily}`;
ctx.fillStyle = '#000';
ctx.textBaseline = 'baseline';
ctx.textAlign = 'left';
}
function getNewChars(key, characterSet) {
const cachedFontAtlas = cache.get(key);
if (!cachedFontAtlas) {
return characterSet;
}
const newChars = [];
const cachedMapping = cachedFontAtlas.mapping;
let cachedCharSet = Object.keys(cachedMapping);
cachedCharSet = new Set(cachedCharSet);
let charSet = characterSet;
if (charSet instanceof Array) {
charSet = new Set(charSet);
}
charSet.forEach(char => {
if (!cachedCharSet.has(char)) {
newChars.push(char);
}
});
return newChars;
}
function populateAlphaChannel(alphaChannel, imageData) {
// populate distance value from tinySDF to image alpha channel
for (let i = 0; i < alphaChannel.length; i++) {
imageData.data[4 * i + 3] = alphaChannel[i];
}
}
export default class FontAtlasManager {
constructor() {
// font settings
this.props = {
fontFamily: DEFAULT_FONT_FAMILY,
fontWeight: DEFAULT_FONT_WEIGHT,
characterSet: DEFAULT_CHAR_SET,
fontSize: DEFAULT_FONT_SIZE,
buffer: DEFAULT_BUFFER,
// sdf only props
// https://github.com/mapbox/tiny-sdf
sdf: true,
cutoff: DEFAULT_CUTOFF,
radius: DEFAULT_RADIUS
};
// key is used for caching generated fontAtlas
this._key = null;
this._texture = new THREE.Texture();
}
get texture() {
return this._texture;
}
get mapping() {
const data = cache.get(this._key);
return data && data.mapping;
}
get scale() {
return HEIGHT_SCALE;
}
get fontAtlas() {
return this._fontAtlas;
}
setProps(props = {}) {
VALID_PROPS.forEach(prop => {
if (prop in props) {
this.props[prop] = props[prop];
}
});
// update cache key
const oldKey = this._key;
this._key = this._getKey();
const charSet = getNewChars(this._key, this.props.characterSet);
const cachedFontAtlas = cache.get(this._key);
// if a fontAtlas associated with the new settings is cached and
// there are no new chars
if (cachedFontAtlas && charSet.length === 0) {
// update texture with cached fontAtlas
if (this._key !== oldKey) {
this._updateTexture(cachedFontAtlas);
}
return;
}
// update fontAtlas with new settings
const fontAtlas = this._generateFontAtlas(this._key, charSet, cachedFontAtlas);
this._fontAtlas = fontAtlas;
this._updateTexture(fontAtlas);
// update cache
cache.set(this._key, fontAtlas);
}
_updateTexture({ data: canvas }) {
this._texture = new THREE.CanvasTexture(canvas);
// this._texture.wrapS = THREE.ClampToEdgeWrapping;
// this._texture.wrapT = THREE.ClampToEdgeWrapping;
this._texture.flipY = false;
this._texture.needUpdate = true;
}
_generateFontAtlas(key, characterSet, cachedFontAtlas) {
const { fontFamily, fontWeight, fontSize, buffer, sdf, radius, cutoff } = this.props;
let canvas = cachedFontAtlas && cachedFontAtlas.data;
if (!canvas) {
canvas = document.createElement('canvas');
canvas.width = MAX_CANVAS_WIDTH;
}
const ctx = canvas.getContext('2d');
setTextStyle(ctx, fontFamily, fontSize, fontWeight);
// 1. build mapping
const { mapping, canvasHeight, xOffset, yOffset } = buildMapping(
Object.assign(
{
getFontWidth: char => ctx.measureText(char).width,
fontHeight: fontSize * HEIGHT_SCALE,
buffer,
characterSet,
maxCanvasWidth: MAX_CANVAS_WIDTH
},
cachedFontAtlas && {
mapping: cachedFontAtlas.mapping,
xOffset: cachedFontAtlas.xOffset,
yOffset: cachedFontAtlas.yOffset
}
)
);
// 2. update canvas
// copy old canvas data to new canvas only when height changed
if (canvas.height !== canvasHeight) {
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
canvas.height = canvasHeight;
ctx.putImageData(imageData, 0, 0);
}
setTextStyle(ctx, fontFamily, fontSize, fontWeight);
// 3. layout characters
if (sdf) {
const tinySDF = new TinySDF(fontSize, buffer, radius, cutoff, fontFamily, fontWeight);
// used to store distance values from tinySDF
// tinySDF.size equals `fontSize + buffer * 2`
const imageData = ctx.getImageData(0, 0, tinySDF.size, tinySDF.size);
for (const char of characterSet) {
populateAlphaChannel(tinySDF.draw(char), imageData);
ctx.putImageData(imageData, mapping[char].x - buffer, mapping[char].y - buffer);
}
} else {
for (const char of characterSet) {
ctx.fillText(char, mapping[char].x, mapping[char].y + fontSize * BASELINE_SCALE);
}
}
logImage(canvas);
return {
xOffset,
yOffset,
mapping,
data: canvas,
width: canvas.width,
height: canvas.height
};
}
_getKey() {
const { fontFamily, fontWeight, fontSize, buffer, sdf, radius, cutoff } = this.props;
if (sdf) {
return `${fontFamily} ${fontWeight} ${fontSize} ${buffer} ${radius} ${cutoff}`;
}
return `${fontFamily} ${fontWeight} ${fontSize} ${buffer}`;
}
}

View File

@ -0,0 +1,71 @@
/**
* LRU Cache class with limit
*
* Update order for each get/set operation
* Delete oldest when reach given limit
*/
export default class LRUCache {
constructor(limit = 5) {
this.limit = limit;
this.clear();
}
clear() {
this._cache = {};
// access/update order, first item is oldest, last item is newest
this._order = [];
}
get(key) {
const value = this._cache[key];
if (value) {
// update order
this._deleteOrder(key);
this._appendOrder(key);
}
return value;
}
set(key, value) {
if (!this._cache[key]) {
// if reach limit, delete the oldest
if (Object.keys(this._cache).length === this.limit) {
this.delete(this._order[0]);
}
this._cache[key] = value;
this._appendOrder(key);
} else {
// if found in cache, delete the old one, insert new one to the first of list
this.delete(key);
this._cache[key] = value;
this._appendOrder(key);
}
}
delete(key) {
const value = this._cache[key];
if (value) {
this._deleteCache(key);
this._deleteOrder(key);
}
}
_deleteCache(key) {
delete this._cache[key];
}
_deleteOrder(key) {
const index = this._order.findIndex(o => o === key);
if (index >= 0) {
this._order.splice(index, 1);
}
}
_appendOrder(key) {
this._order.push(key);
}
}

View File

@ -151,31 +151,7 @@ function drawGlyph(chars, pos, text, pen, size, colors, textureElements, originP
posX + width, posY + height
);
}
pen.x = pen.x + size * 1.8;
pen.x = pen.x + size;
}
// function measureText(text, size) {
// const dimensions = {
// advance: 0
// };
// const metrics = this.metrics;
// const scale = size / metrics.size;
// for (let i = 0; i < text.length; i++) {
// const code = text.charCodeAt(i);
// const horiAdvance = metrics.chars[code][4];
// dimensions.advance += (horiAdvance + Space) * scale;
// }
// return dimensions;
// }
// function creatTexture(image) {
// this.bufferStruct.textSize = [ image.width, image.height ];
// const texture = new THREE.Texture(image);
// texture.minFilter = THREE.LinearFilter;
// texture.magFilter = THREE.ClampToEdgeWrapping;
// texture.needsUpdate = true;
// return texture;
// }

View File

@ -9,12 +9,13 @@ export default function TextMaterial(options) {
u_texture: { value: options.u_texture },
u_strokeWidth: { value: options.u_strokeWidth },
u_stroke: { value: options.u_stroke },
u_textSize: { value: options.u_textSize },
u_textTextureSize: { value: options.u_textTextureSize },
u_scale: { value: options.u_scale },
u_gamma: { value: options.u_gamma },
u_buffer: { value: options.u_buffer },
u_color: { value: options.u_color },
u_glSize: { value: options.u_glSize }
u_glSize: { value: options.u_glSize },
u_activeId: { value: options.u_activeId || 0 },
u_activeColor: { value: options.u_activeColor }
},
vertexShader: vs,

View File

@ -31,7 +31,7 @@ import heatmap_intensity_vert from '../shader/heatmap_intensity_vert.glsl';
// 文本
import text_frag from '../shader/text_frag.glsl';
import text_vert from '../shader/text_vert.glsl';
import text_vert from '../shader/text_vert2.glsl';
// 图像
import image_vert from '../shader/image_vert.glsl';

View File

@ -1,48 +0,0 @@
precision highp float;
#define ambientRatio 0.5
#define diffuseRatio 0.4
#define specularRatio 0.1
attribute vec4 a_color;
attribute vec4 a_idColor;
attribute vec2 faceUv;
attribute vec3 a_shape;
attribute vec3 a_size;
uniform float u_zoom;
varying vec2 v_texCoord;
varying vec4 v_color;
varying float v_lightWeight;
varying float v_size;
void main() {
float scale = pow(2.0,(20.0 - u_zoom));
mat4 matModelViewProjection = projectionMatrix * modelViewMatrix;
vec3 newposition = position;
#ifdef SHAPE
newposition =position + a_size * scale* a_shape;
#endif
v_texCoord = faceUv;
if(normal == vec3(0.,0.,1.)){
v_color = a_color;
gl_Position = matModelViewProjection * vec4(newposition, 1.0);
return;
}
vec3 worldPos = vec3(vec4(newposition,1.0) * modelMatrix);
vec3 worldNormal = vec3(vec4(normal,1.0) * modelMatrix);
// //cal light weight
vec3 viewDir = normalize(cameraPosition - worldPos);
//vec3 lightDir = normalize(vec3(1, -10.5, 12));
vec3 lightDir = normalize(vec3(0.,-10.,1.));
vec3 halfDir = normalize(viewDir+lightDir);
// //lambert
float lambert = dot(worldNormal, lightDir);
//specular
float specular = pow( max(0.0, dot(worldNormal, halfDir)), 32.0);
//sum to light weight
float lightWeight = ambientRatio + diffuseRatio * lambert + specularRatio * specular;
v_texCoord = faceUv;
v_lightWeight = lightWeight;
// v_size = a_size;
v_color =vec4(a_color.rgb*lightWeight, a_color.w);
gl_Position = matModelViewProjection * vec4(newposition, 1.0);
}

View File

@ -1,32 +1,28 @@
precision mediump float;
uniform sampler2D u_texture;
varying vec4 v_color;
varying vec4 v_color;
uniform vec4 u_stroke;
uniform float u_strokeWidth;
uniform float u_buffer;
uniform float u_gamma;
uniform float u_opacity;
varying vec2 v_texcoord;
void main() {
float dist =texture2D(u_texture, vec2(v_texcoord.x,1.0-v_texcoord.y)).r;
void main(){
float dist=texture2D(u_texture,vec2(v_texcoord.x,v_texcoord.y)).a;
float alpha;
if(u_strokeWidth == 0.0){
alpha = smoothstep(u_buffer - u_gamma, u_buffer + u_gamma, dist);
gl_FragColor = vec4(v_color.rgb, alpha * v_color.a);
if(u_strokeWidth==0.){
alpha=smoothstep(u_buffer-u_gamma,u_buffer+u_gamma,dist);
gl_FragColor=vec4(v_color.rgb,alpha*v_color.a);
}else{
if(dist <= u_buffer - u_gamma){
alpha = smoothstep(u_strokeWidth - u_gamma, u_strokeWidth+ u_gamma, dist);
gl_FragColor = vec4(u_stroke.rgb, alpha * u_stroke.a);
}else if(dist < u_buffer){
alpha = smoothstep(u_buffer - u_gamma, u_buffer+u_gamma, dist);
gl_FragColor = vec4(alpha * v_color.rgb + (1.0 - alpha) * u_stroke.rgb, 1.0 * v_color.a * alpha + (1.0 - alpha) * u_stroke.a);
if(dist<=u_buffer-u_gamma){
alpha=smoothstep(u_strokeWidth-u_gamma,u_strokeWidth+u_gamma,dist);
gl_FragColor=vec4(u_stroke.rgb,alpha*u_stroke.a);
}else if(dist<u_buffer){
alpha=smoothstep(u_buffer-u_gamma,u_buffer+u_gamma,dist);
gl_FragColor=vec4(alpha*v_color.rgb+(1.-alpha)*u_stroke.rgb,1.*v_color.a*alpha+(1.-alpha)*u_stroke.a);
}else{
alpha = 1.0;
gl_FragColor = vec4(v_color.rgb, alpha * v_color.a);
alpha=1.;
gl_FragColor=vec4(v_color.rgb,alpha*v_color.a);
}
}

View File

@ -1,5 +1,5 @@
precision mediump float;
attribute vec2 a_txtsize;
attribute vec2 a_txtsize;
attribute vec2 a_txtOffsets;
attribute vec4 a_color;
uniform mat4 u_model;
@ -8,19 +8,23 @@ uniform mat4 u_scale;
uniform vec2 u_textSize;
uniform vec2 u_glSize;
varying vec2 v_texcoord;
varying vec4 v_color;
varying vec4 v_color;
uniform float u_activeId;
uniform vec4 u_activeColor;
void main() {
mat4 matModelViewProjection = projectionMatrix * modelViewMatrix;
vec4 cur_position = matModelViewProjection * vec4(position.xy, 0, 1);
gl_Position = cur_position / cur_position.w + vec4((a_txtOffsets + a_txtsize)/ u_glSize * 2.0,0.0, 0.0) +vec4(abs(a_txtsize.x)/u_glSize.x *2.0, -abs(a_txtsize.y)/u_glSize.y* 2.0, 0.0, 0.0);
highp float camera_to_anchor_distance = gl_Position.w;
// highp float perspective_ratio = clamp(
void main(){
mat4 matModelViewProjection=projectionMatrix*modelViewMatrix;
vec4 cur_position=matModelViewProjection*vec4(position.xy,0,1);
gl_Position=cur_position/cur_position.w+vec4((a_txtOffsets/2.+a_txtsize)/u_glSize*2.,0.,0.);
//+vec4(abs(a_txtsize.x)/u_glSize.x *2.0, -abs(a_txtsize.y)/u_glSize.y* 2.0, 0.0, 0.0);
highp float camera_to_anchor_distance=gl_Position.w;
// highp float perspective_ratio = clamp(
// 0.5 + 0.5 * distance_ratio,
// 0.0, // Prevents oversized near-field symbols in pitched/overzoomed tiles
// 4.0);
v_color = a_color;
v_color.a = v_color.a * camera_to_anchor_distance;
v_texcoord = uv / u_textSize;
// 4.0);
v_color=a_color;
v_color.a=v_color.a*camera_to_anchor_distance;
v_texcoord=uv/u_textSize;
worldId = id_toPickColor(pickingId);
}

View File

@ -0,0 +1,29 @@
precision mediump float;
attribute vec3 a_position;
attribute vec4 textUv;
attribute vec2 a_textSize;
attribute vec2 a_textOffset;// 文本偏移
attribute vec4 a_color;
uniform mat4 u_model;
uniform float u_opacity;
uniform mat4 u_view;
uniform mat4 u_scale;
uniform vec2 u_textTextureSize;// 纹理大小
uniform float u_activeId;
uniform vec4 u_activeColor;
uniform vec2 u_glSize;// 画布大小
varying vec2 v_texcoord;
varying vec4 v_color;
void main(){
mat4 matModelViewProjection=projectionMatrix*modelViewMatrix;
vec4 cur_position=matModelViewProjection*vec4(a_position.xy,0,1);
gl_Position=cur_position/cur_position.w+vec4(a_textSize*position.xy/u_glSize/2.,0.,0.)+vec4(a_textOffset/u_glSize,0,0);
v_color=vec4(a_color.rgb,a_color.a*u_opacity);
if(pickingId == u_activeId) {
v_color = u_activeColor;
}
v_texcoord=(textUv.xy+vec2(uv.x,1.-uv.y)*textUv.zw)/u_textTextureSize;
worldId = id_toPickColor(pickingId);
}

View File

@ -1,10 +1,9 @@
import Layer from '../core/layer';
import * as THREE from '../core/three';
import * as drawPoint from '../layer/render/point';
import TextBuffer2 from '../geom/buffer/point/text';
import DrawText from './render/point/drawText';
import Global from '../global';
// import PointBuffer from '../geom/buffer/point';
import TextBuffer from '../geom/buffer/text';
import TextMaterial from '../geom/material/textMaterial';
import * as PointBuffer from '../geom/buffer/point/index';
const { pointShape } = Global;
/**
@ -101,37 +100,18 @@ export default class PointLayer extends Layer {
}
_textPoint() {
const styleOptions = this.get('styleOptions');
const buffer = new TextBuffer({
type: this.shapeType,
layerData: this.layerData,
style: this.get('styleOptions')
});
buffer.on('completed', () => {
const { color, stroke } = styleOptions;
const geometry = new THREE.BufferGeometry();
geometry.addAttribute('position', new THREE.Float32BufferAttribute(buffer.attributes.originPoints, 3));
geometry.addAttribute('uv', new THREE.Float32BufferAttribute(buffer.attributes.textureElements, 2));
geometry.addAttribute('a_txtsize', new THREE.Float32BufferAttribute(buffer.attributes.textSizes, 2));
geometry.addAttribute('a_txtOffsets', new THREE.Float32BufferAttribute(buffer.attributes.textOffsets, 2));
geometry.addAttribute('a_color', new THREE.Float32BufferAttribute(buffer.attributes.colors, 4));
const { width, height } = this.scene.getSize();
const material = new TextMaterial({
name: this.layerId,
u_texture: buffer.bufferStruct.textTexture,
u_strokeWidth: styleOptions.strokeWidth,
u_stroke: stroke,
u_textSize: buffer.bufferStruct.textSize,
u_gamma: 2 * 1.4142 / 64,
u_buffer: 0.65,
u_color: color,
u_glSize: [ width, height ]
});
const mesh = new THREE.Mesh(geometry, material);
this.add(mesh);
});
const activeOption = this.get('activedOptions');
const { width, height } = this.scene.getSize();
const textCfg = {
...styleOptions,
width,
height,
activeColor: activeOption.fill
};
const buffer2 = new TextBuffer2(this.layerData, this.scene.fontAtlasManager);
const testMesh = new DrawText(buffer2, textCfg);
this.add(testMesh);
return;
}
}

View File

@ -1,23 +1,29 @@
// import * as THREE from '../../../core/three';
// import TextMaterial from '../../../geom/material/textMaterial';
// export default function DawText(attributes, texture, style) {
// const geometry = new THREE.BufferGeometry();
// const { strokeWidth, stroke, opacity } = style;
// geometry.addAttribute('position', new THREE.Float32BufferAttribute(attributes.originPoints, 3));
// geometry.addAttribute('uv', new THREE.Float32BufferAttribute(attributes.textureElements, 2));
// geometry.addAttribute('a_txtsize', new THREE.Float32BufferAttribute(attributes.textSizes, 2));
// geometry.addAttribute('a_txtOffsets', new THREE.Float32BufferAttribute(attributes.textOffsets, 2));
// geometry.addAttribute('a_color', new THREE.Float32BufferAttribute(attributes.colors, 4));
// const material = new TextMaterial({
// name: this.layerId,
// u_texture: texture,
// u_strokeWidth: 1,
// u_stroke: stroke,
// u_textSize: buffer.bufferStruct.textSize,
// u_gamma: 0.11,
// u_buffer: 0.8,
// u_color: color,
// u_glSize: [ width, height ]
// });
// const mesh = new THREE.Mesh(geometry, material);
// }
import * as THREE from '../../../core/three';
import TextMaterial from '../../../geom/material/textMaterial';
export default function DrawText(attributes, style) {
const instancedGeometry = new THREE.InstancedBufferGeometry();
// instancedGeometry.copy(new buildTextbufferGeometry());
instancedGeometry.copy(new THREE.PlaneBufferGeometry(2, 2));
const { strokeWidth, width, stroke, height, opacity, activeColor } = style;
instancedGeometry.addAttribute('a_position', new THREE.InstancedBufferAttribute(new Float32Array(attributes.vertices), 3));
instancedGeometry.addAttribute('a_textSize', new THREE.InstancedBufferAttribute(new Float32Array(attributes.textSizes), 2));
instancedGeometry.addAttribute('a_textOffset', new THREE.InstancedBufferAttribute(new Float32Array(attributes.textOffsets), 2));
instancedGeometry.addAttribute('a_color', new THREE.InstancedBufferAttribute(new Float32Array(attributes.colors), 4));
instancedGeometry.addAttribute('a_size', new THREE.InstancedBufferAttribute(new Float32Array(attributes.textSizes), 1));
instancedGeometry.addAttribute('pickingId', new THREE.InstancedBufferAttribute(new Float32Array(attributes.pickingIds), 1));
instancedGeometry.addAttribute('textUv', new THREE.InstancedBufferAttribute(new Float32Array(attributes.textUv), 4));
const material = new TextMaterial({
name: this.layerId,
u_texture: attributes.texture,
u_strokeWidth: strokeWidth,
u_stroke: stroke,
u_textTextureSize: [ attributes.fontAtlas.width, attributes.fontAtlas.height ],
u_gamma: 0.02,
u_buffer: 0.75,
u_opacity: opacity,
u_glSize: [ width, height ],
u_activeColor: activeColor
});
const textMesh = new THREE.Mesh(instancedGeometry, material);
return textMesh;
}

43
src/util/font-util.js Normal file
View File

@ -0,0 +1,43 @@
export function nextPowOfTwo(number) {
return Math.pow(2, Math.ceil(Math.log2(number)));
}
export function buildMapping({
characterSet,
getFontWidth,
fontHeight,
buffer,
maxCanvasWidth,
mapping = {},
xOffset = 0,
yOffset = 0
}) {
let row = 0;
let x = xOffset;
Array.from(characterSet).forEach((char, i) => {
if (!mapping[char]) {
const width = getFontWidth(char, i);
if (x + width + buffer * 2 > maxCanvasWidth) {
x = 0;
row++;
}
mapping[char] = {
x: x + buffer,
y: yOffset + row * (fontHeight + buffer * 2) + buffer,
width,
height: fontHeight,
mask: true
};
x += width + buffer * 2;
}
});
const rowHeight = fontHeight + buffer * 2;
return {
mapping,
xOffset: x,
yOffset: yOffset + row * rowHeight,
canvasHeight: nextPowOfTwo(yOffset + (row + 1) * rowHeight)
};
}