mirror of
https://github.com/unanmed/HumanBreak.git
synced 2025-11-27 22:03:00 +08:00
refactor: 地图渲染无副作用化
This commit is contained in:
parent
a5ecc45d9b
commit
bfccb33c88
@ -4,57 +4,41 @@ import {
|
|||||||
Transform
|
Transform
|
||||||
} from '@motajs/render-core';
|
} from '@motajs/render-core';
|
||||||
import { ILayerState, state } from '@user/data-state';
|
import { ILayerState, state } from '@user/data-state';
|
||||||
import { IMapRenderer, IMapRendererHooks } from './types';
|
import { IMapRenderer } from './types';
|
||||||
import { MapRenderer } from './renderer';
|
import { MapRenderer } from './renderer';
|
||||||
import { materials } from '@user/client-base';
|
import { materials } from '@user/client-base';
|
||||||
import { ElementNamespace, ComponentInternalInstance } from 'vue';
|
import { ElementNamespace, ComponentInternalInstance } from 'vue';
|
||||||
import { CELL_HEIGHT, CELL_WIDTH, MAP_HEIGHT, MAP_WIDTH } from '../shared';
|
import { CELL_HEIGHT, CELL_WIDTH, MAP_HEIGHT, MAP_WIDTH } from '../shared';
|
||||||
import { IHookController } from '@motajs/common';
|
|
||||||
|
|
||||||
export class MapRender extends RenderItem {
|
export class MapRender extends RenderItem {
|
||||||
/** 地图渲染器 */
|
/** 地图渲染器 */
|
||||||
readonly renderer: IMapRenderer;
|
readonly renderer: IMapRenderer;
|
||||||
/** 地图视角变换矩阵 */
|
|
||||||
readonly camera: Transform = new Transform();
|
|
||||||
|
|
||||||
/** 地图画布 */
|
|
||||||
readonly canvas: HTMLCanvasElement;
|
|
||||||
/** 画布上下文 */
|
|
||||||
readonly gl: WebGL2RenderingContext;
|
|
||||||
|
|
||||||
private rendererHook: IHookController<IMapRendererHooks>;
|
|
||||||
|
|
||||||
constructor(readonly layerState: ILayerState) {
|
constructor(readonly layerState: ILayerState) {
|
||||||
super('static');
|
super('static');
|
||||||
|
|
||||||
this.canvas = document.createElement('canvas');
|
this.renderer = new MapRenderer(materials, state.layer);
|
||||||
const gl = this.canvas.getContext('webgl2')!;
|
|
||||||
this.gl = gl;
|
|
||||||
|
|
||||||
this.renderer = new MapRenderer(
|
|
||||||
materials,
|
|
||||||
this.gl,
|
|
||||||
this.camera,
|
|
||||||
state.layer
|
|
||||||
);
|
|
||||||
this.renderer.setLayerState(layerState);
|
this.renderer.setLayerState(layerState);
|
||||||
this.renderer.useAsset(materials.trackedAsset);
|
this.renderer.useAsset(materials.trackedAsset);
|
||||||
this.rendererHook = this.renderer.addHook(new RendererUpdateHook(this));
|
this.renderer.setCanvasSize(this.width, this.height);
|
||||||
this.rendererHook.load();
|
|
||||||
this.renderer.setCellSize(CELL_WIDTH, CELL_HEIGHT);
|
this.renderer.setCellSize(CELL_WIDTH, CELL_HEIGHT);
|
||||||
this.renderer.setRenderSize(MAP_WIDTH, MAP_HEIGHT);
|
this.renderer.setRenderSize(MAP_WIDTH, MAP_HEIGHT);
|
||||||
|
|
||||||
this.delegateTicker(time => this.renderer.tick(time));
|
this.delegateTicker(time => {
|
||||||
|
this.renderer.tick(time);
|
||||||
gl.viewport(0, 0, this.canvas.width, this.canvas.height);
|
if (this.renderer.needUpdate()) {
|
||||||
|
this.update();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private sizeGL(width: number, height: number) {
|
private sizeGL(width: number, height: number) {
|
||||||
const ratio = this.highResolution ? devicePixelRatio : 1;
|
const ratio = this.highResolution ? devicePixelRatio : 1;
|
||||||
const scale = ratio * this.scale;
|
const scale = ratio * this.scale;
|
||||||
this.canvas.width = width * scale;
|
const w = width * scale;
|
||||||
this.canvas.height = height * scale;
|
const h = height * scale;
|
||||||
this.gl.viewport(0, 0, this.canvas.width, this.canvas.height);
|
this.renderer.setCanvasSize(w, h);
|
||||||
|
this.renderer.setViewport(0, 0, w, h);
|
||||||
}
|
}
|
||||||
|
|
||||||
onResize(scale: number): void {
|
onResize(scale: number): void {
|
||||||
@ -75,11 +59,9 @@ export class MapRender extends RenderItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected render(canvas: MotaOffscreenCanvas2D): void {
|
protected render(canvas: MotaOffscreenCanvas2D): void {
|
||||||
console.time('map-element-render');
|
this.renderer.clear(true, true);
|
||||||
this.renderer.render(this.gl);
|
const map = this.renderer.render();
|
||||||
|
canvas.ctx.drawImage(map, 0, 0, canvas.width, canvas.height);
|
||||||
canvas.ctx.drawImage(this.canvas, 0, 0, canvas.width, canvas.height);
|
|
||||||
console.timeEnd('map-element-render');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
patchProp(
|
patchProp(
|
||||||
@ -98,11 +80,3 @@ export class MapRender extends RenderItem {
|
|||||||
super.patchProp(key, prevValue, nextValue, namespace, parentComponent);
|
super.patchProp(key, prevValue, nextValue, namespace, parentComponent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class RendererUpdateHook implements Partial<IMapRendererHooks> {
|
|
||||||
constructor(readonly element: MapRender) {}
|
|
||||||
|
|
||||||
onUpdate(): void {
|
|
||||||
this.element.update();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import { IMapRenderer, IMapVertexGenerator, IMovingBlock } from './types';
|
|||||||
import { IMaterialFramedData, IMaterialManager } from '@user/client-base';
|
import { IMaterialFramedData, IMaterialManager } from '@user/client-base';
|
||||||
import { logger } from '@motajs/common';
|
import { logger } from '@motajs/common';
|
||||||
import { IMapLayer } from '@user/data-state';
|
import { IMapLayer } from '@user/data-state';
|
||||||
|
import { DynamicBlockStatus } from './status';
|
||||||
|
|
||||||
export interface IMovingRenderer {
|
export interface IMovingRenderer {
|
||||||
/** 素材管理器 */
|
/** 素材管理器 */
|
||||||
@ -22,13 +23,13 @@ export interface IMovingRenderer {
|
|||||||
deleteMoving(block: IMovingBlock): void;
|
deleteMoving(block: IMovingBlock): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class MovingBlock implements IMovingBlock {
|
export class MovingBlock extends DynamicBlockStatus implements IMovingBlock {
|
||||||
readonly texture: IMaterialFramedData;
|
readonly texture: IMaterialFramedData;
|
||||||
readonly tile: number;
|
readonly tile: number;
|
||||||
readonly renderer: IMovingRenderer;
|
readonly renderer: IMovingRenderer;
|
||||||
readonly index: number;
|
|
||||||
readonly layer: IMapLayer;
|
readonly layer: IMapLayer;
|
||||||
|
|
||||||
|
index: number;
|
||||||
x: number = 0;
|
x: number = 0;
|
||||||
y: number = 0;
|
y: number = 0;
|
||||||
|
|
||||||
@ -59,7 +60,10 @@ export class MovingBlock implements IMovingBlock {
|
|||||||
/** 动画开始时纵坐标 */
|
/** 动画开始时纵坐标 */
|
||||||
private startY: number = 0;
|
private startY: number = 0;
|
||||||
/** 当前动画是否已经结束 */
|
/** 当前动画是否已经结束 */
|
||||||
private end: boolean = false;
|
private end: boolean = true;
|
||||||
|
|
||||||
|
/** 是否通过 `setPos` 设置了位置 */
|
||||||
|
private posUpdated: boolean = false;
|
||||||
|
|
||||||
/** 兑现函数 */
|
/** 兑现函数 */
|
||||||
private promiseFunc: () => void = () => {};
|
private promiseFunc: () => void = () => {};
|
||||||
@ -68,15 +72,12 @@ export class MovingBlock implements IMovingBlock {
|
|||||||
renderer: IMovingRenderer & IMapRenderer,
|
renderer: IMovingRenderer & IMapRenderer,
|
||||||
index: number,
|
index: number,
|
||||||
layer: IMapLayer,
|
layer: IMapLayer,
|
||||||
block: number | IMaterialFramedData,
|
block: number | IMaterialFramedData
|
||||||
x: number,
|
|
||||||
y: number
|
|
||||||
) {
|
) {
|
||||||
|
super(layer, renderer.vertex, index);
|
||||||
this.renderer = renderer;
|
this.renderer = renderer;
|
||||||
this.index = index;
|
this.index = index;
|
||||||
this.layer = layer;
|
this.layer = layer;
|
||||||
this.x = x;
|
|
||||||
this.y = y;
|
|
||||||
if (typeof block === 'number') {
|
if (typeof block === 'number') {
|
||||||
this.texture = renderer.manager.getTile(block)!;
|
this.texture = renderer.manager.getTile(block)!;
|
||||||
this.tile = block;
|
this.tile = block;
|
||||||
@ -92,6 +93,13 @@ export class MovingBlock implements IMovingBlock {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setPos(x: number, y: number): void {
|
||||||
|
if (!this.end) return;
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.posUpdated = true;
|
||||||
|
}
|
||||||
|
|
||||||
lineTo(
|
lineTo(
|
||||||
x: number,
|
x: number,
|
||||||
y: number,
|
y: number,
|
||||||
@ -167,7 +175,13 @@ export class MovingBlock implements IMovingBlock {
|
|||||||
}
|
}
|
||||||
|
|
||||||
stepMoving(timestamp: number): boolean {
|
stepMoving(timestamp: number): boolean {
|
||||||
if (this.end) return false;
|
if (this.end) {
|
||||||
|
if (this.posUpdated) {
|
||||||
|
this.posUpdated = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
const dt = timestamp - this.startTime;
|
const dt = timestamp - this.startTime;
|
||||||
if (this.line) {
|
if (this.line) {
|
||||||
if (dt > this.time) {
|
if (dt > this.time) {
|
||||||
@ -211,18 +225,6 @@ export class MovingBlock implements IMovingBlock {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
enableFrameAnimate(): void {
|
|
||||||
this.renderer.vertex.enableDynamicFrameAnimate(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
disableFrameAnimate(): void {
|
|
||||||
this.renderer.vertex.disableDynamicFrameAnimate(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
setAlpha(alpha: number): void {
|
|
||||||
this.renderer.vertex.setDynamicAlpha(this, alpha);
|
|
||||||
}
|
|
||||||
|
|
||||||
destroy(): void {
|
destroy(): void {
|
||||||
this.renderer.deleteMoving(this);
|
this.renderer.deleteMoving(this);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,11 +13,11 @@ import {
|
|||||||
ITrackedAssetData
|
ITrackedAssetData
|
||||||
} from '@user/client-base';
|
} from '@user/client-base';
|
||||||
import {
|
import {
|
||||||
|
IBlockStatus,
|
||||||
IContextData,
|
IContextData,
|
||||||
IMapBackgroundConfig,
|
IMapBackgroundConfig,
|
||||||
IMapRenderConfig,
|
IMapRenderConfig,
|
||||||
IMapRenderer,
|
IMapRenderer,
|
||||||
IMapRendererHooks,
|
|
||||||
IMapVertexGenerator,
|
IMapVertexGenerator,
|
||||||
IMapViewportController,
|
IMapViewportController,
|
||||||
IMovingBlock,
|
IMovingBlock,
|
||||||
@ -27,12 +27,7 @@ import {
|
|||||||
MapTileSizeTestMode
|
MapTileSizeTestMode
|
||||||
} from './types';
|
} from './types';
|
||||||
import { ILayerState, ILayerStateHooks, IMapLayer } from '@user/data-state';
|
import { ILayerState, ILayerStateHooks, IMapLayer } from '@user/data-state';
|
||||||
import {
|
import { IHookController, logger } from '@motajs/common';
|
||||||
Hookable,
|
|
||||||
HookController,
|
|
||||||
IHookController,
|
|
||||||
logger
|
|
||||||
} from '@motajs/common';
|
|
||||||
import { compileProgramWith } from '@motajs/client-base';
|
import { compileProgramWith } from '@motajs/client-base';
|
||||||
import { isNil, maxBy } from 'lodash-es';
|
import { isNil, maxBy } from 'lodash-es';
|
||||||
import { IMapDataGetter, MapVertexGenerator } from './vertex';
|
import { IMapDataGetter, MapVertexGenerator } from './vertex';
|
||||||
@ -50,6 +45,7 @@ import {
|
|||||||
import { ITransformUpdatable, Transform } from '@motajs/render-core';
|
import { ITransformUpdatable, Transform } from '@motajs/render-core';
|
||||||
import { MapViewport } from './viewport';
|
import { MapViewport } from './viewport';
|
||||||
import { INSTANCED_COUNT } from './constant';
|
import { INSTANCED_COUNT } from './constant';
|
||||||
|
import { StaticBlockStatus } from './status';
|
||||||
|
|
||||||
const enum BackgroundType {
|
const enum BackgroundType {
|
||||||
Static,
|
Static,
|
||||||
@ -58,7 +54,6 @@ const enum BackgroundType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class MapRenderer
|
export class MapRenderer
|
||||||
extends Hookable<IMapRendererHooks>
|
|
||||||
implements
|
implements
|
||||||
IMapRenderer,
|
IMapRenderer,
|
||||||
IMovingRenderer,
|
IMovingRenderer,
|
||||||
@ -149,13 +144,6 @@ export class MapRenderer
|
|||||||
/** 是否应该更新偏移池 uniform */
|
/** 是否应该更新偏移池 uniform */
|
||||||
private needUpdateOffsetPool: boolean = true;
|
private needUpdateOffsetPool: boolean = true;
|
||||||
|
|
||||||
/** 顶点数组的图层列表是否需要更新 */
|
|
||||||
private layerListDirty: boolean = false;
|
|
||||||
/** 顶点数组的图层的尺寸是否需要更新 */
|
|
||||||
private layerSizeDirty: boolean = false;
|
|
||||||
/** 是否整个地图都需要更新,一般只有在地图尺寸等发生变动时才会执行 */
|
|
||||||
private layerAllDirty: boolean = false;
|
|
||||||
|
|
||||||
/** 所有正在移动的图块 */
|
/** 所有正在移动的图块 */
|
||||||
private movingBlock: Set<IMovingBlock> = new Set();
|
private movingBlock: Set<IMovingBlock> = new Set();
|
||||||
/** 移动图块对象索引池 */
|
/** 移动图块对象索引池 */
|
||||||
@ -178,10 +166,19 @@ export class MapRenderer
|
|||||||
/** 帧动画速率 */
|
/** 帧动画速率 */
|
||||||
private frameSpeed: number = 300;
|
private frameSpeed: number = 300;
|
||||||
|
|
||||||
|
/** 画布元素 */
|
||||||
|
readonly canvas: HTMLCanvasElement;
|
||||||
|
/** 画布 WebGL2 上下文 */
|
||||||
|
readonly gl: WebGL2RenderingContext;
|
||||||
/** 画布上下文数据 */
|
/** 画布上下文数据 */
|
||||||
private contextData: IContextData;
|
private contextData: IContextData;
|
||||||
|
|
||||||
|
/** 地图变换矩阵 */
|
||||||
|
transform: Transform;
|
||||||
/** 是否需要更新变换矩阵 */
|
/** 是否需要更新变换矩阵 */
|
||||||
private needUpdateTransform: boolean = true;
|
private needUpdateTransform: boolean = true;
|
||||||
|
/** 是否需要重新渲染 */
|
||||||
|
private updateRequired: boolean = true;
|
||||||
|
|
||||||
/** 图块动画器 */
|
/** 图块动画器 */
|
||||||
private readonly tileAnimater: ITextureAnimater<number>;
|
private readonly tileAnimater: ITextureAnimater<number>;
|
||||||
@ -198,11 +195,11 @@ export class MapRenderer
|
|||||||
*/
|
*/
|
||||||
constructor(
|
constructor(
|
||||||
readonly manager: IMaterialManager,
|
readonly manager: IMaterialManager,
|
||||||
readonly gl: WebGL2RenderingContext,
|
|
||||||
readonly transform: Transform,
|
|
||||||
layerState: ILayerState
|
layerState: ILayerState
|
||||||
) {
|
) {
|
||||||
super();
|
this.canvas = document.createElement('canvas');
|
||||||
|
this.gl = this.canvas.getContext('webgl2')!;
|
||||||
|
this.transform = new Transform();
|
||||||
this.layerState = layerState;
|
this.layerState = layerState;
|
||||||
this.layerStateHook = layerState.addHook(
|
this.layerStateHook = layerState.addHook(
|
||||||
new RendererLayerStateHook(this)
|
new RendererLayerStateHook(this)
|
||||||
@ -219,12 +216,9 @@ export class MapRenderer
|
|||||||
this.vertex = new MapVertexGenerator(this, data);
|
this.vertex = new MapVertexGenerator(this, data);
|
||||||
this.autotile = new AutotileProcessor(manager);
|
this.autotile = new AutotileProcessor(manager);
|
||||||
this.tick = this.tick.bind(this);
|
this.tick = this.tick.bind(this);
|
||||||
this.transform.bind(this);
|
|
||||||
this.viewport = new MapViewport(this);
|
this.viewport = new MapViewport(this);
|
||||||
this.viewport.bindTransform(this.transform);
|
|
||||||
this.tileAnimater = new TextureColumnAnimater();
|
this.tileAnimater = new TextureColumnAnimater();
|
||||||
this.initVertexPointer(gl, data);
|
this.initVertexPointer(this.gl, data);
|
||||||
layerState.addHook(new RendererLayerStateHook(this));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -268,7 +262,7 @@ export class MapRenderer
|
|||||||
new Float32Array([
|
new Float32Array([
|
||||||
// 左下,右下,左上,右上,前两个是顶点坐标,后两个是纹理坐标
|
// 左下,右下,左上,右上,前两个是顶点坐标,后两个是纹理坐标
|
||||||
// 因为我们已经在数据处理阶段将数据归一化到了 [-1, 1] 的范围,因此顶点坐标应该是 [0, 1] 的范围
|
// 因为我们已经在数据处理阶段将数据归一化到了 [-1, 1] 的范围,因此顶点坐标应该是 [0, 1] 的范围
|
||||||
// 同时又因为我们以左上角为原点,因此纵坐标需要取反
|
// 同时又因为我们以左上角为原点,纵坐标与 WebGL2 相反,因此纵坐标需要取反
|
||||||
0, 0, 0, 0,
|
0, 0, 0, 0,
|
||||||
1, 0, 1, 0,
|
1, 0, 1, 0,
|
||||||
0, -1, 0, 1,
|
0, -1, 0, 1,
|
||||||
@ -297,10 +291,33 @@ export class MapRenderer
|
|||||||
gl.bindVertexArray(null);
|
gl.bindVertexArray(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected createController(
|
//#endregion
|
||||||
hook: Partial<IMapRendererHooks>
|
|
||||||
): IHookController<IMapRendererHooks> {
|
//#region 状态控制
|
||||||
return new HookController(this, hook);
|
|
||||||
|
setTransform(transform: Transform): void {
|
||||||
|
this.transform.bind();
|
||||||
|
this.transform = transform;
|
||||||
|
transform.bind(this);
|
||||||
|
this.viewport.bindTransform(transform);
|
||||||
|
this.needUpdateTransform = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
setCanvasSize(width: number, height: number): void {
|
||||||
|
this.canvas.width = width;
|
||||||
|
this.canvas.height = height;
|
||||||
|
this.updateRequired = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
setViewport(x: number, y: number, width: number, height: number): void {
|
||||||
|
this.gl.viewport(x, y, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
clear(color: boolean, depth: boolean): void {
|
||||||
|
let bit = 0;
|
||||||
|
if (color) bit |= this.gl.COLOR_BUFFER_BIT;
|
||||||
|
if (depth) bit |= this.gl.DEPTH_BUFFER_BIT;
|
||||||
|
if (bit > 0) this.gl.clear(bit);
|
||||||
}
|
}
|
||||||
|
|
||||||
//#endregion
|
//#endregion
|
||||||
@ -315,21 +332,13 @@ export class MapRenderer
|
|||||||
return a.zIndex - b.zIndex;
|
return a.zIndex - b.zIndex;
|
||||||
});
|
});
|
||||||
this.sortedLayers.forEach((v, i) => this.layerIndexMap.set(v, i));
|
this.sortedLayers.forEach((v, i) => this.layerIndexMap.set(v, i));
|
||||||
this.layerListDirty = true;
|
|
||||||
this.requestUpdate();
|
|
||||||
}
|
|
||||||
|
|
||||||
private updateAllLayers() {
|
|
||||||
this.layerState.layerList.forEach(v => {
|
|
||||||
this.vertex.updateArea(v, 0, 0, v.width, v.height);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updateLayerList() {
|
updateLayerList() {
|
||||||
this.sortLayer();
|
this.sortLayer();
|
||||||
this.resizeLayer();
|
this.resizeLayer();
|
||||||
this.layerCount = this.layerState.layerList.size;
|
this.layerCount = this.layerState.layerList.size;
|
||||||
this.layerAllDirty = true;
|
this.vertex.updateLayerArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
setLayerState(layerState: ILayerState): void {
|
setLayerState(layerState: ILayerState): void {
|
||||||
@ -342,7 +351,8 @@ export class MapRenderer
|
|||||||
this.sortLayer();
|
this.sortLayer();
|
||||||
this.resizeLayer();
|
this.resizeLayer();
|
||||||
this.layerCount = layerState.layerList.size;
|
this.layerCount = layerState.layerList.size;
|
||||||
this.layerAllDirty = true;
|
this.vertex.updateLayerArray();
|
||||||
|
this.vertex.resizeMap();
|
||||||
}
|
}
|
||||||
|
|
||||||
getLayer(identifier: string): IMapLayer | null {
|
getLayer(identifier: string): IMapLayer | null {
|
||||||
@ -367,8 +377,8 @@ export class MapRenderer
|
|||||||
resizeLayer() {
|
resizeLayer() {
|
||||||
const maxWidth = maxBy(this.sortedLayers, v => v.width)?.width ?? 0;
|
const maxWidth = maxBy(this.sortedLayers, v => v.width)?.width ?? 0;
|
||||||
const maxHeight = maxBy(this.sortedLayers, v => v.height)?.height ?? 0;
|
const maxHeight = maxBy(this.sortedLayers, v => v.height)?.height ?? 0;
|
||||||
if (this.mapWidth !== maxWidth || this.mapHeight !== maxHeight) {
|
if (this.mapWidth === maxWidth && this.mapHeight === maxHeight) {
|
||||||
this.layerSizeDirty = true;
|
return;
|
||||||
}
|
}
|
||||||
this.mapWidth = maxWidth;
|
this.mapWidth = maxWidth;
|
||||||
this.mapHeight = maxHeight;
|
this.mapHeight = maxHeight;
|
||||||
@ -378,8 +388,7 @@ export class MapRenderer
|
|||||||
this.contextData.backgroundWidth,
|
this.contextData.backgroundWidth,
|
||||||
this.contextData.backgroundHeight
|
this.contextData.backgroundHeight
|
||||||
);
|
);
|
||||||
this.layerAllDirty = true;
|
this.vertex.resizeMap();
|
||||||
this.requestUpdate();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//#endregion
|
//#endregion
|
||||||
@ -420,25 +429,24 @@ export class MapRenderer
|
|||||||
}
|
}
|
||||||
|
|
||||||
configBackground(config: Partial<IMapBackgroundConfig>): void {
|
configBackground(config: Partial<IMapBackgroundConfig>): void {
|
||||||
let needUpdate = false;
|
|
||||||
if (!isNil(config.renderWidth)) {
|
if (!isNil(config.renderWidth)) {
|
||||||
needUpdate = true;
|
this.updateRequired = true;
|
||||||
this.backRenderWidth = config.renderWidth;
|
this.backRenderWidth = config.renderWidth;
|
||||||
}
|
}
|
||||||
if (!isNil(config.renderHeight)) {
|
if (!isNil(config.renderHeight)) {
|
||||||
needUpdate = true;
|
this.updateRequired = true;
|
||||||
this.backRenderHeight = config.renderHeight;
|
this.backRenderHeight = config.renderHeight;
|
||||||
}
|
}
|
||||||
if (!isNil(config.repeatX)) {
|
if (!isNil(config.repeatX)) {
|
||||||
needUpdate = true;
|
this.updateRequired = true;
|
||||||
this.backRepeatModeX = config.repeatX;
|
this.backRepeatModeX = config.repeatX;
|
||||||
}
|
}
|
||||||
if (!isNil(config.repeatY)) {
|
if (!isNil(config.repeatY)) {
|
||||||
needUpdate = true;
|
this.updateRequired = true;
|
||||||
this.backRepeatModeY = config.repeatY;
|
this.backRepeatModeY = config.repeatY;
|
||||||
}
|
}
|
||||||
if (!isNil(config.useImageSize)) {
|
if (!isNil(config.useImageSize)) {
|
||||||
needUpdate = true;
|
this.updateRequired = true;
|
||||||
this.backUseImageSize = config.useImageSize;
|
this.backUseImageSize = config.useImageSize;
|
||||||
}
|
}
|
||||||
if (!isNil(config.frameSpeed)) {
|
if (!isNil(config.frameSpeed)) {
|
||||||
@ -450,9 +458,6 @@ export class MapRenderer
|
|||||||
this.contextData.backgroundWidth,
|
this.contextData.backgroundWidth,
|
||||||
this.contextData.backgroundHeight
|
this.contextData.backgroundHeight
|
||||||
);
|
);
|
||||||
if (needUpdate) {
|
|
||||||
this.requestUpdate();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getBackgroundConfig(): Readonly<IMapBackgroundConfig> {
|
getBackgroundConfig(): Readonly<IMapBackgroundConfig> {
|
||||||
@ -483,7 +488,6 @@ export class MapRenderer
|
|||||||
this.sortedLayers.forEach(v => {
|
this.sortedLayers.forEach(v => {
|
||||||
this.vertex.updateArea(v, 0, 0, this.mapWidth, this.mapHeight);
|
this.vertex.updateArea(v, 0, 0, this.mapWidth, this.mapHeight);
|
||||||
});
|
});
|
||||||
this.requestUpdate();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setCellSize(width: number, height: number): void {
|
setCellSize(width: number, height: number): void {
|
||||||
@ -492,37 +496,32 @@ export class MapRenderer
|
|||||||
this.sortedLayers.forEach(v => {
|
this.sortedLayers.forEach(v => {
|
||||||
this.vertex.updateArea(v, 0, 0, this.mapWidth, this.mapHeight);
|
this.vertex.updateArea(v, 0, 0, this.mapWidth, this.mapHeight);
|
||||||
});
|
});
|
||||||
this.requestUpdate();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
configRendering(config: Partial<IMapRenderConfig>): void {
|
configRendering(config: Partial<IMapRenderConfig>): void {
|
||||||
let needUpdate = false;
|
|
||||||
if (!isNil(config.minBehavior)) {
|
if (!isNil(config.minBehavior)) {
|
||||||
this.tileMinifyBehavior = config.minBehavior;
|
this.tileMinifyBehavior = config.minBehavior;
|
||||||
needUpdate = true;
|
this.updateRequired = true;
|
||||||
}
|
}
|
||||||
if (!isNil(config.magBehavior)) {
|
if (!isNil(config.magBehavior)) {
|
||||||
this.tileMagnifyBehavior = config.magBehavior;
|
this.tileMagnifyBehavior = config.magBehavior;
|
||||||
needUpdate = true;
|
this.updateRequired = true;
|
||||||
}
|
}
|
||||||
if (!isNil(config.tileAlignX)) {
|
if (!isNil(config.tileAlignX)) {
|
||||||
this.tileAlignX = config.tileAlignX;
|
this.tileAlignX = config.tileAlignX;
|
||||||
needUpdate = true;
|
this.updateRequired = true;
|
||||||
}
|
}
|
||||||
if (!isNil(config.tileAlignY)) {
|
if (!isNil(config.tileAlignY)) {
|
||||||
this.tileAlignY = config.tileAlignY;
|
this.tileAlignY = config.tileAlignY;
|
||||||
needUpdate = true;
|
this.updateRequired = true;
|
||||||
}
|
}
|
||||||
if (!isNil(config.tileTestMode)) {
|
if (!isNil(config.tileTestMode)) {
|
||||||
this.tileTestMode = config.tileTestMode;
|
this.tileTestMode = config.tileTestMode;
|
||||||
needUpdate = true;
|
this.updateRequired = true;
|
||||||
}
|
}
|
||||||
if (!isNil(config.frameSpeed)) {
|
if (!isNil(config.frameSpeed)) {
|
||||||
this.frameSpeed = config.frameSpeed;
|
this.frameSpeed = config.frameSpeed;
|
||||||
}
|
}
|
||||||
if (needUpdate) {
|
|
||||||
this.requestUpdate();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getRenderingConfig(): Readonly<IMapRenderConfig> {
|
getRenderingConfig(): Readonly<IMapRenderConfig> {
|
||||||
@ -536,10 +535,6 @@ export class MapRenderer
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
getTransform(): Transform {
|
|
||||||
return this.transform;
|
|
||||||
}
|
|
||||||
|
|
||||||
private getOffsetPool(): number[] {
|
private getOffsetPool(): number[] {
|
||||||
const pool = new Set([32]);
|
const pool = new Set([32]);
|
||||||
// 其他的都是 bigImage 了,直接遍历获取
|
// 其他的都是 bigImage 了,直接遍历获取
|
||||||
@ -1196,15 +1191,15 @@ export class MapRenderer
|
|||||||
);
|
);
|
||||||
this.backgroundPending = false;
|
this.backgroundPending = false;
|
||||||
gl.bindTexture(gl.TEXTURE_2D_ARRAY, null);
|
gl.bindTexture(gl.TEXTURE_2D_ARRAY, null);
|
||||||
this.requestUpdate();
|
this.updateRequired = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
render(): void {
|
render(): HTMLCanvasElement {
|
||||||
const gl = this.gl;
|
const gl = this.gl;
|
||||||
const data = this.contextData;
|
const data = this.contextData;
|
||||||
if (!this.assetData) {
|
if (!this.assetData) {
|
||||||
logger.error(31);
|
logger.error(31);
|
||||||
return;
|
return this.canvas;
|
||||||
}
|
}
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@ -1227,18 +1222,6 @@ export class MapRenderer
|
|||||||
} = data;
|
} = data;
|
||||||
|
|
||||||
// 图层检查
|
// 图层检查
|
||||||
if (this.layerSizeDirty) {
|
|
||||||
this.vertex.resizeMap();
|
|
||||||
this.layerSizeDirty = false;
|
|
||||||
}
|
|
||||||
if (this.layerListDirty) {
|
|
||||||
this.vertex.updateLayerArray();
|
|
||||||
this.layerListDirty = false;
|
|
||||||
}
|
|
||||||
if (this.layerAllDirty) {
|
|
||||||
this.updateAllLayers();
|
|
||||||
this.layerAllDirty = false;
|
|
||||||
}
|
|
||||||
this.vertex.checkRebuild();
|
this.vertex.checkRebuild();
|
||||||
|
|
||||||
// 数据检查
|
// 数据检查
|
||||||
@ -1272,7 +1255,6 @@ export class MapRenderer
|
|||||||
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
||||||
gl.useProgram(backProgram);
|
gl.useProgram(backProgram);
|
||||||
if (this.needUpdateBackgroundFrame) {
|
if (this.needUpdateBackgroundFrame) {
|
||||||
this.needUpdateBackgroundFrame = false;
|
|
||||||
gl.uniform1f(backNowFrameLocation, this.backgroundFrame);
|
gl.uniform1f(backNowFrameLocation, this.backgroundFrame);
|
||||||
}
|
}
|
||||||
if (this.needUpdateTransform) {
|
if (this.needUpdateTransform) {
|
||||||
@ -1290,11 +1272,9 @@ export class MapRenderer
|
|||||||
// 图块
|
// 图块
|
||||||
gl.useProgram(tileProgram);
|
gl.useProgram(tileProgram);
|
||||||
if (this.needUpdateOffsetPool) {
|
if (this.needUpdateOffsetPool) {
|
||||||
this.needUpdateOffsetPool = false;
|
|
||||||
gl.uniform1fv(offsetPoolLocation, this.normalizedOffsetPool);
|
gl.uniform1fv(offsetPoolLocation, this.normalizedOffsetPool);
|
||||||
}
|
}
|
||||||
if (this.needUpdateFrameCounter) {
|
if (this.needUpdateFrameCounter) {
|
||||||
this.needUpdateFrameCounter = false;
|
|
||||||
gl.uniform1f(nowFrameLocation, this.frameCounter);
|
gl.uniform1f(nowFrameLocation, this.frameCounter);
|
||||||
}
|
}
|
||||||
if (this.needUpdateTransform) {
|
if (this.needUpdateTransform) {
|
||||||
@ -1304,7 +1284,6 @@ export class MapRenderer
|
|||||||
this.transform.mat
|
this.transform.mat
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
this.needUpdateTransform = false;
|
|
||||||
gl.bindTexture(gl.TEXTURE_2D_ARRAY, tileTexture);
|
gl.bindTexture(gl.TEXTURE_2D_ARRAY, tileTexture);
|
||||||
gl.bindVertexArray(tileVAO);
|
gl.bindVertexArray(tileVAO);
|
||||||
|
|
||||||
@ -1325,6 +1304,16 @@ export class MapRenderer
|
|||||||
});
|
});
|
||||||
gl.bindVertexArray(null);
|
gl.bindVertexArray(null);
|
||||||
gl.bindTexture(gl.TEXTURE_2D_ARRAY, null);
|
gl.bindTexture(gl.TEXTURE_2D_ARRAY, null);
|
||||||
|
|
||||||
|
// 清空更新状态标识
|
||||||
|
this.updateRequired = false;
|
||||||
|
this.needUpdateFrameCounter = false;
|
||||||
|
this.needUpdateBackgroundFrame = false;
|
||||||
|
this.needUpdateTransform = false;
|
||||||
|
this.needUpdateOffsetPool = false;
|
||||||
|
this.vertex.renderDynamic();
|
||||||
|
|
||||||
|
return this.canvas;
|
||||||
}
|
}
|
||||||
|
|
||||||
//#endregion
|
//#endregion
|
||||||
@ -1347,7 +1336,7 @@ export class MapRenderer
|
|||||||
h: number
|
h: number
|
||||||
) {
|
) {
|
||||||
this.vertex.updateArea(layer, x, y, w, h);
|
this.vertex.updateArea(layer, x, y, w, h);
|
||||||
this.requestUpdate();
|
this.updateRequired = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1359,7 +1348,18 @@ export class MapRenderer
|
|||||||
*/
|
*/
|
||||||
updateLayerBlock(layer: IMapLayer, block: number, x: number, y: number) {
|
updateLayerBlock(layer: IMapLayer, block: number, x: number, y: number) {
|
||||||
this.vertex.updateBlock(layer, block, x, y);
|
this.vertex.updateBlock(layer, block, x, y);
|
||||||
this.requestUpdate();
|
this.updateRequired = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
getBlockStatus(
|
||||||
|
layer: IMapLayer,
|
||||||
|
x: number,
|
||||||
|
y: number
|
||||||
|
): IBlockStatus | null {
|
||||||
|
if (x < 0 || y < 0 || x > this.mapWidth || y > this.mapHeight) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new StaticBlockStatus(layer, this.vertex, x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
//#endregion
|
//#endregion
|
||||||
@ -1385,22 +1385,10 @@ export class MapRenderer
|
|||||||
private reduceMoving() {
|
private reduceMoving() {
|
||||||
const half = Math.round(this.movingCount / 2);
|
const half = Math.round(this.movingCount / 2);
|
||||||
if (half < DYNAMIC_RESERVE) return;
|
if (half < DYNAMIC_RESERVE) return;
|
||||||
if (this.movingIndexPool.length < half) return;
|
for (const moving of this.movingBlock) {
|
||||||
const needMap: number[] = [];
|
if (moving.index >= half) return;
|
||||||
const restPool: number[] = [];
|
}
|
||||||
this.movingIndexPool.forEach(v => {
|
this.vertex.reduceMoving(half);
|
||||||
if (v < half) restPool.push(v);
|
|
||||||
else needMap.push(v);
|
|
||||||
});
|
|
||||||
// 这个判断理论上不可能成立,但是还是判断下吧
|
|
||||||
if (needMap.length > restPool.length) return;
|
|
||||||
const map = new Map<number, number>();
|
|
||||||
needMap.forEach(v => {
|
|
||||||
const item = restPool.pop()!;
|
|
||||||
map.set(v, item);
|
|
||||||
});
|
|
||||||
this.vertex.reduceMoving(half, map);
|
|
||||||
this.requestUpdate();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1436,11 +1424,11 @@ export class MapRenderer
|
|||||||
y: number
|
y: number
|
||||||
): IMovingBlock {
|
): IMovingBlock {
|
||||||
const index = this.requireMovingIndex();
|
const index = this.requireMovingIndex();
|
||||||
const moving = new MovingBlock(this, index, layer, block, x, y);
|
const moving = new MovingBlock(this, index, layer, block);
|
||||||
|
moving.setPos(x, y);
|
||||||
this.movingBlock.add(moving);
|
this.movingBlock.add(moving);
|
||||||
this.movingIndexMap.set(index, moving);
|
this.movingIndexMap.set(index, moving);
|
||||||
this.vertex.updateMoving(moving, true);
|
this.vertex.updateMoving(moving, true);
|
||||||
this.requestUpdate();
|
|
||||||
return moving;
|
return moving;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1457,7 +1445,6 @@ export class MapRenderer
|
|||||||
this.movingBlock.delete(block);
|
this.movingBlock.delete(block);
|
||||||
this.movingIndexMap.delete(block.index);
|
this.movingIndexMap.delete(block.index);
|
||||||
this.vertex.deleteMoving(block);
|
this.vertex.deleteMoving(block);
|
||||||
this.requestUpdate();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hasMoving(moving: IMovingBlock): boolean {
|
hasMoving(moving: IMovingBlock): boolean {
|
||||||
@ -1466,44 +1453,20 @@ export class MapRenderer
|
|||||||
|
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
//#region 图块配置
|
|
||||||
|
|
||||||
enableTileFrameAnimate(layer: IMapLayer, x: number, y: number): void {
|
|
||||||
this.vertex.enableStaticFrameAnimate(layer, x, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
disableTileFrameAnimate(layer: IMapLayer, x: number, y: number): void {
|
|
||||||
this.vertex.disableStaticFrameAnimate(layer, x, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
setTileAlpha(layer: IMapLayer, alpha: number, x: number, y: number): void {
|
|
||||||
this.vertex.setStaticAlpha(layer, alpha, x, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
//#endregion
|
|
||||||
|
|
||||||
//#region 其他方法
|
//#region 其他方法
|
||||||
|
|
||||||
private requestUpdate() {
|
|
||||||
this.forEachHook((hook, controller) => {
|
|
||||||
hook.onUpdate?.(controller);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
getTimestamp(): number {
|
getTimestamp(): number {
|
||||||
return this.timestamp;
|
return this.timestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
tick(timestamp: number) {
|
tick(timestamp: number) {
|
||||||
this.timestamp = timestamp;
|
this.timestamp = timestamp;
|
||||||
let update = false;
|
|
||||||
|
|
||||||
// 移动数组
|
// 移动数组
|
||||||
const expandDT = timestamp - this.lastExpandTime;
|
const expandDT = timestamp - this.lastExpandTime;
|
||||||
if (expandDT > MOVING_TOLERANCE * 1000) {
|
if (expandDT > MOVING_TOLERANCE * 1000) {
|
||||||
this.reduceMoving();
|
this.reduceMoving();
|
||||||
this.lastExpandTime = timestamp;
|
this.lastExpandTime = timestamp;
|
||||||
update = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 背景
|
// 背景
|
||||||
@ -1513,7 +1476,6 @@ export class MapRenderer
|
|||||||
this.backgroundFrame %= this.backgroundFrameCount;
|
this.backgroundFrame %= this.backgroundFrameCount;
|
||||||
this.backLastFrame = timestamp;
|
this.backLastFrame = timestamp;
|
||||||
this.needUpdateBackgroundFrame = true;
|
this.needUpdateBackgroundFrame = true;
|
||||||
update = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 地图帧动画
|
// 地图帧动画
|
||||||
@ -1522,7 +1484,6 @@ export class MapRenderer
|
|||||||
this.lastFrameTime = timestamp;
|
this.lastFrameTime = timestamp;
|
||||||
this.frameCounter++;
|
this.frameCounter++;
|
||||||
this.needUpdateFrameCounter = true;
|
this.needUpdateFrameCounter = true;
|
||||||
update = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 图块移动
|
// 图块移动
|
||||||
@ -1533,17 +1494,22 @@ export class MapRenderer
|
|||||||
if (move) toUpdate.push(v);
|
if (move) toUpdate.push(v);
|
||||||
});
|
});
|
||||||
this.vertex.updateMovingList(toUpdate, false);
|
this.vertex.updateMovingList(toUpdate, false);
|
||||||
update = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (update) {
|
|
||||||
this.requestUpdate();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateTransform(): void {
|
updateTransform(): void {
|
||||||
this.needUpdateTransform = true;
|
this.needUpdateTransform = true;
|
||||||
this.requestUpdate();
|
}
|
||||||
|
|
||||||
|
needUpdate(): boolean {
|
||||||
|
return (
|
||||||
|
this.updateRequired ||
|
||||||
|
this.needUpdateFrameCounter ||
|
||||||
|
this.needUpdateBackgroundFrame ||
|
||||||
|
this.needUpdateTransform ||
|
||||||
|
this.vertex.dynamicRenderDirty ||
|
||||||
|
this.needUpdateOffsetPool
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|||||||
70
packages-user/client-modules/src/render/map/status.ts
Normal file
70
packages-user/client-modules/src/render/map/status.ts
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
import { IMapLayer } from '@user/data-state';
|
||||||
|
import { IBlockStatus, IMapVertexStatus } from './types';
|
||||||
|
|
||||||
|
export class StaticBlockStatus implements IBlockStatus {
|
||||||
|
/**
|
||||||
|
* @param layer 图层对象
|
||||||
|
* @param vertex 顶点数组生成器对象
|
||||||
|
* @param x 图块横坐标
|
||||||
|
* @param y 图块纵坐标
|
||||||
|
*/
|
||||||
|
constructor(
|
||||||
|
readonly layer: IMapLayer,
|
||||||
|
readonly vertex: IMapVertexStatus,
|
||||||
|
readonly x: number,
|
||||||
|
readonly y: number
|
||||||
|
) {}
|
||||||
|
|
||||||
|
setAlpha(alpha: number): void {
|
||||||
|
this.vertex.setStaticAlpha(this.layer, alpha, this.x, this.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
getAlpha(): number {
|
||||||
|
return this.vertex.getStaticAlpha(this.layer, this.x, this.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
useGlobalFrame(): void {
|
||||||
|
this.vertex.setStaticFrame(this.layer, this.x, this.y, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
useSpecifiedFrame(frame: number): void {
|
||||||
|
this.vertex.setStaticFrame(this.layer, this.x, this.y, frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
getFrame(): number {
|
||||||
|
return this.vertex.getStaticFrame(this.layer, this.x, this.y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DynamicBlockStatus implements IBlockStatus {
|
||||||
|
/**
|
||||||
|
* @param layer 图层对象
|
||||||
|
* @param vertex 顶点数组生成器对象
|
||||||
|
* @param index 图块索引
|
||||||
|
*/
|
||||||
|
constructor(
|
||||||
|
readonly layer: IMapLayer,
|
||||||
|
readonly vertex: IMapVertexStatus,
|
||||||
|
readonly index: number
|
||||||
|
) {}
|
||||||
|
|
||||||
|
setAlpha(alpha: number): void {
|
||||||
|
this.vertex.setDynamicAlpha(this.index, alpha);
|
||||||
|
}
|
||||||
|
|
||||||
|
getAlpha(): number {
|
||||||
|
return this.vertex.getDynamicAlpha(this.index);
|
||||||
|
}
|
||||||
|
|
||||||
|
useGlobalFrame(): void {
|
||||||
|
this.vertex.setDynamicFrame(this.index, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
useSpecifiedFrame(frame: number): void {
|
||||||
|
this.vertex.setDynamicFrame(this.index, frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
getFrame(): number {
|
||||||
|
return this.vertex.getDynamicFrame(this.index);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,10 +1,4 @@
|
|||||||
import {
|
import { IDirtyMark, IDirtyTracker } from '@motajs/common';
|
||||||
IDirtyMark,
|
|
||||||
IDirtyTracker,
|
|
||||||
IHookable,
|
|
||||||
IHookBase,
|
|
||||||
IHookController
|
|
||||||
} from '@motajs/common';
|
|
||||||
import { ITextureRenderable } from '@motajs/render-assets';
|
import { ITextureRenderable } from '@motajs/render-assets';
|
||||||
import { Transform } from '@motajs/render-core';
|
import { Transform } from '@motajs/render-core';
|
||||||
import {
|
import {
|
||||||
@ -153,7 +147,39 @@ export interface IContextData {
|
|||||||
vertexMark: IDirtyMark;
|
vertexMark: IDirtyMark;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IMovingBlock {
|
export interface IBlockStatus {
|
||||||
|
/** 图块所属图层 */
|
||||||
|
readonly layer: IMapLayer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置图块的不透明度
|
||||||
|
* @param alpha 图块不透明度
|
||||||
|
*/
|
||||||
|
setAlpha(alpha: number): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取图块的不透明度
|
||||||
|
*/
|
||||||
|
getAlpha(): number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用全局帧动画
|
||||||
|
*/
|
||||||
|
useGlobalFrame(): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用指定的动画帧数,传入第几帧图块就画第几帧,超过最大帧数会自动取模
|
||||||
|
* @param frame 第几帧
|
||||||
|
*/
|
||||||
|
useSpecifiedFrame(frame: number): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取动画帧数,-1 表示使用全局帧动画,非负整数表示图块是第几帧
|
||||||
|
*/
|
||||||
|
getFrame(): number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IMovingBlock extends IBlockStatus {
|
||||||
/** 移动图块的索引 */
|
/** 移动图块的索引 */
|
||||||
readonly index: number;
|
readonly index: number;
|
||||||
/** 图块数字 */
|
/** 图块数字 */
|
||||||
@ -164,8 +190,13 @@ export interface IMovingBlock {
|
|||||||
readonly y: number;
|
readonly y: number;
|
||||||
/** 图块使用的纹理 */
|
/** 图块使用的纹理 */
|
||||||
readonly texture: IMaterialFramedData;
|
readonly texture: IMaterialFramedData;
|
||||||
/** 该图块所属的图层 */
|
|
||||||
readonly layer: IMapLayer;
|
/**
|
||||||
|
* 直接设置图块的位置,动画中设置无效
|
||||||
|
* @param x 目标横坐标
|
||||||
|
* @param y 目标纵坐标
|
||||||
|
*/
|
||||||
|
setPos(x: number, y: number): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 沿直线移动到目标点
|
* 沿直线移动到目标点
|
||||||
@ -198,22 +229,6 @@ export interface IMovingBlock {
|
|||||||
timing?: TimingFn
|
timing?: TimingFn
|
||||||
): Promise<this>;
|
): Promise<this>;
|
||||||
|
|
||||||
/**
|
|
||||||
* 启用此图块的帧动画
|
|
||||||
*/
|
|
||||||
enableFrameAnimate(): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 禁用此图块的帧动画
|
|
||||||
*/
|
|
||||||
disableFrameAnimate(): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置此图块的不透明度
|
|
||||||
* @param alpha 不透明度
|
|
||||||
*/
|
|
||||||
setAlpha(alpha: number): void;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 进行一步动画移动效果
|
* 进行一步动画移动效果
|
||||||
* @param timestamp 当前时间戳
|
* @param timestamp 当前时间戳
|
||||||
@ -227,14 +242,7 @@ export interface IMovingBlock {
|
|||||||
destroy(): void;
|
destroy(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IMapRendererHooks extends IHookBase {
|
export interface IMapRenderer {
|
||||||
/**
|
|
||||||
* 当需要更新画面时执行
|
|
||||||
*/
|
|
||||||
onUpdate(controller: IHookController<this>): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IMapRenderer extends IHookable<IMapRendererHooks> {
|
|
||||||
/** 地图渲染器使用的资源管理器 */
|
/** 地图渲染器使用的资源管理器 */
|
||||||
readonly manager: IMaterialManager;
|
readonly manager: IMaterialManager;
|
||||||
/** 画布渲染上下文 */
|
/** 画布渲染上下文 */
|
||||||
@ -282,10 +290,38 @@ export interface IMapRenderer extends IHookable<IMapRendererHooks> {
|
|||||||
destroy(): void;
|
destroy(): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 渲染至目标画布
|
* 渲染地图
|
||||||
* @param gl 渲染至的上下文
|
|
||||||
*/
|
*/
|
||||||
render(gl: WebGL2RenderingContext): void;
|
render(): HTMLCanvasElement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置地图的变换矩阵
|
||||||
|
* @param transform 变换矩阵
|
||||||
|
*/
|
||||||
|
setTransform(transform: Transform): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置画布尺寸
|
||||||
|
* @param width 画布宽度
|
||||||
|
* @param height 画布高度
|
||||||
|
*/
|
||||||
|
setCanvasSize(width: number, height: number): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置渲染区域,等于 `gl.viewport`
|
||||||
|
* @param x 左上角横坐标
|
||||||
|
* @param y 左上角纵坐标
|
||||||
|
* @param width 区域宽度
|
||||||
|
* @param height 区域高度
|
||||||
|
*/
|
||||||
|
setViewport(x: number, y: number, width: number, height: number): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清空画布缓冲区
|
||||||
|
* @param color 是否清空颜色缓冲区
|
||||||
|
* @param depth 是否清空深度缓冲区
|
||||||
|
*/
|
||||||
|
clear(color: boolean, depth: boolean): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置渲染器使用的地图状态
|
* 设置渲染器使用的地图状态
|
||||||
@ -402,36 +438,24 @@ export interface IMapRenderer extends IHookable<IMapRendererHooks> {
|
|||||||
*/
|
*/
|
||||||
getMovingBlockByIndex(index: number): Readonly<IMovingBlock> | null;
|
getMovingBlockByIndex(index: number): Readonly<IMovingBlock> | null;
|
||||||
|
|
||||||
/**
|
|
||||||
* 启用指定图块的帧动画效果
|
|
||||||
* @param layer 图层
|
|
||||||
* @param x 图块横坐标
|
|
||||||
* @param y 图块纵坐标
|
|
||||||
*/
|
|
||||||
enableTileFrameAnimate(layer: IMapLayer, x: number, y: number): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 禁用指定图块的帧动画效果
|
|
||||||
* @param layer 图层
|
|
||||||
* @param x 图块横坐标
|
|
||||||
* @param y 图块纵坐标
|
|
||||||
*/
|
|
||||||
disableTileFrameAnimate(layer: IMapLayer, x: number, y: number): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置指定图块的不透明度
|
|
||||||
* @param layer 图层
|
|
||||||
* @param alpha 图块不透明度
|
|
||||||
* @param x 图块横坐标
|
|
||||||
* @param y 图块纵坐标
|
|
||||||
*/
|
|
||||||
setTileAlpha(layer: IMapLayer, alpha: number, x: number, y: number): void;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 进行一帧更新
|
* 进行一帧更新
|
||||||
* @param timestamp 时间戳
|
* @param timestamp 时间戳
|
||||||
*/
|
*/
|
||||||
tick(timestamp: number): void;
|
tick(timestamp: number): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取指定图层的指定图块的状态信息,可以设置与获取图块状态。多次调用的返回值不同引用。
|
||||||
|
* @param layer 图层对象
|
||||||
|
* @param x 图块横坐标
|
||||||
|
* @param y 图块纵坐标
|
||||||
|
*/
|
||||||
|
getBlockStatus(layer: IMapLayer, x: number, y: number): IBlockStatus | null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当前地图状态是否发生改变,需要更新
|
||||||
|
*/
|
||||||
|
needUpdate(): boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IMapVertexArray {
|
export interface IMapVertexArray {
|
||||||
@ -710,11 +734,11 @@ export interface IMapVertexBlock extends IMapVertexData {
|
|||||||
readonly dirty: boolean;
|
readonly dirty: boolean;
|
||||||
/** 渲染是否需要更新 */
|
/** 渲染是否需要更新 */
|
||||||
readonly renderDirty: boolean;
|
readonly renderDirty: boolean;
|
||||||
/** 起始索引,即第一个元素索引 */
|
/** 起始索引,即第一个元素索引,以实例为单位 */
|
||||||
readonly startIndex: number;
|
readonly startIndex: number;
|
||||||
/** 终止索引,即最后一个元素索引+1 */
|
/** 终止索引,即最后一个元素索引+1,以实例为单位 */
|
||||||
readonly endIndex: number;
|
readonly endIndex: number;
|
||||||
/** 元素数量,即终止索引-起始索引 */
|
/** 元素数量,即终止索引-起始索引,以实例为单位 */
|
||||||
readonly count: number;
|
readonly count: number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -771,10 +795,74 @@ export interface IMapBlockUpdateObject {
|
|||||||
readonly y: number;
|
readonly y: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IMapVertexStatus {
|
||||||
|
/**
|
||||||
|
* 设置图块的不透明度
|
||||||
|
* @param layer 图块所属图层
|
||||||
|
* @param x 图块横坐标
|
||||||
|
* @param y 图块纵坐标
|
||||||
|
* @param alpha 目标不透明度
|
||||||
|
*/
|
||||||
|
setStaticAlpha(layer: IMapLayer, x: number, y: number, alpha: number): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置图块显示第几帧
|
||||||
|
* @param layer 图块所属图层
|
||||||
|
* @param x 图块横坐标
|
||||||
|
* @param y 图块纵坐标
|
||||||
|
* @param frame 图块的帧数,-1 表示使用全局帧数,非负整数表示画第几帧,超出最大帧数会自动取余
|
||||||
|
*/
|
||||||
|
setStaticFrame(layer: IMapLayer, x: number, y: number, frame: number): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置移动图块的不透明度
|
||||||
|
* @param index 移动图块索引
|
||||||
|
* @param alpha 目标不透明度
|
||||||
|
*/
|
||||||
|
setDynamicAlpha(index: number, alpha: number): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置移动图块显示第几帧
|
||||||
|
* @param index 移动图块索引
|
||||||
|
* @param frame 图块的帧数,-1 表示使用全局帧数,非负整数表示画第几帧,超出最大帧数会自动取余
|
||||||
|
*/
|
||||||
|
setDynamicFrame(index: number, frame: number): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取指定位置图块的不透明度,如果图块不在地图内则返回 0
|
||||||
|
* @param layer 图块所属图层
|
||||||
|
* @param x 图块横坐标
|
||||||
|
* @param y 图块纵坐标
|
||||||
|
*/
|
||||||
|
getStaticAlpha(layer: IMapLayer, x: number, y: number): number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取指定位置图块的帧数,-1 表示使用全局帧数,非负整数表示当前第几帧,不会超出最大帧数,如果图块不在地图内则返回 -1
|
||||||
|
* @param layer 图块所属图层
|
||||||
|
* @param x 图块横坐标
|
||||||
|
* @param y 图块纵坐标
|
||||||
|
*/
|
||||||
|
getStaticFrame(layer: IMapLayer, x: number, y: number): number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取移动图块的不透明度
|
||||||
|
* @param index 移动图块索引
|
||||||
|
*/
|
||||||
|
getDynamicAlpha(index: number): number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取移动图块的当前帧数,-1 表示使用全局帧数,非负整数表示当前第几帧
|
||||||
|
* @param index 移动图块索引
|
||||||
|
*/
|
||||||
|
getDynamicFrame(index: number): number;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 脏标记表示顶点数组的长度是否发生变化
|
* 脏标记表示顶点数组的长度是否发生变化
|
||||||
*/
|
*/
|
||||||
export interface IMapVertexGenerator extends IDirtyTracker<boolean> {
|
export interface IMapVertexGenerator
|
||||||
|
extends IDirtyTracker<boolean>,
|
||||||
|
IMapVertexStatus {
|
||||||
/** 地图渲染器 */
|
/** 地图渲染器 */
|
||||||
readonly renderer: IMapRenderer;
|
readonly renderer: IMapRenderer;
|
||||||
/** 地图分块 */
|
/** 地图分块 */
|
||||||
@ -782,8 +870,13 @@ export interface IMapVertexGenerator extends IDirtyTracker<boolean> {
|
|||||||
/** 动态部分是否需要更新渲染缓冲区 */
|
/** 动态部分是否需要更新渲染缓冲区 */
|
||||||
readonly dynamicRenderDirty: boolean;
|
readonly dynamicRenderDirty: boolean;
|
||||||
|
|
||||||
|
/** 动态内容起始索引,以实例为单位 */
|
||||||
|
readonly dynamicStart: number;
|
||||||
|
/** 动态内容数量,以实例为单位 */
|
||||||
|
readonly dynamicCount: number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 取消渲染的脏标记
|
* 取消动态内容渲染的脏标记
|
||||||
*/
|
*/
|
||||||
renderDynamic(): void;
|
renderDynamic(): void;
|
||||||
|
|
||||||
@ -809,10 +902,8 @@ export interface IMapVertexGenerator extends IDirtyTracker<boolean> {
|
|||||||
/**
|
/**
|
||||||
* 缩小移动图块数组尺寸
|
* 缩小移动图块数组尺寸
|
||||||
* @param targetSize 目标大小
|
* @param targetSize 目标大小
|
||||||
* @param indexMap 索引映射。由于缩小尺寸后,有可能有一些移动的图块位于较高的索引,
|
|
||||||
* 因此需要一个映射来指定之前的那些移动图块现在应该在哪个索引。
|
|
||||||
*/
|
*/
|
||||||
reduceMoving(targetSize: number, indexMap: Map<number, number>): void;
|
reduceMoving(targetSize: number): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新图层数组
|
* 更新图层数组
|
||||||
@ -886,59 +977,15 @@ export interface IMapVertexGenerator extends IDirtyTracker<boolean> {
|
|||||||
* @param moving 移动图块对象
|
* @param moving 移动图块对象
|
||||||
*/
|
*/
|
||||||
deleteMoving(moving: IMovingBlock): void;
|
deleteMoving(moving: IMovingBlock): void;
|
||||||
|
|
||||||
/**
|
|
||||||
* 启用静态内容的帧动画
|
|
||||||
* @param layer 图层索引
|
|
||||||
* @param x 图块横坐标
|
|
||||||
* @param y 图块纵坐标
|
|
||||||
*/
|
|
||||||
enableStaticFrameAnimate(layer: IMapLayer, x: number, y: number): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 禁用静态内容的帧动画
|
|
||||||
* @param layer 图层索引
|
|
||||||
* @param x 图块横坐标
|
|
||||||
* @param y 图块纵坐标
|
|
||||||
*/
|
|
||||||
disableStaticFrameAnimate(layer: IMapLayer, x: number, y: number): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置静态内容的不透明度
|
|
||||||
* @param layer 图层索引
|
|
||||||
* @param alpha 图块不透明度
|
|
||||||
* @param x 图块横坐标
|
|
||||||
* @param y 图块纵坐标
|
|
||||||
*/
|
|
||||||
setStaticAlpha(layer: IMapLayer, alpha: number, x: number, y: number): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 启用动态内容的帧动画
|
|
||||||
* @param block 移动图块对象
|
|
||||||
*/
|
|
||||||
enableDynamicFrameAnimate(block: IMovingBlock): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 禁用动态内容的帧动画
|
|
||||||
* @param block 移动图块对象
|
|
||||||
*/
|
|
||||||
disableDynamicFrameAnimate(block: IMovingBlock): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置动态内容的不透明度
|
|
||||||
* @param block 移动图块对象
|
|
||||||
* @param alpha 图块的不透明度
|
|
||||||
*/
|
|
||||||
setDynamicAlpha(block: IMovingBlock, alpha: number): void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IMapRenderArea {
|
export interface IMapRenderArea {
|
||||||
/** 顶点起始索引,从哪个顶点开始处理 */
|
/** 顶点起始索引,从哪个顶点开始处理 */
|
||||||
readonly startIndex: number;
|
startIndex: number;
|
||||||
/** 顶点终止索引,处理到哪个顶点 */
|
/** 顶点终止索引,处理到哪个顶点 */
|
||||||
readonly endIndex: number;
|
endIndex: number;
|
||||||
/** 顶点数量,即终止索引减去起始索引 */
|
/** 顶点数量,即终止索引减去起始索引 */
|
||||||
readonly count: number;
|
count: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IMapRenderData {
|
export interface IMapRenderData {
|
||||||
@ -965,3 +1012,5 @@ export interface IMapViewportController {
|
|||||||
*/
|
*/
|
||||||
bindTransform(transform: Transform): void;
|
bindTransform(transform: Transform): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IMapCamera {}
|
||||||
|
|||||||
@ -72,6 +72,15 @@ interface BlockIndex extends IndexedBlockMapPos {
|
|||||||
readonly mapIndex: number;
|
readonly mapIndex: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface VertexArrayOfBlock {
|
||||||
|
/** 图块在数组中的起始索引 */
|
||||||
|
readonly index: number;
|
||||||
|
/** 分块顶点数组 */
|
||||||
|
readonly array: Float32Array;
|
||||||
|
/** 分块数据 */
|
||||||
|
readonly block: IBlockData<MapVertexBlock>;
|
||||||
|
}
|
||||||
|
|
||||||
const enum VertexUpdate {
|
const enum VertexUpdate {
|
||||||
/** 更新顶点位置信息 */
|
/** 更新顶点位置信息 */
|
||||||
Position = 0b01,
|
Position = 0b01,
|
||||||
@ -92,6 +101,9 @@ export class MapVertexGenerator
|
|||||||
|
|
||||||
dynamicRenderDirty: boolean = true;
|
dynamicRenderDirty: boolean = true;
|
||||||
|
|
||||||
|
dynamicStart: number = 0;
|
||||||
|
dynamicCount: number = DYNAMIC_RESERVE;
|
||||||
|
|
||||||
/** 空顶点数组,因为空顶点很常用,所以直接定义一个全局常量 */
|
/** 空顶点数组,因为空顶点很常用,所以直接定义一个全局常量 */
|
||||||
private static readonly EMPTY_VETREX: Float32Array = new Float32Array(
|
private static readonly EMPTY_VETREX: Float32Array = new Float32Array(
|
||||||
INSTANCED_COUNT
|
INSTANCED_COUNT
|
||||||
@ -104,9 +116,6 @@ export class MapVertexGenerator
|
|||||||
/** 动态内容偏移数组 */
|
/** 动态内容偏移数组 */
|
||||||
private dynamicInstancedArray: Float32Array = new Float32Array();
|
private dynamicInstancedArray: Float32Array = new Float32Array();
|
||||||
|
|
||||||
/** 图层列表 */
|
|
||||||
private layers: IMapLayer[] = [];
|
|
||||||
|
|
||||||
/** 分块宽度 */
|
/** 分块宽度 */
|
||||||
private blockWidth: number = MAP_BLOCK_WIDTH;
|
private blockWidth: number = MAP_BLOCK_WIDTH;
|
||||||
/** 分块高度 */
|
/** 分块高度 */
|
||||||
@ -120,11 +129,6 @@ export class MapVertexGenerator
|
|||||||
/** 是否需要重建数组 */
|
/** 是否需要重建数组 */
|
||||||
private needRebuild: boolean = false;
|
private needRebuild: boolean = false;
|
||||||
|
|
||||||
/** 静态内容数组顶点数量 */
|
|
||||||
private staticLength: number = 0;
|
|
||||||
/** 动态内容数组顶点数量。动态内容的数量无法预测,因此使用预留数量+动态扩充的方式 */
|
|
||||||
private dynamicLength: number = DYNAMIC_RESERVE;
|
|
||||||
|
|
||||||
/** 更新图块性能检查防抖起始时刻 */
|
/** 更新图块性能检查防抖起始时刻 */
|
||||||
private updateCallDebounceTime: number = 0;
|
private updateCallDebounceTime: number = 0;
|
||||||
/** 更新图块性能检查的调用次数 */
|
/** 更新图块性能检查的调用次数 */
|
||||||
@ -147,10 +151,10 @@ export class MapVertexGenerator
|
|||||||
// 顶点数组尺寸等于 地图大小 * 每个图块的顶点数量 * 每个顶点的数据量
|
// 顶点数组尺寸等于 地图大小 * 每个图块的顶点数量 * 每个顶点的数据量
|
||||||
const area = this.renderer.mapWidth * this.renderer.mapHeight;
|
const area = this.renderer.mapWidth * this.renderer.mapHeight;
|
||||||
const staticCount = area * this.renderer.layerCount;
|
const staticCount = area * this.renderer.layerCount;
|
||||||
const count = staticCount + this.dynamicLength;
|
const count = staticCount + this.dynamicCount;
|
||||||
const offsetSize = count * INSTANCED_COUNT;
|
const offsetSize = count * INSTANCED_COUNT;
|
||||||
this.instancedArray = new Float32Array(offsetSize);
|
this.instancedArray = new Float32Array(offsetSize);
|
||||||
this.staticLength = staticCount;
|
this.dynamicStart = staticCount;
|
||||||
this.dynamicInstancedArray = this.instancedArray.subarray(
|
this.dynamicInstancedArray = this.instancedArray.subarray(
|
||||||
staticCount * INSTANCED_COUNT,
|
staticCount * INSTANCED_COUNT,
|
||||||
count * INSTANCED_COUNT
|
count * INSTANCED_COUNT
|
||||||
@ -212,7 +216,7 @@ export class MapVertexGenerator
|
|||||||
|
|
||||||
expandMoving(targetSize: number): void {
|
expandMoving(targetSize: number): void {
|
||||||
const beforeOffset = this.instancedArray;
|
const beforeOffset = this.instancedArray;
|
||||||
this.dynamicLength = targetSize;
|
this.dynamicCount = targetSize;
|
||||||
this.mallocVertexArray();
|
this.mallocVertexArray();
|
||||||
this.instancedArray.set(beforeOffset);
|
this.instancedArray.set(beforeOffset);
|
||||||
const array: IMapVertexData = {
|
const array: IMapVertexData = {
|
||||||
@ -224,22 +228,14 @@ export class MapVertexGenerator
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
reduceMoving(targetSize: number, indexMap: Map<number, number>): void {
|
reduceMoving(targetSize: number): void {
|
||||||
const beforeOffsetLength = this.instancedArray.length;
|
const beforeOffsetLength = this.instancedArray.length;
|
||||||
const deltaLength = this.dynamicLength - targetSize;
|
const deltaLength = this.dynamicCount - targetSize;
|
||||||
this.dynamicLength = targetSize;
|
this.dynamicCount = targetSize;
|
||||||
this.instancedArray = this.instancedArray.subarray(
|
this.instancedArray = this.instancedArray.subarray(
|
||||||
0,
|
0,
|
||||||
beforeOffsetLength - deltaLength * INSTANCED_COUNT
|
beforeOffsetLength - deltaLength * INSTANCED_COUNT
|
||||||
);
|
);
|
||||||
indexMap.forEach((target, from) => {
|
|
||||||
const next = from + 1;
|
|
||||||
this.dynamicInstancedArray.copyWithin(
|
|
||||||
target * INSTANCED_COUNT,
|
|
||||||
from * INSTANCED_COUNT,
|
|
||||||
next * INSTANCED_COUNT
|
|
||||||
);
|
|
||||||
});
|
|
||||||
this.dynamicInstancedArray = this.dynamicInstancedArray.subarray(
|
this.dynamicInstancedArray = this.dynamicInstancedArray.subarray(
|
||||||
0,
|
0,
|
||||||
targetSize * INSTANCED_COUNT
|
targetSize * INSTANCED_COUNT
|
||||||
@ -248,14 +244,8 @@ export class MapVertexGenerator
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateLayerArray(): void {
|
updateLayerArray(): void {
|
||||||
const layers = this.renderer.getSortedLayer();
|
|
||||||
if (
|
|
||||||
layers.length !== this.layers.length ||
|
|
||||||
this.layers.some((v, i) => layers[i] !== v)
|
|
||||||
) {
|
|
||||||
this.needRebuild = true;
|
this.needRebuild = true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
checkRebuild() {
|
checkRebuild() {
|
||||||
if (!this.needRebuild) return;
|
if (!this.needRebuild) return;
|
||||||
@ -812,56 +802,6 @@ export class MapVertexGenerator
|
|||||||
|
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
//#region 图块配置
|
|
||||||
|
|
||||||
enableStaticFrameAnimate(layer: IMapLayer, x: number, y: number): void {
|
|
||||||
const data = layer.getMapRef();
|
|
||||||
const block = this.block.getBlockByDataLoc(x, y);
|
|
||||||
if (!block) return;
|
|
||||||
const vertexArray = block.data.getLayerInstanced(layer);
|
|
||||||
if (!vertexArray) return;
|
|
||||||
const mapIndex = y * this.mapWidth + x;
|
|
||||||
const num = data.array[mapIndex];
|
|
||||||
const tile = this.renderer.manager.getIfBigImage(num);
|
|
||||||
if (!tile) return;
|
|
||||||
const bx = x - block.dataX;
|
|
||||||
const by = y - block.dataY;
|
|
||||||
const bIndex = by * block.width + bx;
|
|
||||||
vertexArray[bIndex * INSTANCED_COUNT + 13] = tile.frames;
|
|
||||||
block.data.markRenderDirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
disableStaticFrameAnimate(layer: IMapLayer, x: number, y: number): void {
|
|
||||||
const block = this.block.getBlockByDataLoc(x, y);
|
|
||||||
if (!block) return;
|
|
||||||
const vertexArray = block.data.getLayerInstanced(layer);
|
|
||||||
if (!vertexArray) return;
|
|
||||||
const bx = x - block.dataX;
|
|
||||||
const by = y - block.dataY;
|
|
||||||
const bIndex = by * block.width + bx;
|
|
||||||
vertexArray[bIndex * INSTANCED_COUNT + 13] = 1;
|
|
||||||
block.data.markRenderDirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
setStaticAlpha(
|
|
||||||
layer: IMapLayer,
|
|
||||||
alpha: number,
|
|
||||||
x: number,
|
|
||||||
y: number
|
|
||||||
): void {
|
|
||||||
const block = this.block.getBlockByDataLoc(x, y);
|
|
||||||
if (!block) return;
|
|
||||||
const vertexArray = block.data.getLayerInstanced(layer);
|
|
||||||
if (!vertexArray) return;
|
|
||||||
const bx = x - block.dataX;
|
|
||||||
const by = y - block.dataY;
|
|
||||||
const bIndex = by * block.width + bx;
|
|
||||||
vertexArray[bIndex * INSTANCED_COUNT + 9] = alpha;
|
|
||||||
block.data.markRenderDirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
//#endregion
|
|
||||||
|
|
||||||
//#region 动态图块
|
//#region 动态图块
|
||||||
|
|
||||||
updateMoving(block: IMovingBlock, updateTexture: boolean): void {
|
updateMoving(block: IMovingBlock, updateTexture: boolean): void {
|
||||||
@ -925,11 +865,9 @@ export class MapVertexGenerator
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateMovingList(moving: IMovingBlock[], updateTexture: boolean): void {
|
updateMovingList(moving: IMovingBlock[], updateTexture: boolean): void {
|
||||||
console.time('update-moving');
|
|
||||||
moving.forEach(v => {
|
moving.forEach(v => {
|
||||||
this.updateMoving(v, updateTexture);
|
this.updateMoving(v, updateTexture);
|
||||||
});
|
});
|
||||||
console.timeEnd('update-moving');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteMoving(moving: IMovingBlock): void {
|
deleteMoving(moving: IMovingBlock): void {
|
||||||
@ -942,25 +880,85 @@ export class MapVertexGenerator
|
|||||||
this.dynamicRenderDirty = true;
|
this.dynamicRenderDirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
enableDynamicFrameAnimate(block: IMovingBlock): void {
|
//#endregion
|
||||||
if (!this.renderer.hasMoving(block)) return;
|
|
||||||
const instancedStart = block.index * INSTANCED_COUNT;
|
//#region 图块状态
|
||||||
this.dynamicInstancedArray[instancedStart + 13] = 1;
|
|
||||||
|
/**
|
||||||
|
* 获取指定图层指定坐标的图块对应的分块信息
|
||||||
|
* @param layer 图层对象
|
||||||
|
* @param x 图块横坐标
|
||||||
|
* @param y 图块纵坐标
|
||||||
|
*/
|
||||||
|
private getIndexInBlock(
|
||||||
|
layer: IMapLayer,
|
||||||
|
x: number,
|
||||||
|
y: number
|
||||||
|
): VertexArrayOfBlock | null {
|
||||||
|
const block = this.block.getBlockByDataLoc(x, y);
|
||||||
|
if (!block) return null;
|
||||||
|
const data = block?.data.getLayerInstanced(layer);
|
||||||
|
if (!data) return null;
|
||||||
|
const dx = x - block.x;
|
||||||
|
const dy = y - block.y;
|
||||||
|
const dIndex = dy * block.width + dx;
|
||||||
|
return { array: data, index: dIndex, block };
|
||||||
|
}
|
||||||
|
|
||||||
|
setStaticAlpha(
|
||||||
|
layer: IMapLayer,
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
|
alpha: number
|
||||||
|
): void {
|
||||||
|
const index = this.getIndexInBlock(layer, x, y);
|
||||||
|
if (!index) return;
|
||||||
|
index.array[index.index * INSTANCED_COUNT + 9] = alpha;
|
||||||
|
index.block.data.markRenderDirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
setStaticFrame(
|
||||||
|
layer: IMapLayer,
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
|
frame: number
|
||||||
|
): void {
|
||||||
|
const index = this.getIndexInBlock(layer, x, y);
|
||||||
|
if (!index) return;
|
||||||
|
index.array[index.index * INSTANCED_COUNT + 12] = frame;
|
||||||
|
index.block.data.markRenderDirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
getStaticAlpha(layer: IMapLayer, x: number, y: number): number {
|
||||||
|
const index = this.getIndexInBlock(layer, x, y);
|
||||||
|
if (!index) return 0;
|
||||||
|
return index.array[index.index * INSTANCED_COUNT + 9];
|
||||||
|
}
|
||||||
|
|
||||||
|
getStaticFrame(layer: IMapLayer, x: number, y: number): number {
|
||||||
|
const index = this.getIndexInBlock(layer, x, y);
|
||||||
|
if (!index) return -1;
|
||||||
|
return index.array[index.index * INSTANCED_COUNT + 12];
|
||||||
|
}
|
||||||
|
|
||||||
|
setDynamicAlpha(index: number, alpha: number): void {
|
||||||
|
this.dynamicInstancedArray[index * INSTANCED_COUNT + 9] = alpha;
|
||||||
this.dynamicRenderDirty = true;
|
this.dynamicRenderDirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
disableDynamicFrameAnimate(block: IMovingBlock): void {
|
setDynamicFrame(index: number, frame: number): void {
|
||||||
if (!this.renderer.hasMoving(block)) return;
|
this.dynamicInstancedArray[index * INSTANCED_COUNT + 12] = frame;
|
||||||
const instancedStart = block.index * INSTANCED_COUNT;
|
|
||||||
this.dynamicInstancedArray[instancedStart + 13] = block.texture.frames;
|
|
||||||
this.dynamicRenderDirty = true;
|
this.dynamicRenderDirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
setDynamicAlpha(block: IMovingBlock, alpha: number): void {
|
getDynamicAlpha(index: number): number {
|
||||||
if (!this.renderer.hasMoving(block)) return;
|
if (index > this.dynamicCount) return 0;
|
||||||
const instancedStart = block.index * INSTANCED_COUNT;
|
return this.dynamicInstancedArray[index * INSTANCED_COUNT + 9];
|
||||||
this.dynamicInstancedArray[instancedStart + 9] = alpha;
|
}
|
||||||
this.dynamicRenderDirty = true;
|
|
||||||
|
getDynamicFrame(index: number): number {
|
||||||
|
if (index > this.dynamicCount) return -1;
|
||||||
|
return this.dynamicInstancedArray[index * INSTANCED_COUNT + 12];
|
||||||
}
|
}
|
||||||
|
|
||||||
//#endregion
|
//#endregion
|
||||||
@ -968,15 +966,14 @@ export class MapVertexGenerator
|
|||||||
//#region 其他接口
|
//#region 其他接口
|
||||||
|
|
||||||
renderDynamic(): void {
|
renderDynamic(): void {
|
||||||
// todo: vertex, offset, alpha 的脏标记分开
|
|
||||||
this.dynamicRenderDirty = false;
|
this.dynamicRenderDirty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
getVertexArray(): IMapVertexArray {
|
getVertexArray(): IMapVertexArray {
|
||||||
this.checkRebuild();
|
this.checkRebuild();
|
||||||
return {
|
return {
|
||||||
dynamicStart: this.staticLength,
|
dynamicStart: this.dynamicStart,
|
||||||
dynamicCount: this.dynamicLength,
|
dynamicCount: this.dynamicCount,
|
||||||
tileInstanced: this.instancedArray
|
tileInstanced: this.instancedArray
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -1040,7 +1037,6 @@ class MapVertexBlock implements IMapVertexBlock {
|
|||||||
}
|
}
|
||||||
|
|
||||||
markRenderDirty() {
|
markRenderDirty() {
|
||||||
// todo: 潜在优化点:vertex, offset, alpha 的脏标记分开
|
|
||||||
this.renderDirty = true;
|
this.renderDirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1051,7 +1047,6 @@ class MapVertexBlock implements IMapVertexBlock {
|
|||||||
right: number,
|
right: number,
|
||||||
bottom: number
|
bottom: number
|
||||||
): void {
|
): void {
|
||||||
// todo: 更细致的脏标记是否会更好?
|
|
||||||
const data = this.layerDirty.get(layer);
|
const data = this.layerDirty.get(layer);
|
||||||
if (!data) return;
|
if (!data) return;
|
||||||
const dl = clamp(left, 0, this.blockWidth);
|
const dl = clamp(left, 0, this.blockWidth);
|
||||||
|
|||||||
@ -33,6 +33,24 @@ export class MapViewport implements IMapViewportController {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private checkDynamic(
|
||||||
|
list: IMapRenderArea[],
|
||||||
|
dynamicStart: number,
|
||||||
|
dynamicCount: number
|
||||||
|
) {
|
||||||
|
const last = list[list.length - 1];
|
||||||
|
if (!last || last.endIndex < dynamicStart) {
|
||||||
|
list.push({
|
||||||
|
startIndex: dynamicStart,
|
||||||
|
endIndex: dynamicStart + dynamicCount,
|
||||||
|
count: dynamicCount
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
last.endIndex = dynamicStart + dynamicCount;
|
||||||
|
last.count += dynamicCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
getRenderArea(): IMapRenderData {
|
getRenderArea(): IMapRenderData {
|
||||||
const { cellWidth, cellHeight, renderWidth, renderHeight } =
|
const { cellWidth, cellHeight, renderWidth, renderHeight } =
|
||||||
this.renderer;
|
this.renderer;
|
||||||
@ -53,35 +71,13 @@ export class MapViewport implements IMapViewportController {
|
|||||||
const updateArea: IMapRenderArea[] = [];
|
const updateArea: IMapRenderArea[] = [];
|
||||||
const blockList: IBlockData<IMapVertexBlock>[] = [];
|
const blockList: IBlockData<IMapVertexBlock>[] = [];
|
||||||
|
|
||||||
const widthOne = blockLeft === blockRight;
|
// 内层横向外层纵向的话,索引在换行之前都是连续的,方便整合
|
||||||
const heightOne = blockTop === blockBottom;
|
|
||||||
|
|
||||||
if (widthOne && heightOne) {
|
|
||||||
// 只能看到一个分块
|
|
||||||
const block = this.vertex.block.getBlockByLoc(blockLeft, blockTop)!;
|
|
||||||
blockList.push(block);
|
|
||||||
} else if (widthOne) {
|
|
||||||
// 看到的区域分块宽度是 1
|
|
||||||
for (let ny = blockTop; ny <= blockBottom; ny++) {
|
|
||||||
const block = this.vertex.block.getBlockByLoc(blockLeft, ny)!;
|
|
||||||
blockList.push(block);
|
|
||||||
}
|
|
||||||
} else if (heightOne) {
|
|
||||||
// 看到的区域分块高度是 1
|
|
||||||
for (let nx = blockLeft; nx <= blockRight; nx++) {
|
|
||||||
const block = this.vertex.block.getBlockByLoc(nx, blockTop)!;
|
|
||||||
blockList.push(block);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// 看到的区域分块宽高都不是 1
|
|
||||||
// 使用这种方式的话,索引在换行之前都是连续的,方便整合
|
|
||||||
for (let ny = blockTop; ny <= blockBottom; ny++) {
|
for (let ny = blockTop; ny <= blockBottom; ny++) {
|
||||||
for (let nx = blockLeft; nx <= blockRight; nx++) {
|
for (let nx = blockLeft; nx <= blockRight; nx++) {
|
||||||
const block = this.vertex.block.getBlockByLoc(nx, ny)!;
|
const block = this.vertex.block.getBlockByLoc(nx, ny)!;
|
||||||
blockList.push(block);
|
blockList.push(block);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (blockList.length > 0) {
|
if (blockList.length > 0) {
|
||||||
if (blockList.length === 1) {
|
if (blockList.length === 1) {
|
||||||
@ -122,7 +118,12 @@ export class MapViewport implements IMapViewportController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo: 动态内容
|
const dynamicStart = this.vertex.dynamicStart;
|
||||||
|
const dynamicCount = this.vertex.dynamicCount;
|
||||||
|
this.checkDynamic(renderArea, dynamicStart, dynamicCount);
|
||||||
|
if (this.vertex.dynamicRenderDirty) {
|
||||||
|
this.checkDynamic(updateArea, dynamicStart, dynamicCount);
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
render: renderArea,
|
render: renderArea,
|
||||||
|
|||||||
@ -133,6 +133,7 @@
|
|||||||
"83": "It seems that you call '$1' too frequently. This will extremely affect game's performance, so considering integrate them into one 'updateArea' or 'updateBlockList' call.",
|
"83": "It seems that you call '$1' too frequently. This will extremely affect game's performance, so considering integrate them into one 'updateArea' or 'updateBlockList' call.",
|
||||||
"84": "Cannot set alias '$1' for layer, since '$1' is already an alias for another layer.",
|
"84": "Cannot set alias '$1' for layer, since '$1' is already an alias for another layer.",
|
||||||
"85": "Hook to load does not belong to current hookable object.",
|
"85": "Hook to load does not belong to current hookable object.",
|
||||||
|
"86": "Cannot restore vertex data since delivered state does not belong to current vertex generator instance.",
|
||||||
"1001": "Item-detail extension needs 'floor-binder' and 'floor-damage' extension as dependency.",
|
"1001": "Item-detail extension needs 'floor-binder' and 'floor-damage' extension as dependency.",
|
||||||
"1101": "Cannot add new effect to point effect instance, for there's no more reserve space for it. Please increase the max count of the instance."
|
"1101": "Cannot add new effect to point effect instance, for there's no more reserve space for it. Please increase the max count of the instance."
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user