You've already forked Nitro_Render_V3
mirror of
https://github.com/duckietm/Nitro_Render_V3.git
synced 2026-06-19 15:06:20 +00:00
☁️ Fase 1 done, adding animated landscapes
This commit is contained in:
@@ -14,10 +14,11 @@ export class RoomContentLoader implements IRoomContentLoader
|
||||
private static PLACE_HOLDER_PET: string = 'place_holder_pet';
|
||||
private static PLACE_HOLDER_DEFAULT: string = RoomContentLoader.PLACE_HOLDER;
|
||||
private static ROOM: string = 'room';
|
||||
private static LANDSCAPE: string = 'landscape';
|
||||
private static TILE_CURSOR: string = 'tile_cursor';
|
||||
private static SELECTION_ARROW: string = 'selection_arrow';
|
||||
|
||||
public static MANDATORY_LIBRARIES: string[] = [RoomContentLoader.PLACE_HOLDER, RoomContentLoader.PLACE_HOLDER_WALL, RoomContentLoader.PLACE_HOLDER_PET, RoomContentLoader.ROOM, RoomContentLoader.TILE_CURSOR, RoomContentLoader.SELECTION_ARROW];
|
||||
public static MANDATORY_LIBRARIES: string[] = [RoomContentLoader.PLACE_HOLDER, RoomContentLoader.PLACE_HOLDER_WALL, RoomContentLoader.PLACE_HOLDER_PET, RoomContentLoader.ROOM, RoomContentLoader.LANDSCAPE, RoomContentLoader.TILE_CURSOR, RoomContentLoader.SELECTION_ARROW];
|
||||
|
||||
private _iconListener: IRoomContentListener;
|
||||
private _images: Map<string, HTMLImageElement> = new Map();
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { IAssetPlaneVisualizationLayer, IAssetRoomVisualizationData, IRoomGeometry, IRoomPlane, IVector3D } from '@nitrots/api';
|
||||
import { IAssetPlaneVisualizationAnimatedLayer, IAssetPlaneVisualizationLayer, IAssetRoomVisualizationData, IRoomGeometry, IRoomPlane, IVector3D } from '@nitrots/api';
|
||||
import { GetAssetManager } from '@nitrots/assets';
|
||||
import { GetRenderer, GetTexturePool, PlaneMaskFilter, Vector3d } from '@nitrots/utils';
|
||||
import { Container, Filter, Matrix, Point, Sprite, Texture, TilingSprite } from 'pixi.js';
|
||||
import { Container, Filter, Graphics, Matrix, Point, RenderTexture, Sprite, Texture, TilingSprite } from 'pixi.js';
|
||||
import { RoomGeometry } from '../../../utils';
|
||||
import { PlaneVisualizationAnimationLayer } from './animated';
|
||||
import { RoomPlaneBitmapMask } from './RoomPlaneBitmapMask';
|
||||
import { RoomPlaneRectangleMask } from './RoomPlaneRectangleMask';
|
||||
import { PlaneMaskManager } from './mask';
|
||||
@@ -17,6 +18,7 @@ export class RoomPlane implements IRoomPlane
|
||||
'64': new RoomGeometry(64, new Vector3d(RoomPlane.HORIZONTAL_ANGLE_DEFAULT, RoomPlane.VERTICAL_ANGLE_DEFAULT), new Vector3d(-10, 0, 0))
|
||||
};
|
||||
private static LANDSCAPE_COLOR: number = 0x0082F0;
|
||||
private static ANIMATION_UPDATE_INTERVAL: number = 500;
|
||||
|
||||
public static TYPE_UNDEFINED: number = 0;
|
||||
public static TYPE_WALL: number = 1;
|
||||
@@ -67,6 +69,18 @@ export class RoomPlane implements IRoomPlane
|
||||
private _planeTexture: Texture = null;
|
||||
private _maskFilter: Filter = null;
|
||||
|
||||
private _animationLayers: PlaneVisualizationAnimationLayer[] = [];
|
||||
private _isAnimated: boolean = false;
|
||||
private _lastAnimationUpdate: number = 0;
|
||||
private _animationCanvasWidth: number = 0;
|
||||
private _animationCanvasHeight: number = 0;
|
||||
private _landscapeRenderWidth: number = 0;
|
||||
private _landscapeRenderHeight: number = 0;
|
||||
private _landscapeOffsetX: number = 0;
|
||||
private _landscapeOffsetY: number = 0;
|
||||
private _hasWindowMask: boolean = false;
|
||||
private _windowMasks: { leftSideLoc: number; rightSideLoc: number }[] = [];
|
||||
|
||||
constructor(origin: IVector3D, location: IVector3D, leftSide: IVector3D, rightSide: IVector3D, type: number, usesMask: boolean, secondaryNormals: IVector3D[], randomSeed: number, textureOffsetX: number = 0, textureOffsetY: number = 0, textureMaxX: number = 0, textureMaxY: number = 0)
|
||||
{
|
||||
this._randomSeed = randomSeed;
|
||||
@@ -122,6 +136,15 @@ export class RoomPlane implements IRoomPlane
|
||||
this._planeTexture = null;
|
||||
}
|
||||
|
||||
if(this._animationLayers)
|
||||
{
|
||||
for(const layer of this._animationLayers)
|
||||
{
|
||||
if(layer) layer.dispose();
|
||||
}
|
||||
this._animationLayers = [];
|
||||
}
|
||||
|
||||
this._disposed = true;
|
||||
}
|
||||
|
||||
@@ -136,9 +159,12 @@ export class RoomPlane implements IRoomPlane
|
||||
needsUpdate = true;
|
||||
}
|
||||
|
||||
if(!needsUpdate || !this._canBeVisible)
|
||||
const needsAnimationUpdate = this._isAnimated && this._type === RoomPlane.TYPE_LANDSCAPE &&
|
||||
(timeSinceStartMs - this._lastAnimationUpdate) >= RoomPlane.ANIMATION_UPDATE_INTERVAL;
|
||||
|
||||
if(!needsUpdate && !needsAnimationUpdate)
|
||||
{
|
||||
if(!this.visible) return false;
|
||||
if(!this._canBeVisible || !this.visible) return false;
|
||||
}
|
||||
|
||||
if(needsUpdate)
|
||||
@@ -208,16 +234,43 @@ export class RoomPlane implements IRoomPlane
|
||||
const dataType: keyof IAssetRoomVisualizationData = (planeType === RoomPlane.TYPE_FLOOR) ? 'floorData' : (planeType === RoomPlane.TYPE_WALL) ? 'wallData' : 'landscapeData';
|
||||
|
||||
const roomCollection = GetAssetManager().getCollection('room');
|
||||
const planeVisualizationData = roomCollection?.data?.roomVisualization?.[dataType];
|
||||
const plane = planeVisualizationData?.planes?.find(plane => (plane.id === planeId));
|
||||
let planeVisualizationData = roomCollection?.data?.roomVisualization?.[dataType];
|
||||
let plane = planeVisualizationData?.planes?.find(plane => (plane.id === planeId));
|
||||
let assetCollection = roomCollection;
|
||||
|
||||
if(!plane && planeType === RoomPlane.TYPE_LANDSCAPE)
|
||||
{
|
||||
const landscapeCollection = GetAssetManager().getCollection('landscape');
|
||||
if(landscapeCollection?.data?.roomVisualization?.landscapeData)
|
||||
{
|
||||
planeVisualizationData = landscapeCollection.data.roomVisualization.landscapeData;
|
||||
plane = planeVisualizationData?.planes?.find(p => (p.id === planeId));
|
||||
if(plane) assetCollection = landscapeCollection;
|
||||
}
|
||||
}
|
||||
|
||||
const planeVisualization = ((dataType === 'landscapeData') ? plane?.animatedVisualization : plane?.visualizations)?.find(visualization => (visualization.size === planeGeometry.scale)) ?? null;
|
||||
const planeLayer = planeVisualization?.allLayers?.[0] as IAssetPlaneVisualizationLayer;
|
||||
const planeMaterialId = planeLayer?.materialId;
|
||||
const planeColor = planeLayer?.color;
|
||||
const planeAssetName = planeVisualizationData?.textures?.find(texture => (texture.id === planeMaterialId))?.bitmaps?.[0]?.assetName;
|
||||
const texture = GetAssetManager().getAsset(planeAssetName)?.texture;
|
||||
const texture = assetCollection ? GetAssetManager().getAsset(planeAssetName)?.texture : null;
|
||||
|
||||
return { texture, color: planeColor };
|
||||
const animationLayers: PlaneVisualizationAnimationLayer[] = [];
|
||||
if(planeType === RoomPlane.TYPE_LANDSCAPE && planeVisualization?.allLayers && assetCollection)
|
||||
{
|
||||
for(const layer of planeVisualization.allLayers)
|
||||
{
|
||||
const animatedLayer = layer as IAssetPlaneVisualizationAnimatedLayer;
|
||||
if(animatedLayer?.items && animatedLayer.items.length > 0)
|
||||
{
|
||||
const animLayer = new PlaneVisualizationAnimationLayer(animatedLayer.items, assetCollection);
|
||||
if(animLayer.hasItems) animationLayers.push(animLayer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { texture, color: planeColor, animationLayers };
|
||||
};
|
||||
|
||||
const planeData = getTextureAndColorForPlane(this._id, this._type);
|
||||
@@ -304,6 +357,23 @@ export class RoomPlane implements IRoomPlane
|
||||
const renderOffsetX = Math.trunc(this._textureOffsetX * Math.abs((_local_13.x - _local_15.x)));
|
||||
const renderOffsetY = Math.trunc(this._textureOffsetY * Math.abs((_local_13.y - _local_14.y)));
|
||||
|
||||
this._landscapeRenderWidth = width;
|
||||
this._landscapeRenderHeight = height;
|
||||
this._animationCanvasWidth = renderMaxX;
|
||||
this._animationCanvasHeight = renderMaxY;
|
||||
this._landscapeOffsetX = renderOffsetX;
|
||||
this._landscapeOffsetY = renderOffsetY;
|
||||
|
||||
if(this._animationLayers)
|
||||
{
|
||||
for(const layer of this._animationLayers)
|
||||
{
|
||||
if(layer) layer.dispose();
|
||||
}
|
||||
}
|
||||
this._animationLayers = planeData.animationLayers || [];
|
||||
this._isAnimated = this._animationLayers.length > 0;
|
||||
|
||||
this._planeSprite = new TilingSprite({
|
||||
texture,
|
||||
width,
|
||||
@@ -349,7 +419,18 @@ export class RoomPlane implements IRoomPlane
|
||||
|
||||
this._planeTexture.source.label = `room_plane_${ this._uniqueId.toString() }`;
|
||||
|
||||
if(needsUpdate)
|
||||
let animationUpdate = false;
|
||||
if(this._isAnimated && this._type === RoomPlane.TYPE_LANDSCAPE)
|
||||
{
|
||||
const timeSinceLastUpdate = timeSinceStartMs - this._lastAnimationUpdate;
|
||||
if(timeSinceLastUpdate >= RoomPlane.ANIMATION_UPDATE_INTERVAL || needsUpdate)
|
||||
{
|
||||
animationUpdate = true;
|
||||
this._lastAnimationUpdate = timeSinceStartMs;
|
||||
}
|
||||
}
|
||||
|
||||
if(needsUpdate || animationUpdate)
|
||||
{
|
||||
GetRenderer().render({
|
||||
target: this._planeTexture,
|
||||
@@ -357,11 +438,78 @@ export class RoomPlane implements IRoomPlane
|
||||
transform: this.getMatrixForDimensions(this._planeSprite.width, this._planeSprite.height),
|
||||
clear: true
|
||||
});
|
||||
|
||||
if(this._isAnimated && this._type === RoomPlane.TYPE_LANDSCAPE && this._animationLayers.length > 0 && this._hasWindowMask)
|
||||
{
|
||||
this.renderAnimationLayers(timeSinceStartMs, geometry);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private renderAnimationLayers(timeSinceStartMs: number, geometry: IRoomGeometry): void
|
||||
{
|
||||
if(!this._planeTexture || this._animationCanvasWidth <= 0 || this._animationCanvasHeight <= 0) return;
|
||||
|
||||
const canvasWidth = this._landscapeRenderWidth;
|
||||
const canvasHeight = this._landscapeRenderHeight;
|
||||
|
||||
if(canvasWidth <= 0 || canvasHeight <= 0) return;
|
||||
|
||||
const animationCanvas = RenderTexture.create({ width: canvasWidth, height: canvasHeight });
|
||||
|
||||
for(const layer of this._animationLayers)
|
||||
{
|
||||
if(!layer) continue;
|
||||
|
||||
layer.render(
|
||||
animationCanvas,
|
||||
this._landscapeOffsetX,
|
||||
this._landscapeOffsetY,
|
||||
this._animationCanvasWidth,
|
||||
this._animationCanvasHeight,
|
||||
this._leftSide.length,
|
||||
this._rightSide.length,
|
||||
timeSinceStartMs
|
||||
);
|
||||
}
|
||||
|
||||
const animContainer = new Container();
|
||||
const animSprite = new Sprite(animationCanvas);
|
||||
animContainer.addChild(animSprite);
|
||||
|
||||
if(this._maskFilter)
|
||||
{
|
||||
animContainer.filters = [this._maskFilter];
|
||||
}
|
||||
|
||||
if(this._planeSprite && this._planeSprite.children)
|
||||
{
|
||||
for(const child of this._planeSprite.children)
|
||||
{
|
||||
if(child instanceof Sprite)
|
||||
{
|
||||
const maskClone = new Sprite(child.texture);
|
||||
maskClone.position.copyFrom(child.position);
|
||||
maskClone.scale.copyFrom(child.scale);
|
||||
animContainer.addChild(maskClone);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const transform = this.getMatrixForDimensions(canvasWidth, canvasHeight);
|
||||
|
||||
GetRenderer().render({
|
||||
target: this._planeTexture,
|
||||
container: animContainer,
|
||||
transform,
|
||||
clear: false
|
||||
});
|
||||
|
||||
animationCanvas.destroy(true);
|
||||
}
|
||||
|
||||
private updateCorners(geometry: IRoomGeometry): void
|
||||
{
|
||||
this._cornerA.assign(geometry.getScreenPosition(this._location));
|
||||
@@ -434,6 +582,9 @@ export class RoomPlane implements IRoomPlane
|
||||
|
||||
public resetBitmapMasks(): void
|
||||
{
|
||||
this._hasWindowMask = false;
|
||||
this._windowMasks = [];
|
||||
|
||||
if(this._disposed || !this._useMask || !this._bitmapMasks.length) return;
|
||||
|
||||
this._maskChanged = true;
|
||||
@@ -459,6 +610,12 @@ export class RoomPlane implements IRoomPlane
|
||||
return true;
|
||||
}
|
||||
|
||||
public addWindowMask(leftSideLoc: number, rightSideLoc: number): void
|
||||
{
|
||||
this._windowMasks.push({ leftSideLoc, rightSideLoc });
|
||||
this._hasWindowMask = true;
|
||||
}
|
||||
|
||||
public resetRectangleMasks(): void
|
||||
{
|
||||
if(!this._useMask || !this._rectangleMasks.length) return;
|
||||
@@ -653,4 +810,14 @@ export class RoomPlane implements IRoomPlane
|
||||
{
|
||||
this._isHighlighter = flag;
|
||||
}
|
||||
|
||||
public get hasWindowMask(): boolean
|
||||
{
|
||||
return this._hasWindowMask;
|
||||
}
|
||||
|
||||
public set hasWindowMask(flag: boolean)
|
||||
{
|
||||
this._hasWindowMask = flag;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -817,6 +817,7 @@ export class RoomVisualization extends RoomObjectSpriteVisualization implements
|
||||
if(!plane.canBeVisible) _local_6 = true;
|
||||
|
||||
plane.canBeVisible = true;
|
||||
plane.addWindowMask(leftSideLoc, rightSideLoc);
|
||||
|
||||
_local_5.push(i);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
import { IGraphicAsset } from '@nitrots/api';
|
||||
import { Point } from 'pixi.js';
|
||||
|
||||
export class AnimationItem
|
||||
{
|
||||
private _x: number;
|
||||
private _y: number;
|
||||
private _speedX: number;
|
||||
private _speedY: number;
|
||||
private _asset: IGraphicAsset;
|
||||
|
||||
constructor(x: number, y: number, speedX: number, speedY: number, asset: IGraphicAsset)
|
||||
{
|
||||
this._x = x;
|
||||
this._y = y;
|
||||
this._speedX = speedX;
|
||||
this._speedY = speedY;
|
||||
this._asset = asset;
|
||||
|
||||
if(isNaN(this._x)) this._x = 0;
|
||||
if(isNaN(this._y)) this._y = 0;
|
||||
if(isNaN(this._speedX)) this._speedX = 0;
|
||||
if(isNaN(this._speedY)) this._speedY = 0;
|
||||
}
|
||||
|
||||
public get bitmapData(): IGraphicAsset
|
||||
{
|
||||
return this._asset;
|
||||
}
|
||||
|
||||
public dispose(): void
|
||||
{
|
||||
this._asset = null;
|
||||
}
|
||||
|
||||
public getPosition(maxX: number, maxY: number, dimensionX: number, dimensionY: number, timeSinceStartMs: number): Point
|
||||
{
|
||||
let x = this._x;
|
||||
let y = this._y;
|
||||
|
||||
if(dimensionX > 0) x = (x + (((this._speedX / dimensionX) * timeSinceStartMs) / 1000));
|
||||
if(dimensionY > 0) y = (y + (((this._speedY / dimensionY) * timeSinceStartMs) / 1000));
|
||||
|
||||
const localX = Math.trunc((x % 1) * maxX);
|
||||
const localY = Math.trunc((y % 1) * maxY);
|
||||
|
||||
return new Point(localX, localY);
|
||||
}
|
||||
}
|
||||
+150
@@ -0,0 +1,150 @@
|
||||
import { IAssetPlaneVisualizationAnimatedLayerItem, IGraphicAssetCollection } from '@nitrots/api';
|
||||
import { TextureUtils } from '@nitrots/utils';
|
||||
import { RenderTexture, Sprite } from 'pixi.js';
|
||||
import { AnimationItem } from './AnimationItem';
|
||||
|
||||
export class PlaneVisualizationAnimationLayer
|
||||
{
|
||||
private _isDisposed: boolean = false;
|
||||
private _items: AnimationItem[] = [];
|
||||
|
||||
constructor(items: IAssetPlaneVisualizationAnimatedLayerItem[], assets: IGraphicAssetCollection)
|
||||
{
|
||||
if(items && assets)
|
||||
{
|
||||
for(const item of items)
|
||||
{
|
||||
if(!item) continue;
|
||||
|
||||
const assetName = item.assetId;
|
||||
|
||||
if(assetName)
|
||||
{
|
||||
const asset = assets.getAsset(assetName);
|
||||
|
||||
if(asset)
|
||||
{
|
||||
const x = this.parseCoordinate(item.x, item.randomX);
|
||||
const y = this.parseCoordinate(item.y, item.randomY);
|
||||
|
||||
this._items.push(new AnimationItem(x, y, item.speedX || 0, item.speedY || 0, asset));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private parseCoordinate(value: string, randomValue: string): number
|
||||
{
|
||||
let result = 0;
|
||||
|
||||
if(value)
|
||||
{
|
||||
if(value.includes('%'))
|
||||
{
|
||||
result = parseFloat(value.replace('%', '')) / 100;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = parseFloat(value);
|
||||
}
|
||||
}
|
||||
|
||||
if(randomValue)
|
||||
{
|
||||
const random = parseFloat(randomValue);
|
||||
if(!isNaN(random)) result += (Math.random() * random);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public get disposed(): boolean
|
||||
{
|
||||
return this._isDisposed;
|
||||
}
|
||||
|
||||
public get hasItems(): boolean
|
||||
{
|
||||
return this._items.length > 0;
|
||||
}
|
||||
|
||||
public dispose(): void
|
||||
{
|
||||
this._isDisposed = true;
|
||||
|
||||
if(this._items)
|
||||
{
|
||||
for(const item of this._items)
|
||||
{
|
||||
if(item) item.dispose();
|
||||
}
|
||||
|
||||
this._items = [];
|
||||
}
|
||||
}
|
||||
|
||||
public render(
|
||||
canvas: RenderTexture,
|
||||
offsetX: number,
|
||||
offsetY: number,
|
||||
maxX: number,
|
||||
maxY: number,
|
||||
dimensionX: number,
|
||||
dimensionY: number,
|
||||
timeSinceStartMs: number
|
||||
): RenderTexture
|
||||
{
|
||||
if(maxX <= 0 || maxY <= 0) return canvas;
|
||||
|
||||
for(const item of this._items)
|
||||
{
|
||||
if(!item || !item.bitmapData) continue;
|
||||
|
||||
const point = item.getPosition(maxX, maxY, dimensionX, dimensionY, timeSinceStartMs);
|
||||
point.x = Math.trunc(point.x - offsetX);
|
||||
point.y = Math.trunc(point.y - offsetY);
|
||||
|
||||
const assetWidth = item.bitmapData.width;
|
||||
const assetHeight = item.bitmapData.height;
|
||||
|
||||
// Render at primary position
|
||||
if(this.isVisible(point.x, point.y, assetWidth, assetHeight, canvas.width, canvas.height))
|
||||
{
|
||||
this.renderSprite(item, point.x, point.y, canvas);
|
||||
}
|
||||
|
||||
// Wrap horizontally (left side)
|
||||
if(this.isVisible(point.x - maxX, point.y, assetWidth, assetHeight, canvas.width, canvas.height))
|
||||
{
|
||||
this.renderSprite(item, point.x - maxX, point.y, canvas);
|
||||
}
|
||||
|
||||
// Wrap vertically (top side)
|
||||
if(this.isVisible(point.x, point.y - maxY, assetWidth, assetHeight, canvas.width, canvas.height))
|
||||
{
|
||||
this.renderSprite(item, point.x, point.y - maxY, canvas);
|
||||
}
|
||||
|
||||
// Wrap both (top-left corner)
|
||||
if(this.isVisible(point.x - maxX, point.y - maxY, assetWidth, assetHeight, canvas.width, canvas.height))
|
||||
{
|
||||
this.renderSprite(item, point.x - maxX, point.y - maxY, canvas);
|
||||
}
|
||||
}
|
||||
|
||||
return canvas;
|
||||
}
|
||||
|
||||
private isVisible(x: number, y: number, width: number, height: number, canvasWidth: number, canvasHeight: number): boolean
|
||||
{
|
||||
return (x > -width) && (x < canvasWidth) && (y > -height) && (y < canvasHeight);
|
||||
}
|
||||
|
||||
private renderSprite(item: AnimationItem, x: number, y: number, canvas: RenderTexture): void
|
||||
{
|
||||
const sprite = new Sprite(item.bitmapData.texture);
|
||||
sprite.position.set(x, y);
|
||||
TextureUtils.writeToTexture(sprite, canvas, false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './AnimationItem';
|
||||
export * from './PlaneVisualizationAnimationLayer';
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from './animated';
|
||||
export * from './PlaneDrawingData';
|
||||
export * from './RoomPlane';
|
||||
export * from './RoomPlaneBitmapMask';
|
||||
|
||||
Reference in New Issue
Block a user