You've already forked Nitro_Render_V3
mirror of
https://github.com/duckietm/Nitro_Render_V3.git
synced 2026-06-20 07:26:18 +00:00
🆙 Update
- Animation canvas uses full landscape height for proper vertical positioning - Animation assets always loaded from room collection - Fallback to default plane when specific plane not found
This commit is contained in:
@@ -4,4 +4,5 @@ export interface IAssetPlaneVisualizationLayer
|
|||||||
color?: number;
|
color?: number;
|
||||||
offset?: number;
|
offset?: number;
|
||||||
align?: string;
|
align?: string;
|
||||||
|
backgroundColor?: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -99,6 +99,7 @@ export class AssetManager implements IAssetManager
|
|||||||
{
|
{
|
||||||
if(!url || !url.length) return false;
|
if(!url || !url.length) return false;
|
||||||
|
|
||||||
|
// ✅ NEW: Local bundled assets (no generic.asset.url)
|
||||||
if(url.startsWith('local://'))
|
if(url.startsWith('local://'))
|
||||||
{
|
{
|
||||||
const key = url.substring('local://'.length);
|
const key = url.substring('local://'.length);
|
||||||
@@ -172,32 +173,52 @@ export class AssetManager implements IAssetManager
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ✅ Loads room assets from bundled code:
|
||||||
|
* - /packages/assets/src/assets/room/room.asset.json
|
||||||
|
* - /packages/assets/src/assets/room/images/*.png
|
||||||
|
*
|
||||||
|
* This loads individual PNG files instead of a spritesheet, then uses
|
||||||
|
* the JSON asset definitions for proper x, y offsets, flipH settings, etc.
|
||||||
|
*/
|
||||||
private async loadLocalRoom(): Promise<void>
|
private async loadLocalRoom(): Promise<void>
|
||||||
{
|
{
|
||||||
|
// 1) Load room JSON locally (bundled)
|
||||||
const roomDataModule = await import('./assets/room/room.asset.json');
|
const roomDataModule = await import('./assets/room/room.asset.json');
|
||||||
const roomData = (roomDataModule.default ?? roomDataModule) as IAssetData;
|
const roomData = (roomDataModule.default ?? roomDataModule) as IAssetData;
|
||||||
|
|
||||||
|
// 2) Create collection WITHOUT spritesheet
|
||||||
|
// Note: Constructor calls define(), but assets won't be created since textures don't exist yet
|
||||||
const collection = this.createCollection(roomData, null) as GraphicAssetCollection;
|
const collection = this.createCollection(roomData, null) as GraphicAssetCollection;
|
||||||
if(!collection) return;
|
if(!collection) return;
|
||||||
|
|
||||||
|
// 3) Load all images from the bundled assets
|
||||||
const roomImages = import.meta.glob('./assets/room/*.png', { eager: true });
|
const roomImages = import.meta.glob('./assets/room/*.png', { eager: true });
|
||||||
const roomImagesSub = import.meta.glob('./assets/room/images/*.png', { eager: true });
|
const roomImagesSub = import.meta.glob('./assets/room/images/*.png', { eager: true });
|
||||||
const merged = { ...roomImages, ...roomImagesSub };
|
const merged = { ...roomImages, ...roomImagesSub };
|
||||||
|
|
||||||
|
// 4) Register textures in the collection's _textures map
|
||||||
|
// getLibraryAsset() prepends collection name, so for asset 'wall_texture_64_0_wall_white'
|
||||||
|
// it looks for 'room_wall_texture_64_0_wall_white' in _textures
|
||||||
for(const path in merged)
|
for(const path in merged)
|
||||||
{
|
{
|
||||||
const mod = merged[path];
|
const mod = merged[path];
|
||||||
const imageUrl = (mod.default ?? mod) as string;
|
const imageUrl = (mod.default ?? mod) as string;
|
||||||
|
|
||||||
const file = path.split('/').pop()!;
|
const file = path.split('/').pop()!;
|
||||||
const rawName = file.replace(/\.png$/i, '');
|
const rawName = file.replace(/\.png$/i, ''); // e.g., "room_wall_texture_64_0_wall_white"
|
||||||
|
|
||||||
const texture = await Assets.load<Texture>(imageUrl);
|
const texture = await Assets.load<Texture>(imageUrl);
|
||||||
if(!texture) continue;
|
if(!texture) continue;
|
||||||
|
|
||||||
|
// Register in AssetManager's global _textures for direct lookups
|
||||||
this.setTexture(rawName, texture);
|
this.setTexture(rawName, texture);
|
||||||
|
|
||||||
|
// Register in collection's _textures with the full name (room_...)
|
||||||
|
// This is what getLibraryAsset() will look for when defining assets
|
||||||
collection.textures.set(rawName, texture);
|
collection.textures.set(rawName, texture);
|
||||||
|
|
||||||
|
// Also register without the "room_" prefix for direct lookups
|
||||||
if(rawName.startsWith('room_'))
|
if(rawName.startsWith('room_'))
|
||||||
{
|
{
|
||||||
const normalizedName = rawName.substring('room_'.length);
|
const normalizedName = rawName.substring('room_'.length);
|
||||||
@@ -206,6 +227,8 @@ export class AssetManager implements IAssetManager
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 5) Now call define() again to process asset definitions with correct x, y offsets
|
||||||
|
// Assets that were skipped before (no textures) will now be created properly
|
||||||
collection.define(roomData);
|
collection.define(roomData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5729,7 +5729,8 @@
|
|||||||
"allLayers": [
|
"allLayers": [
|
||||||
{
|
{
|
||||||
"materialId": "landscape_64_background_1",
|
"materialId": "landscape_64_background_1",
|
||||||
"align": "bottom"
|
"align": "bottom",
|
||||||
|
"backgroundColor": "#FEFEFE"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"items": [
|
"items": [
|
||||||
|
|||||||
@@ -83,6 +83,7 @@ export class RoomPlane implements IRoomPlane
|
|||||||
private _landscapeForegroundTint: number = 0xffffff;
|
private _landscapeForegroundTint: number = 0xffffff;
|
||||||
private _landscapeBaseAlignBottom: boolean = false;
|
private _landscapeBaseAlignBottom: boolean = false;
|
||||||
private _landscapeForegroundAlignBottom: boolean = false;
|
private _landscapeForegroundAlignBottom: boolean = false;
|
||||||
|
private _landscapeBackgroundColor: number = null;
|
||||||
private _hasWindowMask: boolean = false;
|
private _hasWindowMask: boolean = false;
|
||||||
private _windowMasks: { leftSideLoc: number; rightSideLoc: number }[] = [];
|
private _windowMasks: { leftSideLoc: number; rightSideLoc: number }[] = [];
|
||||||
|
|
||||||
@@ -256,6 +257,19 @@ export class RoomPlane implements IRoomPlane
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fall back to "default" plane if the requested landscape plane wasn't found
|
||||||
|
if(!plane && planeType === RoomPlane.TYPE_LANDSCAPE)
|
||||||
|
{
|
||||||
|
const roomCollection2 = GetAssetManager().getCollection('room');
|
||||||
|
const defaultPlaneData = roomCollection2?.data?.roomVisualization?.landscapeData;
|
||||||
|
plane = defaultPlaneData?.planes?.find(p => (p.id === 'default'));
|
||||||
|
if(plane)
|
||||||
|
{
|
||||||
|
planeVisualizationData = defaultPlaneData;
|
||||||
|
assetCollection = roomCollection2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let planeVisualization = null;
|
let planeVisualization = null;
|
||||||
if(dataType === 'landscapeData')
|
if(dataType === 'landscapeData')
|
||||||
{
|
{
|
||||||
@@ -280,6 +294,15 @@ export class RoomPlane implements IRoomPlane
|
|||||||
const baseAlignBottom = materialLayers[0]?.align === 'bottom';
|
const baseAlignBottom = materialLayers[0]?.align === 'bottom';
|
||||||
const foregroundAlignBottom = materialLayers[1]?.align === 'bottom';
|
const foregroundAlignBottom = materialLayers[1]?.align === 'bottom';
|
||||||
|
|
||||||
|
// Parse backgroundColor from the first material layer (background layer)
|
||||||
|
const backgroundColorStr = materialLayers[0]?.backgroundColor;
|
||||||
|
let backgroundColor: number = null;
|
||||||
|
if(backgroundColorStr)
|
||||||
|
{
|
||||||
|
// Convert hex string like "#FEFEFE" to number
|
||||||
|
backgroundColor = parseInt(backgroundColorStr.replace('#', ''), 16);
|
||||||
|
}
|
||||||
|
|
||||||
const selectMaterialMatrixForNormal = (matrices = [], normal = null) =>
|
const selectMaterialMatrixForNormal = (matrices = [], normal = null) =>
|
||||||
{
|
{
|
||||||
if(!matrices.length) return null;
|
if(!matrices.length) return null;
|
||||||
@@ -351,20 +374,22 @@ export class RoomPlane implements IRoomPlane
|
|||||||
const foregroundTexture = resolveTextureForMaterial(foregroundMaterialId);
|
const foregroundTexture = resolveTextureForMaterial(foregroundMaterialId);
|
||||||
|
|
||||||
const animationLayers: PlaneVisualizationAnimationLayer[] = [];
|
const animationLayers: PlaneVisualizationAnimationLayer[] = [];
|
||||||
if(planeType === RoomPlane.TYPE_LANDSCAPE && planeVisualization?.allLayers && assetCollection)
|
if(planeType === RoomPlane.TYPE_LANDSCAPE && planeVisualization?.allLayers)
|
||||||
{
|
{
|
||||||
|
// Always use the room collection for animation assets (clouds etc.) since they are stored there
|
||||||
|
const animationAssetCollection = roomCollection;
|
||||||
for(const layer of planeVisualization.allLayers)
|
for(const layer of planeVisualization.allLayers)
|
||||||
{
|
{
|
||||||
const animatedLayer = layer as IAssetPlaneVisualizationAnimatedLayer;
|
const animatedLayer = layer as IAssetPlaneVisualizationAnimatedLayer;
|
||||||
if(animatedLayer?.items && animatedLayer.items.length > 0)
|
if(animatedLayer?.items && animatedLayer.items.length > 0)
|
||||||
{
|
{
|
||||||
const animLayer = new PlaneVisualizationAnimationLayer(animatedLayer.items, assetCollection);
|
const animLayer = new PlaneVisualizationAnimationLayer(animatedLayer.items, animationAssetCollection);
|
||||||
if(animLayer.hasItems) animationLayers.push(animLayer);
|
if(animLayer.hasItems) animationLayers.push(animLayer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { texture, foregroundTexture, color: planeColor, baseAlignBottom, foregroundAlignBottom, animationLayers };
|
return { texture, foregroundTexture, color: planeColor, baseAlignBottom, foregroundAlignBottom, animationLayers, backgroundColor };
|
||||||
};
|
};
|
||||||
|
|
||||||
const planeData = getTextureAndColorForPlane(this._id, this._type, normal);
|
const planeData = getTextureAndColorForPlane(this._id, this._type, normal);
|
||||||
@@ -453,8 +478,10 @@ export class RoomPlane implements IRoomPlane
|
|||||||
|
|
||||||
this._landscapeRenderWidth = width;
|
this._landscapeRenderWidth = width;
|
||||||
this._landscapeRenderHeight = height;
|
this._landscapeRenderHeight = height;
|
||||||
this._animationCanvasWidth = renderMaxX;
|
// Use total landscape width for animation canvas, but always use actual height
|
||||||
this._animationCanvasHeight = renderMaxY;
|
// The renderMaxY is often too small (e.g., 160) while actual height is much larger (e.g., 755)
|
||||||
|
this._animationCanvasWidth = renderMaxX || width;
|
||||||
|
this._animationCanvasHeight = height; // Always use actual landscape height for animations
|
||||||
this._landscapeOffsetX = renderOffsetX;
|
this._landscapeOffsetX = renderOffsetX;
|
||||||
this._landscapeOffsetY = renderOffsetY;
|
this._landscapeOffsetY = renderOffsetY;
|
||||||
|
|
||||||
@@ -475,6 +502,7 @@ export class RoomPlane implements IRoomPlane
|
|||||||
this._landscapeForegroundTint = landscapeTint;
|
this._landscapeForegroundTint = landscapeTint;
|
||||||
this._landscapeBaseAlignBottom = planeData.baseAlignBottom ?? false;
|
this._landscapeBaseAlignBottom = planeData.baseAlignBottom ?? false;
|
||||||
this._landscapeForegroundAlignBottom = planeData.foregroundAlignBottom ?? false;
|
this._landscapeForegroundAlignBottom = planeData.foregroundAlignBottom ?? false;
|
||||||
|
this._landscapeBackgroundColor = planeData.backgroundColor ?? null;
|
||||||
|
|
||||||
this._planeSprite = new TilingSprite({
|
this._planeSprite = new TilingSprite({
|
||||||
texture: Texture.WHITE,
|
texture: Texture.WHITE,
|
||||||
@@ -535,24 +563,37 @@ export class RoomPlane implements IRoomPlane
|
|||||||
|
|
||||||
if(needsUpdate || animationUpdate)
|
if(needsUpdate || animationUpdate)
|
||||||
{
|
{
|
||||||
|
// For landscapes with a custom backgroundColor, render it first
|
||||||
|
if(this._type === RoomPlane.TYPE_LANDSCAPE && this._landscapeBackgroundColor !== null)
|
||||||
|
{
|
||||||
|
this.renderBackgroundColor();
|
||||||
|
}
|
||||||
|
|
||||||
GetRenderer().render({
|
GetRenderer().render({
|
||||||
target: this._planeTexture,
|
target: this._planeTexture,
|
||||||
container: this._planeSprite,
|
container: this._planeSprite,
|
||||||
transform: this.getMatrixForDimensions(this._planeSprite.width, this._planeSprite.height),
|
transform: this.getMatrixForDimensions(this._planeSprite.width, this._planeSprite.height),
|
||||||
clear: true
|
clear: this._landscapeBackgroundColor === null
|
||||||
});
|
});
|
||||||
|
|
||||||
if(this._isAnimated && this._type === RoomPlane.TYPE_LANDSCAPE && this._animationLayers.length > 0 && this._hasWindowMask)
|
// Layer order for landscapes:
|
||||||
{
|
// 1. Background color (rendered above)
|
||||||
this.renderAnimationLayers(timeSinceStartMs, geometry);
|
// 2. Background texture
|
||||||
}
|
// 3. Animation layers (clouds)
|
||||||
|
// 4. Foreground texture
|
||||||
|
|
||||||
if(this._type === RoomPlane.TYPE_LANDSCAPE && this._landscapeBackgroundTexture)
|
if(this._type === RoomPlane.TYPE_LANDSCAPE && this._landscapeBackgroundTexture)
|
||||||
{
|
{
|
||||||
this.renderLandscapeLayer(this._landscapeBackgroundTexture, this._landscapeBackgroundTint, this._landscapeBaseAlignBottom);
|
this.renderLandscapeLayer(this._landscapeBackgroundTexture, this._landscapeBackgroundTint, this._landscapeBaseAlignBottom);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render foreground layer for landscapes on top of background/animation
|
// Render animation layers (clouds) - between background and foreground
|
||||||
|
if(this._isAnimated && this._type === RoomPlane.TYPE_LANDSCAPE && this._animationLayers.length > 0)
|
||||||
|
{
|
||||||
|
this.renderAnimationLayers(timeSinceStartMs, geometry);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render foreground layer for landscapes on top of background and clouds
|
||||||
if(this._type === RoomPlane.TYPE_LANDSCAPE && this._landscapeForegroundTexture)
|
if(this._type === RoomPlane.TYPE_LANDSCAPE && this._landscapeForegroundTexture)
|
||||||
{
|
{
|
||||||
this.renderLandscapeLayer(this._landscapeForegroundTexture, this._landscapeForegroundTint, this._landscapeForegroundAlignBottom);
|
this.renderLandscapeLayer(this._landscapeForegroundTexture, this._landscapeForegroundTint, this._landscapeForegroundAlignBottom);
|
||||||
@@ -682,6 +723,35 @@ export class RoomPlane implements IRoomPlane
|
|||||||
layerSprite.destroy();
|
layerSprite.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private renderBackgroundColor(): void
|
||||||
|
{
|
||||||
|
if(!this._planeTexture || this._landscapeBackgroundColor === null) return;
|
||||||
|
|
||||||
|
const canvasWidth = this._landscapeRenderWidth;
|
||||||
|
const canvasHeight = this._landscapeRenderHeight;
|
||||||
|
|
||||||
|
if(canvasWidth <= 0 || canvasHeight <= 0) return;
|
||||||
|
|
||||||
|
// Create a solid color rectangle to fill the background
|
||||||
|
const colorGraphics = new Graphics();
|
||||||
|
colorGraphics.rect(0, 0, canvasWidth, canvasHeight);
|
||||||
|
colorGraphics.fill(this._landscapeBackgroundColor);
|
||||||
|
|
||||||
|
const colorContainer = new Container();
|
||||||
|
colorContainer.addChild(colorGraphics);
|
||||||
|
|
||||||
|
const transform = this.getMatrixForDimensions(canvasWidth, canvasHeight);
|
||||||
|
|
||||||
|
GetRenderer().render({
|
||||||
|
target: this._planeTexture,
|
||||||
|
container: colorContainer,
|
||||||
|
transform,
|
||||||
|
clear: true
|
||||||
|
});
|
||||||
|
|
||||||
|
colorGraphics.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
private updateCorners(geometry: IRoomGeometry): void
|
private updateCorners(geometry: IRoomGeometry): void
|
||||||
{
|
{
|
||||||
this._cornerA.assign(geometry.getScreenPosition(this._location));
|
this._cornerA.assign(geometry.getScreenPosition(this._location));
|
||||||
|
|||||||
Reference in New Issue
Block a user