feat: 图集脏标记

This commit is contained in:
unanmed 2025-10-28 23:58:37 +08:00
parent fd9d21efa4
commit 637647dc17
5 changed files with 220 additions and 92 deletions

View File

@ -0,0 +1,30 @@
import { ITextureComposedData } from '@motajs/render-assets';
import { IMaterialAsset } from './types';
export class MaterialAsset implements IMaterialAsset {
/** 标记列表 */
private readonly marks: Map<symbol, number> = new Map();
/** 脏标记,所有值小于此标记的都视为需要更新 */
private dirtyFlag: number = 0;
constructor(readonly data: ITextureComposedData) {}
dirty(): void {
this.dirtyFlag++;
}
mark(): symbol {
const symbol = Symbol();
this.marks.set(symbol, this.dirtyFlag);
return symbol;
}
unmark(mark: symbol): void {
this.marks.delete(mark);
}
dirtySince(mark: symbol): boolean {
const value = this.marks.get(mark) ?? -1;
return value < this.dirtyFlag;
}
}

View File

@ -171,6 +171,10 @@ export class AutotileProcessor implements IAutotileProcessor {
return this.fromStaticRenderable(tile.static(), connection);
}
/**
* 根据静态可渲染对象获取自动元件的帧列表
* @param renderable 静态可渲染对象
*/
private getStaticRectList(
renderable: ITextureRenderable
): AutotileFrameList {
@ -194,11 +198,17 @@ export class AutotileProcessor implements IAutotileProcessor {
}
}
/**
* 对自动元件连接执行偏移操作,偏移至自动元件在图像源中的所在矩形范围
* @param ox 横向偏移量
* @param oy 纵向偏移量
* @param connection 自动元件连接信息
*/
private getConnectedRect(
ox: number,
oy: number,
connection: ConnectedAutotile
): ConnectedAutotile | null {
): ConnectedAutotile {
const { lt, rt, rb, lb } = connection;
return {

View File

@ -7,4 +7,10 @@ export function createMaterial() {
});
}
export * from './autotile';
export * from './builder';
export * from './fallback';
export * from './ins';
export * from './manager';
export * from './types';
export * from './utils';

View File

@ -19,12 +19,14 @@ import {
IMaterialAssetData,
BlockCls,
IBigImageData,
IAssetBuilder
IAssetBuilder,
IMaterialAsset
} from './types';
import { logger } from '@motajs/common';
import { getClsByString } from './utils';
import { isNil } from 'lodash-es';
import { AssetBuilder } from './builder';
import { MaterialAsset } from './asset';
export class MaterialManager implements IMaterialManager {
readonly tileStore: ITextureStore = new TextureStore();
@ -34,7 +36,8 @@ export class MaterialManager implements IMaterialManager {
readonly bigImageStore: ITextureStore = new TextureStore();
/** 图集信息存储 */
readonly assetDataStore: Map<number, ITextureComposedData> = new Map();
readonly assetDataStore: Map<number, IMaterialAsset> = new Map();
/** 大怪物数据 */
readonly bigImageData: Map<number, ITexture> = new Map();
/** tileset 中 `Math.floor(id / 10000) + 1` 映射到 tileset 对应索引的映射,用于处理图块超出 10000 的 tileset */
@ -60,6 +63,9 @@ export class MaterialManager implements IMaterialManager {
/** 是否已经构建过素材 */
private built: boolean = false;
/** 标记列表 */
private readonly markList: symbol[] = [];
constructor() {
this.assetBuilder.pipe(this.assetStore);
}
@ -264,6 +270,23 @@ export class MaterialManager implements IMaterialManager {
return newTexture;
}
/**
* 检查图集状态,如果已存在图集则标记为脏,否则新增图集
* @param data 图集数据
*/
private checkAssetDirty(data: ITextureComposedData) {
const asset = this.assetDataStore.get(data.index);
if (asset) {
// 如果不是新图集,需要标记为脏
asset.dirty();
} else {
// 如果有新图集,需要添加
const alias = `asset-${data.index}`;
this.assetStore.alias(data.index, alias);
this.assetDataStore.set(data.index, new MaterialAsset(data));
}
}
cacheTileset(identifier: number): ITexture | null {
const newTexture = this.getTilesetOwnTexture(identifier);
if (!newTexture) return null;
@ -273,6 +296,7 @@ export class MaterialManager implements IMaterialManager {
this.numIdMap.set(identifier, `X${identifier}`);
const data = this.assetBuilder.addTexture(newTexture);
newTexture.toAsset(data);
this.checkAssetDirty(data);
return newTexture;
}
@ -295,10 +319,11 @@ export class MaterialManager implements IMaterialManager {
const data = this.assetBuilder.addTextureList(toAdd);
const res = [...data];
res.forEach(v => {
v.assetMap.keys().forEach(tex => {
if (set.has(tex)) tex.toAsset(v);
res.forEach(data => {
data.assetMap.keys().forEach(tex => {
if (set.has(tex)) tex.toAsset(data);
});
this.checkAssetDirty(data);
});
return toAdd;
@ -313,13 +338,13 @@ export class MaterialManager implements IMaterialManager {
const data = this.assetBuilder.addTextureList(this.tileStore.values());
const arr = [...data];
const res: IMaterialAssetData[] = [];
arr.forEach((v, i) => {
const alias = `asset-${i}`;
this.assetStore.alias(i, alias);
this.assetDataStore.set(i, v);
arr.forEach(v => {
const alias = `asset-${v.index}`;
this.assetStore.alias(v.index, alias);
this.assetDataStore.set(v.index, new MaterialAsset(v));
const data: IMaterialAssetData = {
data: v,
identifier: i,
identifier: v.index,
alias,
store: this.assetStore
};
@ -331,11 +356,11 @@ export class MaterialManager implements IMaterialManager {
return res;
}
getAsset(identifier: number): ITextureComposedData | null {
getAsset(identifier: number): IMaterialAsset | null {
return this.assetDataStore.get(identifier) ?? null;
}
getAssetByAlias(alias: string): ITextureComposedData | null {
getAssetByAlias(alias: string): IMaterialAsset | null {
const id = this.assetStore.identifierOf(alias);
if (isNil(id)) return null;
return this.assetDataStore.get(id) ?? null;
@ -406,4 +431,14 @@ export class MaterialManager implements IMaterialManager {
if (isNil(identifier)) return null;
return this.bigImageData.get(identifier) ?? null;
}
getIfBigImage(identifier: number): ITexture | null {
const bigImage = this.bigImageData.get(identifier) ?? null;
if (bigImage) return bigImage;
if (identifier < 10000) {
return this.tileStore.getTexture(identifier);
} else {
return this.cacheTileset(identifier);
}
}
}

View File

@ -90,6 +90,37 @@ export interface IBigImageData {
readonly store: ITextureStore;
}
export interface IAssetDirtyMarker {
/**
* 标记为脏,即进行了一次更新
*/
dirty(): void;
}
export interface IAssetDirtyTracker {
/**
* 对图集状态进行标记
*/
mark(): symbol;
/**
* 取消指定标记符号
* @param mark 标记符号
*/
unmark(mark: symbol): void;
/**
* 从指定标记符号开始,图集是否发生了变动
* @param mark 标记符号
*/
dirtySince(mark: symbol): boolean;
}
export interface IMaterialAsset extends IAssetDirtyTracker, IAssetDirtyMarker {
/** 图集的贴图数据 */
readonly data: ITextureComposedData;
}
export interface IAutotileProcessor {
/** 该自动元件处理器使用的素材管理器 */
readonly manager: IMaterialManager;
@ -162,7 +193,98 @@ export interface IAutotileProcessor {
): Generator<IAutotileRenderable, void> | null;
}
export interface IMaterialManager {
export interface IMaterialGetter {
/**
* 根据图块数字获取图块,可以获取额外素材,会自动将未缓存的额外素材缓存
* @param identifier 图块的图块数字
*/
getTile(identifier: number): ITexture | null;
/**
* 根据图块标识符获取图块类型
* @param identifier 图块标识符,即图块数字
*/
getBlockCls(identifier: number): BlockCls;
/**
* 判断一个图块是否包含 `bigImage` 贴图,即是否是大怪物
* @param identifier 图块标识符,即图块数字
*/
isBigImage(identifier: number): boolean;
/**
* 根据图块标识符获取一个图块的 `bigImage` 贴图
* @param identifier 图块标识符,即图块数字
*/
getBigImage(identifier: number): ITexture | null;
/**
* 根据图块标识符,首先判断是否是 `bigImage` 贴图,如果是,则返回 `bigImage` 贴图,
* 否则返回普通贴图。如果图块不存在,则返回 `null`
* @param identifier 图块标识符,即图块数字
*/
getIfBigImage(identifier: number): ITexture | null;
/**
* 根据标识符获取图集信息
* @param identifier 图集的标识符
*/
getAsset(identifier: number): IMaterialAsset | null;
/**
* 根据额外素材索引获取额外素材
* @param identifier 额外素材的索引
*/
getTileset(identifier: number): ITexture | null;
/**
* 根据图片的索引获取图片
* @param identifier 图片的索引
*/
getImage(identifier: number): ITexture | null;
}
export interface IMaterialAliasGetter {
/**
* 根据图块 id 获取图块,可以获取额外素材,会自动将未缓存的额外素材缓存
* @param alias 图块 id
*/
getTileByAlias(alias: string): ITexture | null;
/**
* 根据额外素材名称获取额外素材
* @param alias 额外素材名称
*/
getTilesetByAlias(alias: string): ITexture | null;
/**
* 根据图片名称获取图片
* @param alias 图片名称
*/
getImageByAlias(alias: string): ITexture | null;
/**
* 根据别名获取图集信息
* @param alias 图集的别名
*/
getAssetByAlias(alias: string): IMaterialAsset | null;
/**
* 根据图块别名获取图块类型
* @param alias 图块别名,即图块的 id
*/
getBlockClsByAlias(alias: string): BlockCls;
/**
* 根据图块别名获取一个图块的 `bigImage` 贴图
* @param alias 图块别名,即图块的 id
*/
getBigImageByAlias(alias: string): ITexture | null;
}
export interface IMaterialManager
extends IMaterialGetter,
IMaterialAliasGetter {
/** 贴图存储,把 terrains 等内容单独分开存储 */
readonly tileStore: ITextureStore;
/** tilesets 贴图存储,每个 tileset 是一个贴图对象 */
@ -174,6 +296,9 @@ export interface IMaterialManager {
/** bigImage 存储,存储大怪物数据 */
readonly bigImageStore: ITextureStore;
/** 图集信息存储 */
readonly assetDataStore: Iterable<[number, IMaterialAsset]>;
/** 图块类型映射 */
readonly clsMap: Map<number, BlockCls>;
@ -231,42 +356,6 @@ export interface IMaterialManager {
identifier: IIndexedIdentifier
): IMaterialData;
/**
* 根据图块数字获取图块,可以获取额外素材,会自动将未缓存的额外素材缓存
* @param identifier 图块的图块数字
*/
getTile(identifier: number): ITexture | null;
/**
* 根据额外素材索引获取额外素材
* @param identifier 额外素材的索引
*/
getTileset(identifier: number): ITexture | null;
/**
* 根据图片的索引获取图片
* @param identifier 图片的索引
*/
getImage(identifier: number): ITexture | null;
/**
* 根据图块 id 获取图块,可以获取额外素材,会自动将未缓存的额外素材缓存
* @param alias 图块 id
*/
getTileByAlias(alias: string): ITexture | null;
/**
* 根据额外素材名称获取额外素材
* @param alias 额外素材名称
*/
getTilesetByAlias(alias: string): ITexture | null;
/**
* 根据图片名称获取图片
* @param alias 图片名称
*/
getImageByAlias(alias: string): ITexture | null;
/**
* 缓存某个 tileset
* @param identifier tileset 的标识符,即图块数字
@ -286,18 +375,6 @@ export interface IMaterialManager {
*/
buildAssets(): Iterable<IMaterialAssetData>;
/**
* 根据标识符获取图集信息
* @param identifier 图集的标识符
*/
getAsset(identifier: number): ITextureComposedData | null;
/**
* 根据别名获取图集信息
* @param alias 图集的别名
*/
getAssetByAlias(alias: string): ITextureComposedData | null;
/**
* 根据图块标识符在图集中获取对应的可渲染对象
* @param identifier 图块标识符,即图块数字
@ -310,18 +387,6 @@ export interface IMaterialManager {
*/
getRenderableByAlias(alias: string): ITextureRenderable | null;
/**
* 根据图块标识符获取图块类型
* @param identifier 图块标识符,即图块数字
*/
getBlockCls(identifier: number): BlockCls;
/**
* 根据图块别名获取图块类型
* @param alias 图块别名,即图块的 id
*/
getBlockClsByAlias(alias: string): BlockCls;
/**
* 根据图块别名获取图块标识符,即图块数字
* @param alias 图块别名,即图块的 id
@ -340,24 +405,6 @@ export interface IMaterialManager {
* @param image `bigImage` 对应的贴图对象
*/
setBigImage(identifier: number, image: ITexture): IBigImageData;
/**
* 判断一个图块是否包含 `bigImage` 贴图,即是否是大怪物
* @param identifier 图块标识符,即图块数字
*/
isBigImage(identifier: number): boolean;
/**
* 根据图块标识符获取一个图块的 `bigImage` 贴图
* @param identifier 图块标识符,即图块数字
*/
getBigImage(identifier: number): ITexture | null;
/**
* 根据图块别名获取一个图块的 `bigImage` 贴图
* @param alias 图块别名,即图块的 id
*/
getBigImageByAlias(alias: string): ITexture | null;
}
export interface IAssetBuilder {