Integrate Aetheria reactive state worktree
This commit is contained in:
parent
426cd8f656
commit
65133ac8a0
61 changed files with 3427 additions and 402 deletions
|
|
@ -44,6 +44,7 @@ export type AetheriaRuntimeViewportFeedSnapshot = {
|
|||
daemonHealth: DaemonHealthProjection;
|
||||
authorityStatus: AuthorityStatusProjection;
|
||||
starbridgeSession: StarbridgeSessionProjection;
|
||||
assetManifest: AssetManifestProjection;
|
||||
receivedAtUtc: string;
|
||||
sampleMs: number;
|
||||
};
|
||||
|
|
@ -108,6 +109,7 @@ export type ViewObject = {
|
|||
targetEntityIndex: number;
|
||||
isActive: boolean;
|
||||
visibility: number;
|
||||
iconAsset: AssetRef;
|
||||
status: EntityStatus;
|
||||
inventory: InventoryItem[];
|
||||
};
|
||||
|
|
@ -125,6 +127,7 @@ export type InventoryItem = {
|
|||
quality: number;
|
||||
durability: number;
|
||||
enabled: boolean;
|
||||
iconAsset: AssetRef;
|
||||
};
|
||||
|
||||
export type SelectedObjectProjection = {
|
||||
|
|
@ -218,6 +221,33 @@ export type StarbridgeStationStockItemProjection = {
|
|||
quality: number;
|
||||
durability: number;
|
||||
source: string;
|
||||
iconAsset: AssetRef;
|
||||
};
|
||||
|
||||
export type AssetManifestProjection = {
|
||||
schema: string;
|
||||
publishedAtUtc: string;
|
||||
runId: string;
|
||||
baseUri: string;
|
||||
assets: AssetManifestEntry[];
|
||||
};
|
||||
|
||||
export type AssetManifestEntry = {
|
||||
ref: AssetRef;
|
||||
sizeBytes: number;
|
||||
width: number;
|
||||
height: number;
|
||||
tags: string[];
|
||||
};
|
||||
|
||||
export type AssetRef = {
|
||||
assetKey: string;
|
||||
kind: string;
|
||||
uri: string;
|
||||
transport: string;
|
||||
contentHash: string;
|
||||
mimeType: string;
|
||||
metadata: Record<string, string>;
|
||||
};
|
||||
|
||||
export type StarbridgeWaveForecastProjection = {
|
||||
|
|
@ -269,6 +299,7 @@ export type AetheriaRtsApi = {
|
|||
daemonHealth(): Promise<DaemonHealthProjection>;
|
||||
authorityStatus(): Promise<AuthorityStatusProjection>;
|
||||
starbridgeSession(): Promise<StarbridgeSessionProjection>;
|
||||
assetManifest(): Promise<AssetManifestProjection>;
|
||||
watchViewportFeed(request: AetheriaRuntimeViewportFeedRequest, callback: (snapshot: AetheriaRuntimeViewportFeedSnapshot) => void): () => void;
|
||||
setMoveVector(request: AetheriaRuntimeSetMoveVectorRequest): Promise<AetheriaRuntimeDaemonCommandReceipt>;
|
||||
setTarget(request: AetheriaRuntimeSetTargetRequest): Promise<AetheriaRuntimeDaemonCommandReceipt>;
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import {
|
|||
describeAetheriaRuntimeRtsQueryHandles,
|
||||
describeAetheriaRuntimeRtsSurfaceCatalog,
|
||||
type AetheriaRuntimeDaemonCommandReceipt,
|
||||
type AssetManifestProjection,
|
||||
type AuthorityStatusProjection,
|
||||
type DaemonHealthProjection,
|
||||
type GravityViewportResponse,
|
||||
|
|
@ -47,6 +48,7 @@ import {
|
|||
} from "./aetheria-rts-bindings.js";
|
||||
import {
|
||||
projectAuthorityStatus,
|
||||
projectAssetManifest,
|
||||
projectDaemonHealth,
|
||||
projectGravityViewportFromFrame,
|
||||
projectInventoryFromFrame,
|
||||
|
|
@ -127,6 +129,7 @@ export class AetheriaCultMeshClient {
|
|||
daemonHealth: async () => projectDaemonHealth(await this.fetchDaemonHealthDocument()),
|
||||
authorityStatus: async () => projectAuthorityStatus(await this.fetchAuthorityPolicyDocument()),
|
||||
starbridgeSession: async () => projectStarbridgeSessionSummary(await this.fetchStarbridgeSessionSummaryDocument()),
|
||||
assetManifest: async () => projectAssetManifest(await this.fetchAssetManifestDocument()),
|
||||
};
|
||||
this.queries = createAetheriaRuntimeRtsQueryHandles(
|
||||
executors,
|
||||
|
|
@ -139,6 +142,7 @@ export class AetheriaCultMeshClient {
|
|||
daemonHealth: CultMesh.pollingQueryWatcher(executors.daemonHealth, { intervalMs: 250 }),
|
||||
authorityStatus: CultMesh.pollingQueryWatcher(executors.authorityStatus, { intervalMs: 250 }),
|
||||
starbridgeSession: CultMesh.pollingQueryWatcher(executors.starbridgeSession, { intervalMs: 250 }),
|
||||
assetManifest: CultMesh.pollingQueryWatcher(executors.assetManifest, { intervalMs: 1000 }),
|
||||
},
|
||||
);
|
||||
this.documents = createAetheriaRuntimeRtsDocuments(
|
||||
|
|
@ -148,6 +152,7 @@ export class AetheriaCultMeshClient {
|
|||
daemonHealth: async () => this.fetchDaemonHealthDocument(),
|
||||
authorityPolicy: async () => this.fetchAuthorityPolicyDocument(),
|
||||
starbridgeSession: async () => this.fetchStarbridgeSessionSummaryDocument(),
|
||||
assetManifest: async () => this.fetchAssetManifestDocument(),
|
||||
},
|
||||
);
|
||||
this.operations = createAetheriaRuntimeRtsOperationHandles(
|
||||
|
|
@ -223,6 +228,9 @@ export class AetheriaCultMeshClient {
|
|||
return this.aetheria.daemon.starbridgeSession();
|
||||
}
|
||||
|
||||
public async assetManifest(): Promise<AssetManifestProjection> {
|
||||
return this.aetheria.daemon.assetManifest();
|
||||
}
|
||||
|
||||
public projectionDiagnostics(): Readonly<Record<string, AetheriaRuntimeRtsProjectionDiagnostic>> {
|
||||
return describeAetheriaRuntimeRtsQueryHandles(this.queries);
|
||||
|
|
@ -285,6 +293,9 @@ export class AetheriaCultMeshClient {
|
|||
return this.fetchPublicationDocument(AetheriaRtsSchemas.starbridgeSessionSummary);
|
||||
}
|
||||
|
||||
private async fetchAssetManifestDocument(): Promise<unknown> {
|
||||
return this.fetchPublicationDocument(AetheriaRtsSchemas.assetManifest);
|
||||
}
|
||||
|
||||
private fetchPublicationDocument(schemaId: string): Promise<unknown> {
|
||||
return this.publications.latest({ schemaId }, this.queryContext());
|
||||
|
|
@ -321,6 +332,7 @@ export class AetheriaCultMeshClient {
|
|||
...this.queries.daemonHealth.sources,
|
||||
...this.queries.authorityStatus.sources,
|
||||
...this.queries.starbridgeSession.sources,
|
||||
...this.queries.assetManifest.sources,
|
||||
],
|
||||
routeHint: this.queryVerse.context.routeHint,
|
||||
watchFeed: (request, context, callback) => {
|
||||
|
|
@ -363,6 +375,7 @@ export class AetheriaCultMeshClient {
|
|||
daemonHealth,
|
||||
authorityStatus,
|
||||
starbridgeSession,
|
||||
assetManifest,
|
||||
selectedObject,
|
||||
inventory,
|
||||
] = await Promise.all([
|
||||
|
|
@ -370,6 +383,7 @@ export class AetheriaCultMeshClient {
|
|||
this.aetheria.daemon.health(),
|
||||
this.aetheria.daemon.authorityStatus(),
|
||||
this.aetheria.daemon.starbridgeSession(),
|
||||
this.aetheria.daemon.assetManifest(),
|
||||
selectedEntityIndex >= 0
|
||||
? this.aetheria.selectedObject(selectedEntityIndex)
|
||||
: Promise.resolve(null),
|
||||
|
|
@ -385,6 +399,7 @@ export class AetheriaCultMeshClient {
|
|||
daemonHealth,
|
||||
authorityStatus,
|
||||
starbridgeSession,
|
||||
assetManifest,
|
||||
receivedAtUtc: new Date().toISOString(),
|
||||
sampleMs: performance.now() - startedAt,
|
||||
};
|
||||
|
|
@ -514,6 +529,17 @@ function createAetheriaPublicationDocuments(
|
|||
),
|
||||
localPath: `${statePath}.daemon.starbridge.session.cc`,
|
||||
},
|
||||
{
|
||||
...CultMesh.publicationDocument(
|
||||
AetheriaRtsSchemas.assetManifest,
|
||||
"daemon:aetheria.asset_manifest.latest.v1",
|
||||
{
|
||||
documentId: "daemon:aetheria.asset_manifest.latest.v1",
|
||||
sourceId: "daemon:aetheria.asset_manifest.latest.v1",
|
||||
},
|
||||
),
|
||||
localPath: `${statePath}.daemon.assets.cc`,
|
||||
},
|
||||
];
|
||||
|
||||
return documents.map(document => mode === "remote" && document.remoteRecordKey
|
||||
|
|
|
|||
|
|
@ -30,6 +30,9 @@ export type {
|
|||
AetheriaRuntimeSetTargetRequest as RtsSetTargetRequest,
|
||||
AetheriaRuntimeViewportFeedRequest,
|
||||
AetheriaRuntimeViewportFeedSnapshot,
|
||||
AssetManifestEntry,
|
||||
AssetManifestProjection,
|
||||
AssetRef,
|
||||
AuthorityRuleProjection,
|
||||
AuthorityStatusProjection,
|
||||
BodyView,
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ export const AetheriaRtsSchemas = {
|
|||
eveCommand: "gamecult.eve.command.v1",
|
||||
daemonFrame: "gamecult.aetheria.daemon_frame.v1",
|
||||
daemonHealth: "gamecult.aetheria.daemon_health.v1",
|
||||
assetManifest: "gamecult.aetheria.asset_manifest.v1",
|
||||
rtsViewport: "gamecult.aetheria.rts_viewport.v1",
|
||||
objectsViewport: "gamecult.aetheria.objects_viewport.v1",
|
||||
gravityViewport: "gamecult.aetheria.gravity_viewport.v1",
|
||||
|
|
@ -110,6 +111,7 @@ export const AetheriaRtsIpcChannels = {
|
|||
daemonHealth: "aetheria-rts:daemon-health",
|
||||
authorityStatus: "aetheria-rts:authority-status",
|
||||
starbridgeSession: "aetheria-rts:starbridge-session",
|
||||
assetManifest: "aetheria-rts:asset-manifest",
|
||||
setMoveVector: "aetheria-rts:set-move-vector",
|
||||
setTarget: "aetheria-rts:set-target",
|
||||
surfaceCatalog: "aetheria-rts:surface-catalog",
|
||||
|
|
@ -299,6 +301,55 @@ export const aetheriaRuntimeDaemonHealthDocumentSlots: Record<AetheriaRuntimeDae
|
|||
commandBoundaryPath: 12,
|
||||
};
|
||||
|
||||
export type AetheriaRuntimeAssetRefSlot =
|
||||
| "assetKey"
|
||||
| "kind"
|
||||
| "uri"
|
||||
| "transport"
|
||||
| "contentHash"
|
||||
| "mimeType"
|
||||
| "metadata";
|
||||
|
||||
export const aetheriaRuntimeAssetRefSlots: Record<AetheriaRuntimeAssetRefSlot, number> = {
|
||||
assetKey: 0,
|
||||
kind: 1,
|
||||
uri: 2,
|
||||
transport: 3,
|
||||
contentHash: 4,
|
||||
mimeType: 5,
|
||||
metadata: 6,
|
||||
};
|
||||
|
||||
export type AetheriaRuntimeAssetManifestDocumentSlot =
|
||||
| "schema"
|
||||
| "publishedAtUtc"
|
||||
| "runId"
|
||||
| "baseUri"
|
||||
| "assets";
|
||||
|
||||
export const aetheriaRuntimeAssetManifestDocumentSlots: Record<AetheriaRuntimeAssetManifestDocumentSlot, number> = {
|
||||
schema: 0,
|
||||
publishedAtUtc: 1,
|
||||
runId: 2,
|
||||
baseUri: 3,
|
||||
assets: 4,
|
||||
};
|
||||
|
||||
export type AetheriaRuntimeAssetManifestEntrySlot =
|
||||
| "ref"
|
||||
| "sizeBytes"
|
||||
| "width"
|
||||
| "height"
|
||||
| "tags";
|
||||
|
||||
export const aetheriaRuntimeAssetManifestEntrySlots: Record<AetheriaRuntimeAssetManifestEntrySlot, number> = {
|
||||
ref: 0,
|
||||
sizeBytes: 1,
|
||||
width: 2,
|
||||
height: 3,
|
||||
tags: 4,
|
||||
};
|
||||
|
||||
export type AetheriaRuntimeRtsViewportDocumentSlot =
|
||||
| "schema"
|
||||
| "frameId"
|
||||
|
|
@ -1045,7 +1096,8 @@ export type AetheriaRuntimeRtsViewportObjectSlot =
|
|||
| "isActive"
|
||||
| "visibility"
|
||||
| "status"
|
||||
| "inventory";
|
||||
| "inventory"
|
||||
| "iconAsset";
|
||||
|
||||
export const aetheriaRuntimeRtsViewportObjectSlots: Record<AetheriaRuntimeRtsViewportObjectSlot, number> = {
|
||||
entityIndex: 0,
|
||||
|
|
@ -1066,6 +1118,7 @@ export const aetheriaRuntimeRtsViewportObjectSlots: Record<AetheriaRuntimeRtsVie
|
|||
visibility: 15,
|
||||
status: 16,
|
||||
inventory: 17,
|
||||
iconAsset: 18,
|
||||
};
|
||||
|
||||
export type AetheriaRuntimeRtsEntityStatusSlot =
|
||||
|
|
@ -1088,7 +1141,8 @@ export type AetheriaRuntimeRtsInventoryItemSlot =
|
|||
| "enabled"
|
||||
| "sourceIndex"
|
||||
| "x"
|
||||
| "y";
|
||||
| "y"
|
||||
| "iconAsset";
|
||||
|
||||
export const aetheriaRuntimeRtsInventoryItemSlots: Record<AetheriaRuntimeRtsInventoryItemSlot, number> = {
|
||||
source: 0,
|
||||
|
|
@ -1100,6 +1154,7 @@ export const aetheriaRuntimeRtsInventoryItemSlots: Record<AetheriaRuntimeRtsInve
|
|||
sourceIndex: 6,
|
||||
x: 7,
|
||||
y: 8,
|
||||
iconAsset: 9,
|
||||
};
|
||||
|
||||
export type AetheriaRuntimeRtsGravityInfluenceSlot =
|
||||
|
|
@ -1240,7 +1295,8 @@ export type AetheriaRuntimeStarbridgeStationStockItemSlot =
|
|||
| "quantity"
|
||||
| "quality"
|
||||
| "durability"
|
||||
| "source";
|
||||
| "source"
|
||||
| "iconAsset";
|
||||
|
||||
export const aetheriaRuntimeStarbridgeStationStockItemSlots: Record<AetheriaRuntimeStarbridgeStationStockItemSlot, number> = {
|
||||
itemKey: 0,
|
||||
|
|
@ -1248,6 +1304,7 @@ export const aetheriaRuntimeStarbridgeStationStockItemSlots: Record<AetheriaRunt
|
|||
quality: 2,
|
||||
durability: 3,
|
||||
source: 4,
|
||||
iconAsset: 5,
|
||||
};
|
||||
|
||||
export type AetheriaRuntimeStarbridgeWaveDefinitionSlot =
|
||||
|
|
@ -1446,6 +1503,7 @@ export type ViewObject = {
|
|||
targetEntityIndex: number;
|
||||
isActive: boolean;
|
||||
visibility: number;
|
||||
iconAsset: AssetRef;
|
||||
status: EntityStatus;
|
||||
inventory: InventoryItem[];
|
||||
};
|
||||
|
|
@ -1463,6 +1521,7 @@ export type InventoryItem = {
|
|||
quality: number;
|
||||
durability: number;
|
||||
enabled: boolean;
|
||||
iconAsset: AssetRef;
|
||||
};
|
||||
|
||||
export type SelectedObjectProjection = {
|
||||
|
|
@ -1556,6 +1615,33 @@ export type StarbridgeStationStockItemProjection = {
|
|||
quality: number;
|
||||
durability: number;
|
||||
source: string;
|
||||
iconAsset: AssetRef;
|
||||
};
|
||||
|
||||
export type AssetManifestProjection = {
|
||||
schema: string;
|
||||
publishedAtUtc: string;
|
||||
runId: string;
|
||||
baseUri: string;
|
||||
assets: AssetManifestEntry[];
|
||||
};
|
||||
|
||||
export type AssetManifestEntry = {
|
||||
ref: AssetRef;
|
||||
sizeBytes: number;
|
||||
width: number;
|
||||
height: number;
|
||||
tags: string[];
|
||||
};
|
||||
|
||||
export type AssetRef = {
|
||||
assetKey: string;
|
||||
kind: string;
|
||||
uri: string;
|
||||
transport: string;
|
||||
contentHash: string;
|
||||
mimeType: string;
|
||||
metadata: Record<string, string>;
|
||||
};
|
||||
|
||||
export type StarbridgeWaveForecastProjection = {
|
||||
|
|
@ -1632,6 +1718,7 @@ export type AetheriaRuntimeViewportFeedSnapshot = {
|
|||
daemonHealth: DaemonHealthProjection;
|
||||
authorityStatus: AuthorityStatusProjection;
|
||||
starbridgeSession: StarbridgeSessionProjection;
|
||||
assetManifest: AssetManifestProjection;
|
||||
receivedAtUtc: string;
|
||||
sampleMs: number;
|
||||
};
|
||||
|
|
@ -1645,6 +1732,7 @@ export type AetheriaRtsMainClient = {
|
|||
daemonHealth(): Promise<DaemonHealthProjection>;
|
||||
authorityStatus(): Promise<AuthorityStatusProjection>;
|
||||
starbridgeSession(): Promise<StarbridgeSessionProjection>;
|
||||
assetManifest(): Promise<AssetManifestProjection>;
|
||||
watchViewportFeed(request: AetheriaRuntimeViewportFeedRequest, callback: (snapshot: AetheriaRuntimeViewportFeedSnapshot) => void): CultMeshUnsubscribe;
|
||||
setMoveVector(request: AetheriaRuntimeSetMoveVectorRequest): Promise<AetheriaRuntimeDaemonCommandReceipt>;
|
||||
setTarget(request: AetheriaRuntimeSetTargetRequest): Promise<AetheriaRuntimeDaemonCommandReceipt>;
|
||||
|
|
@ -1673,6 +1761,7 @@ export function registerAetheriaRtsIpcHandlers(
|
|||
ipcMain.handle(AetheriaRtsIpcChannels.daemonHealth, async () => getClient().daemonHealth());
|
||||
ipcMain.handle(AetheriaRtsIpcChannels.authorityStatus, async () => getClient().authorityStatus());
|
||||
ipcMain.handle(AetheriaRtsIpcChannels.starbridgeSession, async () => getClient().starbridgeSession());
|
||||
ipcMain.handle(AetheriaRtsIpcChannels.assetManifest, async () => getClient().assetManifest());
|
||||
ipcMain.handle(AetheriaRtsIpcChannels.viewportFeed, async (event, request: { subscriptionId: string; feed: AetheriaRuntimeViewportFeedRequest }) => {
|
||||
viewportFeedSubscriptions.get(request.subscriptionId)?.();
|
||||
const unsubscribe = getClient().watchViewportFeed(request.feed, snapshot => {
|
||||
|
|
@ -1722,6 +1811,7 @@ export type AetheriaRuntimeRtsQueryExecutors = {
|
|||
daemonHealth: AetheriaRuntimeQueryExecutor<void, DaemonHealthProjection>;
|
||||
authorityStatus: AetheriaRuntimeQueryExecutor<void, AuthorityStatusProjection>;
|
||||
starbridgeSession: AetheriaRuntimeQueryExecutor<void, StarbridgeSessionProjection>;
|
||||
assetManifest: AetheriaRuntimeQueryExecutor<void, AssetManifestProjection>;
|
||||
};
|
||||
|
||||
export type AetheriaRuntimeRtsQueryWatchers = Partial<{
|
||||
|
|
@ -1733,6 +1823,7 @@ export type AetheriaRuntimeRtsQueryWatchers = Partial<{
|
|||
daemonHealth: CultMeshQueryWatcher<void, DaemonHealthProjection>;
|
||||
authorityStatus: CultMeshQueryWatcher<void, AuthorityStatusProjection>;
|
||||
starbridgeSession: CultMeshQueryWatcher<void, StarbridgeSessionProjection>;
|
||||
assetManifest: CultMeshQueryWatcher<void, AssetManifestProjection>;
|
||||
}>;
|
||||
|
||||
export type AetheriaRuntimeRtsProjectionDiagnostic = CultMeshQuerySurfaceDiagnostic;
|
||||
|
|
@ -1752,6 +1843,7 @@ export type AetheriaRuntimeRtsDocumentResolvers = Partial<{
|
|||
daemonHealth: (context: CultMeshQueryContext) => Promise<unknown>;
|
||||
authorityPolicy: (context: CultMeshQueryContext) => Promise<unknown>;
|
||||
starbridgeSession: (context: CultMeshQueryContext) => Promise<unknown>;
|
||||
assetManifest: (context: CultMeshQueryContext) => Promise<unknown>;
|
||||
}>;
|
||||
|
||||
export type AetheriaRuntimeRtsStatePointerResolvers = AetheriaRuntimeRtsDocumentResolvers;
|
||||
|
|
@ -1772,6 +1864,10 @@ export const AetheriaRuntimeRtsProjectionSources = {
|
|||
starbridgeSession: CultMesh.projectionSource("daemon:aetheria.starbridge.session.latest.v1", {
|
||||
schemaId: AetheriaRtsSchemas.starbridgeSessionSummary,
|
||||
description: "latest Starbridge session summary"
|
||||
}),
|
||||
assetManifest: CultMesh.projectionSource("daemon:aetheria.asset_manifest.latest.v1", {
|
||||
schemaId: AetheriaRtsSchemas.assetManifest,
|
||||
description: "latest daemon asset manifest"
|
||||
})
|
||||
} as const;
|
||||
|
||||
|
|
@ -1816,6 +1912,15 @@ export function createAetheriaRuntimeRtsDocuments(
|
|||
sources: [AetheriaRuntimeRtsProjectionSources.starbridgeSession],
|
||||
},
|
||||
),
|
||||
assetManifest: CultMesh.document(
|
||||
AetheriaRuntimeRtsProjectionSources.assetManifest.sourceId,
|
||||
{ schemaId: AetheriaRtsSchemas.assetManifest },
|
||||
async (context) => resolvers.assetManifest?.(context),
|
||||
{
|
||||
routeHint,
|
||||
sources: [AetheriaRuntimeRtsProjectionSources.assetManifest],
|
||||
},
|
||||
),
|
||||
} as const;
|
||||
}
|
||||
|
||||
|
|
@ -1875,6 +1980,12 @@ export function createAetheriaRuntimeRtsQueryHandles(
|
|||
executors.starbridgeSession,
|
||||
{ routeHint, watchProjection: watchers.starbridgeSession },
|
||||
).asQuerySurface(),
|
||||
assetManifest: CultMesh.projectionRecipe<void, AssetManifestProjection>(
|
||||
AetheriaRtsSchemas.assetManifest,
|
||||
[AetheriaRuntimeRtsProjectionSources.assetManifest],
|
||||
executors.assetManifest,
|
||||
{ routeHint, watchProjection: watchers.assetManifest },
|
||||
).asQuerySurface(),
|
||||
} as const;
|
||||
}
|
||||
|
||||
|
|
@ -1890,6 +2001,7 @@ export function describeAetheriaRuntimeRtsQueryHandles(
|
|||
daemonHealth: describeAetheriaRuntimeRtsQuerySurface(handles.daemonHealth),
|
||||
authorityStatus: describeAetheriaRuntimeRtsQuerySurface(handles.authorityStatus),
|
||||
starbridgeSession: describeAetheriaRuntimeRtsQuerySurface(handles.starbridgeSession),
|
||||
assetManifest: describeAetheriaRuntimeRtsQuerySurface(handles.assetManifest),
|
||||
} as const;
|
||||
}
|
||||
|
||||
|
|
@ -1905,6 +2017,7 @@ export function describeAetheriaRuntimeRtsSurfaceCatalog(
|
|||
CultMesh.describeSurface(documents.daemonHealth),
|
||||
CultMesh.describeSurface(documents.authorityPolicy),
|
||||
CultMesh.describeSurface(documents.starbridgeSession),
|
||||
CultMesh.describeSurface(documents.assetManifest),
|
||||
CultMesh.describeSurface(handles.mapViewport),
|
||||
CultMesh.describeSurface(handles.objectsViewport),
|
||||
CultMesh.describeSurface(handles.gravityViewport),
|
||||
|
|
@ -1913,6 +2026,7 @@ export function describeAetheriaRuntimeRtsSurfaceCatalog(
|
|||
CultMesh.describeSurface(handles.daemonHealth),
|
||||
CultMesh.describeSurface(handles.authorityStatus),
|
||||
CultMesh.describeSurface(handles.starbridgeSession),
|
||||
CultMesh.describeSurface(handles.assetManifest),
|
||||
...(operations
|
||||
? [
|
||||
CultMesh.describeSurface(operations.setMoveVector),
|
||||
|
|
@ -1998,10 +2112,12 @@ export function createAetheriaRuntimeRtsVerseFacade(
|
|||
const daemonHealth = CultMesh.bindQuery(queryVerse, queries.daemonHealth);
|
||||
const authorityStatus = CultMesh.bindQuery(queryVerse, queries.authorityStatus);
|
||||
const starbridgeSession = CultMesh.bindQuery(queryVerse, queries.starbridgeSession);
|
||||
const assetManifest = CultMesh.bindQuery(queryVerse, queries.assetManifest);
|
||||
const daemonFrameDocument = CultMesh.bindDocument(queryVerse, documents.daemonFrame);
|
||||
const daemonHealthDocument = CultMesh.bindDocument(queryVerse, documents.daemonHealth);
|
||||
const authorityPolicyDocument = CultMesh.bindDocument(queryVerse, documents.authorityPolicy);
|
||||
const starbridgeSessionDocument = CultMesh.bindDocument(queryVerse, documents.starbridgeSession);
|
||||
const assetManifestDocument = CultMesh.bindDocument(queryVerse, documents.assetManifest);
|
||||
const setMoveVector = CultMesh.bindOperation(commandVerse, operations.setMoveVector);
|
||||
const setTarget = CultMesh.bindOperation(commandVerse, operations.setTarget);
|
||||
|
||||
|
|
@ -2064,10 +2180,12 @@ export function createAetheriaRuntimeRtsVerseFacade(
|
|||
health: () => daemonHealthDocument.latest(),
|
||||
authorityPolicy: () => authorityPolicyDocument.latest(),
|
||||
starbridgeSession: () => starbridgeSessionDocument.latest(),
|
||||
assetManifest: () => assetManifestDocument.latest(),
|
||||
},
|
||||
health: () => daemonHealth.execute(undefined),
|
||||
authorityStatus: () => authorityStatus.execute(undefined),
|
||||
starbridgeSession: () => starbridgeSession.execute(undefined),
|
||||
assetManifest: () => assetManifest.execute(undefined),
|
||||
},
|
||||
} as const;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
import {
|
||||
AetheriaRtsSchemas,
|
||||
aetheriaRuntimeAssetManifestDocumentSlots as assetManifestSlots,
|
||||
aetheriaRuntimeAssetManifestEntrySlots as assetManifestEntrySlots,
|
||||
aetheriaRuntimeAssetRefSlots as assetRefSlots,
|
||||
aetheriaRuntimeAuthorityRuleSlots as authorityRuleSlots,
|
||||
aetheriaRuntimeBodySnapshotCommitSlots as bodySlots,
|
||||
aetheriaRuntimeCargoBayLoadoutCommitSlots as cargoBaySlots,
|
||||
|
|
@ -20,6 +23,8 @@ import {
|
|||
} from "./aetheria-rts-generated-bindings.js";
|
||||
import { cultMeshRectFromBounds, cultMeshViewportRequest } from "cultmesh-ts";
|
||||
import type {
|
||||
AssetManifestProjection,
|
||||
AssetRef,
|
||||
AuthorityStatusProjection,
|
||||
BodyView,
|
||||
DaemonHealthProjection,
|
||||
|
|
@ -222,6 +227,17 @@ export function projectStarbridgeSessionSummary(summaryDocument: unknown): Starb
|
|||
};
|
||||
}
|
||||
|
||||
export function projectAssetManifest(assetManifestDocument: unknown): AssetManifestProjection {
|
||||
const manifest = arr(assetManifestDocument);
|
||||
return {
|
||||
schema: str(manifest[assetManifestSlots.schema]) || AetheriaRtsSchemas.assetManifest,
|
||||
publishedAtUtc: str(manifest[assetManifestSlots.publishedAtUtc]),
|
||||
runId: str(manifest[assetManifestSlots.runId]),
|
||||
baseUri: str(manifest[assetManifestSlots.baseUri]),
|
||||
assets: list<unknown[]>(manifest[assetManifestSlots.assets]).map(toAssetManifestEntry),
|
||||
};
|
||||
}
|
||||
|
||||
function frameContext(frameDocument: unknown): {
|
||||
frameId: number;
|
||||
runId: string;
|
||||
|
|
@ -251,11 +267,13 @@ function normalizeViewport(request: ViewportRequest): ViewportRequest {
|
|||
}
|
||||
|
||||
function toViewObject(entity: unknown[], runId: string, zoneIndex: number): ViewObject {
|
||||
const kind = str(entity[entitySlots.kind]);
|
||||
const controlled = isPlayerControlled(entity);
|
||||
return {
|
||||
entityIndex: num(entity[entitySlots.entityIndex], -1),
|
||||
entityKey: entityKey(runId, zoneIndex, num(entity[entitySlots.entityIndex], -1)),
|
||||
displayName: str(entity[entitySlots.name]),
|
||||
kind: str(entity[entitySlots.kind]),
|
||||
kind,
|
||||
factionKey: str(entity[entitySlots.factionKey]),
|
||||
x: num(entity[entitySlots.positionX]),
|
||||
y: num(entity[entitySlots.positionZ]),
|
||||
|
|
@ -264,10 +282,11 @@ function toViewObject(entity: unknown[], runId: string, zoneIndex: number): View
|
|||
directionY: num(entity[entitySlots.directionY]),
|
||||
velocityX: num(entity[entitySlots.velocityX]),
|
||||
velocityY: num(entity[entitySlots.velocityY]),
|
||||
controlled: isPlayerControlled(entity),
|
||||
controlled,
|
||||
targetEntityIndex: num(entity[entitySlots.targetEntityIndex], -1),
|
||||
isActive: bool(entity[entitySlots.isActive]),
|
||||
visibility: num(entity[entitySlots.visibility]),
|
||||
iconAsset: entityIconAsset(kind, controlled),
|
||||
status: {
|
||||
hull: stat(entity, "hull"),
|
||||
shield: stat(entity, "shield"),
|
||||
|
|
@ -289,12 +308,14 @@ function toStarbridgeBaseStatus(status: unknown[]): StarbridgeSessionProjection[
|
|||
}
|
||||
|
||||
function toStarbridgeStationStockItem(item: unknown[]): StarbridgeSessionProjection["stationStock"][number] {
|
||||
const itemKey = str(item[starbridgeStockSlots.itemKey]);
|
||||
return {
|
||||
itemKey: str(item[starbridgeStockSlots.itemKey]),
|
||||
itemKey,
|
||||
quantity: num(item[starbridgeStockSlots.quantity]),
|
||||
quality: num(item[starbridgeStockSlots.quality]),
|
||||
durability: num(item[starbridgeStockSlots.durability]),
|
||||
source: str(item[starbridgeStockSlots.source]) || "station",
|
||||
iconAsset: assetRef(arr(item[starbridgeStockSlots.iconAsset]), itemIconAsset(itemKey)),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -359,13 +380,15 @@ function inventory(entity: unknown[]): InventoryItem[] {
|
|||
|
||||
function addSlot(items: InventoryItem[], source: string, slot: unknown[]): void {
|
||||
const item = arr(slot[itemSlotSlots.item]);
|
||||
const itemKey = str(item[itemSlots.itemKey]);
|
||||
items.push({
|
||||
source,
|
||||
itemKey: str(item[itemSlots.itemKey]),
|
||||
itemKey,
|
||||
quantity: num(item[itemSlots.quantity]),
|
||||
quality: num(item[itemSlots.quality]),
|
||||
durability: num(item[itemSlots.durability]),
|
||||
enabled: bool(item[itemSlots.enabled]),
|
||||
iconAsset: itemIconAsset(itemKey),
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -416,6 +439,84 @@ function entityKey(runId: string, zoneIndex: number, entityIndex: number): strin
|
|||
return `global:aetheria.run_state.${runId}.zone.${zoneIndex}.entity.${entityIndex}.v1`;
|
||||
}
|
||||
|
||||
function toAssetManifestEntry(entry: unknown[]): AssetManifestProjection["assets"][number] {
|
||||
return {
|
||||
ref: assetRef(arr(entry[assetManifestEntrySlots.ref])),
|
||||
sizeBytes: num(entry[assetManifestEntrySlots.sizeBytes]),
|
||||
width: num(entry[assetManifestEntrySlots.width]),
|
||||
height: num(entry[assetManifestEntrySlots.height]),
|
||||
tags: stringList(entry[assetManifestEntrySlots.tags]),
|
||||
};
|
||||
}
|
||||
|
||||
function entityIconAsset(kind: string, controlled: boolean): AssetRef {
|
||||
if (controlled)
|
||||
return spriteAsset("map.entity.player", "Sprites/Map/player");
|
||||
|
||||
const normalized = kind.trim().toLowerCase();
|
||||
if (normalized.includes("station"))
|
||||
return spriteAsset("map.entity.station", "Sprites/Map/station");
|
||||
if (normalized.includes("orbital"))
|
||||
return spriteAsset("map.entity.orbital", "Sprites/Map/orbital");
|
||||
|
||||
return spriteAsset("map.entity.ship", "Sprites/Map/ship");
|
||||
}
|
||||
|
||||
function itemIconAsset(itemKey: string): AssetRef {
|
||||
const key = itemKey.trim();
|
||||
return key.length > 0
|
||||
? textureAsset(`item.${key}.icon`, `item.${key}.icon`)
|
||||
: emptyAsset("texture");
|
||||
}
|
||||
|
||||
function spriteAsset(assetKey: string, resourcePath: string): AssetRef {
|
||||
return asset(assetKey, "sprite", `resources://${resourcePath}`, "resources", "image/*");
|
||||
}
|
||||
|
||||
function textureAsset(assetKey: string, resourcePath: string): AssetRef {
|
||||
return asset(assetKey, "texture", resourcePath.startsWith("resources://") ? resourcePath : `resources://${resourcePath}`, "resources", "image/*");
|
||||
}
|
||||
|
||||
function assetRef(value: unknown[], fallback: AssetRef = emptyAsset()): AssetRef {
|
||||
const assetKey = str(value[assetRefSlots.assetKey]);
|
||||
if (assetKey.length === 0)
|
||||
return fallback;
|
||||
|
||||
return asset(
|
||||
assetKey,
|
||||
str(value[assetRefSlots.kind]),
|
||||
str(value[assetRefSlots.uri]),
|
||||
str(value[assetRefSlots.transport]),
|
||||
str(value[assetRefSlots.mimeType]),
|
||||
str(value[assetRefSlots.contentHash]),
|
||||
stringRecord(value[assetRefSlots.metadata]),
|
||||
);
|
||||
}
|
||||
|
||||
function asset(
|
||||
assetKey: string,
|
||||
kind: string,
|
||||
uri: string,
|
||||
transport: string,
|
||||
mimeType: string,
|
||||
contentHash = "",
|
||||
metadata: Record<string, string> = {},
|
||||
): AssetRef {
|
||||
return {
|
||||
assetKey,
|
||||
kind,
|
||||
uri,
|
||||
transport,
|
||||
contentHash,
|
||||
mimeType,
|
||||
metadata,
|
||||
};
|
||||
}
|
||||
|
||||
function emptyAsset(kind = ""): AssetRef {
|
||||
return asset("", kind, "", "", "");
|
||||
}
|
||||
|
||||
function arr(value: unknown): unknown[] {
|
||||
return Array.isArray(value) ? value : [];
|
||||
}
|
||||
|
|
@ -432,6 +533,17 @@ function stringList(value: unknown): string[] {
|
|||
return Array.isArray(value) ? value.map(candidate => str(candidate)).filter(candidate => candidate.length > 0) : [];
|
||||
}
|
||||
|
||||
function stringRecord(value: unknown): Record<string, string> {
|
||||
if (!value || typeof value !== "object" || Array.isArray(value))
|
||||
return {};
|
||||
|
||||
return Object.fromEntries(
|
||||
Object.entries(value)
|
||||
.map(([key, candidate]) => [key, str(candidate)])
|
||||
.filter(([, candidate]) => candidate.length > 0),
|
||||
);
|
||||
}
|
||||
|
||||
function str(value: unknown): string {
|
||||
return typeof value === "string" ? value : "";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -147,6 +147,7 @@ async function runElectronSmoke(window: BrowserWindow): Promise<Record<string, u
|
|||
const health = api ? await api.daemonHealth() : null;
|
||||
const authority = api ? await api.authorityStatus() : null;
|
||||
const starbridge = api ? await api.starbridgeSession() : null;
|
||||
const assetManifest = api ? await api.assetManifest() : null;
|
||||
const surfaceCatalog = api ? await api.surfaceCatalog() : null;
|
||||
const surfaceCatalogIndex = api ? await api.surfaceCatalogIndex() : null;
|
||||
const viewport = api ? await api.mapViewport({ minX: -5000, minY: -5000, maxX: 5000, maxY: 5000 }) : null;
|
||||
|
|
@ -168,6 +169,7 @@ async function runElectronSmoke(window: BrowserWindow): Promise<Record<string, u
|
|||
typeof api.daemonHealth === "function" &&
|
||||
typeof api.authorityStatus === "function" &&
|
||||
typeof api.starbridgeSession === "function" &&
|
||||
typeof api.assetManifest === "function" &&
|
||||
typeof api.surfaceCatalog === "function" &&
|
||||
typeof api.surfaceCatalogIndex === "function",
|
||||
status,
|
||||
|
|
@ -183,6 +185,7 @@ async function runElectronSmoke(window: BrowserWindow): Promise<Record<string, u
|
|||
health,
|
||||
authority,
|
||||
starbridge,
|
||||
assetManifest,
|
||||
surfaceCatalog,
|
||||
surfaceCatalogIndex,
|
||||
moveReceipt
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ const channels = {
|
|||
daemonHealth: "aetheria-rts:daemon-health",
|
||||
authorityStatus: "aetheria-rts:authority-status",
|
||||
starbridgeSession: "aetheria-rts:starbridge-session",
|
||||
assetManifest: "aetheria-rts:asset-manifest",
|
||||
setMoveVector: "aetheria-rts:set-move-vector",
|
||||
setTarget: "aetheria-rts:set-target",
|
||||
surfaceCatalog: "aetheria-rts:surface-catalog",
|
||||
|
|
@ -45,6 +46,7 @@ contextBridge.exposeInMainWorld("aetheriaRts", {
|
|||
daemonHealth: () => ipcRenderer.invoke(channels.daemonHealth),
|
||||
authorityStatus: () => ipcRenderer.invoke(channels.authorityStatus),
|
||||
starbridgeSession: () => ipcRenderer.invoke(channels.starbridgeSession),
|
||||
assetManifest: () => ipcRenderer.invoke(channels.assetManifest),
|
||||
setMoveVector: request => ipcRenderer.invoke(channels.setMoveVector, request),
|
||||
setTarget: request => ipcRenderer.invoke(channels.setTarget, request),
|
||||
surfaceCatalog: () => ipcRenderer.invoke(channels.surfaceCatalog),
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ const repoRoot = resolve(root, "..");
|
|||
const sources = [
|
||||
resolve(repoRoot, "Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeDaemonDocuments.cs"),
|
||||
resolve(repoRoot, "Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeEveCommandDocument.cs"),
|
||||
resolve(repoRoot, "Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeAssetDocuments.cs"),
|
||||
resolve(repoRoot, "Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeSnapshotDocuments.cs"),
|
||||
resolve(repoRoot, "Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeRtsViewportDocuments.cs"),
|
||||
resolve(repoRoot, "Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeStarbridgeDocuments.cs"),
|
||||
|
|
@ -31,6 +32,7 @@ const ipcChannels = [
|
|||
["daemonHealth", "aetheria-rts:daemon-health"],
|
||||
["authorityStatus", "aetheria-rts:authority-status"],
|
||||
["starbridgeSession", "aetheria-rts:starbridge-session"],
|
||||
["assetManifest", "aetheria-rts:asset-manifest"],
|
||||
["setMoveVector", "aetheria-rts:set-move-vector"],
|
||||
["setTarget", "aetheria-rts:set-target"],
|
||||
["surfaceCatalog", "aetheria-rts:surface-catalog"],
|
||||
|
|
@ -71,6 +73,19 @@ const documents = [
|
|||
className: "AetheriaRuntimeDaemonHealthDocument",
|
||||
schemaName: "daemonHealth",
|
||||
},
|
||||
{
|
||||
exportName: "aetheriaRuntimeAssetRef",
|
||||
className: "AetheriaRuntimeAssetRef",
|
||||
},
|
||||
{
|
||||
exportName: "aetheriaRuntimeAssetManifestDocument",
|
||||
className: "AetheriaRuntimeAssetManifestDocument",
|
||||
schemaName: "assetManifest",
|
||||
},
|
||||
{
|
||||
exportName: "aetheriaRuntimeAssetManifestEntry",
|
||||
className: "AetheriaRuntimeAssetManifestEntry",
|
||||
},
|
||||
{
|
||||
exportName: "aetheriaRuntimeRtsViewportDocument",
|
||||
className: "AetheriaRuntimeRtsViewportDocument",
|
||||
|
|
@ -285,18 +300,12 @@ if (check) {
|
|||
|
||||
function readExisting(path) {
|
||||
try {
|
||||
return normalizeGeneratedText(readFileSync(path, "utf8"));
|
||||
return readFileSync(path, "utf8");
|
||||
} catch {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeGeneratedText(text) {
|
||||
return text
|
||||
.replace(/^\uFEFF/u, "")
|
||||
.replace(/\r\n/gu, "\n");
|
||||
}
|
||||
|
||||
function parseEnum(text, enumName) {
|
||||
const body = readTypeBody(text, `enum ${enumName}`);
|
||||
const entries = [];
|
||||
|
|
@ -438,6 +447,7 @@ export type AetheriaRuntimeViewportFeedSnapshot = {
|
|||
daemonHealth: DaemonHealthProjection;
|
||||
authorityStatus: AuthorityStatusProjection;
|
||||
starbridgeSession: StarbridgeSessionProjection;
|
||||
assetManifest: AssetManifestProjection;
|
||||
receivedAtUtc: string;
|
||||
sampleMs: number;
|
||||
};
|
||||
|
|
@ -451,6 +461,7 @@ export type AetheriaRtsMainClient = {
|
|||
daemonHealth(): Promise<DaemonHealthProjection>;
|
||||
authorityStatus(): Promise<AuthorityStatusProjection>;
|
||||
starbridgeSession(): Promise<StarbridgeSessionProjection>;
|
||||
assetManifest(): Promise<AssetManifestProjection>;
|
||||
watchViewportFeed(request: AetheriaRuntimeViewportFeedRequest, callback: (snapshot: AetheriaRuntimeViewportFeedSnapshot) => void): CultMeshUnsubscribe;
|
||||
setMoveVector(request: AetheriaRuntimeSetMoveVectorRequest): Promise<AetheriaRuntimeDaemonCommandReceipt>;
|
||||
setTarget(request: AetheriaRuntimeSetTargetRequest): Promise<AetheriaRuntimeDaemonCommandReceipt>;
|
||||
|
|
@ -479,6 +490,7 @@ export function registerAetheriaRtsIpcHandlers(
|
|||
ipcMain.handle(AetheriaRtsIpcChannels.daemonHealth, async () => getClient().daemonHealth());
|
||||
ipcMain.handle(AetheriaRtsIpcChannels.authorityStatus, async () => getClient().authorityStatus());
|
||||
ipcMain.handle(AetheriaRtsIpcChannels.starbridgeSession, async () => getClient().starbridgeSession());
|
||||
ipcMain.handle(AetheriaRtsIpcChannels.assetManifest, async () => getClient().assetManifest());
|
||||
ipcMain.handle(AetheriaRtsIpcChannels.viewportFeed, async (event, request: { subscriptionId: string; feed: AetheriaRuntimeViewportFeedRequest }) => {
|
||||
viewportFeedSubscriptions.get(request.subscriptionId)?.();
|
||||
const unsubscribe = getClient().watchViewportFeed(request.feed, snapshot => {
|
||||
|
|
@ -528,6 +540,7 @@ export type AetheriaRuntimeRtsQueryExecutors = {
|
|||
daemonHealth: AetheriaRuntimeQueryExecutor<void, DaemonHealthProjection>;
|
||||
authorityStatus: AetheriaRuntimeQueryExecutor<void, AuthorityStatusProjection>;
|
||||
starbridgeSession: AetheriaRuntimeQueryExecutor<void, StarbridgeSessionProjection>;
|
||||
assetManifest: AetheriaRuntimeQueryExecutor<void, AssetManifestProjection>;
|
||||
};
|
||||
|
||||
export type AetheriaRuntimeRtsQueryWatchers = Partial<{
|
||||
|
|
@ -539,6 +552,7 @@ export type AetheriaRuntimeRtsQueryWatchers = Partial<{
|
|||
daemonHealth: CultMeshQueryWatcher<void, DaemonHealthProjection>;
|
||||
authorityStatus: CultMeshQueryWatcher<void, AuthorityStatusProjection>;
|
||||
starbridgeSession: CultMeshQueryWatcher<void, StarbridgeSessionProjection>;
|
||||
assetManifest: CultMeshQueryWatcher<void, AssetManifestProjection>;
|
||||
}>;
|
||||
|
||||
export type AetheriaRuntimeRtsProjectionDiagnostic = CultMeshQuerySurfaceDiagnostic;
|
||||
|
|
@ -558,6 +572,7 @@ export type AetheriaRuntimeRtsDocumentResolvers = Partial<{
|
|||
daemonHealth: (context: CultMeshQueryContext) => Promise<unknown>;
|
||||
authorityPolicy: (context: CultMeshQueryContext) => Promise<unknown>;
|
||||
starbridgeSession: (context: CultMeshQueryContext) => Promise<unknown>;
|
||||
assetManifest: (context: CultMeshQueryContext) => Promise<unknown>;
|
||||
}>;
|
||||
|
||||
export type AetheriaRuntimeRtsStatePointerResolvers = AetheriaRuntimeRtsDocumentResolvers;
|
||||
|
|
@ -578,6 +593,10 @@ export const AetheriaRuntimeRtsProjectionSources = {
|
|||
starbridgeSession: CultMesh.projectionSource("daemon:aetheria.starbridge.session.latest.v1", {
|
||||
schemaId: AetheriaRtsSchemas.starbridgeSessionSummary,
|
||||
description: "latest Starbridge session summary"
|
||||
}),
|
||||
assetManifest: CultMesh.projectionSource("daemon:aetheria.asset_manifest.latest.v1", {
|
||||
schemaId: AetheriaRtsSchemas.assetManifest,
|
||||
description: "latest daemon asset manifest"
|
||||
})
|
||||
} as const;
|
||||
|
||||
|
|
@ -622,6 +641,15 @@ export function createAetheriaRuntimeRtsDocuments(
|
|||
sources: [AetheriaRuntimeRtsProjectionSources.starbridgeSession],
|
||||
},
|
||||
),
|
||||
assetManifest: CultMesh.document(
|
||||
AetheriaRuntimeRtsProjectionSources.assetManifest.sourceId,
|
||||
{ schemaId: AetheriaRtsSchemas.assetManifest },
|
||||
async (context) => resolvers.assetManifest?.(context),
|
||||
{
|
||||
routeHint,
|
||||
sources: [AetheriaRuntimeRtsProjectionSources.assetManifest],
|
||||
},
|
||||
),
|
||||
} as const;
|
||||
}
|
||||
|
||||
|
|
@ -681,6 +709,12 @@ export function createAetheriaRuntimeRtsQueryHandles(
|
|||
executors.starbridgeSession,
|
||||
{ routeHint, watchProjection: watchers.starbridgeSession },
|
||||
).asQuerySurface(),
|
||||
assetManifest: CultMesh.projectionRecipe<void, AssetManifestProjection>(
|
||||
AetheriaRtsSchemas.assetManifest,
|
||||
[AetheriaRuntimeRtsProjectionSources.assetManifest],
|
||||
executors.assetManifest,
|
||||
{ routeHint, watchProjection: watchers.assetManifest },
|
||||
).asQuerySurface(),
|
||||
} as const;
|
||||
}
|
||||
|
||||
|
|
@ -696,6 +730,7 @@ export function describeAetheriaRuntimeRtsQueryHandles(
|
|||
daemonHealth: describeAetheriaRuntimeRtsQuerySurface(handles.daemonHealth),
|
||||
authorityStatus: describeAetheriaRuntimeRtsQuerySurface(handles.authorityStatus),
|
||||
starbridgeSession: describeAetheriaRuntimeRtsQuerySurface(handles.starbridgeSession),
|
||||
assetManifest: describeAetheriaRuntimeRtsQuerySurface(handles.assetManifest),
|
||||
} as const;
|
||||
}
|
||||
|
||||
|
|
@ -711,6 +746,7 @@ export function describeAetheriaRuntimeRtsSurfaceCatalog(
|
|||
CultMesh.describeSurface(documents.daemonHealth),
|
||||
CultMesh.describeSurface(documents.authorityPolicy),
|
||||
CultMesh.describeSurface(documents.starbridgeSession),
|
||||
CultMesh.describeSurface(documents.assetManifest),
|
||||
CultMesh.describeSurface(handles.mapViewport),
|
||||
CultMesh.describeSurface(handles.objectsViewport),
|
||||
CultMesh.describeSurface(handles.gravityViewport),
|
||||
|
|
@ -719,6 +755,7 @@ export function describeAetheriaRuntimeRtsSurfaceCatalog(
|
|||
CultMesh.describeSurface(handles.daemonHealth),
|
||||
CultMesh.describeSurface(handles.authorityStatus),
|
||||
CultMesh.describeSurface(handles.starbridgeSession),
|
||||
CultMesh.describeSurface(handles.assetManifest),
|
||||
...(operations
|
||||
? [
|
||||
CultMesh.describeSurface(operations.setMoveVector),
|
||||
|
|
@ -804,10 +841,12 @@ export function createAetheriaRuntimeRtsVerseFacade(
|
|||
const daemonHealth = CultMesh.bindQuery(queryVerse, queries.daemonHealth);
|
||||
const authorityStatus = CultMesh.bindQuery(queryVerse, queries.authorityStatus);
|
||||
const starbridgeSession = CultMesh.bindQuery(queryVerse, queries.starbridgeSession);
|
||||
const assetManifest = CultMesh.bindQuery(queryVerse, queries.assetManifest);
|
||||
const daemonFrameDocument = CultMesh.bindDocument(queryVerse, documents.daemonFrame);
|
||||
const daemonHealthDocument = CultMesh.bindDocument(queryVerse, documents.daemonHealth);
|
||||
const authorityPolicyDocument = CultMesh.bindDocument(queryVerse, documents.authorityPolicy);
|
||||
const starbridgeSessionDocument = CultMesh.bindDocument(queryVerse, documents.starbridgeSession);
|
||||
const assetManifestDocument = CultMesh.bindDocument(queryVerse, documents.assetManifest);
|
||||
const setMoveVector = CultMesh.bindOperation(commandVerse, operations.setMoveVector);
|
||||
const setTarget = CultMesh.bindOperation(commandVerse, operations.setTarget);
|
||||
|
||||
|
|
@ -870,10 +909,12 @@ export function createAetheriaRuntimeRtsVerseFacade(
|
|||
health: () => daemonHealthDocument.latest(),
|
||||
authorityPolicy: () => authorityPolicyDocument.latest(),
|
||||
starbridgeSession: () => starbridgeSessionDocument.latest(),
|
||||
assetManifest: () => assetManifestDocument.latest(),
|
||||
},
|
||||
health: () => daemonHealth.execute(undefined),
|
||||
authorityStatus: () => authorityStatus.execute(undefined),
|
||||
starbridgeSession: () => starbridgeSession.execute(undefined),
|
||||
assetManifest: () => assetManifest.execute(undefined),
|
||||
},
|
||||
} as const;
|
||||
}
|
||||
|
|
@ -991,6 +1032,7 @@ export type AetheriaRuntimeViewportFeedSnapshot = {
|
|||
daemonHealth: DaemonHealthProjection;
|
||||
authorityStatus: AuthorityStatusProjection;
|
||||
starbridgeSession: StarbridgeSessionProjection;
|
||||
assetManifest: AssetManifestProjection;
|
||||
receivedAtUtc: string;
|
||||
sampleMs: number;
|
||||
};
|
||||
|
|
@ -1006,6 +1048,7 @@ export type AetheriaRtsApi = {
|
|||
daemonHealth(): Promise<DaemonHealthProjection>;
|
||||
authorityStatus(): Promise<AuthorityStatusProjection>;
|
||||
starbridgeSession(): Promise<StarbridgeSessionProjection>;
|
||||
assetManifest(): Promise<AssetManifestProjection>;
|
||||
watchViewportFeed(request: AetheriaRuntimeViewportFeedRequest, callback: (snapshot: AetheriaRuntimeViewportFeedSnapshot) => void): () => void;
|
||||
setMoveVector(request: AetheriaRuntimeSetMoveVectorRequest): Promise<AetheriaRuntimeDaemonCommandReceipt>;
|
||||
setTarget(request: AetheriaRuntimeSetTargetRequest): Promise<AetheriaRuntimeDaemonCommandReceipt>;
|
||||
|
|
@ -1077,6 +1120,7 @@ export type ViewObject = {
|
|||
targetEntityIndex: number;
|
||||
isActive: boolean;
|
||||
visibility: number;
|
||||
iconAsset: AssetRef;
|
||||
status: EntityStatus;
|
||||
inventory: InventoryItem[];
|
||||
};
|
||||
|
|
@ -1094,6 +1138,7 @@ export type InventoryItem = {
|
|||
quality: number;
|
||||
durability: number;
|
||||
enabled: boolean;
|
||||
iconAsset: AssetRef;
|
||||
};
|
||||
|
||||
export type SelectedObjectProjection = {
|
||||
|
|
@ -1187,6 +1232,33 @@ export type StarbridgeStationStockItemProjection = {
|
|||
quality: number;
|
||||
durability: number;
|
||||
source: string;
|
||||
iconAsset: AssetRef;
|
||||
};
|
||||
|
||||
export type AssetManifestProjection = {
|
||||
schema: string;
|
||||
publishedAtUtc: string;
|
||||
runId: string;
|
||||
baseUri: string;
|
||||
assets: AssetManifestEntry[];
|
||||
};
|
||||
|
||||
export type AssetManifestEntry = {
|
||||
ref: AssetRef;
|
||||
sizeBytes: number;
|
||||
width: number;
|
||||
height: number;
|
||||
tags: string[];
|
||||
};
|
||||
|
||||
export type AssetRef = {
|
||||
assetKey: string;
|
||||
kind: string;
|
||||
uri: string;
|
||||
transport: string;
|
||||
contentHash: string;
|
||||
mimeType: string;
|
||||
metadata: Record<string, string>;
|
||||
};
|
||||
|
||||
export type StarbridgeWaveForecastProjection = {
|
||||
|
|
@ -1261,6 +1333,7 @@ function renderPreload() {
|
|||
name === "daemonHealth" ||
|
||||
name === "authorityStatus" ||
|
||||
name === "starbridgeSession" ||
|
||||
name === "assetManifest" ||
|
||||
name === "surfaceCatalog" ||
|
||||
name === "surfaceCatalogIndex";
|
||||
const parameters = hasNoArguments ? "()" : "request";
|
||||
|
|
|
|||
|
|
@ -167,6 +167,7 @@ static async Task<AetheriaRuntimeDaemonTickResult> TickAsync(
|
|||
PreRejectedCommandIds = policyRejectedCommandIds,
|
||||
CumulativeAppliedCommandIds = currentFrame?.CumulativeAppliedCommandIds ?? currentFrame?.AppliedCommandIds ?? Array.Empty<string>(),
|
||||
CumulativeRejectedCommandIds = currentFrame?.CumulativeRejectedCommandIds ?? currentFrame?.RejectedCommandIds ?? Array.Empty<string>(),
|
||||
Catalog = node.OpenRuntimeCatalog(),
|
||||
StarbridgeScenario = starbridgeScenario,
|
||||
StarbridgeSession = starbridgeSession,
|
||||
PublishWitnesses = publishWitnesses,
|
||||
|
|
|
|||
|
|
@ -983,7 +983,10 @@ static void RequirePackageSerializerBoundary(string root)
|
|||
"AetheriaRuntimeDaemonDocuments.cs",
|
||||
"AetheriaRuntimeDaemonSoaDocuments.cs",
|
||||
"AetheriaRuntimeRtsViewportDocuments.cs",
|
||||
"AetheriaRuntimeAssetDocuments.cs",
|
||||
"AetheriaRuntimeRenderSplatDocuments.cs",
|
||||
"AetheriaRuntimeStarbridgeDocuments.cs",
|
||||
"AetheriaRuntimeStarbridgePlayerSeatDocuments.cs",
|
||||
"AetheriaRuntimeVerseAuthorityPolicy.cs"
|
||||
};
|
||||
|
||||
|
|
@ -1688,9 +1691,9 @@ static void RequireTypedOrbitConsumerKeys(string root)
|
|||
{
|
||||
"private readonly List<AetheriaRuntimeDaemonBodyView> _daemonBodyViews",
|
||||
"private void SyncDaemonBodyViews()",
|
||||
"private AetheriaRuntimeGravityViewportDocument _daemonGravityViewport;",
|
||||
"GravityViewportAsync(viewport)",
|
||||
"foreach (var bodyView in _daemonGravityViewport?.Bodies ?? Array.Empty<AetheriaRuntimeRtsBodyView>())",
|
||||
"private IReadOnlyList<AetheriaRuntimeBodySnapshotCommit> _zoneRenderBodies",
|
||||
"_zoneRenderBodies = render?.Bodies ?? Array.Empty<AetheriaRuntimeBodySnapshotCommit>();",
|
||||
"foreach (var bodyPose in _zoneRenderBodyPoses ?? Array.Empty<AetheriaRuntimeZoneRenderBodyPose>())",
|
||||
"ResolveDaemonRenderViewport()",
|
||||
"private AetheriaRuntimeXzRect ResolveDaemonRenderViewport()",
|
||||
"private void UnloadBodyView(string bodyKey)",
|
||||
|
|
@ -2345,7 +2348,7 @@ static void RequireDaemonRenderQueryAuthority(string root)
|
|||
"private string _daemonCurrentEntityKey = \"\";",
|
||||
"private double _daemonSimulationTimeSeconds;",
|
||||
"private readonly List<AetheriaRuntimeDaemonBodyView> _daemonBodyViews",
|
||||
"private AetheriaRuntimeGravityViewportDocument _daemonGravityViewport;",
|
||||
"private IReadOnlyList<AetheriaRuntimeBodySnapshotCommit> _zoneRenderBodies",
|
||||
"private readonly List<AetheriaRuntimeZoneRenderBodyPose> _daemonBodyPoses",
|
||||
"private readonly Dictionary<string, AetheriaRuntimeZoneRenderBodyPose> _daemonBodyPosesByBodyKey",
|
||||
"private readonly Dictionary<string, PlanetObject> _bodyViewsByBodyKey",
|
||||
|
|
@ -2373,14 +2376,14 @@ static void RequireDaemonRenderQueryAuthority(string root)
|
|||
"public void ApplyZoneRender(AetheriaRuntimeZoneRenderDocument render)",
|
||||
"_daemonCurrentEntityKey = render?.CurrentEntityKey ?? \"\";",
|
||||
"_daemonSimulationTimeSeconds = render?.SimulationTimeSeconds ?? 0;",
|
||||
"_zoneRenderBodies = render?.Bodies ?? Array.Empty<AetheriaRuntimeBodySnapshotCommit>();",
|
||||
"_observedEntityFacadesByDaemonIndex = observedEntityFacadesByDaemonIndex;",
|
||||
"render?.ZoneRenderRadius ?? 2000",
|
||||
"private readonly HashSet<string> _daemonVisibleBodyKeys",
|
||||
"private void SyncDaemonBodyViews()",
|
||||
"GravityViewportAsync(viewport)",
|
||||
"foreach (var bodyView in _daemonBodyViews)",
|
||||
"if (bodyView.IsAsteroidBelt)",
|
||||
"foreach (var bodyView in _daemonGravityViewport?.Bodies ?? Array.Empty<AetheriaRuntimeRtsBodyView>())",
|
||||
"foreach (var bodyPose in _zoneRenderBodyPoses ?? Array.Empty<AetheriaRuntimeZoneRenderBodyPose>())",
|
||||
"ResolveDaemonRenderViewport()",
|
||||
"private AetheriaRuntimeXzRect ResolveDaemonRenderViewport()",
|
||||
"private static AetheriaRuntimeRtsViewportBounds ToViewportBounds(AetheriaRuntimeXzRect viewport)",
|
||||
|
|
@ -2409,7 +2412,9 @@ static void RequireDaemonRenderQueryAuthority(string root)
|
|||
"foreach (var beltPose in _daemonAsteroidBeltPoses)",
|
||||
"beltPose.InstancePoses ?? Array.Empty<AetheriaRuntimeZoneRenderAsteroidInstancePose>()",
|
||||
"private void RefreshDaemonContactRows()",
|
||||
"ZoneContactsAsync()",
|
||||
".Aetheria()",
|
||||
".ZoneContacts",
|
||||
".LatestAsync()",
|
||||
"private void RefreshDaemonCompassMarkers()",
|
||||
"private void RefreshDaemonVisibleEntityInstances()",
|
||||
"PowerPulse(",
|
||||
|
|
@ -2514,7 +2519,9 @@ static void RequireDaemonRenderQueryAuthority(string root)
|
|||
{
|
||||
"private AetheriaRuntimeCurrentEntityDocument _currentEntityDocument;",
|
||||
"ResolveCurrentEntityHudStatus()",
|
||||
".CurrentEntityAsync()",
|
||||
".Current",
|
||||
".Entity",
|
||||
".LatestAsync()",
|
||||
"hud.OverrideShutdown",
|
||||
"hud.HeatsinksEnabled",
|
||||
"hud.Heatstroke",
|
||||
|
|
@ -4472,7 +4479,9 @@ static void RequireSectorMapZoneDetailsUseEveSurface(string root)
|
|||
"AetheriaRuntimeZoneDetailsSurfaceBuilder.Project(",
|
||||
"ProjectZoneDetailsSurfaceState(",
|
||||
"private AetheriaClient _client",
|
||||
"SectorMapAsync()",
|
||||
".Aetheria()",
|
||||
".SectorMap",
|
||||
".LatestAsync()",
|
||||
"ZoneDetailsAsync(zoneIndex)",
|
||||
"OpenRuntimeCatalog()",
|
||||
"PlayerSettingsAsync()",
|
||||
|
|
@ -4646,7 +4655,8 @@ static void RequireRuntimeMenuTabsUseEveSurface(string root)
|
|||
"AetheriaRuntimeMenuTabsSurfaceBuilder.Project(",
|
||||
"new AetheriaRuntimeMenuTabProjectionOption(",
|
||||
"ResolveVisibleTabs(",
|
||||
"CurrentDockingAsync()",
|
||||
".DockingState",
|
||||
".Latest()",
|
||||
"ResolveCurrentDocking()?.IsDocked == true",
|
||||
"AetheriaClient",
|
||||
"OpenLocalAsync(",
|
||||
|
|
@ -4748,12 +4758,12 @@ static void RequireRuntimeMenuTabsUseEveSurface(string root)
|
|||
var requiredDockedStoryObserverSymbols = new[]
|
||||
{
|
||||
"TryResolveDockedLocalStory(out _currentLocation)",
|
||||
"private bool TryResolveCurrentDocking(out AetheriaRuntimeCurrentDockingDocument docking)",
|
||||
"private bool TryResolveDockingState(out AetheriaClientDockingSnapshot dockingState)",
|
||||
"private bool TryResolveDockedLocalStory(out LocationStory story)",
|
||||
"SetObservedFacadeIndex(AetheriaUnityObservedFacadeIndex observedFacadeIndex)",
|
||||
"_observedFacadeIndex.TryResolveDockingBayByRecordKey(",
|
||||
"docking.DockParentEntityKey",
|
||||
"docking.DockingBayIndex",
|
||||
"dockingState.CurrentDocking.DockParentEntityKey",
|
||||
"dockingState.CurrentDocking.DockingBayIndex",
|
||||
"dockingBay?.Entity is not OrbitalEntity { Story: { } dockedStory }",
|
||||
"AetheriaRuntimeLocalStorySurfaceBuilder.Build(ProjectStorySurface())",
|
||||
"AetheriaRuntimeLocalStorySurfaceBuilder.Project(",
|
||||
|
|
@ -4761,7 +4771,8 @@ static void RequireRuntimeMenuTabsUseEveSurface(string root)
|
|||
"AetheriaRuntimeLocalStoryCommandKind.Continue",
|
||||
"AetheriaRuntimeLocalStoryCommandKind.Choose",
|
||||
"new AetheriaRuntimeLocalStoryChoiceState(",
|
||||
"CurrentDockingAsync()",
|
||||
".DockingState",
|
||||
".Latest()",
|
||||
"unity-runtime-local-story"
|
||||
};
|
||||
var dockedStoryObserverCorpus = localMenu;
|
||||
|
|
@ -4873,15 +4884,15 @@ static void RequireInventoryShipSettingsUseEveSurface(string root)
|
|||
"ResolveDefaultShutdownPerformance()",
|
||||
"ResolvePlayerSettings()?.DefaultShutdownPerformance",
|
||||
"TryResolveCurrentEntityDocument(out var currentEntity)",
|
||||
"TryGetTypedCurrentEntityFacade(out var currentEntity)",
|
||||
"SetObservedFacadeIndex(AetheriaUnityObservedFacadeIndex observedFacadeIndex)",
|
||||
"_observedFacadeIndex.TryResolveEntityByRecordKey",
|
||||
"private AetheriaRuntimeCurrentEntityDocument _shipSettingsCurrentEntity;",
|
||||
"!TryResolveCurrentEntityDocument(out var latestCurrentEntity)",
|
||||
"RequestEntityShutdownPerformance(",
|
||||
"latestCurrentEntity.EntityKey",
|
||||
"(float)latestCurrentEntity.ShutdownPerformance",
|
||||
"CurrentEntityAsync()"
|
||||
".DockingState",
|
||||
".Latest()",
|
||||
"?.CurrentEntity"
|
||||
};
|
||||
|
||||
var missingSymbols = requiredSymbols
|
||||
|
|
@ -5589,8 +5600,8 @@ static void RequireTradeCargoSelectorUseEveSurface(string root)
|
|||
"AetheriaRuntimeTradeCargoSelectorSurfaceCommands.TryRead(request, out var command)",
|
||||
"AetheriaRuntimeTradeCargoSelectorCommandKind.Close",
|
||||
"AetheriaRuntimeTradeCargoSelectorCommandKind.Select",
|
||||
"CurrentDockingAsync()",
|
||||
"StationRefitAsync()",
|
||||
".DockingState",
|
||||
".Latest()",
|
||||
"SetTargetCargo(",
|
||||
"selection.EntityKey",
|
||||
"OwnedQuantity",
|
||||
|
|
@ -6148,7 +6159,8 @@ static void RequireInventoryDropdownUseEveSurface(string root)
|
|||
"SetObservedFacadeIndex(AetheriaUnityObservedFacadeIndex observedFacadeIndex)",
|
||||
"_observedFacadeIndex.TryResolveEntityByRecordKey",
|
||||
"_observedFacadeIndex.TryResolveDockingBayByRecordKey",
|
||||
"CurrentDockingAsync",
|
||||
".DockingState",
|
||||
".Latest()",
|
||||
"LoadoutRestoreOptions"
|
||||
};
|
||||
|
||||
|
|
@ -6214,7 +6226,9 @@ static void RequireInventoryDropdownUseEveSurface(string root)
|
|||
}
|
||||
|
||||
if (!inventoryMenu.Contains("TryGetTypedCurrentDockingBayFacade(out var dockingBay)", StringComparison.Ordinal) ||
|
||||
!inventoryMenu.Contains("CurrentDockingAsync()", StringComparison.Ordinal) ||
|
||||
!inventoryMenu.Contains(".DockingState", StringComparison.Ordinal) ||
|
||||
!inventoryMenu.Contains(".Latest()", StringComparison.Ordinal) ||
|
||||
!inventoryMenu.Contains("ResolveDockingState()?.CurrentDocking", StringComparison.Ordinal) ||
|
||||
!inventoryMenu.Contains("_observedFacadeIndex.TryResolveDockingBayByRecordKey", StringComparison.Ordinal) ||
|
||||
inventoryMenu.Contains("GameManager.TryGetObservedDockingBay(", StringComparison.Ordinal) ||
|
||||
inventoryMenu.Contains("GameManager.DockingBay", StringComparison.Ordinal))
|
||||
|
|
@ -6373,12 +6387,11 @@ static void RequireStationRefitDockingBaysUseTypedProjection(string root)
|
|||
|
||||
var requiredPanelSymbols = new[]
|
||||
{
|
||||
"TryResolveCurrentDockingBayRow(out _)",
|
||||
"TryResolveCurrentDockingBayRow(out var currentDockingBay)",
|
||||
"AetheriaRuntimeStationDockingBayRow dockingBay",
|
||||
"stationRefit.DockingBays",
|
||||
"row.DockingBayIndex == stationRefit.DockingBayIndex",
|
||||
"dockingBayRow.DockingBayIndex"
|
||||
"currentDockingBay.DockingBayIndex"
|
||||
};
|
||||
var missingPanelSymbols = requiredPanelSymbols
|
||||
.Where(symbol => !inventoryPanel.Contains(symbol, StringComparison.Ordinal))
|
||||
|
|
@ -6393,11 +6406,10 @@ static void RequireStationRefitDockingBaysUseTypedProjection(string root)
|
|||
|
||||
var requiredMenuSymbols = new[]
|
||||
{
|
||||
"TryResolveCurrentDockingBayRow(out var dockingBayRow)",
|
||||
"TryResolveCurrentDockingBayRow(out AetheriaRuntimeStationDockingBayRow dockingBay)",
|
||||
"AetheriaRuntimeStationDockingBayRow dockingBay",
|
||||
"stationRefit.DockingBays",
|
||||
"row.DockingBayIndex == stationRefit.DockingBayIndex",
|
||||
"dockingBayRow.DockingBayIndex"
|
||||
"row.DockingBayIndex == stationRefit.DockingBayIndex"
|
||||
};
|
||||
var missingMenuSymbols = requiredMenuSymbols
|
||||
.Where(symbol => !inventoryMenu.Contains(symbol, StringComparison.Ordinal))
|
||||
|
|
@ -7354,7 +7366,9 @@ static void RequireClientTargetBootAuthority(string root)
|
|||
"CurrentStateBoot()",
|
||||
"LatestSectorMap(AetheriaRuntimeStateBootReport stateBoot)",
|
||||
"AetheriaClient",
|
||||
".SectorMapAsync()",
|
||||
".Aetheria()",
|
||||
".SectorMap",
|
||||
".LatestAsync()",
|
||||
"LatestVerseHostSettings(AetheriaRuntimeStateBootReport stateBoot)",
|
||||
"AetheriaState.At(AetheriaUnityRuntimePaths.GameDataDirectory)",
|
||||
".ClientTarget",
|
||||
|
|
@ -9194,9 +9208,10 @@ static void RequireAetheriaRuntimeVerseClientContract(string root)
|
|||
var clientPath = Path.Combine(root, "Packages", "org.gamecult.aetheria.state", "Runtime", "AetheriaRuntimeVerseClient.cs");
|
||||
var surfaceStatePath = Path.Combine(root, "Packages", "org.gamecult.aetheria.state", "Runtime", "AetheriaRuntimeEveSurfaceState.cs");
|
||||
var oldSurfaceStatePath = Path.Combine(root, "Aetheria.State", "Documents", "EveSurfaceState.cs");
|
||||
var stateReaderPath = Path.Combine(root, "Packages", "org.gamecult.aetheria.state", "Runtime", "AetheriaRuntimeStateReader.cs");
|
||||
var docPath = Path.Combine(root, "docs", "aetheria-verse-client-contract.md");
|
||||
|
||||
var requiredFiles = new[] { clientPath, surfaceStatePath, docPath };
|
||||
var requiredFiles = new[] { clientPath, surfaceStatePath, stateReaderPath, docPath };
|
||||
var missingFiles = requiredFiles
|
||||
.Where(path => !File.Exists(path))
|
||||
.Select(path => Path.GetRelativePath(root, path))
|
||||
|
|
@ -9210,6 +9225,7 @@ static void RequireAetheriaRuntimeVerseClientContract(string root)
|
|||
|
||||
var client = File.ReadAllText(clientPath);
|
||||
var surfaceState = File.ReadAllText(surfaceStatePath);
|
||||
var stateReader = File.ReadAllText(stateReaderPath);
|
||||
var doc = File.ReadAllText(docPath);
|
||||
|
||||
if (File.Exists(oldSurfaceStatePath))
|
||||
|
|
@ -9246,8 +9262,7 @@ static void RequireAetheriaRuntimeVerseClientContract(string root)
|
|||
"VerseHostSettingsAsync()",
|
||||
"GetLoadoutTemplatesAsync()",
|
||||
"CreateEveSurfaceStateRefResolver(",
|
||||
"AetheriaRuntimeStateReader.TryResolveDaemonStateRef(",
|
||||
"AetheriaRuntimeStateReader.TryResolveDaemonItemStatRef(",
|
||||
"AetheriaRuntimeStateReader.CreateEveSurfaceCultMeshStateRefResolver(",
|
||||
"SubmitDaemonCommandAsync(",
|
||||
"SubmitEveCommandAsync(",
|
||||
"AetheriaRuntimeVerseRecordKeys.DaemonCommand(command.CommandId)",
|
||||
|
|
@ -9265,6 +9280,21 @@ static void RequireAetheriaRuntimeVerseClientContract(string root)
|
|||
string.Join(", ", missingClientSymbols));
|
||||
}
|
||||
|
||||
var requiredStateReaderSymbols = new[]
|
||||
{
|
||||
"TryResolveDaemonStateRef(",
|
||||
"TryResolveDaemonItemStatRef("
|
||||
};
|
||||
var missingStateReaderSymbols = requiredStateReaderSymbols
|
||||
.Where(symbol => !stateReader.Contains(symbol, StringComparison.Ordinal))
|
||||
.ToArray();
|
||||
if (missingStateReaderSymbols.Length > 0)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"Aetheria runtime state reader is missing typed Eve state-ref resolver symbols: " +
|
||||
string.Join(", ", missingStateReaderSymbols));
|
||||
}
|
||||
|
||||
if (client.Contains("public async Task<AetheriaRuntimeDaemonCommandEnvelope> SubmitDaemonCommandAsync(", StringComparison.Ordinal) ||
|
||||
client.Contains("public async Task<AetheriaRuntimeEveCommandEnvelope> SubmitEveCommandAsync(", StringComparison.Ordinal))
|
||||
{
|
||||
|
|
@ -9950,7 +9980,9 @@ static void RequireMainMenuContinueRunState(string root)
|
|||
{
|
||||
"LatestSectorMap",
|
||||
"AetheriaClient",
|
||||
".SectorMapAsync()",
|
||||
".Aetheria()",
|
||||
".SectorMap",
|
||||
".LatestAsync()",
|
||||
"ContinueGame()",
|
||||
"AetheriaUnityObservedRunProjection.Project(",
|
||||
"SceneManager.LoadScene(\"ARPG\")"
|
||||
|
|
@ -10218,7 +10250,9 @@ static void RequireMainMenuContinueRunState(string root)
|
|||
{
|
||||
"public sealed class AetheriaUnityObservedFrameApplier",
|
||||
"public bool ApplyLatestZoneRender()",
|
||||
"ZoneRenderAsync()",
|
||||
".Aetheria()",
|
||||
".ZoneRender",
|
||||
".LatestAsync()",
|
||||
"private bool TryRestoreEntityGraphFromZoneRender(",
|
||||
"if (string.IsNullOrWhiteSpace(render.RunId))",
|
||||
"Aetheria zone-render feed does not identify a run id.",
|
||||
|
|
@ -10281,7 +10315,9 @@ static void RequireMainMenuContinueRunState(string root)
|
|||
"private bool TryQueryEntityTarget(",
|
||||
"AetheriaRuntimeZoneContactRow",
|
||||
"AetheriaRuntimeZoneTargetRow",
|
||||
"ZoneContactsAsync()",
|
||||
".Aetheria()",
|
||||
".ZoneContacts",
|
||||
".LatestAsync()",
|
||||
"_facadeIndex.TryResolveEntityByDaemonIndex(targetEntityIndex, out var targetEntity)"
|
||||
};
|
||||
var missingObservedTargetQuerySymbols = requiredObservedTargetQuerySymbols
|
||||
|
|
@ -10317,7 +10353,9 @@ static void RequireMainMenuContinueRunState(string root)
|
|||
var requiredObservedDockingSymbols = new[]
|
||||
{
|
||||
"public sealed class AetheriaUnityObservedDockingIndex",
|
||||
"CurrentDockingAsync()",
|
||||
".Aetheria()",
|
||||
".DockingState",
|
||||
".Latest()",
|
||||
"public bool IsEntityUndocked(Entity entity)",
|
||||
"public bool TryResolveDockingBay(",
|
||||
"out AetheriaRuntimeCurrentDockingDocument docking",
|
||||
|
|
@ -11069,7 +11107,9 @@ static void RequireUnityObserverDoesNotTickLocalSimulation(string root)
|
|||
{
|
||||
"public sealed class AetheriaUnityObservedFrameApplier",
|
||||
"public bool ApplyLatestZoneRender()",
|
||||
"ZoneRenderAsync()",
|
||||
".Aetheria()",
|
||||
".ZoneRender",
|
||||
".LatestAsync()",
|
||||
"private bool TryRestoreEntityGraphFromZoneRender(",
|
||||
"AetheriaUnityDaemonEntitySnapshotProjector.CreateSnapshots(runId, render.ZoneIndex, render.EntityFacades)",
|
||||
"AetheriaUnityDaemonEntitySnapshotProjector.EntityIndexFromRecordKey(entity.RecordKey)",
|
||||
|
|
@ -11237,7 +11277,9 @@ static void RequireUnityObserverDoesNotTickLocalSimulation(string root)
|
|||
"private bool TryQueryEntityTarget(",
|
||||
"AetheriaRuntimeZoneContactRow",
|
||||
"AetheriaRuntimeZoneTargetRow",
|
||||
"ZoneContactsAsync()",
|
||||
".Aetheria()",
|
||||
".ZoneContacts",
|
||||
".LatestAsync()",
|
||||
"_facadeIndex.TryResolveEntityByDaemonIndex(targetEntityIndex, out var targetEntity)"
|
||||
};
|
||||
var missingObservedTargetQuerySymbols = requiredObservedTargetQuerySymbols
|
||||
|
|
@ -11365,15 +11407,18 @@ static void RequireUnityObserverDoesNotTickLocalSimulation(string root)
|
|||
|
||||
if (!mapRenderer.Contains("private AetheriaClient _client", StringComparison.Ordinal) ||
|
||||
!mapRenderer.Contains("ObjectsViewportAsync", StringComparison.Ordinal) ||
|
||||
!mapRenderer.Contains("GravityViewportAsync", StringComparison.Ordinal) ||
|
||||
!mapRenderer.Contains("PlayerSettingsAsync()", StringComparison.Ordinal) ||
|
||||
!sectorRenderer.Contains("private AetheriaClient _client", StringComparison.Ordinal) ||
|
||||
!sectorRenderer.Contains("SectorMapAsync()", StringComparison.Ordinal) ||
|
||||
!sectorRenderer.Contains(".Aetheria()", StringComparison.Ordinal) ||
|
||||
!sectorRenderer.Contains(".SectorMap", StringComparison.Ordinal) ||
|
||||
!sectorRenderer.Contains(".LatestAsync()", StringComparison.Ordinal) ||
|
||||
!sectorRenderer.Contains("ZoneDetailsAsync(zoneIndex)", StringComparison.Ordinal) ||
|
||||
!sectorRenderer.Contains("OpenRuntimeCatalog()", StringComparison.Ordinal) ||
|
||||
!sectorRenderer.Contains("PlayerSettingsAsync()", StringComparison.Ordinal) ||
|
||||
!sectorMap.Contains("private AetheriaClient _client", StringComparison.Ordinal) ||
|
||||
!sectorMap.Contains("SectorMapAsync()", StringComparison.Ordinal) ||
|
||||
!sectorMap.Contains(".Aetheria()", StringComparison.Ordinal) ||
|
||||
!sectorMap.Contains(".SectorMap", StringComparison.Ordinal) ||
|
||||
!sectorMap.Contains(".LatestAsync()", StringComparison.Ordinal) ||
|
||||
!sectorRenderer.Contains("AetheriaRuntimeZoneDetailsSurfaceBuilder.ProjectDaemonZone(", StringComparison.Ordinal))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
|
|
@ -12016,7 +12061,9 @@ static void RequireUnityObserverDoesNotTickLocalSimulation(string root)
|
|||
{
|
||||
"TryStartDaemonObservedGame",
|
||||
"LatestSectorMap(stateBoot)",
|
||||
".SectorMapAsync()",
|
||||
".Aetheria()",
|
||||
".SectorMap",
|
||||
".LatestAsync()",
|
||||
"AetheriaUnityObservedRunProjection.Project(",
|
||||
"sectorMap.FrameId",
|
||||
"sectorMap.IsTutorial",
|
||||
|
|
@ -12835,7 +12882,9 @@ static void RequireRuntimeStateReaderOwnsUnityStateAcquisition(string root)
|
|||
|
||||
if (!observedFrameApplier.Contains("private AetheriaRuntimeZoneRenderDocument _lastZoneRender;", StringComparison.Ordinal) ||
|
||||
!observedFrameApplier.Contains("ApplyLatestZoneRender()", StringComparison.Ordinal) ||
|
||||
!observedFrameApplier.Contains("ZoneRenderAsync()", StringComparison.Ordinal))
|
||||
!observedFrameApplier.Contains(".Aetheria()", StringComparison.Ordinal) ||
|
||||
!observedFrameApplier.Contains(".ZoneRender", StringComparison.Ordinal) ||
|
||||
!observedFrameApplier.Contains(".LatestAsync()", StringComparison.Ordinal))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"Observed zone-render state acquisition must live behind AetheriaUnityObservedFrameApplier.");
|
||||
|
|
@ -12888,7 +12937,9 @@ static void RequireRuntimeStateReaderOwnsUnityStateAcquisition(string root)
|
|||
|
||||
if (!mainMenu.Contains("AetheriaClient", StringComparison.Ordinal) ||
|
||||
!mainMenu.Contains("LatestSectorMap(AetheriaRuntimeStateBootReport stateBoot)", StringComparison.Ordinal) ||
|
||||
!mainMenu.Contains(".SectorMapAsync()", StringComparison.Ordinal))
|
||||
!mainMenu.Contains(".Aetheria()", StringComparison.Ordinal) ||
|
||||
!mainMenu.Contains(".SectorMap", StringComparison.Ordinal) ||
|
||||
!mainMenu.Contains(".LatestAsync()", StringComparison.Ordinal))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"MainMenu no longer routes sector-map lookup through the shared Aetheria client facade.");
|
||||
|
|
@ -14579,7 +14630,8 @@ static void RequireDockedCurrentShipRequestAuthority(string root)
|
|||
}
|
||||
|
||||
if (!inventoryPanel.Contains("TryResolveCurrentEntityKey(out var currentEntityKey)", StringComparison.Ordinal) ||
|
||||
!inventoryPanel.Contains("CurrentEntityAsync()", StringComparison.Ordinal) ||
|
||||
!inventoryPanel.Contains(".DockingState", StringComparison.Ordinal) ||
|
||||
!inventoryPanel.Contains("snapshot?.CurrentEntityKey", StringComparison.Ordinal) ||
|
||||
inventoryPanel.Contains("GameManager.CurrentEntity", StringComparison.Ordinal))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
|
|
|
|||
|
|
@ -70,6 +70,25 @@ public static class AetheriaVerseReplica
|
|||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public static async Task<int> ApplyRawSnapshotResponseAsync(
|
||||
CultMeshNode node,
|
||||
CultNetSnapshotResponseRawMessage response,
|
||||
bool flush = true)
|
||||
{
|
||||
if (node == null)
|
||||
throw new ArgumentNullException(nameof(node));
|
||||
if (response == null)
|
||||
throw new ArgumentNullException(nameof(response));
|
||||
|
||||
var applied = await node.Database.Documents
|
||||
.ApplyRawSnapshotResponseAsync(node.Cache, response)
|
||||
.ConfigureAwait(false);
|
||||
if (flush)
|
||||
await node.FlushAsync().ConfigureAwait(false);
|
||||
|
||||
return applied.Count;
|
||||
}
|
||||
|
||||
public static async Task<IReadOnlyList<T>> FetchScopedDocumentsAsync<T>(
|
||||
string endpoint,
|
||||
IReadOnlyList<string>? schemaIds = null,
|
||||
|
|
|
|||
82
Aetheria.sln
82
Aetheria.sln
|
|
@ -1,43 +1,75 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
Microsoft Visual Studio Solution File, Format Version 11.00
|
||||
# Visual Studio 2010
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GameCult.Aetheria.State.Unity", "GameCult.Aetheria.State.Unity.csproj", "{D6094CFD-26D1-83D1-2366-5762CCA2BC33}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Assembly-CSharp-firstpass", "Assembly-CSharp-firstpass.csproj", "{7c9c511c-1c6f-9c1f-c455-88359f498803}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GameCult.Aetheria.EveRuntime", "GameCult.Aetheria.EveRuntime.csproj", "{A1A8B65B-CA79-2FA2-3803-5F4833778BDB}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Assembly-CSharp", "Assembly-CSharp.csproj", "{47728ed0-fa53-fa99-9dca-8eb78b1dbfe4}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GameCult.Brokkr.Editor", "GameCult.Brokkr.Editor.csproj", "{F041E9DC-DC28-1D6C-75A3-824162B4A1AE}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GameCult.Aetheria.State.Unity", "GameCult.Aetheria.State.Unity.csproj", "{d6094cfd-26d1-83d1-2366-5762cca2bc33}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CultMath", "CultMath.csproj", "{6F7A47C4-21DC-64EF-0969-96B956A000F4}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UniRx", "UniRx.csproj", "{99486114-c78b-e9b0-654f-710398497645}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GameCult.Brokkr", "GameCult.Brokkr.csproj", "{FC28BCBE-9EF4-444F-C1C9-DDE8A3A864AD}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GameCult.Aetheria.EveRuntime", "GameCult.Aetheria.EveRuntime.csproj", "{a1a8b65b-ca79-2fa2-3803-5f4833778bdb}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GameCult.Eve.UnityUIToolkit", "GameCult.Eve.UnityUIToolkit.csproj", "{5CDB0413-71D7-C6B3-4AD9-D8E730B55FFB}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Aetheria.Shared.Unity", "Aetheria.Shared.Unity.csproj", "{31542707-f1f7-33c1-eef6-37982be14f82}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GameCult.Eve.Surface", "GameCult.Eve.Surface.csproj", "{037DACF0-10F4-20ED-7963-3A1E1AC15B64}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Assembly-CSharp-Editor-firstpass", "Assembly-CSharp-Editor-firstpass.csproj", "{b690125b-09ac-e423-4873-1648ff205166}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MessagePack.SourceDisabled", "MessagePack.SourceDisabled.csproj", "{B916E744-81E2-A0BA-3884-E845F1C5EF88}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Assembly-CSharp-Editor", "Assembly-CSharp-Editor.csproj", "{6529161a-3c3e-1394-61a8-ab66047d3732}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ink", "Ink.csproj", "{6bae5155-742f-1170-f8b5-ff696c59aad8}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GameCult.Brokkr.Editor", "GameCult.Brokkr.Editor.csproj", "{f041e9dc-dc28-1d6c-75a3-824162b4a1ae}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CultMath", "CultMath.csproj", "{6f7a47c4-21dc-64ef-0969-96b956a000f4}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GameCult.Brokkr", "GameCult.Brokkr.csproj", "{fc28bcbe-9ef4-444f-c1c9-dde8a3a864ad}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests.csproj", "{28568750-512b-4d45-69bf-887882a23da7}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GameCult.Eve.UnityUIToolkit", "GameCult.Eve.UnityUIToolkit.csproj", "{5cdb0413-71d7-c6b3-4ad9-d8e730b55ffb}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GameCult.Eve.Surface", "GameCult.Eve.Surface.csproj", "{037dacf0-10f4-20ed-7963-3a1e1ac15b64}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MessagePack.SourceDisabled", "MessagePack.SourceDisabled.csproj", "{b916e744-81e2-a0ba-3884-e845f1c5ef88}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{D6094CFD-26D1-83D1-2366-5762CCA2BC33}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D6094CFD-26D1-83D1-2366-5762CCA2BC33}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A1A8B65B-CA79-2FA2-3803-5F4833778BDB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A1A8B65B-CA79-2FA2-3803-5F4833778BDB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F041E9DC-DC28-1D6C-75A3-824162B4A1AE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{F041E9DC-DC28-1D6C-75A3-824162B4A1AE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{6F7A47C4-21DC-64EF-0969-96B956A000F4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{6F7A47C4-21DC-64EF-0969-96B956A000F4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{FC28BCBE-9EF4-444F-C1C9-DDE8A3A864AD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{FC28BCBE-9EF4-444F-C1C9-DDE8A3A864AD}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{5CDB0413-71D7-C6B3-4AD9-D8E730B55FFB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{5CDB0413-71D7-C6B3-4AD9-D8E730B55FFB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{037DACF0-10F4-20ED-7963-3A1E1AC15B64}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{037DACF0-10F4-20ED-7963-3A1E1AC15B64}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B916E744-81E2-A0BA-3884-E845F1C5EF88}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B916E744-81E2-A0BA-3884-E845F1C5EF88}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{7c9c511c-1c6f-9c1f-c455-88359f498803}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{7c9c511c-1c6f-9c1f-c455-88359f498803}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{47728ed0-fa53-fa99-9dca-8eb78b1dbfe4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{47728ed0-fa53-fa99-9dca-8eb78b1dbfe4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{d6094cfd-26d1-83d1-2366-5762cca2bc33}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{d6094cfd-26d1-83d1-2366-5762cca2bc33}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{99486114-c78b-e9b0-654f-710398497645}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{99486114-c78b-e9b0-654f-710398497645}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{a1a8b65b-ca79-2fa2-3803-5f4833778bdb}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{a1a8b65b-ca79-2fa2-3803-5f4833778bdb}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{31542707-f1f7-33c1-eef6-37982be14f82}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{31542707-f1f7-33c1-eef6-37982be14f82}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{b690125b-09ac-e423-4873-1648ff205166}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{b690125b-09ac-e423-4873-1648ff205166}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{6529161a-3c3e-1394-61a8-ab66047d3732}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{6529161a-3c3e-1394-61a8-ab66047d3732}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{6bae5155-742f-1170-f8b5-ff696c59aad8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{6bae5155-742f-1170-f8b5-ff696c59aad8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{f041e9dc-dc28-1d6c-75a3-824162b4a1ae}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{f041e9dc-dc28-1d6c-75a3-824162b4a1ae}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{6f7a47c4-21dc-64ef-0969-96b956a000f4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{6f7a47c4-21dc-64ef-0969-96b956a000f4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{fc28bcbe-9ef4-444f-c1c9-dde8a3a864ad}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{fc28bcbe-9ef4-444f-c1c9-dde8a3a864ad}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{28568750-512b-4d45-69bf-887882a23da7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{28568750-512b-4d45-69bf-887882a23da7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{5cdb0413-71d7-c6b3-4ad9-d8e730b55ffb}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{5cdb0413-71d7-c6b3-4ad9-d8e730b55ffb}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{037dacf0-10f4-20ed-7963-3a1e1ac15b64}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{037dacf0-10f4-20ed-7963-3a1e1ac15b64}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{b916e744-81e2-a0ba-3884-e845f1c5ef88}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{b916e744-81e2-a0ba-3884-e845f1c5ef88}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
|
|||
|
|
@ -67,8 +67,8 @@ public sealed class AetheriaDaemonIndirectRenderer : MonoBehaviour
|
|||
foreach (var renderGroup in renderGroups)
|
||||
{
|
||||
_seenGroupIds.Add(renderGroup.GroupId);
|
||||
if (!assetCatalog.TryResolveMesh(renderGroup.MeshKey, out var mesh) ||
|
||||
!assetCatalog.TryResolveMaterial(renderGroup.MaterialKey, out var material))
|
||||
if (!TryResolveMesh(renderGroup, out var mesh) ||
|
||||
!TryResolveMaterial(renderGroup, out var material))
|
||||
{
|
||||
if (logMissingAssets)
|
||||
{
|
||||
|
|
@ -237,4 +237,28 @@ public sealed class AetheriaDaemonIndirectRenderer : MonoBehaviour
|
|||
|
||||
_staleGroupIds.Clear();
|
||||
}
|
||||
|
||||
private bool TryResolveMesh(AetheriaRuntimeDaemonRenderGroupDocument renderGroup, out Mesh mesh)
|
||||
{
|
||||
if (renderGroup?.MeshAsset != null &&
|
||||
!string.IsNullOrWhiteSpace(renderGroup.MeshAsset.AssetKey) &&
|
||||
assetCatalog.TryResolveMesh(renderGroup.MeshAsset, out mesh))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return assetCatalog.TryResolveMesh(renderGroup?.MeshKey, out mesh);
|
||||
}
|
||||
|
||||
private bool TryResolveMaterial(AetheriaRuntimeDaemonRenderGroupDocument renderGroup, out Material material)
|
||||
{
|
||||
if (renderGroup?.MaterialAsset != null &&
|
||||
!string.IsNullOrWhiteSpace(renderGroup.MaterialAsset.AssetKey) &&
|
||||
assetCatalog.TryResolveMaterial(renderGroup.MaterialAsset, out material))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return assetCatalog.TryResolveMaterial(renderGroup?.MaterialKey, out material);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using GameCult.Aetheria.State.Verse;
|
||||
using UnityEngine;
|
||||
|
||||
[CreateAssetMenu(
|
||||
|
|
@ -33,6 +34,11 @@ public sealed class AetheriaDaemonRenderAssetCatalog : ScriptableObject
|
|||
return mesh != null;
|
||||
}
|
||||
|
||||
public bool TryResolveMesh(AetheriaRuntimeAssetRef asset, out Mesh mesh)
|
||||
{
|
||||
return TryResolveMesh(ResolveLocalKey(asset), out mesh);
|
||||
}
|
||||
|
||||
public bool TryResolveMaterial(string key, out Material material)
|
||||
{
|
||||
material = null;
|
||||
|
|
@ -54,6 +60,22 @@ public sealed class AetheriaDaemonRenderAssetCatalog : ScriptableObject
|
|||
return material != null;
|
||||
}
|
||||
|
||||
public bool TryResolveMaterial(AetheriaRuntimeAssetRef asset, out Material material)
|
||||
{
|
||||
return TryResolveMaterial(ResolveLocalKey(asset), out material);
|
||||
}
|
||||
|
||||
public bool TryResolveTexture(AetheriaRuntimeAssetRef asset, out Texture2D texture)
|
||||
{
|
||||
texture = null;
|
||||
var key = ResolveLocalKey(asset);
|
||||
if (string.IsNullOrWhiteSpace(key))
|
||||
return false;
|
||||
|
||||
texture = Resources.Load<Texture2D>(NormalizeResourceKey(key));
|
||||
return texture != null;
|
||||
}
|
||||
|
||||
private static string NormalizeResourceKey(string key)
|
||||
{
|
||||
const string resourcesPrefix = "resources://";
|
||||
|
|
@ -65,6 +87,17 @@ public sealed class AetheriaDaemonRenderAssetCatalog : ScriptableObject
|
|||
return key;
|
||||
}
|
||||
|
||||
private static string ResolveLocalKey(AetheriaRuntimeAssetRef asset)
|
||||
{
|
||||
if (asset == null)
|
||||
return "";
|
||||
|
||||
if (string.Equals(asset.Transport, AetheriaRuntimeAssetTransports.Resources, StringComparison.OrdinalIgnoreCase))
|
||||
return asset.Uri;
|
||||
|
||||
return string.IsNullOrWhiteSpace(asset.Uri) ? asset.AssetKey : asset.Uri;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
private struct MeshEntry
|
||||
{
|
||||
|
|
|
|||
137
Assets/Scripts/Gameplay/AetheriaRenderSplatBuffer.cs
Normal file
137
Assets/Scripts/Gameplay/AetheriaRenderSplatBuffer.cs
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
using GameCult.Aetheria.State.Verse;
|
||||
using Unity.Mathematics;
|
||||
using UnityEngine;
|
||||
|
||||
public sealed class AetheriaRenderSplatBuffer : System.IDisposable
|
||||
{
|
||||
public const string SplatBufferPropertyName = "_AetheriaSplats";
|
||||
public const string SplatCountPropertyName = "_AetheriaSplatCount";
|
||||
public const string ViewportToClipPropertyName = "_AetheriaViewportToClip";
|
||||
public const string ChannelFilterPropertyName = "_AetheriaChannelFilter";
|
||||
|
||||
private const int SplatStrideBytes = 80;
|
||||
|
||||
private AetheriaRenderSplat[] _splats = System.Array.Empty<AetheriaRenderSplat>();
|
||||
private GraphicsBuffer _buffer;
|
||||
private int _capacity;
|
||||
|
||||
public int Count { get; private set; }
|
||||
public GraphicsBuffer Buffer => _buffer;
|
||||
public bool HasGpuBuffer => _buffer != null && _buffer.IsValid() && Count > 0;
|
||||
|
||||
public bool Upload(AetheriaRuntimeRenderSplatSoa splats, int channelFilter = -1, int layerFilter = -1)
|
||||
{
|
||||
Count = Pack(splats, channelFilter, layerFilter);
|
||||
EnsureCapacity(Count);
|
||||
if (Count <= 0)
|
||||
return false;
|
||||
|
||||
_buffer.SetData(_splats, 0, 0, Count);
|
||||
return true;
|
||||
}
|
||||
|
||||
private int Pack(AetheriaRuntimeRenderSplatSoa splats, int channelFilter, int layerFilter)
|
||||
{
|
||||
if (splats == null || splats.Count <= 0)
|
||||
return 0;
|
||||
|
||||
EnsureCpuCapacity(splats.Count);
|
||||
var count = 0;
|
||||
for (var i = 0; i < splats.Count; i++)
|
||||
{
|
||||
var channel = Read(splats.Channel, i, 0);
|
||||
if (channelFilter >= 0 && channel != channelFilter)
|
||||
continue;
|
||||
|
||||
var layerIndex = Read(splats.LayerIndex, i, -1);
|
||||
if (layerFilter >= 0 && layerIndex != layerFilter)
|
||||
continue;
|
||||
|
||||
var halfExtentX = (float)Read(splats.HalfExtentX, i, 0);
|
||||
var halfExtentY = (float)Read(splats.HalfExtentY, i, 0);
|
||||
if (halfExtentX <= 0 || halfExtentY <= 0)
|
||||
continue;
|
||||
|
||||
_splats[count++] = new AetheriaRenderSplat
|
||||
{
|
||||
CenterHalfExtent = new float4(
|
||||
(float)Read(splats.CenterX, i, 0),
|
||||
(float)Read(splats.CenterY, i, 0),
|
||||
halfExtentX,
|
||||
halfExtentY),
|
||||
RotationChannelFalloff = new float4(
|
||||
(float)Read(splats.RotationCos, i, 1),
|
||||
(float)Read(splats.RotationSin, i, 0),
|
||||
channel,
|
||||
Read(splats.Falloff, i, AetheriaRuntimeRenderSplatFalloffs.Smooth)),
|
||||
LayerSource = new float4(
|
||||
layerIndex,
|
||||
Read(splats.SourceKind, i, AetheriaRuntimeRenderSplatSourceKinds.Constant),
|
||||
(float)Read(splats.AnimationSpeed, i, 0),
|
||||
(float)Read(splats.SourceFlags, i, 0)),
|
||||
SourceFrequencyPhase = new float4(
|
||||
(float)Read(splats.FrequencyX, i, 1),
|
||||
(float)Read(splats.FrequencyY, i, 1),
|
||||
(float)Read(splats.PhaseX, i, 0),
|
||||
(float)Read(splats.PhaseY, i, 0)),
|
||||
Value = new float4(
|
||||
(float)Read(splats.ValueR, i, 0),
|
||||
(float)Read(splats.ValueG, i, 0),
|
||||
(float)Read(splats.ValueB, i, 0),
|
||||
(float)Read(splats.ValueA, i, 1))
|
||||
};
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
private void EnsureCpuCapacity(int count)
|
||||
{
|
||||
if (_splats.Length >= count)
|
||||
return;
|
||||
|
||||
_splats = new AetheriaRenderSplat[math.ceilpow2(math.max(1, count))];
|
||||
}
|
||||
|
||||
private void EnsureCapacity(int count)
|
||||
{
|
||||
if (_buffer != null && _buffer.IsValid() && _capacity >= count)
|
||||
return;
|
||||
|
||||
_buffer?.Release();
|
||||
_buffer = null;
|
||||
_capacity = 0;
|
||||
if (count <= 0)
|
||||
return;
|
||||
|
||||
_capacity = math.ceilpow2(math.max(1, count));
|
||||
_buffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, _capacity, SplatStrideBytes);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_buffer?.Release();
|
||||
_buffer = null;
|
||||
_capacity = 0;
|
||||
Count = 0;
|
||||
}
|
||||
|
||||
private static double Read(System.Collections.Generic.IReadOnlyList<double> values, int index, double fallback)
|
||||
{
|
||||
return values != null && index >= 0 && index < values.Count ? values[index] : fallback;
|
||||
}
|
||||
|
||||
private static int Read(System.Collections.Generic.IReadOnlyList<int> values, int index, int fallback)
|
||||
{
|
||||
return values != null && index >= 0 && index < values.Count ? values[index] : fallback;
|
||||
}
|
||||
|
||||
private struct AetheriaRenderSplat
|
||||
{
|
||||
public float4 CenterHalfExtent;
|
||||
public float4 RotationChannelFalloff;
|
||||
public float4 LayerSource;
|
||||
public float4 SourceFrequencyPhase;
|
||||
public float4 Value;
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/Gameplay/AetheriaRenderSplatBuffer.cs.meta
Normal file
11
Assets/Scripts/Gameplay/AetheriaRenderSplatBuffer.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 32e6f17486f34e8c8c5f7640bff319af
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
241
Assets/Scripts/Gameplay/AetheriaRenderSplatLayerRenderer.cs
Normal file
241
Assets/Scripts/Gameplay/AetheriaRenderSplatLayerRenderer.cs
Normal file
|
|
@ -0,0 +1,241 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using GameCult.Aetheria.State.Verse;
|
||||
using Unity.Mathematics;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Experimental.Rendering;
|
||||
using UnityEngine.UI;
|
||||
|
||||
public sealed class AetheriaRenderSplatLayerRenderer : MonoBehaviour
|
||||
{
|
||||
[SerializeField]
|
||||
private AetheriaRenderSplatRasterizer rasterizer;
|
||||
|
||||
[SerializeField]
|
||||
private AetheriaRenderSplatLayerBinding[] bindings =
|
||||
{
|
||||
new AetheriaRenderSplatLayerBinding
|
||||
{
|
||||
LayerKey = AetheriaRuntimeRenderSplatLayerKeys.GravityHeight,
|
||||
MaterialTextureProperty = "_DetailTex",
|
||||
GlobalTextureName = "_AetheriaGravityHeight"
|
||||
},
|
||||
new AetheriaRenderSplatLayerBinding
|
||||
{
|
||||
LayerKey = AetheriaRuntimeRenderSplatLayerKeys.FogTint,
|
||||
MaterialTextureProperty = "_DetailTex",
|
||||
GlobalTextureName = "_NebulaTint"
|
||||
},
|
||||
new AetheriaRenderSplatLayerBinding
|
||||
{
|
||||
LayerKey = AetheriaRuntimeRenderSplatLayerKeys.FogSurfaceHeight,
|
||||
GlobalTextureName = "_NebulaSurfaceHeight",
|
||||
WidthScale = 0.5f,
|
||||
HeightScale = 0.5f
|
||||
},
|
||||
new AetheriaRenderSplatLayerBinding
|
||||
{
|
||||
LayerKey = AetheriaRuntimeRenderSplatLayerKeys.FogPatchHeight,
|
||||
GlobalTextureName = "_NebulaPatchHeight",
|
||||
WidthScale = 0.5f,
|
||||
HeightScale = 0.5f
|
||||
},
|
||||
new AetheriaRenderSplatLayerBinding
|
||||
{
|
||||
LayerKey = AetheriaRuntimeRenderSplatLayerKeys.FogPatch,
|
||||
GlobalTextureName = "_NebulaPatch",
|
||||
WidthScale = 0.5f,
|
||||
HeightScale = 0.5f
|
||||
},
|
||||
new AetheriaRenderSplatLayerBinding
|
||||
{
|
||||
LayerKey = AetheriaRuntimeRenderSplatLayerKeys.Influence,
|
||||
MaterialTextureProperty = "_DetailTex",
|
||||
GlobalTextureName = "_AetheriaInfluence"
|
||||
}
|
||||
};
|
||||
|
||||
private readonly Dictionary<string, RenderTexture> _texturesByLayerKey =
|
||||
new Dictionary<string, RenderTexture>(StringComparer.Ordinal);
|
||||
|
||||
private void Reset()
|
||||
{
|
||||
rasterizer = GetComponent<AetheriaRenderSplatRasterizer>();
|
||||
}
|
||||
|
||||
public bool TryGetTexture(string layerKey, out RenderTexture texture)
|
||||
{
|
||||
return _texturesByLayerKey.TryGetValue(layerKey ?? "", out texture);
|
||||
}
|
||||
|
||||
public void Render(
|
||||
AetheriaRuntimeRenderSplatsViewportDocument document,
|
||||
int width,
|
||||
int height)
|
||||
{
|
||||
if (document == null)
|
||||
return;
|
||||
|
||||
ResolveRasterizer();
|
||||
if (rasterizer == null)
|
||||
return;
|
||||
|
||||
var layers = document.Layers ?? Array.Empty<AetheriaRuntimeRenderSplatLayerDefinition>();
|
||||
for (var layerIndex = 0; layerIndex < layers.Count; layerIndex++)
|
||||
{
|
||||
var layer = layers[layerIndex];
|
||||
if (layer == null || string.IsNullOrWhiteSpace(layer.LayerKey))
|
||||
continue;
|
||||
|
||||
foreach (var binding in bindings ?? Array.Empty<AetheriaRenderSplatLayerBinding>())
|
||||
{
|
||||
if (binding == null ||
|
||||
!binding.Enabled ||
|
||||
!string.Equals(binding.LayerKey, layer.LayerKey, StringComparison.Ordinal))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var texture = EnsureTexture(binding, layer, width, height);
|
||||
rasterizer.RenderLayerToTarget(
|
||||
document,
|
||||
texture,
|
||||
layerIndex,
|
||||
ResolveMaterialPass(layer.BlendMode),
|
||||
ResolveClearColor(layer));
|
||||
ApplyBinding(binding, texture);
|
||||
_texturesByLayerKey[layer.LayerKey] = texture;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ResolveRasterizer()
|
||||
{
|
||||
if (rasterizer != null)
|
||||
return;
|
||||
|
||||
rasterizer = GetComponent<AetheriaRenderSplatRasterizer>();
|
||||
if (rasterizer == null)
|
||||
rasterizer = gameObject.AddComponent<AetheriaRenderSplatRasterizer>();
|
||||
}
|
||||
|
||||
private RenderTexture EnsureTexture(
|
||||
AetheriaRenderSplatLayerBinding binding,
|
||||
AetheriaRuntimeRenderSplatLayerDefinition layer,
|
||||
int width,
|
||||
int height)
|
||||
{
|
||||
var targetWidth = math.max(1, Mathf.RoundToInt(width * Mathf.Max(0.01f, binding.WidthScale)));
|
||||
var targetHeight = math.max(1, Mathf.RoundToInt(height * Mathf.Max(0.01f, binding.HeightScale)));
|
||||
var format = ResolveGraphicsFormat(binding.GraphicsFormatOverride, layer.GraphicsFormat);
|
||||
var existing = binding.TargetTexture;
|
||||
if (existing != null &&
|
||||
existing.width == targetWidth &&
|
||||
existing.height == targetHeight &&
|
||||
existing.graphicsFormat == format)
|
||||
{
|
||||
return existing;
|
||||
}
|
||||
|
||||
if (existing != null)
|
||||
{
|
||||
existing.Release();
|
||||
Destroy(existing);
|
||||
}
|
||||
|
||||
var descriptor = new RenderTextureDescriptor(targetWidth, targetHeight)
|
||||
{
|
||||
depthBufferBits = 0,
|
||||
graphicsFormat = format,
|
||||
msaaSamples = 1,
|
||||
sRGB = false,
|
||||
useMipMap = binding.UseMipMaps,
|
||||
autoGenerateMips = binding.UseMipMaps
|
||||
};
|
||||
binding.TargetTexture = new RenderTexture(descriptor)
|
||||
{
|
||||
name = $"Aetheria {layer.LayerKey}",
|
||||
filterMode = binding.FilterMode,
|
||||
wrapMode = TextureWrapMode.Clamp
|
||||
};
|
||||
binding.TargetTexture.Create();
|
||||
return binding.TargetTexture;
|
||||
}
|
||||
|
||||
private static GraphicsFormat ResolveGraphicsFormat(string bindingOverride, string layerFormat)
|
||||
{
|
||||
var value = string.IsNullOrWhiteSpace(bindingOverride) ? layerFormat : bindingOverride;
|
||||
return Enum.TryParse(value, out GraphicsFormat format)
|
||||
? format
|
||||
: GraphicsFormat.R16_SFloat;
|
||||
}
|
||||
|
||||
private static int ResolveMaterialPass(string blendMode)
|
||||
{
|
||||
switch (blendMode)
|
||||
{
|
||||
case AetheriaRuntimeRenderSplatBlendModes.Max:
|
||||
return 1;
|
||||
case AetheriaRuntimeRenderSplatBlendModes.Alpha:
|
||||
return 2;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static Color ResolveClearColor(AetheriaRuntimeRenderSplatLayerDefinition layer)
|
||||
{
|
||||
return new Color(
|
||||
(float)layer.ClearR,
|
||||
(float)layer.ClearG,
|
||||
(float)layer.ClearB,
|
||||
(float)layer.ClearA);
|
||||
}
|
||||
|
||||
private static void ApplyBinding(AetheriaRenderSplatLayerBinding binding, RenderTexture texture)
|
||||
{
|
||||
if (binding.Display != null)
|
||||
{
|
||||
var material = binding.Display.material;
|
||||
if (material != null && !string.IsNullOrWhiteSpace(binding.MaterialTextureProperty))
|
||||
material.SetTexture(binding.MaterialTextureProperty, texture);
|
||||
else
|
||||
binding.Display.material.mainTexture = texture;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(binding.GlobalTextureName))
|
||||
Shader.SetGlobalTexture(binding.GlobalTextureName, texture);
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
foreach (var binding in bindings ?? Array.Empty<AetheriaRenderSplatLayerBinding>())
|
||||
{
|
||||
if (binding?.TargetTexture == null)
|
||||
continue;
|
||||
|
||||
binding.TargetTexture.Release();
|
||||
Destroy(binding.TargetTexture);
|
||||
binding.TargetTexture = null;
|
||||
}
|
||||
|
||||
_texturesByLayerKey.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public sealed class AetheriaRenderSplatLayerBinding
|
||||
{
|
||||
public bool Enabled = true;
|
||||
public string LayerKey = "";
|
||||
public Image Display;
|
||||
public string MaterialTextureProperty = "_DetailTex";
|
||||
public string GlobalTextureName = "";
|
||||
public string GraphicsFormatOverride = "";
|
||||
public float WidthScale = 1.0f;
|
||||
public float HeightScale = 1.0f;
|
||||
public bool UseMipMaps;
|
||||
public FilterMode FilterMode = FilterMode.Bilinear;
|
||||
[NonSerialized]
|
||||
public RenderTexture TargetTexture;
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 0f3c143f0a844f38a109f2de02ce4423
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
300
Assets/Scripts/Gameplay/AetheriaRenderSplatRasterizer.cs
Normal file
300
Assets/Scripts/Gameplay/AetheriaRenderSplatRasterizer.cs
Normal file
|
|
@ -0,0 +1,300 @@
|
|||
using GameCult.Aetheria.State.Verse;
|
||||
using Unity.Mathematics;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Experimental.Rendering;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
public sealed class AetheriaRenderSplatRasterizer : MonoBehaviour
|
||||
{
|
||||
[SerializeField]
|
||||
private Material splatMaterial;
|
||||
|
||||
[SerializeField]
|
||||
private RenderTexture targetTexture;
|
||||
|
||||
[SerializeField]
|
||||
private GraphicsFormat targetFormat = GraphicsFormat.R16_SFloat;
|
||||
|
||||
[SerializeField]
|
||||
private int width = 512;
|
||||
|
||||
[SerializeField]
|
||||
private int height = 512;
|
||||
|
||||
[SerializeField]
|
||||
private int channelFilter = -1;
|
||||
|
||||
[SerializeField]
|
||||
private int layerFilter = -1;
|
||||
|
||||
[SerializeField]
|
||||
private int materialPass;
|
||||
|
||||
[SerializeField]
|
||||
private Color clearColor = Color.clear;
|
||||
|
||||
[SerializeField]
|
||||
private bool clearBeforeDraw = true;
|
||||
|
||||
private readonly AetheriaRenderSplatBuffer _buffer = new AetheriaRenderSplatBuffer();
|
||||
private CommandBuffer _commandBuffer;
|
||||
private RenderTexture _ownedTargetTexture;
|
||||
private Material _ownedSplatMaterial;
|
||||
private static readonly int SplatBufferPropertyId =
|
||||
Shader.PropertyToID(AetheriaRenderSplatBuffer.SplatBufferPropertyName);
|
||||
private static readonly int SplatCountPropertyId =
|
||||
Shader.PropertyToID(AetheriaRenderSplatBuffer.SplatCountPropertyName);
|
||||
private static readonly int ViewportToClipPropertyId =
|
||||
Shader.PropertyToID(AetheriaRenderSplatBuffer.ViewportToClipPropertyName);
|
||||
private static readonly int ChannelFilterPropertyId =
|
||||
Shader.PropertyToID(AetheriaRenderSplatBuffer.ChannelFilterPropertyName);
|
||||
|
||||
public RenderTexture TargetTexture => targetTexture != null ? targetTexture : _ownedTargetTexture;
|
||||
public int ChannelFilter
|
||||
{
|
||||
get => channelFilter;
|
||||
set => channelFilter = value;
|
||||
}
|
||||
|
||||
public int LayerFilter
|
||||
{
|
||||
get => layerFilter;
|
||||
set => layerFilter = value;
|
||||
}
|
||||
|
||||
public int LastDrawnCount { get; private set; }
|
||||
|
||||
public RenderTexture Render(
|
||||
AetheriaRuntimeRenderSplatsViewportDocument document,
|
||||
int overrideWidth = 0,
|
||||
int overrideHeight = 0)
|
||||
{
|
||||
if (document == null)
|
||||
{
|
||||
LastDrawnCount = 0;
|
||||
return TargetTexture;
|
||||
}
|
||||
|
||||
var output = EnsureTarget(overrideWidth, overrideHeight);
|
||||
var material = ResolveMaterial();
|
||||
if (output == null || material == null)
|
||||
{
|
||||
LastDrawnCount = 0;
|
||||
return output;
|
||||
}
|
||||
|
||||
_buffer.Upload(document.Splats, channelFilter, layerFilter);
|
||||
LastDrawnCount = _buffer.Count;
|
||||
if (!_buffer.HasGpuBuffer)
|
||||
{
|
||||
if (clearBeforeDraw)
|
||||
Clear(output);
|
||||
return output;
|
||||
}
|
||||
|
||||
_commandBuffer ??= new CommandBuffer { name = "Aetheria Render Splats" };
|
||||
_commandBuffer.Clear();
|
||||
_commandBuffer.SetRenderTarget(output);
|
||||
if (clearBeforeDraw)
|
||||
_commandBuffer.ClearRenderTarget(false, true, clearColor);
|
||||
|
||||
_commandBuffer.SetGlobalBuffer(SplatBufferPropertyId, _buffer.Buffer);
|
||||
_commandBuffer.SetGlobalInt(SplatCountPropertyId, _buffer.Count);
|
||||
_commandBuffer.SetGlobalInt(ChannelFilterPropertyId, channelFilter);
|
||||
_commandBuffer.SetGlobalMatrix(ViewportToClipPropertyId, BuildViewportToClip(document.Viewport));
|
||||
_commandBuffer.DrawProcedural(
|
||||
Matrix4x4.identity,
|
||||
material,
|
||||
math.max(0, materialPass),
|
||||
MeshTopology.Triangles,
|
||||
6,
|
||||
_buffer.Count);
|
||||
Graphics.ExecuteCommandBuffer(_commandBuffer);
|
||||
return output;
|
||||
}
|
||||
|
||||
public RenderTexture Render(
|
||||
AetheriaRuntimeRenderSplatsViewportDocument document,
|
||||
int overrideWidth,
|
||||
int overrideHeight,
|
||||
int overrideChannelFilter)
|
||||
{
|
||||
var previousChannelFilter = channelFilter;
|
||||
channelFilter = overrideChannelFilter;
|
||||
try
|
||||
{
|
||||
return Render(document, overrideWidth, overrideHeight);
|
||||
}
|
||||
finally
|
||||
{
|
||||
channelFilter = previousChannelFilter;
|
||||
}
|
||||
}
|
||||
|
||||
public RenderTexture RenderLayer(
|
||||
AetheriaRuntimeRenderSplatsViewportDocument document,
|
||||
int overrideWidth,
|
||||
int overrideHeight,
|
||||
int overrideLayerFilter,
|
||||
int overrideMaterialPass,
|
||||
GraphicsFormat overrideTargetFormat,
|
||||
Color overrideClearColor)
|
||||
{
|
||||
var previousLayerFilter = layerFilter;
|
||||
var previousMaterialPass = materialPass;
|
||||
var previousTargetFormat = targetFormat;
|
||||
var previousClearColor = clearColor;
|
||||
layerFilter = overrideLayerFilter;
|
||||
materialPass = overrideMaterialPass;
|
||||
targetFormat = overrideTargetFormat;
|
||||
clearColor = overrideClearColor;
|
||||
try
|
||||
{
|
||||
return Render(document, overrideWidth, overrideHeight);
|
||||
}
|
||||
finally
|
||||
{
|
||||
layerFilter = previousLayerFilter;
|
||||
materialPass = previousMaterialPass;
|
||||
targetFormat = previousTargetFormat;
|
||||
clearColor = previousClearColor;
|
||||
}
|
||||
}
|
||||
|
||||
public RenderTexture RenderLayerToTarget(
|
||||
AetheriaRuntimeRenderSplatsViewportDocument document,
|
||||
RenderTexture output,
|
||||
int overrideLayerFilter,
|
||||
int overrideMaterialPass,
|
||||
Color overrideClearColor)
|
||||
{
|
||||
var previousTargetTexture = targetTexture;
|
||||
var previousLayerFilter = layerFilter;
|
||||
var previousMaterialPass = materialPass;
|
||||
var previousClearColor = clearColor;
|
||||
targetTexture = output;
|
||||
layerFilter = overrideLayerFilter;
|
||||
materialPass = overrideMaterialPass;
|
||||
clearColor = overrideClearColor;
|
||||
try
|
||||
{
|
||||
return Render(document, output != null ? output.width : 0, output != null ? output.height : 0);
|
||||
}
|
||||
finally
|
||||
{
|
||||
targetTexture = previousTargetTexture;
|
||||
layerFilter = previousLayerFilter;
|
||||
materialPass = previousMaterialPass;
|
||||
clearColor = previousClearColor;
|
||||
}
|
||||
}
|
||||
|
||||
private Material ResolveMaterial()
|
||||
{
|
||||
if (splatMaterial != null)
|
||||
return splatMaterial;
|
||||
|
||||
if (_ownedSplatMaterial != null)
|
||||
return _ownedSplatMaterial;
|
||||
|
||||
var shader = Shader.Find("Aetheria/Render Splats");
|
||||
if (shader == null)
|
||||
return null;
|
||||
|
||||
_ownedSplatMaterial = new Material(shader)
|
||||
{
|
||||
hideFlags = HideFlags.DontSave
|
||||
};
|
||||
return _ownedSplatMaterial;
|
||||
}
|
||||
|
||||
private RenderTexture EnsureTarget(int overrideWidth, int overrideHeight)
|
||||
{
|
||||
if (targetTexture != null)
|
||||
return targetTexture;
|
||||
|
||||
var targetWidth = math.max(1, overrideWidth > 0 ? overrideWidth : width);
|
||||
var targetHeight = math.max(1, overrideHeight > 0 ? overrideHeight : height);
|
||||
if (_ownedTargetTexture != null &&
|
||||
_ownedTargetTexture.width == targetWidth &&
|
||||
_ownedTargetTexture.height == targetHeight &&
|
||||
_ownedTargetTexture.graphicsFormat == targetFormat)
|
||||
{
|
||||
return _ownedTargetTexture;
|
||||
}
|
||||
|
||||
ReleaseOwnedTarget();
|
||||
var descriptor = new RenderTextureDescriptor(targetWidth, targetHeight)
|
||||
{
|
||||
depthBufferBits = 0,
|
||||
graphicsFormat = targetFormat,
|
||||
msaaSamples = 1,
|
||||
sRGB = false,
|
||||
useMipMap = false,
|
||||
autoGenerateMips = false
|
||||
};
|
||||
_ownedTargetTexture = new RenderTexture(descriptor)
|
||||
{
|
||||
name = "Aetheria Render Splats",
|
||||
filterMode = FilterMode.Bilinear,
|
||||
wrapMode = TextureWrapMode.Clamp
|
||||
};
|
||||
_ownedTargetTexture.Create();
|
||||
return _ownedTargetTexture;
|
||||
}
|
||||
|
||||
private void Clear(RenderTexture output)
|
||||
{
|
||||
_commandBuffer ??= new CommandBuffer { name = "Aetheria Render Splats" };
|
||||
_commandBuffer.Clear();
|
||||
_commandBuffer.SetRenderTarget(output);
|
||||
_commandBuffer.ClearRenderTarget(false, true, clearColor);
|
||||
Graphics.ExecuteCommandBuffer(_commandBuffer);
|
||||
}
|
||||
|
||||
private static Matrix4x4 BuildViewportToClip(AetheriaRuntimeRtsViewportBounds viewport)
|
||||
{
|
||||
viewport ??= new AetheriaRuntimeRtsViewportBounds();
|
||||
var minX = (float)math.min(viewport.MinX, viewport.MaxX);
|
||||
var minY = (float)math.min(viewport.MinY, viewport.MaxY);
|
||||
var maxX = (float)math.max(viewport.MinX, viewport.MaxX);
|
||||
var maxY = (float)math.max(viewport.MinY, viewport.MaxY);
|
||||
var width = math.max(0.0001f, maxX - minX);
|
||||
var height = math.max(0.0001f, maxY - minY);
|
||||
|
||||
return new Matrix4x4(
|
||||
new Vector4(2f / width, 0, 0, 0),
|
||||
new Vector4(0, 2f / height, 0, 0),
|
||||
new Vector4(0, 0, 1, 0),
|
||||
new Vector4(-(maxX + minX) / width, -(maxY + minY) / height, 0, 1));
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
_buffer.Dispose();
|
||||
_commandBuffer?.Release();
|
||||
_commandBuffer = null;
|
||||
DestroyOwnedMaterial();
|
||||
ReleaseOwnedTarget();
|
||||
LastDrawnCount = 0;
|
||||
}
|
||||
|
||||
private void DestroyOwnedMaterial()
|
||||
{
|
||||
if (_ownedSplatMaterial == null)
|
||||
return;
|
||||
|
||||
Destroy(_ownedSplatMaterial);
|
||||
_ownedSplatMaterial = null;
|
||||
}
|
||||
|
||||
private void ReleaseOwnedTarget()
|
||||
{
|
||||
if (_ownedTargetTexture == null)
|
||||
return;
|
||||
|
||||
_ownedTargetTexture.Release();
|
||||
Destroy(_ownedTargetTexture);
|
||||
_ownedTargetTexture = null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f5150857a83849f195312d13b8f4d19b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -36,7 +36,6 @@ public sealed class AetheriaUnityPilotFrameAdapter
|
|||
return;
|
||||
}
|
||||
|
||||
var renderSettings = ZoneRenderer?.RenderSettings;
|
||||
TargetPresentation?.Tick(currentEntity, timeSeconds);
|
||||
|
||||
var look = Input.Player.Look.ReadValue<Vector2>();
|
||||
|
|
@ -50,13 +49,14 @@ public sealed class AetheriaUnityPilotFrameAdapter
|
|||
_setViewDirection(viewDirection);
|
||||
PilotCommands.RequestLookDirection(viewDirection);
|
||||
|
||||
if (renderSettings != null)
|
||||
if (ZoneRenderer != null)
|
||||
{
|
||||
var renderSettings = ZoneRenderer.RenderSettings;
|
||||
if (HeatstrokePost != null)
|
||||
HeatstrokePost.weight = (float)renderSettings.Value.NormalizeHeatstrokePost(currentEntity.Heatstroke);
|
||||
HeatstrokePost.weight = (float)renderSettings.NormalizeHeatstrokePost(currentEntity.Heatstroke);
|
||||
if (SevereHeatstrokePost != null)
|
||||
SevereHeatstrokePost.weight =
|
||||
(float)renderSettings.Value.ResolveSevereHeatstrokePostWeight(currentEntity.Heatstroke, timeSeconds);
|
||||
(float)renderSettings.ResolveSevereHeatstrokePostWeight(currentEntity.Heatstroke, timeSeconds);
|
||||
}
|
||||
|
||||
if (currentEntity is Ship)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,164 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using GameCult.Aetheria.State.Verse;
|
||||
using UnityEngine;
|
||||
|
||||
public sealed class AetheriaUnityRenderSplatViewportSource : MonoBehaviour
|
||||
{
|
||||
[SerializeField]
|
||||
private AetheriaRenderSplatRasterizer rasterizer;
|
||||
|
||||
[SerializeField]
|
||||
private AetheriaRenderSplatLayerRenderer layerRenderer;
|
||||
|
||||
[SerializeField]
|
||||
private string layerKey = AetheriaRuntimeRenderSplatLayerKeys.GravityHeight;
|
||||
|
||||
[SerializeField]
|
||||
private Vector2 position;
|
||||
|
||||
[SerializeField]
|
||||
private Vector2 size = new Vector2(1024, 1024);
|
||||
|
||||
[SerializeField]
|
||||
private float refreshIntervalSeconds = 0.5f;
|
||||
|
||||
[SerializeField]
|
||||
private bool renderInLateUpdate = true;
|
||||
|
||||
private AetheriaClient _client;
|
||||
private string _clientStatePath = "";
|
||||
private float _nextRefreshTime;
|
||||
private AetheriaRuntimeRenderSplatsViewportDocument _document;
|
||||
|
||||
public RenderTexture TargetTexture
|
||||
{
|
||||
get
|
||||
{
|
||||
if (layerRenderer != null && layerRenderer.TryGetTexture(layerKey, out var texture))
|
||||
return texture;
|
||||
|
||||
return rasterizer != null ? rasterizer.TargetTexture : null;
|
||||
}
|
||||
}
|
||||
|
||||
private void Reset()
|
||||
{
|
||||
rasterizer = GetComponent<AetheriaRenderSplatRasterizer>();
|
||||
layerRenderer = GetComponent<AetheriaRenderSplatLayerRenderer>();
|
||||
}
|
||||
|
||||
private void LateUpdate()
|
||||
{
|
||||
if (renderInLateUpdate)
|
||||
RenderLatest();
|
||||
}
|
||||
|
||||
public bool RenderLatest()
|
||||
{
|
||||
ResolveRenderers();
|
||||
if (layerRenderer == null && rasterizer == null)
|
||||
return false;
|
||||
|
||||
RefreshDocument(false);
|
||||
if (_document == null)
|
||||
return false;
|
||||
|
||||
if (layerRenderer != null)
|
||||
{
|
||||
layerRenderer.Render(_document, Mathf.RoundToInt(size.x), Mathf.RoundToInt(size.y));
|
||||
return true;
|
||||
}
|
||||
|
||||
rasterizer.Render(_document, Mathf.RoundToInt(size.x), Mathf.RoundToInt(size.y));
|
||||
return true;
|
||||
}
|
||||
|
||||
private void ResolveRenderers()
|
||||
{
|
||||
if (layerRenderer == null)
|
||||
layerRenderer = GetComponent<AetheriaRenderSplatLayerRenderer>();
|
||||
if (rasterizer == null)
|
||||
rasterizer = GetComponent<AetheriaRenderSplatRasterizer>();
|
||||
|
||||
if (layerRenderer == null && rasterizer == null)
|
||||
layerRenderer = gameObject.AddComponent<AetheriaRenderSplatLayerRenderer>();
|
||||
}
|
||||
|
||||
public void SetLayerKey(string value)
|
||||
{
|
||||
layerKey = string.IsNullOrWhiteSpace(value)
|
||||
? AetheriaRuntimeRenderSplatLayerKeys.GravityHeight
|
||||
: value;
|
||||
}
|
||||
|
||||
public void SetViewport(Vector2 center, Vector2 viewportSize)
|
||||
{
|
||||
position = center;
|
||||
size = viewportSize;
|
||||
RefreshDocument(true);
|
||||
}
|
||||
|
||||
private void RefreshDocument(bool force)
|
||||
{
|
||||
if (!force && Time.unscaledTime < _nextRefreshTime)
|
||||
return;
|
||||
|
||||
_nextRefreshTime = Time.unscaledTime + Mathf.Max(0.02f, refreshIntervalSeconds);
|
||||
try
|
||||
{
|
||||
_document = ResolveClient()
|
||||
.RenderSplatsViewportAsync(ResolveViewportBounds())
|
||||
.GetAwaiter()
|
||||
.GetResult();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogWarning($"Failed to read Aetheria render splats from local Verse state: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private AetheriaRuntimeRtsViewportBounds ResolveViewportBounds()
|
||||
{
|
||||
var halfSize = size * 0.5f;
|
||||
return new AetheriaRuntimeRtsViewportBounds
|
||||
{
|
||||
MinX = position.x - halfSize.x,
|
||||
MinY = position.y - halfSize.y,
|
||||
MaxX = position.x + halfSize.x,
|
||||
MaxY = position.y + halfSize.y
|
||||
};
|
||||
}
|
||||
|
||||
private AetheriaClient ResolveClient()
|
||||
{
|
||||
var gameDataDirectory = new DirectoryInfo(Path.Combine(Application.dataPath, "..", "GameData"));
|
||||
var stateBoot = AetheriaRuntimeStateBoot.Inspect(gameDataDirectory);
|
||||
if (_client != null && string.Equals(_clientStatePath, stateBoot.StateFilePath, StringComparison.Ordinal))
|
||||
return _client;
|
||||
|
||||
DisposeClient();
|
||||
_client = AetheriaClient
|
||||
.OpenLocalAsync(
|
||||
gameDataDirectory,
|
||||
"unity-render-splat-viewport",
|
||||
"local",
|
||||
pullOnOpen: true)
|
||||
.GetAwaiter()
|
||||
.GetResult();
|
||||
_clientStatePath = stateBoot.StateFilePath;
|
||||
return _client;
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
DisposeClient();
|
||||
}
|
||||
|
||||
private void DisposeClient()
|
||||
{
|
||||
_client?.Dispose();
|
||||
_client = null;
|
||||
_clientStatePath = "";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 473b4029da16430aa0c0ef89298070d8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -61,12 +61,12 @@ public sealed class AetheriaUnityTargetPresentation
|
|||
_observedHostileIndicatorTargets.Clear();
|
||||
_observedFriendlyIndicatorTargets.Clear();
|
||||
|
||||
var renderSettings = ZoneRenderer?.RenderSettings;
|
||||
if (currentEntity != null && renderSettings != null)
|
||||
if (currentEntity != null && ZoneRenderer != null)
|
||||
{
|
||||
var renderSettings = ZoneRenderer.RenderSettings;
|
||||
var contacts = ResolveVisibleContacts?.Invoke(
|
||||
currentEntity,
|
||||
renderSettings.Value.TargetDetectionInfoThreshold,
|
||||
renderSettings.TargetDetectionInfoThreshold,
|
||||
true) ?? Array.Empty<AetheriaRuntimeZoneContactRow>();
|
||||
|
||||
foreach (var contact in contacts)
|
||||
|
|
@ -153,10 +153,10 @@ public sealed class AetheriaUnityTargetPresentation
|
|||
float time,
|
||||
bool blinkSpottedTargets)
|
||||
{
|
||||
var renderSettings = ZoneRenderer?.RenderSettings;
|
||||
if (renderSettings == null)
|
||||
if (ZoneRenderer == null)
|
||||
return;
|
||||
|
||||
var renderSettings = ZoneRenderer.RenderSettings;
|
||||
foreach (var indicator in indicators)
|
||||
{
|
||||
indicator.Value.gameObject.SetActive(indicator.Key != observedTarget);
|
||||
|
|
@ -168,9 +168,9 @@ public sealed class AetheriaUnityTargetPresentation
|
|||
}
|
||||
|
||||
var infoGathered = ResolveInfoGathered?.Invoke(indicator.Key, currentEntity) ?? 0f;
|
||||
indicator.Value.Fill.fillAmount = (float)renderSettings.Value.NormalizeDetectionProgress(infoGathered);
|
||||
indicator.Value.Fill.fillAmount = (float)renderSettings.NormalizeDetectionProgress(infoGathered);
|
||||
indicator.Value.Fill.enabled = !blinkSpottedTargets ||
|
||||
renderSettings.Value.ResolveTargetSpottedFillEnabled(infoGathered, time);
|
||||
renderSettings.ResolveTargetSpottedFillEnabled(infoGathered, time);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -89,9 +89,8 @@ public class InventoryMenu : MonoBehaviour
|
|||
// {
|
||||
// _destroyItem = false;
|
||||
// });
|
||||
var hasDockingIndex = TryResolveObservedDockingIndex(out var dockingIndex);
|
||||
var hasObservedCurrentEntity = hasDockingIndex && dockingIndex.TryResolveCurrentEntity(out var currentEntity);
|
||||
var cargo = hasDockingIndex && dockingIndex.TryResolveCurrentDockingBay(out var dockingBay)
|
||||
var hasObservedCurrentEntity = TryResolveCurrentEntityFacade(out var currentEntity);
|
||||
var cargo = TryGetTypedCurrentDockingBayFacade(out var dockingBay)
|
||||
? dockingBay
|
||||
: currentEntity?.CargoBays.FirstOrDefault();
|
||||
if (cargo!=null)
|
||||
|
|
@ -727,46 +726,81 @@ public class InventoryMenu : MonoBehaviour
|
|||
private bool TryResolveCurrentDockingBayRow(out AetheriaRuntimeStationDockingBayRow dockingBay)
|
||||
{
|
||||
dockingBay = null;
|
||||
return TryResolveObservedDockingIndex(out var dockingIndex) &&
|
||||
dockingIndex.TryResolveCurrentDockingBayRow(out dockingBay);
|
||||
var stationRefit = ResolveStationRefit();
|
||||
dockingBay = stationRefit?.IsDocked == true && stationRefit.DockingBayIndex >= 0
|
||||
? (stationRefit.DockingBays ?? Array.Empty<AetheriaRuntimeStationDockingBayRow>())
|
||||
.FirstOrDefault(row => row != null && row.DockingBayIndex == stationRefit.DockingBayIndex)
|
||||
: null;
|
||||
return dockingBay != null;
|
||||
}
|
||||
|
||||
private bool TryResolveCurrentEntityKey(out string currentEntityKey)
|
||||
{
|
||||
currentEntityKey = "";
|
||||
return TryResolveObservedDockingIndex(out var dockingIndex) &&
|
||||
dockingIndex.TryResolveCurrentEntityKey(out currentEntityKey);
|
||||
currentEntityKey = ResolveDockingState()?.CurrentEntityKey ?? "";
|
||||
return !string.IsNullOrWhiteSpace(currentEntityKey);
|
||||
}
|
||||
|
||||
private bool TryResolveCurrentEntityDocument(out AetheriaRuntimeCurrentEntityDocument currentEntity)
|
||||
{
|
||||
currentEntity = null;
|
||||
return TryResolveObservedDockingIndex(out var dockingIndex) &&
|
||||
dockingIndex.TryResolveCurrentEntityDocument(out currentEntity);
|
||||
try
|
||||
{
|
||||
currentEntity = ResolveClient()
|
||||
.Aetheria()
|
||||
.DockingState
|
||||
.Latest()
|
||||
?.CurrentEntity;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogWarning($"Failed to read Aetheria current entity for inventory ship settings: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
|
||||
return currentEntity != null;
|
||||
}
|
||||
|
||||
private AetheriaRuntimeStationRefitDocument ResolveStationRefit()
|
||||
{
|
||||
return TryResolveObservedDockingIndex(out var dockingIndex)
|
||||
? dockingIndex.ResolveStationRefit()
|
||||
: null;
|
||||
return ResolveDockingState()?.StationRefit;
|
||||
}
|
||||
|
||||
private bool TryResolveCurrentDocking(out AetheriaRuntimeCurrentDockingDocument docking)
|
||||
private bool TryResolveCurrentEntityFacade(out Entity currentEntity)
|
||||
{
|
||||
docking = null;
|
||||
return TryResolveObservedDockingIndex(out var dockingIndex) &&
|
||||
dockingIndex.TryResolveCurrentDocking(out docking);
|
||||
currentEntity = null;
|
||||
var currentEntityKey = ResolveDockingState()?.CurrentEntityKey;
|
||||
return _observedFacadeIndex != null &&
|
||||
!string.IsNullOrWhiteSpace(currentEntityKey) &&
|
||||
_observedFacadeIndex.TryResolveEntityByRecordKey(currentEntityKey, out currentEntity);
|
||||
}
|
||||
|
||||
private bool TryResolveObservedDockingIndex(out AetheriaUnityObservedDockingIndex dockingIndex)
|
||||
private bool TryGetTypedCurrentDockingBayFacade(out EquippedCargoBay dockingBay)
|
||||
{
|
||||
dockingIndex = null;
|
||||
if (_observedFacadeIndex == null)
|
||||
return false;
|
||||
dockingBay = null;
|
||||
var docking = ResolveDockingState()?.CurrentDocking;
|
||||
return _observedFacadeIndex != null &&
|
||||
docking?.IsDocked == true &&
|
||||
_observedFacadeIndex.TryResolveDockingBayByRecordKey(
|
||||
docking.DockParentEntityKey,
|
||||
docking.DockingBayIndex,
|
||||
out dockingBay);
|
||||
}
|
||||
|
||||
dockingIndex = new AetheriaUnityObservedDockingIndex(ResolveClient, _observedFacadeIndex);
|
||||
return true;
|
||||
private AetheriaClientDockingSnapshot ResolveDockingState()
|
||||
{
|
||||
try
|
||||
{
|
||||
return ResolveClient()
|
||||
.Aetheria()
|
||||
.DockingState
|
||||
.Latest();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogWarning($"Failed to read Aetheria docking state for inventory menu: {ex.Message}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryResolveCargoBay(EquippedCargoBay cargoBay, out string entityKey, out int cargoIndex)
|
||||
|
|
|
|||
|
|
@ -260,9 +260,7 @@ public class InventoryPanel : MonoBehaviour, IPointerClickHandler
|
|||
}
|
||||
return;
|
||||
case AetheriaRuntimeInventoryDropdownSelectionKind.DockingBay:
|
||||
if (TryResolveObservedDockingIndex(out var dockingIndex) &&
|
||||
dockingIndex.TryResolveCurrentDockingBayRow(out _) &&
|
||||
dockingIndex.TryResolveCurrentDockingBay(out var dockingBay))
|
||||
if (TryResolveCurrentDockingBayFacade(out var dockingBay))
|
||||
{
|
||||
Display(dockingBay);
|
||||
}
|
||||
|
|
@ -1133,29 +1131,53 @@ private void Update()
|
|||
private bool TryResolveCurrentEntityKey(out string currentEntityKey)
|
||||
{
|
||||
currentEntityKey = "";
|
||||
return TryResolveObservedDockingIndex(out var dockingIndex) &&
|
||||
dockingIndex.TryResolveCurrentEntityKey(out currentEntityKey);
|
||||
var snapshot = ResolveDockingState();
|
||||
currentEntityKey = snapshot?.CurrentEntityKey ?? "";
|
||||
return !string.IsNullOrWhiteSpace(currentEntityKey);
|
||||
}
|
||||
|
||||
private bool TryResolveCurrentDockingBayRow(out AetheriaRuntimeStationDockingBayRow dockingBay)
|
||||
{
|
||||
dockingBay = null;
|
||||
return TryResolveObservedDockingIndex(out var dockingIndex) &&
|
||||
dockingIndex.TryResolveCurrentDockingBayRow(out dockingBay);
|
||||
var stationRefit = ResolveStationRefit();
|
||||
dockingBay = stationRefit?.IsDocked == true && stationRefit.DockingBayIndex >= 0
|
||||
? (stationRefit.DockingBays ?? Array.Empty<AetheriaRuntimeStationDockingBayRow>())
|
||||
.FirstOrDefault(row => row != null && row.DockingBayIndex == stationRefit.DockingBayIndex)
|
||||
: null;
|
||||
return dockingBay != null;
|
||||
}
|
||||
|
||||
private bool TryResolveCurrentDocking(out AetheriaRuntimeCurrentDockingDocument docking)
|
||||
private bool TryResolveCurrentDockingBayFacade(out EquippedCargoBay dockingBay)
|
||||
{
|
||||
docking = null;
|
||||
return TryResolveObservedDockingIndex(out var dockingIndex) &&
|
||||
dockingIndex.TryResolveCurrentDocking(out docking);
|
||||
dockingBay = null;
|
||||
var docking = ResolveDockingState()?.CurrentDocking;
|
||||
return _observedFacadeIndex != null &&
|
||||
docking?.IsDocked == true &&
|
||||
_observedFacadeIndex.TryResolveDockingBayByRecordKey(
|
||||
docking.DockParentEntityKey,
|
||||
docking.DockingBayIndex,
|
||||
out dockingBay);
|
||||
}
|
||||
|
||||
private AetheriaRuntimeStationRefitDocument ResolveStationRefit()
|
||||
{
|
||||
return TryResolveObservedDockingIndex(out var dockingIndex)
|
||||
? dockingIndex.ResolveStationRefit()
|
||||
: null;
|
||||
return ResolveDockingState()?.StationRefit;
|
||||
}
|
||||
|
||||
private AetheriaClientDockingSnapshot ResolveDockingState()
|
||||
{
|
||||
try
|
||||
{
|
||||
return ResolveClient()
|
||||
.Aetheria()
|
||||
.DockingState
|
||||
.Latest();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogWarning($"Failed to read Aetheria docking state for inventory panel: {ex.Message}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryResolveObservedDockingIndex(out AetheriaUnityObservedDockingIndex dockingIndex)
|
||||
|
|
|
|||
|
|
@ -205,38 +205,36 @@ public class LocalMenu : MonoBehaviour
|
|||
return "Local";
|
||||
}
|
||||
|
||||
private bool TryResolveCurrentDocking(out AetheriaRuntimeCurrentDockingDocument docking)
|
||||
private bool TryResolveDockingState(out AetheriaClientDockingSnapshot dockingState)
|
||||
{
|
||||
docking = null;
|
||||
dockingState = null;
|
||||
|
||||
try
|
||||
{
|
||||
docking = ResolveClient()
|
||||
dockingState = ResolveClient()
|
||||
.Aetheria()
|
||||
.Current
|
||||
.Docking
|
||||
.LatestAsync()
|
||||
.GetAwaiter()
|
||||
.GetResult();
|
||||
.DockingState
|
||||
.Latest();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogWarning($"Failed to read Aetheria current docking for local story surface: {ex.Message}");
|
||||
Debug.LogWarning($"Failed to read Aetheria docking state for local story surface: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
|
||||
return docking != null;
|
||||
return dockingState != null;
|
||||
}
|
||||
|
||||
private bool TryResolveDockedLocalStory(out LocationStory story)
|
||||
{
|
||||
story = null;
|
||||
if (_observedFacadeIndex == null ||
|
||||
!TryResolveCurrentDocking(out var docking) ||
|
||||
!docking.IsDocked ||
|
||||
!TryResolveDockingState(out var dockingState) ||
|
||||
dockingState?.IsDocked != true ||
|
||||
dockingState.CurrentDocking == null ||
|
||||
!_observedFacadeIndex.TryResolveDockingBayByRecordKey(
|
||||
docking.DockParentEntityKey,
|
||||
docking.DockingBayIndex,
|
||||
dockingState.CurrentDocking.DockParentEntityKey,
|
||||
dockingState.CurrentDocking.DockingBayIndex,
|
||||
out var dockingBay) ||
|
||||
dockingBay?.Entity is not OrbitalEntity { Story: { } dockedStory })
|
||||
{
|
||||
|
|
|
|||
|
|
@ -25,24 +25,27 @@ public class MapRenderer : MonoBehaviour
|
|||
public Camera InfluenceCamera;
|
||||
public Image OverlayDisplay;
|
||||
public Image GravityDisplay;
|
||||
public Material GravityBackdropMaterial;
|
||||
public Image TintDisplay;
|
||||
public Image InfluenceDisplay;
|
||||
public AetheriaRenderSplatLayerRenderer SplatLayerRenderer;
|
||||
public AetheriaDaemonRenderAssetCatalog AssetCatalog;
|
||||
public RectTransform RtsIconRoot;
|
||||
public RawImage RtsIconPrototype;
|
||||
public float Scale;
|
||||
public float2 Position;
|
||||
public float IconSize = 1f/128;
|
||||
|
||||
private RectTransform _rect;
|
||||
private RenderTexture _mapTexture;
|
||||
private RenderTexture _gravityTexture;
|
||||
private RenderTexture _tintTexture;
|
||||
private RenderTexture _influenceTexture;
|
||||
private int2 _size;
|
||||
private bool _init;
|
||||
private AetheriaClient _client;
|
||||
private string _clientStatePath = "";
|
||||
private AetheriaRuntimeObjectsViewportDocument _objectsViewport;
|
||||
private AetheriaRuntimeGravityViewportDocument _gravityViewport;
|
||||
private AetheriaRuntimeRenderSplatsViewportDocument _renderSplatsViewport;
|
||||
private float _nextViewportRefreshTime;
|
||||
private readonly List<RawImage> _rtsIconPool = new List<RawImage>();
|
||||
|
||||
void Start()
|
||||
{
|
||||
|
|
@ -52,10 +55,16 @@ public class MapRenderer : MonoBehaviour
|
|||
private void OnEnable()
|
||||
{
|
||||
_init = true;
|
||||
MapOverlayCamera.gameObject.SetActive(true);
|
||||
GravityCamera.gameObject.SetActive(true);
|
||||
TintCamera.gameObject.SetActive(true);
|
||||
InfluenceCamera.gameObject.SetActive(true);
|
||||
SetCameraActive(MapOverlayCamera, false);
|
||||
SetCameraActive(GravityCamera, false);
|
||||
SetCameraActive(TintCamera, false);
|
||||
SetCameraActive(InfluenceCamera, false);
|
||||
if (OverlayDisplay != null)
|
||||
OverlayDisplay.gameObject.SetActive(false);
|
||||
if (GravityBackdropMaterial != null && GravityDisplay != null)
|
||||
GravityDisplay.material = GravityBackdropMaterial;
|
||||
EnsureRtsIconRoot();
|
||||
SetRtsIconsActive(true);
|
||||
RefreshViewportDocuments(force: true);
|
||||
|
||||
// If hiding minimap asteroids, turn them back on for the map screen
|
||||
|
|
@ -69,10 +78,13 @@ public class MapRenderer : MonoBehaviour
|
|||
{
|
||||
ReleaseTextures();
|
||||
}
|
||||
MapOverlayCamera.gameObject.SetActive(false);
|
||||
GravityCamera.gameObject.SetActive(false);
|
||||
TintCamera.gameObject.SetActive(false);
|
||||
InfluenceCamera.gameObject.SetActive(false);
|
||||
SetCameraActive(MapOverlayCamera, false);
|
||||
SetCameraActive(GravityCamera, false);
|
||||
SetCameraActive(TintCamera, false);
|
||||
SetCameraActive(InfluenceCamera, false);
|
||||
if (OverlayDisplay != null)
|
||||
OverlayDisplay.gameObject.SetActive(false);
|
||||
SetRtsIconsActive(false);
|
||||
|
||||
// If hiding minimap asteroids, turn them back off when leaving the map screen
|
||||
if (!ResolveShowAsteroidsInMinimap())
|
||||
|
|
@ -88,12 +100,6 @@ public class MapRenderer : MonoBehaviour
|
|||
{
|
||||
_mapTexture.Release();
|
||||
_mapTexture = null;
|
||||
_gravityTexture.Release();
|
||||
_gravityTexture = null;
|
||||
_tintTexture.Release();
|
||||
_tintTexture = null;
|
||||
_influenceTexture.Release();
|
||||
_influenceTexture = null;
|
||||
}
|
||||
|
||||
void LateUpdate()
|
||||
|
|
@ -108,40 +114,12 @@ public class MapRenderer : MonoBehaviour
|
|||
ReleaseTextures();
|
||||
}
|
||||
|
||||
_mapTexture = new RenderTexture(_size.x, _size.y, 0, RenderTextureFormat.Default);
|
||||
MapOverlayCamera.targetTexture = _mapTexture;
|
||||
OverlayDisplay.material.SetTexture("_DetailTex", _mapTexture);
|
||||
|
||||
_gravityTexture = new RenderTexture(_size.x, _size.y, 0, RenderTextureFormat.RFloat);
|
||||
GravityCamera.targetTexture = _gravityTexture;
|
||||
GravityDisplay.material.SetTexture("_DetailTex", _gravityTexture);
|
||||
|
||||
_tintTexture = new RenderTexture(_size.x / 2, _size.y / 2, 0, RenderTextureFormat.RGB111110Float);
|
||||
TintCamera.targetTexture = _tintTexture;
|
||||
TintDisplay.material.SetTexture("_DetailTex", _tintTexture);
|
||||
|
||||
_influenceTexture = new RenderTexture(_size.x, _size.y, 0, RenderTextureFormat.RFloat);
|
||||
InfluenceCamera.targetTexture = _influenceTexture;
|
||||
InfluenceDisplay.material.SetTexture("_DetailTex", _influenceTexture);
|
||||
}
|
||||
|
||||
var pos = ((Vector2) Position).Flatland(1);
|
||||
|
||||
MapOverlayCamera.transform.position = pos;
|
||||
MapOverlayCamera.orthographicSize = _size.y * Scale * .5f;
|
||||
|
||||
GravityCamera.transform.position = pos;
|
||||
GravityCamera.orthographicSize = _size.y * Scale * .5f;
|
||||
GravityDisplay.material.SetFloat("_Scale", Scale / 2);
|
||||
|
||||
TintCamera.transform.position = pos;
|
||||
TintCamera.orthographicSize = _size.y * Scale * .5f;
|
||||
|
||||
InfluenceCamera.transform.position = pos;
|
||||
InfluenceCamera.orthographicSize = _size.y * Scale * .5f;
|
||||
|
||||
ZoneRenderer.SetIconSize(IconSize * Scale);
|
||||
RefreshViewportDocuments(force: false);
|
||||
RenderSplatLayers();
|
||||
RenderRtsIcons();
|
||||
}
|
||||
|
||||
private void RefreshViewportDocuments(bool force)
|
||||
|
|
@ -158,8 +136,8 @@ public class MapRenderer : MonoBehaviour
|
|||
.ObjectsViewportAsync(viewport)
|
||||
.GetAwaiter()
|
||||
.GetResult();
|
||||
_gravityViewport = client
|
||||
.GravityViewportAsync(viewport)
|
||||
_renderSplatsViewport = client
|
||||
.RenderSplatsViewportAsync(viewport)
|
||||
.GetAwaiter()
|
||||
.GetResult();
|
||||
|
||||
|
|
@ -175,6 +153,153 @@ public class MapRenderer : MonoBehaviour
|
|||
}
|
||||
}
|
||||
|
||||
private void RenderSplatLayers()
|
||||
{
|
||||
if (_renderSplatsViewport == null)
|
||||
return;
|
||||
|
||||
if (SplatLayerRenderer == null)
|
||||
SplatLayerRenderer = GetComponent<AetheriaRenderSplatLayerRenderer>() ??
|
||||
gameObject.AddComponent<AetheriaRenderSplatLayerRenderer>();
|
||||
|
||||
SplatLayerRenderer.Render(_renderSplatsViewport, _size.x, _size.y);
|
||||
ApplyLayerTexture(AetheriaRuntimeRenderSplatLayerKeys.GravityHeight, GravityDisplay);
|
||||
ApplyLayerTexture(AetheriaRuntimeRenderSplatLayerKeys.FogTint, TintDisplay);
|
||||
ApplyLayerTexture(AetheriaRuntimeRenderSplatLayerKeys.Influence, InfluenceDisplay);
|
||||
}
|
||||
|
||||
private void ApplyLayerTexture(string layerKey, Image display)
|
||||
{
|
||||
if (display == null ||
|
||||
SplatLayerRenderer == null ||
|
||||
!SplatLayerRenderer.TryGetTexture(layerKey, out var texture) ||
|
||||
texture == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
display.material.SetTexture("_DetailTex", texture);
|
||||
}
|
||||
|
||||
private static void SetCameraActive(Camera camera, bool active)
|
||||
{
|
||||
if (camera != null)
|
||||
camera.gameObject.SetActive(active);
|
||||
}
|
||||
|
||||
private void RenderRtsIcons()
|
||||
{
|
||||
EnsureRtsIconRoot();
|
||||
if (RtsIconRoot == null)
|
||||
return;
|
||||
|
||||
var objects = _objectsViewport?.Objects ?? Array.Empty<AetheriaRuntimeRtsViewportObject>();
|
||||
for (var i = 0; i < objects.Count; i++)
|
||||
{
|
||||
var icon = ResolveRtsIcon(i);
|
||||
ApplyRtsIcon(icon, objects[i]);
|
||||
}
|
||||
|
||||
for (var i = objects.Count; i < _rtsIconPool.Count; i++)
|
||||
if (_rtsIconPool[i] != null)
|
||||
_rtsIconPool[i].gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
private RawImage ResolveRtsIcon(int index)
|
||||
{
|
||||
while (_rtsIconPool.Count <= index)
|
||||
{
|
||||
RawImage icon;
|
||||
if (RtsIconPrototype != null)
|
||||
{
|
||||
icon = Instantiate(RtsIconPrototype, RtsIconRoot);
|
||||
}
|
||||
else
|
||||
{
|
||||
var go = new GameObject("RTS Map Icon", typeof(RectTransform), typeof(CanvasRenderer), typeof(RawImage));
|
||||
go.transform.SetParent(RtsIconRoot, false);
|
||||
icon = go.GetComponent<RawImage>();
|
||||
icon.texture = Texture2D.whiteTexture;
|
||||
icon.raycastTarget = false;
|
||||
}
|
||||
|
||||
_rtsIconPool.Add(icon);
|
||||
}
|
||||
|
||||
return _rtsIconPool[index];
|
||||
}
|
||||
|
||||
private void ApplyRtsIcon(RawImage icon, AetheriaRuntimeRtsViewportObject obj)
|
||||
{
|
||||
if (icon == null || obj == null)
|
||||
return;
|
||||
|
||||
icon.gameObject.SetActive(true);
|
||||
if (AssetCatalog != null && AssetCatalog.TryResolveTexture(obj.IconAsset, out var texture))
|
||||
icon.texture = texture;
|
||||
icon.color = ResolveIconColor(obj);
|
||||
var rectTransform = icon.rectTransform;
|
||||
var pixelSize = Mathf.Max(4f, IconSize * (obj.Controlled ? 1.25f : 1f));
|
||||
rectTransform.anchorMin = rectTransform.anchorMax = new Vector2(0.5f, 0.5f);
|
||||
rectTransform.pivot = new Vector2(0.5f, 0.5f);
|
||||
rectTransform.sizeDelta = new Vector2(pixelSize, pixelSize);
|
||||
rectTransform.anchoredPosition = WorldToMapPosition(obj.X, obj.Y);
|
||||
rectTransform.localRotation = Quaternion.Euler(
|
||||
0,
|
||||
0,
|
||||
Mathf.Atan2((float)obj.DirectionY, (float)obj.DirectionX) * Mathf.Rad2Deg - 90f);
|
||||
}
|
||||
|
||||
private Vector2 WorldToMapPosition(double worldX, double worldY)
|
||||
{
|
||||
var viewport = _objectsViewport?.Viewport ?? ResolveViewportBounds();
|
||||
var minX = Math.Min(viewport.MinX, viewport.MaxX);
|
||||
var maxX = Math.Max(viewport.MinX, viewport.MaxX);
|
||||
var minY = Math.Min(viewport.MinY, viewport.MaxY);
|
||||
var maxY = Math.Max(viewport.MinY, viewport.MaxY);
|
||||
var width = Math.Max(0.0001, maxX - minX);
|
||||
var height = Math.Max(0.0001, maxY - minY);
|
||||
var rootRect = RtsIconRoot != null ? RtsIconRoot.rect : new Rect(0, 0, _size.x, _size.y);
|
||||
var u = (float)((worldX - minX) / width);
|
||||
var v = (float)((worldY - minY) / height);
|
||||
return new Vector2(
|
||||
(u - 0.5f) * rootRect.width,
|
||||
(v - 0.5f) * rootRect.height);
|
||||
}
|
||||
|
||||
private static Color ResolveIconColor(AetheriaRuntimeRtsViewportObject obj)
|
||||
{
|
||||
var color = obj.Controlled
|
||||
? new Color(0.3f, 0.9f, 1.0f, 1.0f)
|
||||
: Color.HSVToRGB(frac((obj.FactionKey ?? obj.Kind ?? "").GetHashCode() * 0.0137f), 0.55f, 0.95f);
|
||||
color.a = Mathf.Clamp01(Mathf.Max(0.25f, (float)obj.Visibility));
|
||||
return color;
|
||||
}
|
||||
|
||||
private void EnsureRtsIconRoot()
|
||||
{
|
||||
if (RtsIconRoot != null)
|
||||
return;
|
||||
|
||||
var parent = GravityDisplay != null
|
||||
? GravityDisplay.transform.parent
|
||||
: transform;
|
||||
var go = new GameObject("RTS Command Icons", typeof(RectTransform));
|
||||
go.transform.SetParent(parent, false);
|
||||
RtsIconRoot = go.GetComponent<RectTransform>();
|
||||
RtsIconRoot.anchorMin = Vector2.zero;
|
||||
RtsIconRoot.anchorMax = Vector2.one;
|
||||
RtsIconRoot.offsetMin = Vector2.zero;
|
||||
RtsIconRoot.offsetMax = Vector2.zero;
|
||||
RtsIconRoot.SetAsLastSibling();
|
||||
}
|
||||
|
||||
private void SetRtsIconsActive(bool active)
|
||||
{
|
||||
if (RtsIconRoot != null)
|
||||
RtsIconRoot.gameObject.SetActive(active);
|
||||
}
|
||||
|
||||
private AetheriaRuntimeRtsViewportBounds ResolveViewportBounds()
|
||||
{
|
||||
var screenHeight = _size.y <= 0 ? Math.Max(1, Screen.height) : _size.y;
|
||||
|
|
|
|||
|
|
@ -169,17 +169,14 @@ public class MenuPanel : MonoBehaviour
|
|||
.ToArray();
|
||||
}
|
||||
|
||||
private AetheriaRuntimeCurrentDockingDocument ResolveCurrentDocking()
|
||||
private AetheriaClientDockingSnapshot ResolveCurrentDocking()
|
||||
{
|
||||
try
|
||||
{
|
||||
return ResolveClient()
|
||||
.Aetheria()
|
||||
.Current
|
||||
.Docking
|
||||
.LatestAsync()
|
||||
.GetAwaiter()
|
||||
.GetResult();
|
||||
.DockingState
|
||||
.Latest();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ using float2 = Unity.Mathematics.float2;
|
|||
public class SectorMap : MonoBehaviour
|
||||
{
|
||||
public Camera InfluenceCamera;
|
||||
public AetheriaRenderSplatRasterizer InfluenceSplatRasterizer;
|
||||
public Prototype InfluenceRendererPrototype;
|
||||
public MeshRenderer SectorRenderer;
|
||||
public Prototype ZonePrototype;
|
||||
|
|
@ -216,6 +217,7 @@ public class SectorMap : MonoBehaviour
|
|||
public void Start()
|
||||
{
|
||||
EnsureSectorMapLoaded();
|
||||
DisableLegacyInfluenceCamera();
|
||||
}
|
||||
|
||||
private void EnsureSectorMapLoaded()
|
||||
|
|
@ -341,33 +343,162 @@ public class SectorMap : MonoBehaviour
|
|||
|
||||
private void RenderInfluence()
|
||||
{
|
||||
DisableLegacyInfluenceCamera();
|
||||
foreach (var pair in _factionMaterials)
|
||||
{
|
||||
foreach (var zone in _sectorMap?.Zones ?? Array.Empty<AetheriaRuntimeSectorMapZone>())
|
||||
{
|
||||
if (!_zoneInstances.TryGetValue(zone.ZoneIndex, out var instance))
|
||||
continue;
|
||||
|
||||
var influence = 0f;
|
||||
if (zone.FactionIndices?.Count > 0)
|
||||
{
|
||||
if (zone.FactionIndices.Contains(pair.Key))
|
||||
{
|
||||
influence = 10;
|
||||
if (zone.OwnerFactionIndex != pair.Key)
|
||||
influence *= .5f;
|
||||
}
|
||||
else influence = -10;
|
||||
}
|
||||
|
||||
instance.Influence.material.SetFloat("_Depth", influence);
|
||||
}
|
||||
|
||||
InfluenceCamera.targetTexture = pair.Value.influence;
|
||||
InfluenceCamera.Render();
|
||||
var document = BuildInfluenceSplatDocument(pair.Key);
|
||||
ResolveInfluenceSplatRasterizer().RenderLayerToTarget(
|
||||
document,
|
||||
pair.Value.influence,
|
||||
0,
|
||||
0,
|
||||
Color.clear);
|
||||
}
|
||||
}
|
||||
|
||||
private AetheriaRenderSplatRasterizer ResolveInfluenceSplatRasterizer()
|
||||
{
|
||||
if (InfluenceSplatRasterizer != null)
|
||||
return InfluenceSplatRasterizer;
|
||||
|
||||
InfluenceSplatRasterizer = GetComponent<AetheriaRenderSplatRasterizer>();
|
||||
if (InfluenceSplatRasterizer == null)
|
||||
InfluenceSplatRasterizer = gameObject.AddComponent<AetheriaRenderSplatRasterizer>();
|
||||
return InfluenceSplatRasterizer;
|
||||
}
|
||||
|
||||
private AetheriaRuntimeRenderSplatsViewportDocument BuildInfluenceSplatDocument(int factionIndex)
|
||||
{
|
||||
var zones = (_sectorMap?.Zones ?? Array.Empty<AetheriaRuntimeSectorMapZone>()).ToArray();
|
||||
var minX = zones.Length > 0 ? zones.Min(zone => zone.X) : -1;
|
||||
var maxX = zones.Length > 0 ? zones.Max(zone => zone.X) : 1;
|
||||
var minY = zones.Length > 0 ? zones.Min(zone => zone.Y) : -1;
|
||||
var maxY = zones.Length > 0 ? zones.Max(zone => zone.Y) : 1;
|
||||
var padding = Math.Max(IconBackgroundSize * 2, 4);
|
||||
|
||||
var centerX = new List<double>();
|
||||
var centerY = new List<double>();
|
||||
var halfExtentX = new List<double>();
|
||||
var halfExtentY = new List<double>();
|
||||
var rotationCos = new List<double>();
|
||||
var rotationSin = new List<double>();
|
||||
var channel = new List<int>();
|
||||
var falloff = new List<int>();
|
||||
var valueR = new List<double>();
|
||||
var valueG = new List<double>();
|
||||
var valueB = new List<double>();
|
||||
var valueA = new List<double>();
|
||||
var sourceKey = new List<string>();
|
||||
var layerIndex = new List<int>();
|
||||
var sourceKind = new List<int>();
|
||||
var frequencyX = new List<double>();
|
||||
var frequencyY = new List<double>();
|
||||
var phaseX = new List<double>();
|
||||
var phaseY = new List<double>();
|
||||
var animationSpeed = new List<double>();
|
||||
var sourceFlags = new List<double>();
|
||||
|
||||
foreach (var zone in zones)
|
||||
{
|
||||
if (!_zoneInstances.ContainsKey(zone.ZoneIndex))
|
||||
continue;
|
||||
|
||||
var influence = ResolveZoneInfluence(zone, factionIndex);
|
||||
if (Math.Abs(influence) <= 0.0001f)
|
||||
continue;
|
||||
|
||||
centerX.Add(zone.X);
|
||||
centerY.Add(zone.Y);
|
||||
halfExtentX.Add(Math.Max(IconBackgroundSize, 1));
|
||||
halfExtentY.Add(Math.Max(IconBackgroundSize, 1));
|
||||
rotationCos.Add(1);
|
||||
rotationSin.Add(0);
|
||||
channel.Add(AetheriaRuntimeRenderSplatChannels.Influence);
|
||||
falloff.Add(AetheriaRuntimeRenderSplatFalloffs.Smooth);
|
||||
valueR.Add(influence);
|
||||
valueG.Add(influence);
|
||||
valueB.Add(influence);
|
||||
valueA.Add(1);
|
||||
sourceKey.Add($"sector-zone:{zone.ZoneIndex}:faction:{factionIndex}");
|
||||
layerIndex.Add(0);
|
||||
sourceKind.Add(AetheriaRuntimeRenderSplatSourceKinds.Constant);
|
||||
frequencyX.Add(0);
|
||||
frequencyY.Add(0);
|
||||
phaseX.Add(0);
|
||||
phaseY.Add(0);
|
||||
animationSpeed.Add(0);
|
||||
sourceFlags.Add(0);
|
||||
}
|
||||
|
||||
return new AetheriaRuntimeRenderSplatsViewportDocument
|
||||
{
|
||||
Schema = AetheriaRuntimeDaemonSchemas.RenderSplatsViewport,
|
||||
Viewport = new AetheriaRuntimeRtsViewportBounds
|
||||
{
|
||||
MinX = minX - padding,
|
||||
MinY = minY - padding,
|
||||
MaxX = maxX + padding,
|
||||
MaxY = maxY + padding
|
||||
},
|
||||
Layers = new[]
|
||||
{
|
||||
new AetheriaRuntimeRenderSplatLayerDefinition
|
||||
{
|
||||
LayerKey = AetheriaRuntimeRenderSplatLayerKeys.Influence,
|
||||
DisplayName = "Sector Influence",
|
||||
Channel = AetheriaRuntimeRenderSplatChannels.Influence,
|
||||
BlendMode = AetheriaRuntimeRenderSplatBlendModes.Add,
|
||||
GraphicsFormat = "R16_SFloat"
|
||||
}
|
||||
},
|
||||
Splats = new AetheriaRuntimeRenderSplatSoa
|
||||
{
|
||||
Count = centerX.Count,
|
||||
CenterX = centerX,
|
||||
CenterY = centerY,
|
||||
HalfExtentX = halfExtentX,
|
||||
HalfExtentY = halfExtentY,
|
||||
RotationCos = rotationCos,
|
||||
RotationSin = rotationSin,
|
||||
Channel = channel,
|
||||
Falloff = falloff,
|
||||
ValueR = valueR,
|
||||
ValueG = valueG,
|
||||
ValueB = valueB,
|
||||
ValueA = valueA,
|
||||
SourceKey = sourceKey,
|
||||
LayerIndex = layerIndex,
|
||||
SourceKind = sourceKind,
|
||||
FrequencyX = frequencyX,
|
||||
FrequencyY = frequencyY,
|
||||
PhaseX = phaseX,
|
||||
PhaseY = phaseY,
|
||||
AnimationSpeed = animationSpeed,
|
||||
SourceFlags = sourceFlags
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static float ResolveZoneInfluence(AetheriaRuntimeSectorMapZone zone, int factionIndex)
|
||||
{
|
||||
if (zone.FactionIndices?.Count <= 0)
|
||||
return 0f;
|
||||
|
||||
if (!zone.FactionIndices.Contains(factionIndex))
|
||||
return -10f;
|
||||
|
||||
return zone.OwnerFactionIndex == factionIndex ? 10f : 5f;
|
||||
}
|
||||
|
||||
private void DisableLegacyInfluenceCamera()
|
||||
{
|
||||
if (InfluenceCamera == null)
|
||||
return;
|
||||
|
||||
InfluenceCamera.targetTexture = null;
|
||||
InfluenceCamera.gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
private AetheriaClient ResolveClient()
|
||||
{
|
||||
var gameDataDirectory = new DirectoryInfo(Path.Combine(Application.dataPath, "..", "GameData"));
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ public class TradeMenu : MonoBehaviour
|
|||
private AetheriaRuntimeTradeCargoSelectorSurfaceProjection _cargoSelectorSurfaceProjection;
|
||||
private AetheriaRuntimeStationCargoTargetRow[] _cargoSelectorStationRefitTargets =
|
||||
Array.Empty<AetheriaRuntimeStationCargoTargetRow>();
|
||||
private AetheriaClientDockingSnapshot _dockingState;
|
||||
private AetheriaRuntimeStationRefitDocument _stationRefit;
|
||||
private AetheriaRuntimeTradeFilterSurfaceProjection _filterSurfaceProjection;
|
||||
private Action[] _rowActionCallbacks = Array.Empty<Action>();
|
||||
|
|
@ -56,8 +57,10 @@ public class TradeMenu : MonoBehaviour
|
|||
|
||||
private void OnEnable()
|
||||
{
|
||||
if (!TryResolveCurrentDocking(out var docking) ||
|
||||
!docking.IsDocked ||
|
||||
var dockingState = ResolveDockingState();
|
||||
var docking = dockingState?.CurrentDocking;
|
||||
if (dockingState?.IsDocked != true ||
|
||||
docking == null ||
|
||||
string.IsNullOrWhiteSpace(docking.DockParentEntityKey) ||
|
||||
docking.DockingBayIndex < 0)
|
||||
{
|
||||
|
|
@ -514,7 +517,8 @@ public class TradeMenu : MonoBehaviour
|
|||
private bool TryResolveCurrentDockingTargetEntityKey(out string targetEntityKey)
|
||||
{
|
||||
targetEntityKey = "";
|
||||
if (!TryResolveCurrentDocking(out var docking) ||
|
||||
var docking = ResolveDockingState()?.CurrentDocking;
|
||||
if (docking == null ||
|
||||
!docking.IsDocked ||
|
||||
string.IsNullOrWhiteSpace(docking.DockParentEntityKey))
|
||||
{
|
||||
|
|
@ -525,54 +529,40 @@ public class TradeMenu : MonoBehaviour
|
|||
return true;
|
||||
}
|
||||
|
||||
private bool TryResolveCurrentDocking(out AetheriaRuntimeCurrentDockingDocument docking)
|
||||
{
|
||||
docking = null;
|
||||
|
||||
try
|
||||
{
|
||||
docking = ResolveClient()
|
||||
.Aetheria()
|
||||
.Current
|
||||
.Docking
|
||||
.LatestAsync()
|
||||
.GetAwaiter()
|
||||
.GetResult();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogWarning($"Failed to read Aetheria current docking for trade menu: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
|
||||
return docking != null;
|
||||
}
|
||||
|
||||
private AetheriaRuntimeStationRefitDocument ResolveStationRefit()
|
||||
{
|
||||
if (_stationRefit != null)
|
||||
return _stationRefit;
|
||||
|
||||
_stationRefit = ResolveDockingState()?.StationRefit;
|
||||
return _stationRefit;
|
||||
}
|
||||
|
||||
private AetheriaClientDockingSnapshot ResolveDockingState()
|
||||
{
|
||||
if (_dockingState != null)
|
||||
return _dockingState;
|
||||
|
||||
try
|
||||
{
|
||||
_stationRefit = ResolveClient()
|
||||
_dockingState = ResolveClient()
|
||||
.Aetheria()
|
||||
.StationRefit
|
||||
.LatestAsync()
|
||||
.GetAwaiter()
|
||||
.GetResult();
|
||||
.DockingState
|
||||
.Latest();
|
||||
_stationRefit = _dockingState?.StationRefit;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogWarning($"Failed to read Aetheria station refit projection for trade menu: {ex.Message}");
|
||||
Debug.LogWarning($"Failed to read Aetheria docking state for trade menu: {ex.Message}");
|
||||
return null;
|
||||
}
|
||||
|
||||
return _stationRefit;
|
||||
return _dockingState;
|
||||
}
|
||||
|
||||
private void InvalidateStationRefit()
|
||||
{
|
||||
_dockingState = null;
|
||||
_stationRefit = null;
|
||||
}
|
||||
|
||||
|
|
@ -649,6 +639,7 @@ public class TradeMenu : MonoBehaviour
|
|||
_clientStatePath = "";
|
||||
_catalog = null;
|
||||
_playerSettings = null;
|
||||
_dockingState = null;
|
||||
_stationRefit = null;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using GameCult.Aetheria.State.Verse;
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
using UnityEngine.Serialization;
|
||||
|
|
@ -15,6 +16,7 @@ public class MapView : MonoBehaviour
|
|||
public Camera MinimapGravity;
|
||||
public Camera Minimap;
|
||||
public MeshRenderer GravityRenderer;
|
||||
public AetheriaUnityRenderSplatViewportSource GravitySplatSource;
|
||||
public float ZoomSpeed;
|
||||
[FormerlySerializedAs("SectorRenderer")] public ZoneRenderer ZoneRenderer;
|
||||
|
||||
|
|
@ -26,7 +28,6 @@ public class MapView : MonoBehaviour
|
|||
private RenderTexture _minimapGravityTexture;
|
||||
private Transform _gravityRendererTransform;
|
||||
private CopyTransform _gravityRendererCopyTransform;
|
||||
private RenderTexture _fullscreenGravityTexture;
|
||||
private AetheriaInput.UIActions _input;
|
||||
private float _viewDistance = 1024;
|
||||
private float _aspectRatio;
|
||||
|
|
@ -49,9 +50,14 @@ public class MapView : MonoBehaviour
|
|||
_gravityRendererCopyTransform.enabled = false;
|
||||
_previousGravityRendererScale = _gravityRendererTransform.localScale;
|
||||
|
||||
_fullscreenGravityTexture = new RenderTexture(Screen.width / 2, Screen.height / 2, 1, RenderTextureFormat.RFloat);
|
||||
GravityRenderer.material.mainTexture = _fullscreenGravityTexture;
|
||||
MinimapGravity.targetTexture = _fullscreenGravityTexture;
|
||||
GravitySplatSource = ResolveGravitySplatSource();
|
||||
GravitySplatSource.SetLayerKey(AetheriaRuntimeRenderSplatLayerKeys.GravityHeight);
|
||||
GravitySplatSource.SetViewport(
|
||||
new Vector2(_minimapTransform.position.x, _minimapTransform.position.z),
|
||||
new Vector2(_viewDistance * _aspectRatio * 2, _viewDistance * 2));
|
||||
GravitySplatSource.RenderLatest();
|
||||
GravityRenderer.material.mainTexture = GravitySplatSource.TargetTexture;
|
||||
MinimapGravity.targetTexture = null;
|
||||
Minimap.targetTexture = null;
|
||||
SetZoom();
|
||||
}
|
||||
|
|
@ -61,6 +67,14 @@ public class MapView : MonoBehaviour
|
|||
//ZoneRenderer.ViewDistance = _viewDistance * _aspectRatio;
|
||||
Minimap.orthographicSize = MinimapGravity.orthographicSize = _viewDistance;
|
||||
_gravityRendererTransform.localScale = new Vector3(_viewDistance*_aspectRatio*2, _viewDistance*2, 1);
|
||||
if (GravitySplatSource != null && _minimapTransform != null)
|
||||
{
|
||||
GravitySplatSource.SetViewport(
|
||||
new Vector2(_minimapTransform.position.x, _minimapTransform.position.z),
|
||||
new Vector2(_viewDistance * _aspectRatio * 2, _viewDistance * 2));
|
||||
GravitySplatSource.RenderLatest();
|
||||
GravityRenderer.material.mainTexture = GravitySplatSource.TargetTexture;
|
||||
}
|
||||
}
|
||||
|
||||
// public void BindInput(AetheriaInput.UIActions input)
|
||||
|
|
@ -91,6 +105,7 @@ public class MapView : MonoBehaviour
|
|||
|
||||
_gravityRendererTransform.position = gravPos;
|
||||
Main.transform.position = _minimapTransform.position = _minimapGravityTransform.position = pos;
|
||||
SetZoom();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -106,6 +121,20 @@ public class MapView : MonoBehaviour
|
|||
Minimap.targetTexture = _minimapTexture;
|
||||
MinimapGravity.targetTexture = _minimapGravityTexture;
|
||||
GravityRenderer.material.mainTexture = _minimapGravityTexture;
|
||||
_fullscreenGravityTexture.Release();
|
||||
}
|
||||
|
||||
private AetheriaUnityRenderSplatViewportSource ResolveGravitySplatSource()
|
||||
{
|
||||
if (GravitySplatSource != null)
|
||||
return GravitySplatSource;
|
||||
|
||||
GravitySplatSource = GetComponent<AetheriaUnityRenderSplatViewportSource>();
|
||||
if (GravitySplatSource == null)
|
||||
GravitySplatSource = gameObject.AddComponent<AetheriaUnityRenderSplatViewportSource>();
|
||||
|
||||
if (GetComponent<AetheriaRenderSplatLayerRenderer>() == null)
|
||||
gameObject.AddComponent<AetheriaRenderSplatLayerRenderer>();
|
||||
|
||||
return GravitySplatSource;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
using System;
|
||||
using GameCult.Aetheria.State.Verse;
|
||||
using UnityEngine;
|
||||
using Unity.Mathematics;
|
||||
using UnityEngine.Experimental.Rendering;
|
||||
|
|
@ -26,6 +27,8 @@ public class VolumeSampling : MonoBehaviour
|
|||
public RenderTexture NebulaPatchHeight;
|
||||
public RenderTexture NebulaPatch;
|
||||
public RenderTexture NebulaTint;
|
||||
public AetheriaUnityRenderSplatViewportSource SplatViewportSource;
|
||||
public AetheriaRenderSplatLayerRenderer SplatLayerRenderer;
|
||||
|
||||
private MeshRenderer _gridMeshRenderer;
|
||||
private float _flowScroll;
|
||||
|
|
@ -42,6 +45,8 @@ public class VolumeSampling : MonoBehaviour
|
|||
|
||||
if (GridMesh != null)
|
||||
_gridMeshRenderer = GridMesh.GetComponent<MeshRenderer>();
|
||||
|
||||
ResolveSplatRenderers();
|
||||
}
|
||||
|
||||
void Update()
|
||||
|
|
@ -52,6 +57,8 @@ public class VolumeSampling : MonoBehaviour
|
|||
if(GridCamera != null)
|
||||
Shader.SetGlobalVector("_GridTransform", new Vector4(_gridTransform.position.x,_gridTransform.position.z,GridCamera.orthographicSize*2));
|
||||
|
||||
RenderSplatTextures();
|
||||
|
||||
var environment = Settings.DefaultEnvironment;
|
||||
|
||||
if (GridMesh != null)
|
||||
|
|
@ -106,4 +113,48 @@ public class VolumeSampling : MonoBehaviour
|
|||
Shader.SetKeyword(_keywordFlowSlope, environment.Flow.SlopeAmplitude != 0 || environment.Flow.SwirlAmplitude != 0);
|
||||
Shader.SetKeyword(_keywordNoiseSlope, environment.Noise.SlopeExponent != 0);
|
||||
}
|
||||
|
||||
private void RenderSplatTextures()
|
||||
{
|
||||
ResolveSplatRenderers();
|
||||
if (SplatViewportSource == null)
|
||||
return;
|
||||
|
||||
var size = GridCamera != null
|
||||
? new Vector2(GridCamera.orthographicSize * 2, GridCamera.orthographicSize * 2)
|
||||
: new Vector2(1024, 1024);
|
||||
var center = _gridTransform != null
|
||||
? new Vector2(_gridTransform.position.x, _gridTransform.position.z)
|
||||
: Vector2.zero;
|
||||
SplatViewportSource.SetViewport(center, size);
|
||||
SplatViewportSource.RenderLatest();
|
||||
|
||||
if (SplatLayerRenderer == null)
|
||||
return;
|
||||
|
||||
NebulaSurfaceHeight = ResolveLayerTexture(AetheriaRuntimeRenderSplatLayerKeys.FogSurfaceHeight, NebulaSurfaceHeight);
|
||||
NebulaPatchHeight = ResolveLayerTexture(AetheriaRuntimeRenderSplatLayerKeys.FogPatchHeight, NebulaPatchHeight);
|
||||
NebulaPatch = ResolveLayerTexture(AetheriaRuntimeRenderSplatLayerKeys.FogPatch, NebulaPatch);
|
||||
NebulaTint = ResolveLayerTexture(AetheriaRuntimeRenderSplatLayerKeys.FogTint, NebulaTint);
|
||||
}
|
||||
|
||||
private RenderTexture ResolveLayerTexture(string layerKey, RenderTexture fallback)
|
||||
{
|
||||
return SplatLayerRenderer.TryGetTexture(layerKey, out var texture) && texture != null
|
||||
? texture
|
||||
: fallback;
|
||||
}
|
||||
|
||||
private void ResolveSplatRenderers()
|
||||
{
|
||||
if (SplatLayerRenderer == null)
|
||||
SplatLayerRenderer = GetComponent<AetheriaRenderSplatLayerRenderer>();
|
||||
if (SplatLayerRenderer == null)
|
||||
SplatLayerRenderer = gameObject.AddComponent<AetheriaRenderSplatLayerRenderer>();
|
||||
|
||||
if (SplatViewportSource == null)
|
||||
SplatViewportSource = GetComponent<AetheriaUnityRenderSplatViewportSource>();
|
||||
if (SplatViewportSource == null)
|
||||
SplatViewportSource = gameObject.AddComponent<AetheriaUnityRenderSplatViewportSource>();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ public class ZoneRenderer : MonoBehaviour
|
|||
public CinemachineVirtualCamera SceneCamera;
|
||||
public Camera[] FogCameras;
|
||||
public Camera[] MinimapCameras;
|
||||
public bool UseRenderSplatTextureLayers = true;
|
||||
public Material FogMaterial;
|
||||
public float FogFarFadeFraction = .125f;
|
||||
public float FarPlaneDistanceMultiplier = 2;
|
||||
|
|
@ -115,7 +116,8 @@ public class ZoneRenderer : MonoBehaviour
|
|||
Array.Empty<AetheriaRuntimeZoneRenderBodyPose>();
|
||||
private IReadOnlyList<AetheriaRuntimeZoneRenderAsteroidBeltPose> _zoneRenderAsteroidBeltPoses =
|
||||
Array.Empty<AetheriaRuntimeZoneRenderAsteroidBeltPose>();
|
||||
private AetheriaRuntimeGravityViewportDocument _daemonGravityViewport;
|
||||
private IReadOnlyList<AetheriaRuntimeBodySnapshotCommit> _zoneRenderBodies =
|
||||
Array.Empty<AetheriaRuntimeBodySnapshotCommit>();
|
||||
private AetheriaClient _client;
|
||||
private string _clientStatePath = "";
|
||||
private AetheriaRuntimeCatalogSnapshot _catalog;
|
||||
|
|
@ -146,11 +148,16 @@ public class ZoneRenderer : MonoBehaviour
|
|||
set
|
||||
{
|
||||
_viewDistance = value;
|
||||
foreach (var camera in FogCameras)
|
||||
camera.orthographicSize = value;
|
||||
SceneCamera.m_Lens.FarClipPlane = value * FarPlaneDistanceMultiplier;
|
||||
FogMaterial.SetFloat("_DepthCeiling", value);
|
||||
FogMaterial.SetFloat("_DepthBlend", FogFarFadeFraction * value);
|
||||
foreach (var camera in FogCameras ?? Array.Empty<Camera>())
|
||||
if (camera != null)
|
||||
camera.orthographicSize = value;
|
||||
if (SceneCamera != null)
|
||||
SceneCamera.m_Lens.FarClipPlane = value * FarPlaneDistanceMultiplier;
|
||||
if (FogMaterial != null)
|
||||
{
|
||||
FogMaterial.SetFloat("_DepthCeiling", value);
|
||||
FogMaterial.SetFloat("_DepthBlend", FogFarFadeFraction * value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -159,9 +166,11 @@ public class ZoneRenderer : MonoBehaviour
|
|||
set
|
||||
{
|
||||
_minimapDistance = value;
|
||||
foreach (var camera in MinimapCameras)
|
||||
camera.orthographicSize = value;
|
||||
SetIconSize((float)RenderSettings.ResolveMinimapIconSize(value));
|
||||
foreach (var camera in MinimapCameras ?? Array.Empty<Camera>())
|
||||
if (camera != null)
|
||||
camera.orthographicSize = value;
|
||||
if (RenderSettings != null)
|
||||
SetIconSize((float)RenderSettings.ResolveMinimapIconSize(value));
|
||||
// MinimapGravityQuad.transform.localScale = value * 2 * Vector3.one;
|
||||
}
|
||||
}
|
||||
|
|
@ -235,6 +244,7 @@ public class ZoneRenderer : MonoBehaviour
|
|||
|
||||
_tourTimer = TourSwitchTime;
|
||||
_transposer = SceneCamera.GetCinemachineComponent<CinemachineTransposer>();
|
||||
DisableLegacyTextureCameras();
|
||||
}
|
||||
|
||||
public void LoadDaemonZoneView(
|
||||
|
|
@ -244,9 +254,6 @@ public class ZoneRenderer : MonoBehaviour
|
|||
ClearZone();
|
||||
_observedEntityFacadesByDaemonIndex = observedEntityFacadesByDaemonIndex;
|
||||
ApplyZoneRender(render);
|
||||
RefreshDaemonBodyPoses();
|
||||
RefreshDaemonAsteroidBeltPoses();
|
||||
SyncDaemonBodyViews();
|
||||
RefreshDaemonVisibleEntityInstances();
|
||||
SyncDaemonEntityInstances();
|
||||
|
||||
|
|
@ -260,11 +267,17 @@ public class ZoneRenderer : MonoBehaviour
|
|||
_daemonSimulationTimeSeconds = render?.SimulationTimeSeconds ?? 0;
|
||||
_zoneRenderBodyPoses = render?.BodyPoses ?? Array.Empty<AetheriaRuntimeZoneRenderBodyPose>();
|
||||
_zoneRenderAsteroidBeltPoses = render?.AsteroidBeltPoses ?? Array.Empty<AetheriaRuntimeZoneRenderAsteroidBeltPose>();
|
||||
_zoneRenderBodies = render?.Bodies ?? Array.Empty<AetheriaRuntimeBodySnapshotCommit>();
|
||||
RefreshDaemonContactRows();
|
||||
var zoneRenderRadius = (float)Math.Max(0, render?.ZoneRenderRadius ?? 2000);
|
||||
SectorBrushes.localScale = zoneRenderRadius * 2 * Vector3.one;
|
||||
SlimeGravityCamera.orthographicSize = zoneRenderRadius;
|
||||
if (SlimeGravityCamera != null)
|
||||
SlimeGravityCamera.orthographicSize = zoneRenderRadius;
|
||||
SlimeRenderer.ZoneRadius = zoneRenderRadius;
|
||||
DisableLegacyTextureCameras();
|
||||
RefreshDaemonBodyPoses();
|
||||
RefreshDaemonAsteroidBeltPoses();
|
||||
SyncDaemonBodyViews();
|
||||
_daemonWormholeExits.Clear();
|
||||
foreach (var exit in render?.WormholeExits ?? Array.Empty<AetheriaRuntimeZoneRenderWormholeExit>())
|
||||
{
|
||||
|
|
@ -673,30 +686,23 @@ public class ZoneRenderer : MonoBehaviour
|
|||
|
||||
private void SyncDaemonBodyViews()
|
||||
{
|
||||
try
|
||||
{
|
||||
var viewport = ToViewportBounds(ResolveDaemonRenderViewport());
|
||||
var gravity = ResolveClient()
|
||||
.GravityViewportAsync(viewport)
|
||||
.GetAwaiter()
|
||||
.GetResult();
|
||||
_daemonGravityViewport = gravity;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogWarning($"Failed to read Aetheria gravity viewport for zone renderer terrain: {ex.Message}");
|
||||
}
|
||||
|
||||
_daemonBodyViews.Clear();
|
||||
foreach (var bodyView in _daemonGravityViewport?.Bodies ?? Array.Empty<AetheriaRuntimeRtsBodyView>())
|
||||
var bodySnapshotsByBodyKey = _zoneRenderBodies
|
||||
.Where(body => body != null && !string.IsNullOrWhiteSpace(body.BodyKey))
|
||||
.ToDictionary(body => body.BodyKey, StringComparer.Ordinal);
|
||||
foreach (var bodyPose in _zoneRenderBodyPoses ?? Array.Empty<AetheriaRuntimeZoneRenderBodyPose>())
|
||||
{
|
||||
if (bodyView?.Body == null || string.IsNullOrWhiteSpace(bodyView.BodyKey))
|
||||
if (bodyPose == null ||
|
||||
string.IsNullOrWhiteSpace(bodyPose.BodyKey) ||
|
||||
!bodySnapshotsByBodyKey.TryGetValue(bodyPose.BodyKey, out var body))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
_daemonBodyViews.Add(new AetheriaRuntimeDaemonBodyView(
|
||||
bodyView.Body,
|
||||
ToDaemonBodyPose(bodyView),
|
||||
bodyView.IsAsteroidBelt));
|
||||
body,
|
||||
ToDaemonBodyPose(bodyPose),
|
||||
IsAsteroidBelt(body)));
|
||||
}
|
||||
var beltPosesByBodyKey = _daemonAsteroidBeltPoses
|
||||
.Where(pose => !string.IsNullOrWhiteSpace(pose.BodyKey))
|
||||
|
|
@ -739,6 +745,19 @@ public class ZoneRenderer : MonoBehaviour
|
|||
_suns = _bodyViewsByBodyKey.Values.Where(p => p is SunObject).ToArray();
|
||||
}
|
||||
|
||||
private void DisableLegacyTextureCameras()
|
||||
{
|
||||
if (!UseRenderSplatTextureLayers)
|
||||
return;
|
||||
|
||||
foreach (var camera in FogCameras ?? Array.Empty<Camera>())
|
||||
if (camera != null)
|
||||
camera.gameObject.SetActive(false);
|
||||
|
||||
if (SlimeGravityCamera != null)
|
||||
SlimeGravityCamera.gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
private AetheriaRuntimeXzRect ResolveDaemonRenderViewport()
|
||||
{
|
||||
var center = PerspectiveEntity?.CultPositionXZ ?? default;
|
||||
|
|
@ -960,42 +979,31 @@ public class ZoneRenderer : MonoBehaviour
|
|||
|
||||
private double EvaluateGravityTerrainHeight(double positionX, double positionZ)
|
||||
{
|
||||
var gravity = _daemonGravityViewport;
|
||||
if (gravity == null)
|
||||
return 0;
|
||||
|
||||
var height = 0.0;
|
||||
if (gravity.TerrainRadius > 0 && gravity.TerrainDepth != 0)
|
||||
foreach (var body in _zoneRenderBodies ?? Array.Empty<AetheriaRuntimeBodySnapshotCommit>())
|
||||
{
|
||||
var distance = Math.Sqrt(positionX * positionX + positionZ * positionZ);
|
||||
height -= PowerPulse(
|
||||
distance / (gravity.TerrainRadius * 2.0),
|
||||
Math.Max(0.0001, gravity.TerrainDepthExponent)) * gravity.TerrainDepth;
|
||||
}
|
||||
|
||||
foreach (var brush in gravity.GravityInfluences ?? Array.Empty<AetheriaRuntimeRtsGravityInfluence>())
|
||||
{
|
||||
if (brush == null)
|
||||
if (body == null)
|
||||
continue;
|
||||
|
||||
var dx = positionX - brush.X;
|
||||
var dz = positionZ - brush.Y;
|
||||
var dx = positionX - body.GravityInfluenceCenterX;
|
||||
var dz = positionZ - body.GravityInfluenceCenterZ;
|
||||
var distance = Math.Sqrt(dx * dx + dz * dz);
|
||||
if (brush.Radius > 0 && distance < brush.Radius && brush.GravityDepth != 0)
|
||||
var radius = ResolveGravityRadius(body);
|
||||
if (radius > 0 && distance < radius && body.GravityWellDepth != 0)
|
||||
{
|
||||
height -= PowerPulse(
|
||||
distance / brush.Radius,
|
||||
Math.Max(0.0001, brush.GravityDepthExponent)) * brush.GravityDepth;
|
||||
distance / radius,
|
||||
Math.Max(0.0001, body.GravityDepthExponent)) * body.GravityWellDepth;
|
||||
}
|
||||
|
||||
if (brush.WaveRadius > 0 && distance < brush.WaveRadius && brush.WaveDepth != 0)
|
||||
if (body.GravityWaveRadius > 0 && distance < body.GravityWaveRadius && body.GravityWaveDepth != 0)
|
||||
{
|
||||
height -= RadialWaves(
|
||||
distance / brush.WaveRadius,
|
||||
distance / body.GravityWaveRadius,
|
||||
8.0,
|
||||
1.25,
|
||||
gravity.TerrainWaveFrequency,
|
||||
DaemonSimulationTimeSeconds * brush.WaveSpeed) * brush.WaveDepth;
|
||||
1.0,
|
||||
DaemonSimulationTimeSeconds * body.GravityWaveSpeed) * body.GravityWaveDepth;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1006,33 +1014,45 @@ public class ZoneRenderer : MonoBehaviour
|
|||
double minimapGravityRange,
|
||||
double maxDepth)
|
||||
{
|
||||
var gravity = _daemonGravityViewport;
|
||||
if (gravity == null)
|
||||
return new AetheriaRuntimeGravityTerrainBand(0, Math.Max(0, maxDepth));
|
||||
|
||||
var startDepth = PowerPulse(
|
||||
minimapGravityRange,
|
||||
Math.Max(0.0001, gravity.TerrainDepthExponent)) * gravity.TerrainDepth;
|
||||
var terrainDepth = (_zoneRenderBodies ?? Array.Empty<AetheriaRuntimeBodySnapshotCommit>())
|
||||
.Where(body => body != null)
|
||||
.Select(body => Math.Max(0, body.GravityWellDepth))
|
||||
.DefaultIfEmpty(0)
|
||||
.Max();
|
||||
var startDepth = Math.Min(terrainDepth, Math.Max(0, minimapGravityRange));
|
||||
return new AetheriaRuntimeGravityTerrainBand(
|
||||
startDepth,
|
||||
gravity.TerrainDepth - startDepth + maxDepth);
|
||||
Math.Max(0, terrainDepth - startDepth) + maxDepth);
|
||||
}
|
||||
|
||||
private static AetheriaRuntimeDaemonBodyPose ToDaemonBodyPose(AetheriaRuntimeRtsBodyView bodyView)
|
||||
private static AetheriaRuntimeDaemonBodyPose ToDaemonBodyPose(AetheriaRuntimeZoneRenderBodyPose bodyPose)
|
||||
{
|
||||
if (bodyView == null)
|
||||
if (bodyPose == null)
|
||||
return default;
|
||||
|
||||
return new AetheriaRuntimeDaemonBodyPose(
|
||||
bodyView.BodyKey,
|
||||
bodyView.OrbitKey,
|
||||
"",
|
||||
bodyView.Kind,
|
||||
bodyView.X,
|
||||
bodyView.Y,
|
||||
0,
|
||||
0,
|
||||
bodyView.Body?.GravityWaveSpeed ?? 0);
|
||||
bodyPose.BodyKey,
|
||||
bodyPose.OrbitKey,
|
||||
bodyPose.ParentOrbitKey,
|
||||
bodyPose.Kind,
|
||||
bodyPose.CenterX,
|
||||
bodyPose.CenterZ,
|
||||
bodyPose.ParentCenterX,
|
||||
bodyPose.ParentCenterZ,
|
||||
bodyPose.GravityWaveSpeed);
|
||||
}
|
||||
|
||||
private static bool IsAsteroidBelt(AetheriaRuntimeBodySnapshotCommit body)
|
||||
{
|
||||
return (body?.Kind ?? "").IndexOf("asteroid", StringComparison.OrdinalIgnoreCase) >= 0;
|
||||
}
|
||||
|
||||
private static double ResolveGravityRadius(AetheriaRuntimeBodySnapshotCommit body)
|
||||
{
|
||||
if (body != null && double.IsFinite(body.GravityInfluenceRadius) && body.GravityInfluenceRadius > 0)
|
||||
return body.GravityInfluenceRadius;
|
||||
|
||||
return Math.Max(32, (body?.BodyRadiusMultiplier ?? 1) * 70);
|
||||
}
|
||||
|
||||
private static double PowerPulse(double x, double exponent)
|
||||
|
|
|
|||
68
Assets/Shaders/AetheriaRenderSplats.shader
Normal file
68
Assets/Shaders/AetheriaRenderSplats.shader
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
Shader "Aetheria/Render Splats"
|
||||
{
|
||||
Properties
|
||||
{
|
||||
_ValueScale("Value Scale", Float) = 1
|
||||
}
|
||||
|
||||
SubShader
|
||||
{
|
||||
Tags { "RenderType" = "Transparent" "Queue" = "Transparent" }
|
||||
|
||||
Pass
|
||||
{
|
||||
Name "Add"
|
||||
Cull Off
|
||||
ZWrite Off
|
||||
ZTest Always
|
||||
BlendOp Add
|
||||
Blend One One
|
||||
|
||||
HLSLPROGRAM
|
||||
#pragma target 4.5
|
||||
#pragma vertex Vert
|
||||
#pragma fragment Frag
|
||||
#include "UnityCG.cginc"
|
||||
#define AETHERIA_SPLAT_PASS
|
||||
#include "AetheriaRenderSplatsCore.hlsl"
|
||||
ENDHLSL
|
||||
}
|
||||
|
||||
Pass
|
||||
{
|
||||
Name "Max"
|
||||
Cull Off
|
||||
ZWrite Off
|
||||
ZTest Always
|
||||
BlendOp Max
|
||||
Blend One One
|
||||
|
||||
HLSLPROGRAM
|
||||
#pragma target 4.5
|
||||
#pragma vertex Vert
|
||||
#pragma fragment Frag
|
||||
#include "UnityCG.cginc"
|
||||
#define AETHERIA_SPLAT_PASS
|
||||
#include "AetheriaRenderSplatsCore.hlsl"
|
||||
ENDHLSL
|
||||
}
|
||||
|
||||
Pass
|
||||
{
|
||||
Name "Alpha"
|
||||
Cull Off
|
||||
ZWrite Off
|
||||
ZTest Always
|
||||
Blend SrcAlpha OneMinusSrcAlpha
|
||||
|
||||
HLSLPROGRAM
|
||||
#pragma target 4.5
|
||||
#pragma vertex Vert
|
||||
#pragma fragment Frag
|
||||
#include "UnityCG.cginc"
|
||||
#define AETHERIA_SPLAT_PASS
|
||||
#include "AetheriaRenderSplatsCore.hlsl"
|
||||
ENDHLSL
|
||||
}
|
||||
}
|
||||
}
|
||||
10
Assets/Shaders/AetheriaRenderSplats.shader.meta
Normal file
10
Assets/Shaders/AetheriaRenderSplats.shader.meta
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a1aa8658e37f433cb08a29ab01f4e8e5
|
||||
ShaderImporter:
|
||||
externalObjects: {}
|
||||
defaultTextures: []
|
||||
nonModifiableTextures: []
|
||||
preprocessorOverride: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
115
Assets/Shaders/AetheriaRenderSplatsCore.hlsl
Normal file
115
Assets/Shaders/AetheriaRenderSplatsCore.hlsl
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
#ifndef AETHERIA_RENDER_SPLATS_CORE_INCLUDED
|
||||
#define AETHERIA_RENDER_SPLATS_CORE_INCLUDED
|
||||
|
||||
struct AetheriaSplat
|
||||
{
|
||||
float4 centerHalfExtent;
|
||||
float4 rotationChannelFalloff;
|
||||
float4 layerSource;
|
||||
float4 sourceFrequencyPhase;
|
||||
float4 value;
|
||||
};
|
||||
|
||||
StructuredBuffer<AetheriaSplat> _AetheriaSplats;
|
||||
float4x4 _AetheriaViewportToClip;
|
||||
int _AetheriaSplatCount;
|
||||
int _AetheriaChannelFilter;
|
||||
float _ValueScale;
|
||||
|
||||
float hash21(float2 p)
|
||||
{
|
||||
p = frac(p * float2(123.34, 456.21));
|
||||
p += dot(p, p + 45.32);
|
||||
return frac(p.x * p.y);
|
||||
}
|
||||
|
||||
float valueNoise(float2 p)
|
||||
{
|
||||
float2 i = floor(p);
|
||||
float2 f = frac(p);
|
||||
float a = hash21(i);
|
||||
float b = hash21(i + float2(1, 0));
|
||||
float c = hash21(i + float2(0, 1));
|
||||
float d = hash21(i + float2(1, 1));
|
||||
float2 u = f * f * (3 - 2 * f);
|
||||
return lerp(lerp(a, b, u.x), lerp(c, d, u.x), u.y) * 2 - 1;
|
||||
}
|
||||
|
||||
struct Varyings
|
||||
{
|
||||
float4 position : SV_POSITION;
|
||||
float2 localUv : TEXCOORD0;
|
||||
nointerpolation uint instanceId : TEXCOORD1;
|
||||
};
|
||||
|
||||
Varyings Vert(uint vertexId : SV_VertexID, uint instanceId : SV_InstanceID)
|
||||
{
|
||||
static const float2 corners[6] =
|
||||
{
|
||||
float2(-1, -1),
|
||||
float2(1, -1),
|
||||
float2(1, 1),
|
||||
float2(-1, -1),
|
||||
float2(1, 1),
|
||||
float2(-1, 1)
|
||||
};
|
||||
|
||||
AetheriaSplat splat = _AetheriaSplats[instanceId];
|
||||
float2 local = corners[vertexId];
|
||||
float2 scaled = local * splat.centerHalfExtent.zw;
|
||||
float c = splat.rotationChannelFalloff.x;
|
||||
float s = splat.rotationChannelFalloff.y;
|
||||
float2 world = splat.centerHalfExtent.xy + float2(
|
||||
scaled.x * c - scaled.y * s,
|
||||
scaled.x * s + scaled.y * c);
|
||||
|
||||
Varyings output;
|
||||
output.position = mul(_AetheriaViewportToClip, float4(world, 0, 1));
|
||||
output.localUv = local;
|
||||
output.instanceId = instanceId;
|
||||
return output;
|
||||
}
|
||||
|
||||
float ResolveFalloff(float2 localUv, int falloff)
|
||||
{
|
||||
float distance01 = saturate(length(localUv));
|
||||
if (falloff == 0)
|
||||
return 1;
|
||||
if (falloff == 1)
|
||||
return saturate(1 - distance01);
|
||||
if (falloff == 3)
|
||||
{
|
||||
float t = smoothstep(0, 1, distance01);
|
||||
return t;
|
||||
}
|
||||
|
||||
return 1 - smoothstep(0, 1, distance01);
|
||||
}
|
||||
|
||||
float4 Frag(Varyings input) : SV_Target
|
||||
{
|
||||
AetheriaSplat splat = _AetheriaSplats[input.instanceId];
|
||||
int channel = (int)round(splat.rotationChannelFalloff.z);
|
||||
if (_AetheriaChannelFilter >= 0 && channel != _AetheriaChannelFilter)
|
||||
discard;
|
||||
|
||||
int falloff = (int)round(splat.rotationChannelFalloff.w);
|
||||
float alpha = ResolveFalloff(input.localUv, falloff);
|
||||
clip(alpha - 0.0001);
|
||||
|
||||
int sourceKind = (int)round(splat.layerSource.y);
|
||||
float source = 1;
|
||||
if (sourceKind == 1 || sourceKind == 2)
|
||||
{
|
||||
float2 frequency = splat.sourceFrequencyPhase.xy;
|
||||
float2 phase = splat.sourceFrequencyPhase.zw;
|
||||
float timeOffset = sourceKind == 2 ? _Time.y * splat.layerSource.z : 0;
|
||||
source = valueNoise(input.localUv * frequency + phase + timeOffset);
|
||||
if (splat.layerSource.w != 0)
|
||||
source = abs(source);
|
||||
}
|
||||
|
||||
return splat.value * (alpha * source * _ValueScale);
|
||||
}
|
||||
|
||||
#endif
|
||||
10
Assets/Shaders/AetheriaRenderSplatsCore.hlsl.meta
Normal file
10
Assets/Shaders/AetheriaRenderSplatsCore.hlsl.meta
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 91df759011f84ed8b003bb7828809e0b
|
||||
ShaderImporter:
|
||||
externalObjects: {}
|
||||
defaultTextures: []
|
||||
nonModifiableTextures: []
|
||||
preprocessorOverride: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -106,9 +106,9 @@ public class Slime : MonoBehaviour
|
|||
_trailKernel = SlimeCompute.FindKernel("UpdateTrailPoints");
|
||||
_geometryKernel = SlimeCompute.FindKernel("CreateTrailGeometry");
|
||||
|
||||
_accumulationTexture = new RenderTexture(TextureSize, TextureSize, 1, RenderTextureFormat.RFloat);
|
||||
_accumulationTexture = new RenderTexture(TextureSize, TextureSize, 0, RenderTextureFormat.RFloat);
|
||||
_accumulationTexture.enableRandomWrite = true;
|
||||
_previousAccumulationTexture = new RenderTexture(TextureSize, TextureSize, 1, RenderTextureFormat.RFloat);
|
||||
_previousAccumulationTexture = new RenderTexture(TextureSize, TextureSize, 0, RenderTextureFormat.RFloat);
|
||||
_previousAccumulationTexture.enableRandomWrite = true;
|
||||
AccumulationMaterial.SetTexture("_MainTex", _accumulationTexture);
|
||||
|
||||
|
|
|
|||
|
|
@ -57,11 +57,11 @@
|
|||
<Compile Include="E:\Projects\CultMath\src\CultMath\double3.cs" />
|
||||
<Compile Include="E:\Projects\CultMath\src\CultMath\float4.cs" />
|
||||
<Compile Include="E:\Projects\CultMath\src\CultMath\math.cs" />
|
||||
<Compile Include="E:\Projects\CultMath\src\CultMath\rect.cs" />
|
||||
<Compile Include="E:\Projects\CultMath\src\CultMath\float2.cs" />
|
||||
<Compile Include="E:\Projects\CultMath\src\CultMath\float3.cs" />
|
||||
<Compile Include="E:\Projects\CultMath\src\CultMath\IsExternalInit.cs" />
|
||||
<Compile Include="E:\Projects\CultMath\src\CultMath\double2.cs" />
|
||||
<Compile Include="E:\Projects\CultMath\src\CultMath\rect.cs" />
|
||||
<Compile Include="E:\Projects\CultMath\src\CultMath\quaternion.cs" />
|
||||
<None Include="E:\Projects\CultMath\src\CultMath\CultMath.asmdef" />
|
||||
<Reference Include="Unity.Cecil.Awesome">
|
||||
|
|
@ -224,7 +224,7 @@
|
|||
<HintPath>C:\Program Files\Unity\Hub\Editor\6000.4.2f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Collections.Specialized.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.ComponentModel.Annotations">
|
||||
<HintPath>E:\Projects\Brokkr\surfaces\unity\Packages\com.gamecult.brokkr\Plugins\CultMesh\System.ComponentModel.Annotations.dll</HintPath>
|
||||
<HintPath>C:\Program Files\Unity\Hub\Editor\6000.4.2f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.ComponentModel.Annotations.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.ComponentModel">
|
||||
<HintPath>C:\Program Files\Unity\Hub\Editor\6000.4.2f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.ComponentModel.dll</HintPath>
|
||||
|
|
@ -488,7 +488,7 @@
|
|||
<HintPath>C:\Program Files\Unity\Hub\Editor\6000.4.2f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Threading.Tasks.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Threading.Tasks.Extensions">
|
||||
<HintPath>E:\Projects\Brokkr\surfaces\unity\Packages\com.gamecult.brokkr\Plugins\CultMesh\System.Threading.Tasks.Extensions.dll</HintPath>
|
||||
<HintPath>C:\Program Files\Unity\Hub\Editor\6000.4.2f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Threading.Tasks.Extensions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Threading.Tasks.Parallel">
|
||||
<HintPath>C:\Program Files\Unity\Hub\Editor\6000.4.2f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Threading.Tasks.Parallel.dll</HintPath>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<LangVersion>latest</LangVersion>
|
||||
|
|
@ -48,83 +48,87 @@
|
|||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeObservedDaemonState.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeAssemblyInfo.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeEquippedItemDetailsSurfaceBuilder.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeLoadoutSnapshotProjector.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeBehaviorMetadata.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeAssets.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeStateBoundary.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeTradeItemDetailsSurfaceBuilder.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeDaemonGameSurfaceBuilder.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeClientTargetStore.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaState.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeCatalogSnapshot.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeDaemonSoaViewIndex.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeBehaviorKinds.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeDaemonIntentState.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeDaemonOperationsClient.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeCatalogStore.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeDaemonDocuments.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeLoadoutTemplateCommands.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeCatalogCommands.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeOperationsCommands.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeStatRecipeCommands.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeCultCacheDocumentStore.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeAssemblyInfo.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeVerseAuthorityPolicy.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaUi.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeStateBoot.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeVerseHostCommands.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeDaemonSurfaceCommands.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeDaemonSoaViewStore.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimePlayerSettingsCommands.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaControl.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeDaemonStateRefs.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeMainMenuSurfaceBuilder.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeRenderSplatDocuments.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeDaemonPublicationStore.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeInputSettingsSurfaceBuilder.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeItemCategories.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeClientTargetSurfaceBuilder.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeCargoItemDetailsSurfaceBuilder.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeEquippedItemDetailsSurfaceBuilder.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeShipSettingsSurfaceBuilder.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeZoneDetailsSurfaceBuilder.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeTradeItemDetailsSurfaceBuilder.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeInventoryDropdownSurfaceBuilder.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeTradeInteractionSurfaceBuilder.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeTradeCargoSelectorSurfaceBuilder.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeMainMenuSurfaceBuilder.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeMenuTabsSurfaceBuilder.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimePlayerSettingsSurfaceBuilder.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeDaemonStateRefs.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeDaemonItemStatQueries.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeDaemonGameSurfaceBuilder.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeDaemonEditorSurfaceBuilder.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeDaemonOperationClient.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaClient.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaClientState.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaControl.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaUi.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeVerseClient.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeVerseAuthorityPolicy.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeDaemonSurfaceCommandCatalog.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeDaemonSurfaceCommands.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeDaemonOperations.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeDaemonOperationsClient.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeDaemonIntentState.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeDaemonSoaDocuments.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeDaemonSoaFramePublisher.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeDaemonRenderQueries.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeCommittedFactImporter.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeRtsProjection.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeRtsSimulation.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeRtsViewportDocuments.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeEveCommandClient.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeAssetDocuments.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeDaemonTradeItemQueries.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeClientTargetSurfaceBuilder.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimePlayerSettingsSurfaceBuilder.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeTradeValuePolicySurfaceBuilder.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeLocalStorySurfaceBuilder.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeEveSurfaceAdapter.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeStarbridgeDocuments.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeStarbridgePlayerSeatDocuments.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeInventoryDropdownSurfaceBuilder.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeSnapshotDocuments.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeRtsViewportDocuments.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeZoneDetailsSurfaceBuilder.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeEveSurfaceState.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeDaemonSoaDocuments.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeVerseClient.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeCargoItemDetailsSurfaceBuilder.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeTradeInteractionSurfaceBuilder.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeStateReader.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeDaemonItemStatQueries.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeDaemonObservationCursor.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeVerseDiscovery.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeEveSurfaceAdapter.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeOperationsCommands.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeDaemonEditorSurfaceBuilder.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeTradeCargoSelectorSurfaceBuilder.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeStatRecipeSurfaceBuilder.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeTradeValuePolicySurfaceBuilder.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeRtsProjection.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeDaemonSoaFramePublisher.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeStatRecipes.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeDaemonRenderQueries.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeStarbridgePlayerSeatDocuments.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeMenuTabsSurfaceBuilder.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeShipSettingsSurfaceBuilder.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeClientTargetCommands.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeEveCommandDocument.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeEveSurfaceState.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeVerseReplicaBridge.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeDaemonFrameStore.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeDaemonPublicationStore.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeCatalogCommands.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeDaemonTickRunner.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeLoadoutSnapshotProjector.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeDaemonTradeItemQueries.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeSnapshotDocuments.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeEveCommandClient.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeLoadoutTemplateCommands.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeVerseReplicaBridge.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeDaemonOperationClient.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeDaemonFrameStore.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeDaemonOperations.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeCommittedFactImporter.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeInputSettingsCommands.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeRtsSimulation.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaState.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaClientState.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaClient.cs" />
|
||||
<Compile Include="Packages\org.gamecult.aetheria.state\Runtime\AetheriaRuntimeCultCacheDocumentStore.cs" />
|
||||
<None Include="Packages\org.gamecult.aetheria.state\Runtime\GameCult.Aetheria.State.Unity.asmdef" />
|
||||
<Reference Include="ConcurrentCollections">
|
||||
<HintPath>E:\Projects\Brokkr\surfaces\unity\Packages\com.gamecult.brokkr\Plugins\CultMesh\ConcurrentCollections.dll</HintPath>
|
||||
|
|
@ -265,7 +269,7 @@
|
|||
<HintPath>C:\Program Files\Unity\Hub\Editor\6000.4.2f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Collections.Specialized.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.ComponentModel.Annotations">
|
||||
<HintPath>E:\Projects\Brokkr\surfaces\unity\Packages\com.gamecult.brokkr\Plugins\CultMesh\System.ComponentModel.Annotations.dll</HintPath>
|
||||
<HintPath>C:\Program Files\Unity\Hub\Editor\6000.4.2f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.ComponentModel.Annotations.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.ComponentModel">
|
||||
<HintPath>C:\Program Files\Unity\Hub\Editor\6000.4.2f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.ComponentModel.dll</HintPath>
|
||||
|
|
@ -529,7 +533,7 @@
|
|||
<HintPath>C:\Program Files\Unity\Hub\Editor\6000.4.2f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Threading.Tasks.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Threading.Tasks.Extensions">
|
||||
<HintPath>E:\Projects\Brokkr\surfaces\unity\Packages\com.gamecult.brokkr\Plugins\CultMesh\System.Threading.Tasks.Extensions.dll</HintPath>
|
||||
<HintPath>C:\Program Files\Unity\Hub\Editor\6000.4.2f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Threading.Tasks.Extensions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Threading.Tasks.Parallel">
|
||||
<HintPath>C:\Program Files\Unity\Hub\Editor\6000.4.2f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Threading.Tasks.Parallel.dll</HintPath>
|
||||
|
|
@ -566,14 +570,14 @@
|
|||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="CultMath.csproj">
|
||||
<Project>{6f7a47c4-21dc-64ef-0969-96b956a000f4}</Project>
|
||||
<Name>CultMath</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="GameCult.Eve.Surface.csproj">
|
||||
<Project>{037dacf0-10f4-20ed-7963-3a1e1ac15b64}</Project>
|
||||
<Name>GameCult.Eve.Surface</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="CultMath.csproj">
|
||||
<Project>{6f7a47c4-21dc-64ef-0969-96b956a000f4}</Project>
|
||||
<Name>CultMath</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
|
|
|
|||
|
|
@ -209,7 +209,7 @@
|
|||
<HintPath>C:\Program Files\Unity\Hub\Editor\6000.4.2f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Collections.Specialized.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.ComponentModel.Annotations">
|
||||
<HintPath>E:\Projects\Brokkr\surfaces\unity\Packages\com.gamecult.brokkr\Plugins\CultMesh\System.ComponentModel.Annotations.dll</HintPath>
|
||||
<HintPath>C:\Program Files\Unity\Hub\Editor\6000.4.2f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.ComponentModel.Annotations.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.ComponentModel">
|
||||
<HintPath>C:\Program Files\Unity\Hub\Editor\6000.4.2f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.ComponentModel.dll</HintPath>
|
||||
|
|
@ -473,7 +473,7 @@
|
|||
<HintPath>C:\Program Files\Unity\Hub\Editor\6000.4.2f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Threading.Tasks.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Threading.Tasks.Extensions">
|
||||
<HintPath>E:\Projects\Brokkr\surfaces\unity\Packages\com.gamecult.brokkr\Plugins\CultMesh\System.Threading.Tasks.Extensions.dll</HintPath>
|
||||
<HintPath>C:\Program Files\Unity\Hub\Editor\6000.4.2f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Threading.Tasks.Extensions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Threading.Tasks.Parallel">
|
||||
<HintPath>C:\Program Files\Unity\Hub\Editor\6000.4.2f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Threading.Tasks.Parallel.dll</HintPath>
|
||||
|
|
|
|||
|
|
@ -197,6 +197,23 @@ namespace GameCult.Aetheria.State.Verse
|
|||
return AetheriaRuntimeRtsProjection.ProjectGravityViewport(frame, viewport);
|
||||
}
|
||||
|
||||
public async Task<AetheriaRuntimeRenderSplatsViewportDocument> RenderSplatsViewportAsync(
|
||||
AetheriaRuntimeRtsViewportBounds viewport)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
var frame = await RequireFrameAsync().ConfigureAwait(false);
|
||||
return AetheriaRuntimeRtsProjection.ProjectRenderSplatsViewport(frame, viewport);
|
||||
}
|
||||
|
||||
public async Task<AetheriaRuntimeAssetManifestDocument> AssetManifestAsync()
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
var frame = await RequireFrameAsync().ConfigureAwait(false);
|
||||
return AetheriaRuntimeAssets.ProjectManifest(
|
||||
_verse.OpenRuntimeCatalog(),
|
||||
frame.Run?.RunId ?? "");
|
||||
}
|
||||
|
||||
public async Task<AetheriaRuntimeCurrentZoneDocument> CurrentZoneAsync()
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
|
@ -254,7 +271,11 @@ namespace GameCult.Aetheria.State.Verse
|
|||
var frame = await RequireFrameAsync().ConfigureAwait(false);
|
||||
scenario ??= await _verse.GetStarbridgeScenarioAsync().ConfigureAwait(false);
|
||||
session ??= await _verse.GetStarbridgeSessionAsync().ConfigureAwait(false);
|
||||
return AetheriaRuntimeStarbridgeProjection.ProjectSessionSummary(frame, scenario, session);
|
||||
return AetheriaRuntimeStarbridgeProjection.ProjectSessionSummary(
|
||||
frame,
|
||||
scenario,
|
||||
session,
|
||||
_verse.OpenRuntimeCatalog());
|
||||
}
|
||||
|
||||
public async Task<AetheriaRuntimeSelectedObjectDocument> SelectedObjectAsync(int entityIndex)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,118 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using GameCult.Caching;
|
||||
using MessagePack;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace GameCult.Aetheria.State.Verse
|
||||
{
|
||||
public static class AetheriaRuntimeAssetKinds
|
||||
{
|
||||
public const string Texture = "texture";
|
||||
public const string Sprite = "sprite";
|
||||
public const string Mesh = "mesh";
|
||||
public const string Material = "material";
|
||||
public const string Shader = "shader";
|
||||
public const string Audio = "audio";
|
||||
public const string Prefab = "prefab";
|
||||
}
|
||||
|
||||
public static class AetheriaRuntimeAssetTransports
|
||||
{
|
||||
public const string CultMesh = "cultmesh";
|
||||
public const string Resources = "resources";
|
||||
public const string UnityAddressable = "unity_addressable";
|
||||
}
|
||||
|
||||
[MessagePackObject]
|
||||
public sealed class AetheriaRuntimeAssetRef
|
||||
{
|
||||
[Key(0)]
|
||||
public string AssetKey { get; set; } = "";
|
||||
|
||||
[Key(1)]
|
||||
public string Kind { get; set; } = "";
|
||||
|
||||
[Key(2)]
|
||||
public string Uri { get; set; } = "";
|
||||
|
||||
[Key(3)]
|
||||
public string Transport { get; set; } = AetheriaRuntimeAssetTransports.CultMesh;
|
||||
|
||||
[Key(4)]
|
||||
public string ContentHash { get; set; } = "";
|
||||
|
||||
[Key(5)]
|
||||
public string MimeType { get; set; } = "";
|
||||
|
||||
[Key(6)]
|
||||
public IReadOnlyDictionary<string, string> Metadata { get; set; } =
|
||||
new Dictionary<string, string>();
|
||||
|
||||
public static AetheriaRuntimeAssetRef Empty(string kind = "")
|
||||
{
|
||||
return new AetheriaRuntimeAssetRef { Kind = kind ?? "" };
|
||||
}
|
||||
|
||||
public static AetheriaRuntimeAssetRef FromKey(
|
||||
string key,
|
||||
string kind,
|
||||
string? uri = null,
|
||||
string transport = AetheriaRuntimeAssetTransports.CultMesh,
|
||||
string? mimeType = null)
|
||||
{
|
||||
key ??= "";
|
||||
return new AetheriaRuntimeAssetRef
|
||||
{
|
||||
AssetKey = key,
|
||||
Kind = kind ?? "",
|
||||
Uri = string.IsNullOrWhiteSpace(uri) ? key : uri ?? "",
|
||||
Transport = string.IsNullOrWhiteSpace(transport)
|
||||
? AetheriaRuntimeAssetTransports.CultMesh
|
||||
: transport,
|
||||
MimeType = mimeType ?? ""
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[CultDocument("gamecult.aetheria.asset_manifest", "gamecult.aetheria.asset_manifest.v1")]
|
||||
[MessagePackObject]
|
||||
public sealed class AetheriaRuntimeAssetManifestDocument
|
||||
{
|
||||
[Key(0)]
|
||||
public string Schema { get; set; } = AetheriaRuntimeDaemonSchemas.AssetManifest;
|
||||
|
||||
[Key(1)]
|
||||
public string PublishedAtUtc { get; set; } = "";
|
||||
|
||||
[Key(2)]
|
||||
public string RunId { get; set; } = "";
|
||||
|
||||
[Key(3)]
|
||||
public string BaseUri { get; set; } = "cultmesh://aetheria.local/assets";
|
||||
|
||||
[Key(4)]
|
||||
public IReadOnlyList<AetheriaRuntimeAssetManifestEntry> Assets { get; set; } =
|
||||
Array.Empty<AetheriaRuntimeAssetManifestEntry>();
|
||||
}
|
||||
|
||||
[MessagePackObject]
|
||||
public sealed class AetheriaRuntimeAssetManifestEntry
|
||||
{
|
||||
[Key(0)]
|
||||
public AetheriaRuntimeAssetRef Ref { get; set; } = new AetheriaRuntimeAssetRef();
|
||||
|
||||
[Key(1)]
|
||||
public long SizeBytes { get; set; } = -1;
|
||||
|
||||
[Key(2)]
|
||||
public int Width { get; set; }
|
||||
|
||||
[Key(3)]
|
||||
public int Height { get; set; }
|
||||
|
||||
[Key(4)]
|
||||
public IReadOnlyList<string> Tags { get; set; } = Array.Empty<string>();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9ff95c77b30d4b8c8d2df0f2a4de0ef8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,134 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace GameCult.Aetheria.State.Verse
|
||||
{
|
||||
public static class AetheriaRuntimeAssets
|
||||
{
|
||||
public static AetheriaRuntimeAssetManifestDocument ProjectManifest(
|
||||
AetheriaRuntimeCatalogSnapshot? catalog,
|
||||
string runId = "",
|
||||
string baseUri = "cultmesh://aetheria.local/assets")
|
||||
{
|
||||
var entries = new Dictionary<string, AetheriaRuntimeAssetManifestEntry>(StringComparer.Ordinal);
|
||||
Add(entries, MapIcon("entity.player", "Player", "Sprites/Map/player"));
|
||||
Add(entries, MapIcon("entity.ship", "Ship", "Sprites/Map/ship"));
|
||||
Add(entries, MapIcon("entity.orbital", "Orbital", "Sprites/Map/orbital"));
|
||||
Add(entries, MapIcon("entity.station", "Station", "Sprites/Map/station"));
|
||||
Add(entries, MapIcon("body.planet", "Planet", "Sprites/Map/planet"));
|
||||
Add(entries, MapIcon("body.sun", "Sun", "Sprites/Map/sun"));
|
||||
Add(entries, MapIcon("body.asteroid", "Asteroid", "Sprites/Map/asteroid"));
|
||||
|
||||
foreach (var item in catalog?.Items ?? Array.Empty<AetheriaRuntimeCatalogItem>())
|
||||
{
|
||||
if (item == null || string.IsNullOrWhiteSpace(item.ItemKey))
|
||||
continue;
|
||||
|
||||
var icon = AssetRefFromCatalogIcon(item.ActionBarIcon, $"item.{item.ItemKey}.icon");
|
||||
if (!string.IsNullOrWhiteSpace(icon.AssetKey))
|
||||
{
|
||||
Add(entries, new AetheriaRuntimeAssetManifestEntry
|
||||
{
|
||||
Ref = icon,
|
||||
Tags = new[] { "item", item.ItemKey, item.Category ?? "" }
|
||||
.Where(tag => !string.IsNullOrWhiteSpace(tag))
|
||||
.ToArray()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return new AetheriaRuntimeAssetManifestDocument
|
||||
{
|
||||
PublishedAtUtc = DateTime.UtcNow.ToString("O"),
|
||||
RunId = runId ?? "",
|
||||
BaseUri = string.IsNullOrWhiteSpace(baseUri) ? "cultmesh://aetheria.local/assets" : baseUri,
|
||||
Assets = entries.Values
|
||||
.OrderBy(entry => entry.Ref.AssetKey, StringComparer.Ordinal)
|
||||
.ToArray()
|
||||
};
|
||||
}
|
||||
|
||||
public static AetheriaRuntimeAssetRef ResolveEntityIcon(AetheriaRuntimeRtsViewportObject? obj)
|
||||
{
|
||||
if (obj?.Controlled == true)
|
||||
return Sprite("map.entity.player", "Sprites/Map/player");
|
||||
|
||||
var kind = (obj?.Kind ?? "").Trim().ToLowerInvariant();
|
||||
if (kind.IndexOf("station", StringComparison.Ordinal) >= 0)
|
||||
return Sprite("map.entity.station", "Sprites/Map/station");
|
||||
if (kind.IndexOf("orbital", StringComparison.Ordinal) >= 0)
|
||||
return Sprite("map.entity.orbital", "Sprites/Map/orbital");
|
||||
|
||||
return Sprite("map.entity.ship", "Sprites/Map/ship");
|
||||
}
|
||||
|
||||
public static AetheriaRuntimeAssetRef AssetRefFromCatalogIcon(
|
||||
string? catalogPath,
|
||||
string fallbackAssetKey)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(catalogPath))
|
||||
return AetheriaRuntimeAssetRef.Empty(AetheriaRuntimeAssetKinds.Texture);
|
||||
|
||||
var resourcePath = NormalizeResourcePath(catalogPath ?? "");
|
||||
var assetKey = string.IsNullOrWhiteSpace(fallbackAssetKey)
|
||||
? $"catalog.{resourcePath}".Replace('/', '.').Replace('\\', '.')
|
||||
: fallbackAssetKey;
|
||||
return AetheriaRuntimeAssetRef.FromKey(
|
||||
assetKey,
|
||||
AetheriaRuntimeAssetKinds.Texture,
|
||||
$"resources://{resourcePath}",
|
||||
AetheriaRuntimeAssetTransports.Resources,
|
||||
"image/*");
|
||||
}
|
||||
|
||||
private static AetheriaRuntimeAssetManifestEntry MapIcon(
|
||||
string key,
|
||||
string label,
|
||||
string resourcePath)
|
||||
{
|
||||
return new AetheriaRuntimeAssetManifestEntry
|
||||
{
|
||||
Ref = Sprite($"map.{key}", resourcePath),
|
||||
Tags = new[] { "map", "icon", label }
|
||||
};
|
||||
}
|
||||
|
||||
private static AetheriaRuntimeAssetRef Sprite(string key, string resourcePath)
|
||||
{
|
||||
return AetheriaRuntimeAssetRef.FromKey(
|
||||
key,
|
||||
AetheriaRuntimeAssetKinds.Sprite,
|
||||
$"resources://{NormalizeResourcePath(resourcePath)}",
|
||||
AetheriaRuntimeAssetTransports.Resources,
|
||||
"image/*");
|
||||
}
|
||||
|
||||
private static string NormalizeResourcePath(string path)
|
||||
{
|
||||
path ??= "";
|
||||
const string resourcesPrefix = "Assets/Resources/";
|
||||
if (path.StartsWith(resourcesPrefix, StringComparison.OrdinalIgnoreCase))
|
||||
path = path.Substring(resourcesPrefix.Length);
|
||||
|
||||
var extensionIndex = path.LastIndexOf('.');
|
||||
if (extensionIndex > 0)
|
||||
path = path.Substring(0, extensionIndex);
|
||||
|
||||
return path.Replace('\\', '/').Trim('/');
|
||||
}
|
||||
|
||||
private static void Add(
|
||||
Dictionary<string, AetheriaRuntimeAssetManifestEntry> entries,
|
||||
AetheriaRuntimeAssetManifestEntry entry)
|
||||
{
|
||||
var key = entry.Ref?.AssetKey ?? "";
|
||||
if (key.Length == 0)
|
||||
return;
|
||||
|
||||
entries[key] = entry;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8e38efc9379f4a7ea4b5df5ca079b5c1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -111,6 +111,7 @@ namespace GameCult.Aetheria.State.Verse
|
|||
AccountedCommandIds = currentFrame.AccountedCommandIds,
|
||||
CumulativeAppliedCommandIds = currentFrame.CumulativeAppliedCommandIds,
|
||||
CumulativeRejectedCommandIds = currentFrame.CumulativeRejectedCommandIds,
|
||||
Catalog = AetheriaRuntimeCatalogStore.OpenReadOnly(stateFilePath),
|
||||
PublishWitnesses = false
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -83,6 +83,24 @@ namespace GameCult.Aetheria.State.Verse
|
|||
CultMesh.ReadSingleFileDocumentPayload(path, AetheriaRuntimeDaemonSchemas.SoaView));
|
||||
}
|
||||
|
||||
public static void WriteAssetManifest(string path, AetheriaRuntimeAssetManifestDocument document)
|
||||
{
|
||||
WriteDocument(
|
||||
path,
|
||||
$"latest:gamecult.aetheria.asset_manifest.{document.RunId}.v1",
|
||||
AetheriaRuntimeDaemonSchemas.AssetManifest,
|
||||
"gamecult.aetheria.asset_manifest",
|
||||
"gamecult.aetheria.asset_manifest.v1",
|
||||
document.PublishedAtUtc,
|
||||
MessagePackSerializer.Serialize(document));
|
||||
}
|
||||
|
||||
public static AetheriaRuntimeAssetManifestDocument ReadAssetManifest(string path)
|
||||
{
|
||||
return MessagePackSerializer.Deserialize<AetheriaRuntimeAssetManifestDocument>(
|
||||
CultMesh.ReadSingleFileDocumentPayload(path, AetheriaRuntimeDaemonSchemas.AssetManifest));
|
||||
}
|
||||
|
||||
public static void WriteDaemonProviderAdvertisement(
|
||||
string path,
|
||||
AetheriaRuntimeDaemonProviderAdvertisementDocument document)
|
||||
|
|
|
|||
|
|
@ -65,6 +65,8 @@ namespace GameCult.Aetheria.State.Verse
|
|||
public const string RtsViewport = "gamecult.aetheria.rts_viewport.v1";
|
||||
public const string ObjectsViewport = "gamecult.aetheria.objects_viewport.v1";
|
||||
public const string GravityViewport = "gamecult.aetheria.gravity_viewport.v1";
|
||||
public const string RenderSplatsViewport = "gamecult.aetheria.render_splats_viewport.v1";
|
||||
public const string AssetManifest = "gamecult.aetheria.asset_manifest.v1";
|
||||
public const string CurrentZone = "gamecult.aetheria.current_zone.v1";
|
||||
public const string CurrentEntity = "gamecult.aetheria.current_entity.v1";
|
||||
public const string CurrentDocking = "gamecult.aetheria.current_docking.v1";
|
||||
|
|
@ -258,6 +260,9 @@ namespace GameCult.Aetheria.State.Verse
|
|||
[Key(20)]
|
||||
public string CultMeshAddress { get; set; } = "cultmesh://aetheria.local/eve/providers/aetheria.daemon";
|
||||
|
||||
[Key(21)]
|
||||
public string AssetManifestWitnessPath { get; set; } = "";
|
||||
|
||||
public static AetheriaRuntimeDaemonProviderAdvertisementDocument Create(
|
||||
string stateFilePath,
|
||||
string daemonId,
|
||||
|
|
@ -273,6 +278,7 @@ namespace GameCult.Aetheria.State.Verse
|
|||
FrameWitnessPath = AetheriaRuntimeStateBoundary.GetDaemonFramePath(stateFilePath ?? ""),
|
||||
SoaWitnessPath = AetheriaRuntimeStateBoundary.GetDaemonSoaViewPath(stateFilePath ?? ""),
|
||||
HealthWitnessPath = AetheriaRuntimeStateBoundary.GetDaemonHealthPath(stateFilePath ?? ""),
|
||||
AssetManifestWitnessPath = AetheriaRuntimeStateBoundary.GetDaemonAssetManifestPath(stateFilePath ?? ""),
|
||||
CommandBoundaryWitnessPath = AetheriaRuntimeStateBoundary.GetDaemonCommandBoundaryPath(stateFilePath ?? ""),
|
||||
EveGuiSurfaceWitnessPath = AetheriaRuntimeStateBoundary.GetDaemonGameSurfacePath(stateFilePath ?? ""),
|
||||
EveTuiSurfaceWitnessPath = AetheriaRuntimeStateBoundary.GetDaemonGameTuiSurfacePath(stateFilePath ?? ""),
|
||||
|
|
@ -293,6 +299,8 @@ namespace GameCult.Aetheria.State.Verse
|
|||
AetheriaRuntimeDaemonSchemas.CommandBoundary,
|
||||
AetheriaRuntimeDaemonSchemas.ObjectsViewport,
|
||||
AetheriaRuntimeDaemonSchemas.GravityViewport,
|
||||
AetheriaRuntimeDaemonSchemas.RenderSplatsViewport,
|
||||
AetheriaRuntimeDaemonSchemas.AssetManifest,
|
||||
AetheriaRuntimeDaemonSchemas.CurrentZone,
|
||||
AetheriaRuntimeDaemonSchemas.CurrentEntity,
|
||||
AetheriaRuntimeDaemonSchemas.CurrentDocking,
|
||||
|
|
|
|||
|
|
@ -27,6 +27,11 @@ namespace GameCult.Aetheria.State.Verse
|
|||
return AetheriaRuntimeStateBoundary.GetDaemonCommandBoundaryPath(stateFilePath);
|
||||
}
|
||||
|
||||
public static string GetAssetManifestPath(string stateFilePath)
|
||||
{
|
||||
return AetheriaRuntimeStateBoundary.GetDaemonAssetManifestPath(stateFilePath);
|
||||
}
|
||||
|
||||
public static string GetStarbridgeSessionSummaryPath(string stateFilePath)
|
||||
{
|
||||
return AetheriaRuntimeStateBoundary.GetDaemonStarbridgeSessionSummaryPath(stateFilePath);
|
||||
|
|
@ -114,6 +119,22 @@ namespace GameCult.Aetheria.State.Verse
|
|||
return path;
|
||||
}
|
||||
|
||||
public static string PublishAssetManifest(
|
||||
string stateFilePath,
|
||||
AetheriaRuntimeAssetManifestDocument document)
|
||||
{
|
||||
if (document == null) throw new ArgumentNullException(nameof(document));
|
||||
|
||||
document.Schema = AetheriaRuntimeDaemonSchemas.AssetManifest;
|
||||
if (string.IsNullOrWhiteSpace(document.PublishedAtUtc))
|
||||
document.PublishedAtUtc = DateTime.UtcNow.ToString("O");
|
||||
|
||||
var path = GetAssetManifestPath(stateFilePath);
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(path) ?? ".");
|
||||
AetheriaRuntimeCultCacheDocumentStore.WriteAssetManifest(path, document);
|
||||
return path;
|
||||
}
|
||||
|
||||
public static string PublishStarbridgeSessionSummary(
|
||||
string stateFilePath,
|
||||
AetheriaRuntimeStarbridgeSessionSummaryDocument document)
|
||||
|
|
@ -228,6 +249,21 @@ namespace GameCult.Aetheria.State.Verse
|
|||
return true;
|
||||
}
|
||||
|
||||
public static bool TryReadAssetManifest(
|
||||
string stateFilePath,
|
||||
out AetheriaRuntimeAssetManifestDocument document)
|
||||
{
|
||||
var path = GetAssetManifestPath(stateFilePath);
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
document = new AetheriaRuntimeAssetManifestDocument();
|
||||
return false;
|
||||
}
|
||||
|
||||
document = AetheriaRuntimeCultCacheDocumentStore.ReadAssetManifest(path);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool TryReadStarbridgeSessionSummary(
|
||||
string stateFilePath,
|
||||
out AetheriaRuntimeStarbridgeSessionSummaryDocument document)
|
||||
|
|
|
|||
|
|
@ -186,6 +186,14 @@ namespace GameCult.Aetheria.State.Verse
|
|||
|
||||
[Key(17)]
|
||||
public int Lod { get; set; } = -1;
|
||||
|
||||
[Key(18)]
|
||||
public AetheriaRuntimeAssetRef MeshAsset { get; set; } =
|
||||
AetheriaRuntimeAssetRef.Empty(AetheriaRuntimeAssetKinds.Mesh);
|
||||
|
||||
[Key(19)]
|
||||
public AetheriaRuntimeAssetRef MaterialAsset { get; set; } =
|
||||
AetheriaRuntimeAssetRef.Empty(AetheriaRuntimeAssetKinds.Material);
|
||||
}
|
||||
|
||||
[MessagePackObject]
|
||||
|
|
|
|||
|
|
@ -99,6 +99,16 @@ namespace GameCult.Aetheria.State.Verse
|
|||
GroupId = EntityRenderGroupId,
|
||||
MeshKey = "resources://Aetheria/Daemon/EntityProxy",
|
||||
MaterialKey = "resources://Aetheria/Daemon/EntityProxy",
|
||||
MeshAsset = AetheriaRuntimeAssetRef.FromKey(
|
||||
"daemon.entity_proxy.mesh",
|
||||
AetheriaRuntimeAssetKinds.Mesh,
|
||||
"resources://Aetheria/Daemon/EntityProxy",
|
||||
AetheriaRuntimeAssetTransports.Resources),
|
||||
MaterialAsset = AetheriaRuntimeAssetRef.FromKey(
|
||||
"daemon.entity_proxy.material",
|
||||
AetheriaRuntimeAssetKinds.Material,
|
||||
"resources://Aetheria/Daemon/EntityProxy",
|
||||
AetheriaRuntimeAssetTransports.Resources),
|
||||
SubMeshIndex = 0,
|
||||
Layer = 0,
|
||||
ShaderKey = "aetheria.daemon.entity-proxy",
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ namespace GameCult.Aetheria.State.Verse
|
|||
new AetheriaRuntimeDaemonOperationContext();
|
||||
public AetheriaRuntimeStarbridgeScenarioDocument? StarbridgeScenario { get; set; }
|
||||
public AetheriaRuntimeStarbridgeSessionDocument? StarbridgeSession { get; set; }
|
||||
public AetheriaRuntimeCatalogSnapshot? Catalog { get; set; }
|
||||
public bool PublishWitnesses { get; set; } = true;
|
||||
}
|
||||
|
||||
|
|
@ -156,6 +157,7 @@ namespace GameCult.Aetheria.State.Verse
|
|||
"aetheria.daemon.operation_execute.v1",
|
||||
"aetheria.daemon.intent_state.v1",
|
||||
"aetheria.daemon.authoritative_frame.v1",
|
||||
AetheriaRuntimeDaemonSchemas.AssetManifest,
|
||||
AetheriaRuntimeDaemonSchemas.ProviderAdvertisement,
|
||||
AetheriaRuntimeDaemonSchemas.Health,
|
||||
AetheriaRuntimeDaemonSchemas.CommandBoundary,
|
||||
|
|
@ -212,10 +214,17 @@ namespace GameCult.Aetheria.State.Verse
|
|||
CommandBoundaryPath = commandBoundaryPath
|
||||
});
|
||||
AetheriaRuntimeDaemonPublicationStore.TryReadHealth(stateFilePath, out var health);
|
||||
AetheriaRuntimeDaemonPublicationStore.PublishAssetManifest(
|
||||
stateFilePath,
|
||||
AetheriaRuntimeAssets.ProjectManifest(
|
||||
options.Catalog,
|
||||
frame.Run?.RunId ?? "",
|
||||
$"cultmesh://{options.VerseId}/assets"));
|
||||
var starbridgeSummary = AetheriaRuntimeStarbridgeProjection.ProjectSessionSummary(
|
||||
frame,
|
||||
options.StarbridgeScenario,
|
||||
options.StarbridgeSession);
|
||||
options.StarbridgeSession,
|
||||
options.Catalog);
|
||||
AetheriaRuntimeDaemonPublicationStore.PublishStarbridgeSessionSummary(
|
||||
stateFilePath,
|
||||
starbridgeSummary);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,192 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using GameCult.Caching;
|
||||
using MessagePack;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace GameCult.Aetheria.State.Verse
|
||||
{
|
||||
public static class AetheriaRuntimeRenderSplatChannels
|
||||
{
|
||||
public const int Visibility = 0;
|
||||
public const int Gravity = 1;
|
||||
public const int GravityWave = 2;
|
||||
public const int Influence = 3;
|
||||
public const int Tint = 4;
|
||||
}
|
||||
|
||||
public static class AetheriaRuntimeRenderSplatLayerKeys
|
||||
{
|
||||
public const string GravityHeight = "gravity.height";
|
||||
public const string GravityWave = "gravity.wave";
|
||||
public const string Visibility = "visibility.mask";
|
||||
public const string FogSurfaceHeight = "fog.surface_height";
|
||||
public const string FogPatchHeight = "fog.patch_height";
|
||||
public const string FogPatch = "fog.patch";
|
||||
public const string FogTint = "fog.tint";
|
||||
public const string Influence = "influence.mask";
|
||||
}
|
||||
|
||||
public static class AetheriaRuntimeRenderSplatBlendModes
|
||||
{
|
||||
public const string Add = "add";
|
||||
public const string Max = "max";
|
||||
public const string Alpha = "alpha";
|
||||
}
|
||||
|
||||
public static class AetheriaRuntimeRenderSplatSourceKinds
|
||||
{
|
||||
public const int Constant = 0;
|
||||
public const int SimplexNoise = 1;
|
||||
public const int AnimatedSimplexNoise = 2;
|
||||
}
|
||||
|
||||
public static class AetheriaRuntimeRenderSplatFalloffs
|
||||
{
|
||||
public const int Solid = 0;
|
||||
public const int Linear = 1;
|
||||
public const int Smooth = 2;
|
||||
public const int InverseSmooth = 3;
|
||||
}
|
||||
|
||||
[CultDocument("gamecult.aetheria.render_splats_viewport", "gamecult.aetheria.render_splats_viewport.v1")]
|
||||
[MessagePackObject]
|
||||
public sealed class AetheriaRuntimeRenderSplatsViewportDocument
|
||||
{
|
||||
[Key(0)]
|
||||
public string Schema { get; set; } = AetheriaRuntimeDaemonSchemas.RenderSplatsViewport;
|
||||
|
||||
[Key(1)]
|
||||
public long FrameId { get; set; }
|
||||
|
||||
[Key(2)]
|
||||
public string PublishedAtUtc { get; set; } = "";
|
||||
|
||||
[Key(3)]
|
||||
public double SimulationTimeSeconds { get; set; }
|
||||
|
||||
[Key(4)]
|
||||
public string RunId { get; set; } = "";
|
||||
|
||||
[Key(5)]
|
||||
public int ZoneIndex { get; set; }
|
||||
|
||||
[Key(6)]
|
||||
public string ZoneName { get; set; } = "";
|
||||
|
||||
[Key(7)]
|
||||
public AetheriaRuntimeRtsViewportBounds Viewport { get; set; } = new AetheriaRuntimeRtsViewportBounds();
|
||||
|
||||
[Key(8)]
|
||||
public IReadOnlyList<AetheriaRuntimeRenderSplatLayerDefinition> Layers { get; set; } =
|
||||
Array.Empty<AetheriaRuntimeRenderSplatLayerDefinition>();
|
||||
|
||||
[Key(9)]
|
||||
public AetheriaRuntimeRenderSplatSoa Splats { get; set; } = new AetheriaRuntimeRenderSplatSoa();
|
||||
}
|
||||
|
||||
[MessagePackObject]
|
||||
public sealed class AetheriaRuntimeRenderSplatLayerDefinition
|
||||
{
|
||||
[Key(0)]
|
||||
public string LayerKey { get; set; } = "";
|
||||
|
||||
[Key(1)]
|
||||
public string DisplayName { get; set; } = "";
|
||||
|
||||
[Key(2)]
|
||||
public int Channel { get; set; }
|
||||
|
||||
[Key(3)]
|
||||
public string BlendMode { get; set; } = AetheriaRuntimeRenderSplatBlendModes.Add;
|
||||
|
||||
[Key(4)]
|
||||
public string GraphicsFormat { get; set; } = "R16_SFloat";
|
||||
|
||||
[Key(5)]
|
||||
public bool ClearBeforeDraw { get; set; } = true;
|
||||
|
||||
[Key(6)]
|
||||
public double ClearR { get; set; }
|
||||
|
||||
[Key(7)]
|
||||
public double ClearG { get; set; }
|
||||
|
||||
[Key(8)]
|
||||
public double ClearB { get; set; }
|
||||
|
||||
[Key(9)]
|
||||
public double ClearA { get; set; }
|
||||
}
|
||||
|
||||
[MessagePackObject]
|
||||
public sealed class AetheriaRuntimeRenderSplatSoa
|
||||
{
|
||||
[Key(0)]
|
||||
public int Count { get; set; }
|
||||
|
||||
[Key(1)]
|
||||
public IReadOnlyList<double> CenterX { get; set; } = Array.Empty<double>();
|
||||
|
||||
[Key(2)]
|
||||
public IReadOnlyList<double> CenterY { get; set; } = Array.Empty<double>();
|
||||
|
||||
[Key(3)]
|
||||
public IReadOnlyList<double> HalfExtentX { get; set; } = Array.Empty<double>();
|
||||
|
||||
[Key(4)]
|
||||
public IReadOnlyList<double> HalfExtentY { get; set; } = Array.Empty<double>();
|
||||
|
||||
[Key(5)]
|
||||
public IReadOnlyList<double> RotationCos { get; set; } = Array.Empty<double>();
|
||||
|
||||
[Key(6)]
|
||||
public IReadOnlyList<double> RotationSin { get; set; } = Array.Empty<double>();
|
||||
|
||||
[Key(7)]
|
||||
public IReadOnlyList<int> Channel { get; set; } = Array.Empty<int>();
|
||||
|
||||
[Key(8)]
|
||||
public IReadOnlyList<int> Falloff { get; set; } = Array.Empty<int>();
|
||||
|
||||
[Key(9)]
|
||||
public IReadOnlyList<double> ValueR { get; set; } = Array.Empty<double>();
|
||||
|
||||
[Key(10)]
|
||||
public IReadOnlyList<double> ValueG { get; set; } = Array.Empty<double>();
|
||||
|
||||
[Key(11)]
|
||||
public IReadOnlyList<double> ValueB { get; set; } = Array.Empty<double>();
|
||||
|
||||
[Key(12)]
|
||||
public IReadOnlyList<double> ValueA { get; set; } = Array.Empty<double>();
|
||||
|
||||
[Key(13)]
|
||||
public IReadOnlyList<string> SourceKey { get; set; } = Array.Empty<string>();
|
||||
|
||||
[Key(14)]
|
||||
public IReadOnlyList<int> LayerIndex { get; set; } = Array.Empty<int>();
|
||||
|
||||
[Key(15)]
|
||||
public IReadOnlyList<int> SourceKind { get; set; } = Array.Empty<int>();
|
||||
|
||||
[Key(16)]
|
||||
public IReadOnlyList<double> FrequencyX { get; set; } = Array.Empty<double>();
|
||||
|
||||
[Key(17)]
|
||||
public IReadOnlyList<double> FrequencyY { get; set; } = Array.Empty<double>();
|
||||
|
||||
[Key(18)]
|
||||
public IReadOnlyList<double> PhaseX { get; set; } = Array.Empty<double>();
|
||||
|
||||
[Key(19)]
|
||||
public IReadOnlyList<double> PhaseY { get; set; } = Array.Empty<double>();
|
||||
|
||||
[Key(20)]
|
||||
public IReadOnlyList<double> AnimationSpeed { get; set; } = Array.Empty<double>();
|
||||
|
||||
[Key(21)]
|
||||
public IReadOnlyList<double> SourceFlags { get; set; } = Array.Empty<double>();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5d69b2814aaf449fa925fd2f158ee9b7
|
||||
|
|
@ -107,6 +107,273 @@ namespace GameCult.Aetheria.State.Verse
|
|||
};
|
||||
}
|
||||
|
||||
public static AetheriaRuntimeRenderSplatsViewportDocument ProjectRenderSplatsViewport(
|
||||
AetheriaRuntimeDaemonFrameDocument frame,
|
||||
AetheriaRuntimeRtsViewportBounds viewport)
|
||||
{
|
||||
frame ??= new AetheriaRuntimeDaemonFrameDocument();
|
||||
viewport ??= new AetheriaRuntimeRtsViewportBounds();
|
||||
|
||||
var normalizedViewport = Normalize(viewport);
|
||||
var context = Context(frame);
|
||||
var zone = context.Zone;
|
||||
var layers = BuildDefaultRenderSplatLayers();
|
||||
var layerIndices = layers
|
||||
.Select((layer, index) => (layer.LayerKey, index))
|
||||
.ToDictionary(pair => pair.LayerKey, pair => pair.index, StringComparer.Ordinal);
|
||||
var splats = new RenderSplatBuilder();
|
||||
var viewportCenterX = (normalizedViewport.MinX + normalizedViewport.MaxX) * 0.5;
|
||||
var viewportCenterY = (normalizedViewport.MinY + normalizedViewport.MaxY) * 0.5;
|
||||
var viewportHalfX = Math.Max(0.0001, (normalizedViewport.MaxX - normalizedViewport.MinX) * 0.5);
|
||||
var viewportHalfY = Math.Max(0.0001, (normalizedViewport.MaxY - normalizedViewport.MinY) * 0.5);
|
||||
if (zone.GravityTerrainDepth != 0)
|
||||
{
|
||||
splats.Add(
|
||||
layerIndices[AetheriaRuntimeRenderSplatLayerKeys.GravityHeight],
|
||||
viewportCenterX,
|
||||
viewportCenterY,
|
||||
viewportHalfX,
|
||||
viewportHalfY,
|
||||
AetheriaRuntimeRenderSplatChannels.Gravity,
|
||||
AetheriaRuntimeRenderSplatFalloffs.Solid,
|
||||
-zone.GravityTerrainDepth,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
"environment.gravity_terrain",
|
||||
sourceKind: AetheriaRuntimeRenderSplatSourceKinds.AnimatedSimplexNoise,
|
||||
frequencyX: 3.0,
|
||||
frequencyY: 3.0,
|
||||
animationSpeed: zone.GravityTerrainWaveFrequency * 0.025,
|
||||
sourceFlags: 1);
|
||||
}
|
||||
|
||||
splats.Add(
|
||||
layerIndices[AetheriaRuntimeRenderSplatLayerKeys.FogSurfaceHeight],
|
||||
viewportCenterX,
|
||||
viewportCenterY,
|
||||
viewportHalfX,
|
||||
viewportHalfY,
|
||||
AetheriaRuntimeRenderSplatChannels.Tint,
|
||||
AetheriaRuntimeRenderSplatFalloffs.Solid,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
"environment.fog_surface_height",
|
||||
sourceKind: AetheriaRuntimeRenderSplatSourceKinds.AnimatedSimplexNoise,
|
||||
frequencyX: 4.0,
|
||||
frequencyY: 4.0,
|
||||
animationSpeed: 0.015,
|
||||
sourceFlags: 1);
|
||||
splats.Add(
|
||||
layerIndices[AetheriaRuntimeRenderSplatLayerKeys.FogPatchHeight],
|
||||
viewportCenterX,
|
||||
viewportCenterY,
|
||||
viewportHalfX,
|
||||
viewportHalfY,
|
||||
AetheriaRuntimeRenderSplatChannels.Tint,
|
||||
AetheriaRuntimeRenderSplatFalloffs.Solid,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
"environment.fog_patch_height",
|
||||
sourceKind: AetheriaRuntimeRenderSplatSourceKinds.AnimatedSimplexNoise,
|
||||
frequencyX: 9.0,
|
||||
frequencyY: 9.0,
|
||||
animationSpeed: 0.02,
|
||||
sourceFlags: 1);
|
||||
splats.Add(
|
||||
layerIndices[AetheriaRuntimeRenderSplatLayerKeys.FogPatch],
|
||||
viewportCenterX,
|
||||
viewportCenterY,
|
||||
viewportHalfX,
|
||||
viewportHalfY,
|
||||
AetheriaRuntimeRenderSplatChannels.Tint,
|
||||
AetheriaRuntimeRenderSplatFalloffs.Solid,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
"environment.fog_patch",
|
||||
sourceKind: AetheriaRuntimeRenderSplatSourceKinds.AnimatedSimplexNoise,
|
||||
frequencyX: 6.0,
|
||||
frequencyY: 6.0,
|
||||
animationSpeed: 0.01,
|
||||
sourceFlags: 1);
|
||||
|
||||
foreach (var body in zone.Bodies ?? Array.Empty<AetheriaRuntimeBodySnapshotCommit>())
|
||||
{
|
||||
if (!GravityInfluenceIntersectsViewport(body, normalizedViewport))
|
||||
continue;
|
||||
|
||||
var radius = ResolveGravityRadius(body);
|
||||
splats.Add(
|
||||
layerIndices[AetheriaRuntimeRenderSplatLayerKeys.GravityHeight],
|
||||
body.GravityInfluenceCenterX,
|
||||
body.GravityInfluenceCenterZ,
|
||||
radius,
|
||||
radius,
|
||||
AetheriaRuntimeRenderSplatChannels.Gravity,
|
||||
AetheriaRuntimeRenderSplatFalloffs.InverseSmooth,
|
||||
body.GravityWellDepth,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
body.BodyKey ?? "");
|
||||
|
||||
if (body.GravityWaveRadius > 0 && body.GravityWaveDepth != 0)
|
||||
{
|
||||
splats.Add(
|
||||
layerIndices[AetheriaRuntimeRenderSplatLayerKeys.GravityWave],
|
||||
body.GravityInfluenceCenterX,
|
||||
body.GravityInfluenceCenterZ,
|
||||
body.GravityWaveRadius,
|
||||
body.GravityWaveRadius,
|
||||
AetheriaRuntimeRenderSplatChannels.GravityWave,
|
||||
AetheriaRuntimeRenderSplatFalloffs.Smooth,
|
||||
body.GravityWaveDepth,
|
||||
body.GravityWaveSpeed,
|
||||
0,
|
||||
1,
|
||||
body.BodyKey ?? "");
|
||||
}
|
||||
|
||||
if (IsBodyKind(body, "sun"))
|
||||
{
|
||||
var tintRadius = Math.Max(radius, Math.Max(32, body.BodyRadiusMultiplier * 70) *
|
||||
Math.Max(0.01, body.SunVisual?.LightRadiusMultiplier ?? 1.0));
|
||||
splats.Add(
|
||||
layerIndices[AetheriaRuntimeRenderSplatLayerKeys.FogTint],
|
||||
body.GravityInfluenceCenterX,
|
||||
body.GravityInfluenceCenterZ,
|
||||
tintRadius,
|
||||
tintRadius,
|
||||
AetheriaRuntimeRenderSplatChannels.Tint,
|
||||
AetheriaRuntimeRenderSplatFalloffs.Smooth,
|
||||
body.SunVisual?.FogTintColorX ?? 0,
|
||||
body.SunVisual?.FogTintColorY ?? 0,
|
||||
body.SunVisual?.FogTintColorZ ?? 0,
|
||||
1,
|
||||
body.BodyKey ?? "");
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var entity in zone.Entities ?? Array.Empty<AetheriaRuntimeEntitySnapshotCommit>())
|
||||
{
|
||||
if (!IsPlayerControlled(entity))
|
||||
continue;
|
||||
|
||||
var visibility = Math.Max(180, entity.Visibility);
|
||||
if (entity.PositionX + visibility < normalizedViewport.MinX ||
|
||||
entity.PositionX - visibility > normalizedViewport.MaxX ||
|
||||
entity.PositionZ + visibility < normalizedViewport.MinY ||
|
||||
entity.PositionZ - visibility > normalizedViewport.MaxY)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
splats.Add(
|
||||
layerIndices[AetheriaRuntimeRenderSplatLayerKeys.Visibility],
|
||||
entity.PositionX,
|
||||
entity.PositionZ,
|
||||
visibility,
|
||||
visibility,
|
||||
AetheriaRuntimeRenderSplatChannels.Visibility,
|
||||
AetheriaRuntimeRenderSplatFalloffs.Smooth,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
BuildEntityKey(context.RunId, zone.ZoneIndex, entity.EntityIndex));
|
||||
}
|
||||
|
||||
return new AetheriaRuntimeRenderSplatsViewportDocument
|
||||
{
|
||||
FrameId = frame.FrameId,
|
||||
PublishedAtUtc = frame.PublishedAtUtc ?? "",
|
||||
SimulationTimeSeconds = frame.SimulationTimeSeconds,
|
||||
RunId = context.RunId,
|
||||
ZoneIndex = zone.ZoneIndex,
|
||||
ZoneName = string.IsNullOrWhiteSpace(zone.Name) ? $"Zone {zone.ZoneIndex}" : zone.Name,
|
||||
Viewport = normalizedViewport,
|
||||
Layers = layers,
|
||||
Splats = splats.Build()
|
||||
};
|
||||
}
|
||||
|
||||
private static IReadOnlyList<AetheriaRuntimeRenderSplatLayerDefinition> BuildDefaultRenderSplatLayers()
|
||||
{
|
||||
return new[]
|
||||
{
|
||||
new AetheriaRuntimeRenderSplatLayerDefinition
|
||||
{
|
||||
LayerKey = AetheriaRuntimeRenderSplatLayerKeys.GravityHeight,
|
||||
DisplayName = "Gravity Height",
|
||||
Channel = AetheriaRuntimeRenderSplatChannels.Gravity,
|
||||
BlendMode = AetheriaRuntimeRenderSplatBlendModes.Add,
|
||||
GraphicsFormat = "R16_SFloat"
|
||||
},
|
||||
new AetheriaRuntimeRenderSplatLayerDefinition
|
||||
{
|
||||
LayerKey = AetheriaRuntimeRenderSplatLayerKeys.GravityWave,
|
||||
DisplayName = "Gravity Wave",
|
||||
Channel = AetheriaRuntimeRenderSplatChannels.GravityWave,
|
||||
BlendMode = AetheriaRuntimeRenderSplatBlendModes.Add,
|
||||
GraphicsFormat = "R16_SFloat"
|
||||
},
|
||||
new AetheriaRuntimeRenderSplatLayerDefinition
|
||||
{
|
||||
LayerKey = AetheriaRuntimeRenderSplatLayerKeys.Visibility,
|
||||
DisplayName = "Visibility Mask",
|
||||
Channel = AetheriaRuntimeRenderSplatChannels.Visibility,
|
||||
BlendMode = AetheriaRuntimeRenderSplatBlendModes.Max,
|
||||
GraphicsFormat = "R16_SFloat"
|
||||
},
|
||||
new AetheriaRuntimeRenderSplatLayerDefinition
|
||||
{
|
||||
LayerKey = AetheriaRuntimeRenderSplatLayerKeys.FogSurfaceHeight,
|
||||
DisplayName = "Fog Surface Height",
|
||||
Channel = AetheriaRuntimeRenderSplatChannels.Tint,
|
||||
BlendMode = AetheriaRuntimeRenderSplatBlendModes.Add,
|
||||
GraphicsFormat = "R16_SFloat"
|
||||
},
|
||||
new AetheriaRuntimeRenderSplatLayerDefinition
|
||||
{
|
||||
LayerKey = AetheriaRuntimeRenderSplatLayerKeys.FogPatchHeight,
|
||||
DisplayName = "Fog Patch Height",
|
||||
Channel = AetheriaRuntimeRenderSplatChannels.Tint,
|
||||
BlendMode = AetheriaRuntimeRenderSplatBlendModes.Add,
|
||||
GraphicsFormat = "R16_SFloat"
|
||||
},
|
||||
new AetheriaRuntimeRenderSplatLayerDefinition
|
||||
{
|
||||
LayerKey = AetheriaRuntimeRenderSplatLayerKeys.FogPatch,
|
||||
DisplayName = "Fog Patch",
|
||||
Channel = AetheriaRuntimeRenderSplatChannels.Tint,
|
||||
BlendMode = AetheriaRuntimeRenderSplatBlendModes.Max,
|
||||
GraphicsFormat = "R16_SFloat"
|
||||
},
|
||||
new AetheriaRuntimeRenderSplatLayerDefinition
|
||||
{
|
||||
LayerKey = AetheriaRuntimeRenderSplatLayerKeys.FogTint,
|
||||
DisplayName = "Fog Tint",
|
||||
Channel = AetheriaRuntimeRenderSplatChannels.Tint,
|
||||
BlendMode = AetheriaRuntimeRenderSplatBlendModes.Add,
|
||||
GraphicsFormat = "B10G11R11_UFloatPack32"
|
||||
},
|
||||
new AetheriaRuntimeRenderSplatLayerDefinition
|
||||
{
|
||||
LayerKey = AetheriaRuntimeRenderSplatLayerKeys.Influence,
|
||||
DisplayName = "Influence",
|
||||
Channel = AetheriaRuntimeRenderSplatChannels.Influence,
|
||||
BlendMode = AetheriaRuntimeRenderSplatBlendModes.Add,
|
||||
GraphicsFormat = "R16_SFloat"
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static AetheriaRuntimeCurrentZoneDocument ProjectCurrentZone(
|
||||
AetheriaRuntimeDaemonFrameDocument frame)
|
||||
{
|
||||
|
|
@ -553,7 +820,7 @@ namespace GameCult.Aetheria.State.Verse
|
|||
string runId,
|
||||
int zoneIndex)
|
||||
{
|
||||
return new AetheriaRuntimeRtsViewportObject
|
||||
var obj = new AetheriaRuntimeRtsViewportObject
|
||||
{
|
||||
EntityIndex = entity.EntityIndex,
|
||||
EntityKey = BuildEntityKey(runId, zoneIndex, entity.EntityIndex),
|
||||
|
|
@ -579,6 +846,8 @@ namespace GameCult.Aetheria.State.Verse
|
|||
},
|
||||
Inventory = Inventory(entity)
|
||||
};
|
||||
obj.IconAsset = AetheriaRuntimeAssets.ResolveEntityIcon(obj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
private static AetheriaRuntimeRtsBodyView ToBodyView(AetheriaRuntimeBodySnapshotCommit body)
|
||||
|
|
@ -1101,7 +1370,11 @@ namespace GameCult.Aetheria.State.Verse
|
|||
Enabled = item.Enabled,
|
||||
SourceIndex = sourceIndex,
|
||||
X = slot.X,
|
||||
Y = slot.Y
|
||||
Y = slot.Y,
|
||||
IconAsset = AetheriaRuntimeAssetRef.FromKey(
|
||||
$"item.{item.ItemKey ?? ""}.icon",
|
||||
AetheriaRuntimeAssetKinds.Texture,
|
||||
$"item.{item.ItemKey ?? ""}.icon")
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -1123,6 +1396,111 @@ namespace GameCult.Aetheria.State.Verse
|
|||
return string.Equals(entity.FactionKey, "player", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
private static bool IsBodyKind(AetheriaRuntimeBodySnapshotCommit body, string kind)
|
||||
{
|
||||
return body != null &&
|
||||
string.Equals(body.Kind ?? "", kind ?? "", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
private sealed class RenderSplatBuilder
|
||||
{
|
||||
private readonly List<double> _centerX = new List<double>();
|
||||
private readonly List<double> _centerY = new List<double>();
|
||||
private readonly List<double> _halfExtentX = new List<double>();
|
||||
private readonly List<double> _halfExtentY = new List<double>();
|
||||
private readonly List<double> _rotationCos = new List<double>();
|
||||
private readonly List<double> _rotationSin = new List<double>();
|
||||
private readonly List<int> _channel = new List<int>();
|
||||
private readonly List<int> _falloff = new List<int>();
|
||||
private readonly List<double> _valueR = new List<double>();
|
||||
private readonly List<double> _valueG = new List<double>();
|
||||
private readonly List<double> _valueB = new List<double>();
|
||||
private readonly List<double> _valueA = new List<double>();
|
||||
private readonly List<string> _sourceKey = new List<string>();
|
||||
private readonly List<int> _layerIndex = new List<int>();
|
||||
private readonly List<int> _sourceKind = new List<int>();
|
||||
private readonly List<double> _frequencyX = new List<double>();
|
||||
private readonly List<double> _frequencyY = new List<double>();
|
||||
private readonly List<double> _phaseX = new List<double>();
|
||||
private readonly List<double> _phaseY = new List<double>();
|
||||
private readonly List<double> _animationSpeed = new List<double>();
|
||||
private readonly List<double> _sourceFlags = new List<double>();
|
||||
|
||||
public void Add(
|
||||
int layerIndex,
|
||||
double centerX,
|
||||
double centerY,
|
||||
double halfExtentX,
|
||||
double halfExtentY,
|
||||
int channel,
|
||||
int falloff,
|
||||
double valueR,
|
||||
double valueG,
|
||||
double valueB,
|
||||
double valueA,
|
||||
string sourceKey,
|
||||
double rotationRadians = 0,
|
||||
int sourceKind = AetheriaRuntimeRenderSplatSourceKinds.Constant,
|
||||
double frequencyX = 1,
|
||||
double frequencyY = 1,
|
||||
double phaseX = 0,
|
||||
double phaseY = 0,
|
||||
double animationSpeed = 0,
|
||||
double sourceFlags = 0)
|
||||
{
|
||||
_layerIndex.Add(Math.Max(0, layerIndex));
|
||||
_centerX.Add(centerX);
|
||||
_centerY.Add(centerY);
|
||||
_halfExtentX.Add(Math.Max(0, halfExtentX));
|
||||
_halfExtentY.Add(Math.Max(0, halfExtentY));
|
||||
_rotationCos.Add(Math.Cos(rotationRadians));
|
||||
_rotationSin.Add(Math.Sin(rotationRadians));
|
||||
_channel.Add(channel);
|
||||
_falloff.Add(falloff);
|
||||
_valueR.Add(valueR);
|
||||
_valueG.Add(valueG);
|
||||
_valueB.Add(valueB);
|
||||
_valueA.Add(valueA);
|
||||
_sourceKey.Add(sourceKey ?? "");
|
||||
_sourceKind.Add(sourceKind);
|
||||
_frequencyX.Add(frequencyX);
|
||||
_frequencyY.Add(frequencyY);
|
||||
_phaseX.Add(phaseX);
|
||||
_phaseY.Add(phaseY);
|
||||
_animationSpeed.Add(animationSpeed);
|
||||
_sourceFlags.Add(sourceFlags);
|
||||
}
|
||||
|
||||
public AetheriaRuntimeRenderSplatSoa Build()
|
||||
{
|
||||
return new AetheriaRuntimeRenderSplatSoa
|
||||
{
|
||||
Count = _centerX.Count,
|
||||
CenterX = _centerX.ToArray(),
|
||||
CenterY = _centerY.ToArray(),
|
||||
HalfExtentX = _halfExtentX.ToArray(),
|
||||
HalfExtentY = _halfExtentY.ToArray(),
|
||||
RotationCos = _rotationCos.ToArray(),
|
||||
RotationSin = _rotationSin.ToArray(),
|
||||
Channel = _channel.ToArray(),
|
||||
Falloff = _falloff.ToArray(),
|
||||
ValueR = _valueR.ToArray(),
|
||||
ValueG = _valueG.ToArray(),
|
||||
ValueB = _valueB.ToArray(),
|
||||
ValueA = _valueA.ToArray(),
|
||||
SourceKey = _sourceKey.ToArray(),
|
||||
LayerIndex = _layerIndex.ToArray(),
|
||||
SourceKind = _sourceKind.ToArray(),
|
||||
FrequencyX = _frequencyX.ToArray(),
|
||||
FrequencyY = _frequencyY.ToArray(),
|
||||
PhaseX = _phaseX.ToArray(),
|
||||
PhaseY = _phaseY.ToArray(),
|
||||
AnimationSpeed = _animationSpeed.ToArray(),
|
||||
SourceFlags = _sourceFlags.ToArray()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private static ProjectionContext Context(AetheriaRuntimeDaemonFrameDocument frame)
|
||||
{
|
||||
var run = frame.Run ?? new AetheriaRuntimeRunCheckpointCommit();
|
||||
|
|
|
|||
|
|
@ -1123,6 +1123,10 @@ namespace GameCult.Aetheria.State.Verse
|
|||
[Key(17)]
|
||||
public IReadOnlyList<AetheriaRuntimeRtsInventoryItem> Inventory { get; set; } =
|
||||
Array.Empty<AetheriaRuntimeRtsInventoryItem>();
|
||||
|
||||
[Key(18)]
|
||||
public AetheriaRuntimeAssetRef IconAsset { get; set; } =
|
||||
AetheriaRuntimeAssetRef.Empty(AetheriaRuntimeAssetKinds.Sprite);
|
||||
}
|
||||
|
||||
[MessagePackObject]
|
||||
|
|
@ -1167,6 +1171,10 @@ namespace GameCult.Aetheria.State.Verse
|
|||
|
||||
[Key(8)]
|
||||
public int Y { get; set; } = -1;
|
||||
|
||||
[Key(9)]
|
||||
public AetheriaRuntimeAssetRef IconAsset { get; set; } =
|
||||
AetheriaRuntimeAssetRef.Empty(AetheriaRuntimeAssetKinds.Texture);
|
||||
}
|
||||
|
||||
[MessagePackObject]
|
||||
|
|
|
|||
|
|
@ -75,6 +75,8 @@ namespace GameCult.Aetheria.State.Verse
|
|||
[Key(2)] public double Quality { get; set; }
|
||||
[Key(3)] public double Durability { get; set; }
|
||||
[Key(4)] public string Source { get; set; } = "station";
|
||||
[Key(5)] public AetheriaRuntimeAssetRef IconAsset { get; set; } =
|
||||
AetheriaRuntimeAssetRef.Empty(AetheriaRuntimeAssetKinds.Texture);
|
||||
}
|
||||
|
||||
[MessagePackObject]
|
||||
|
|
@ -122,7 +124,8 @@ namespace GameCult.Aetheria.State.Verse
|
|||
public static AetheriaRuntimeStarbridgeSessionSummaryDocument ProjectSessionSummary(
|
||||
AetheriaRuntimeDaemonFrameDocument frame,
|
||||
AetheriaRuntimeStarbridgeScenarioDocument? scenario = null,
|
||||
AetheriaRuntimeStarbridgeSessionDocument? session = null)
|
||||
AetheriaRuntimeStarbridgeSessionDocument? session = null,
|
||||
AetheriaRuntimeCatalogSnapshot? catalog = null)
|
||||
{
|
||||
frame ??= new AetheriaRuntimeDaemonFrameDocument();
|
||||
var run = frame.Run ?? new AetheriaRuntimeRunCheckpointCommit();
|
||||
|
|
@ -145,7 +148,7 @@ namespace GameCult.Aetheria.State.Verse
|
|||
Phase = FirstNonEmpty(session?.Phase, "setup"),
|
||||
CurrentWaveIndex = Math.Max(0, session?.CurrentWaveIndex ?? 0),
|
||||
BaseStatus = ToBaseStatus(baseEntity, runId, zone.ZoneIndex),
|
||||
StationStock = ResolveStationStock(scenario, session, baseEntity),
|
||||
StationStock = ResolveStationStock(scenario, session, baseEntity, catalog),
|
||||
WaveForecast = ResolveWaveForecast(scenario, session),
|
||||
RuntimeRoles = ResolveRuntimeRoles(scenario, session)
|
||||
};
|
||||
|
|
@ -215,10 +218,11 @@ namespace GameCult.Aetheria.State.Verse
|
|||
private static AetheriaRuntimeStarbridgeStationStockItem[] ResolveStationStock(
|
||||
AetheriaRuntimeStarbridgeScenarioDocument? scenario,
|
||||
AetheriaRuntimeStarbridgeSessionDocument? session,
|
||||
AetheriaRuntimeEntitySnapshotCommit? baseEntity)
|
||||
AetheriaRuntimeEntitySnapshotCommit? baseEntity,
|
||||
AetheriaRuntimeCatalogSnapshot? catalog)
|
||||
{
|
||||
if (scenario?.StationStock?.Length > 0)
|
||||
return scenario.StationStock;
|
||||
return scenario.StationStock.Select(item => WithIconAsset(item, catalog)).ToArray();
|
||||
|
||||
if (baseEntity == null)
|
||||
return Array.Empty<AetheriaRuntimeStarbridgeStationStockItem>();
|
||||
|
|
@ -233,11 +237,40 @@ namespace GameCult.Aetheria.State.Verse
|
|||
Quantity = item.Quantity,
|
||||
Quality = item.Quality,
|
||||
Durability = item.Durability,
|
||||
Source = string.IsNullOrWhiteSpace(session?.StationEntityKey) ? "base-cargo" : session!.StationEntityKey
|
||||
Source = string.IsNullOrWhiteSpace(session?.StationEntityKey) ? "base-cargo" : session!.StationEntityKey,
|
||||
IconAsset = ResolveItemIconAsset(item.ItemKey, catalog)
|
||||
})
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
private static AetheriaRuntimeStarbridgeStationStockItem WithIconAsset(
|
||||
AetheriaRuntimeStarbridgeStationStockItem item,
|
||||
AetheriaRuntimeCatalogSnapshot? catalog)
|
||||
{
|
||||
if (item == null)
|
||||
return new AetheriaRuntimeStarbridgeStationStockItem();
|
||||
|
||||
item.IconAsset = ResolveItemIconAsset(item.ItemKey, catalog);
|
||||
return item;
|
||||
}
|
||||
|
||||
private static AetheriaRuntimeAssetRef ResolveItemIconAsset(
|
||||
string? itemKey,
|
||||
AetheriaRuntimeCatalogSnapshot? catalog)
|
||||
{
|
||||
itemKey ??= "";
|
||||
var typedItem = catalog?.FindItem(itemKey);
|
||||
var icon = AetheriaRuntimeAssets.AssetRefFromCatalogIcon(
|
||||
typedItem?.ActionBarIcon,
|
||||
$"item.{itemKey}.icon");
|
||||
return string.IsNullOrWhiteSpace(icon.AssetKey)
|
||||
? AetheriaRuntimeAssetRef.FromKey(
|
||||
$"item.{itemKey}.icon",
|
||||
AetheriaRuntimeAssetKinds.Texture,
|
||||
$"item.{itemKey}.icon")
|
||||
: icon;
|
||||
}
|
||||
|
||||
private static AetheriaRuntimeStarbridgeWaveForecast[] ResolveWaveForecast(
|
||||
AetheriaRuntimeStarbridgeScenarioDocument? scenario,
|
||||
AetheriaRuntimeStarbridgeSessionDocument? session)
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ namespace GameCult.Aetheria.State.Verse
|
|||
public const string RuntimeReplicaDirectoryName = "Verses";
|
||||
public const string RuntimeDaemonFrameFileSuffix = ".daemon.frame.cc";
|
||||
public const string RuntimeDaemonSoaViewFileSuffix = ".daemon.soa.cc";
|
||||
public const string RuntimeDaemonAssetManifestFileSuffix = ".daemon.assets.cc";
|
||||
public const string RuntimeDaemonProviderFileSuffix = ".daemon.provider.cc";
|
||||
public const string RuntimeDaemonHealthFileSuffix = ".daemon.health.cc";
|
||||
public const string RuntimeVerseAuthorityPolicyFileSuffix = ".authority.policy.cc";
|
||||
|
|
@ -44,6 +45,11 @@ namespace GameCult.Aetheria.State.Verse
|
|||
return stateFilePath + RuntimeDaemonSoaViewFileSuffix;
|
||||
}
|
||||
|
||||
public static string GetDaemonAssetManifestPath(string stateFilePath)
|
||||
{
|
||||
return stateFilePath + RuntimeDaemonAssetManifestFileSuffix;
|
||||
}
|
||||
|
||||
public static string GetDaemonProviderPath(string stateFilePath)
|
||||
{
|
||||
return stateFilePath + RuntimeDaemonProviderFileSuffix;
|
||||
|
|
|
|||
|
|
@ -89,6 +89,7 @@ namespace GameCult.Aetheria.State.Verse
|
|||
typeof(AetheriaRuntimeDaemonCommandBoundaryDocument),
|
||||
typeof(AetheriaRuntimeDaemonFrameDocument),
|
||||
typeof(AetheriaRuntimeDaemonSoaViewDocument),
|
||||
typeof(AetheriaRuntimeAssetManifestDocument),
|
||||
typeof(AetheriaRuntimeObjectsViewportDocument),
|
||||
typeof(AetheriaRuntimeGravityViewportDocument),
|
||||
typeof(AetheriaRuntimeCurrentZoneDocument),
|
||||
|
|
|
|||
Loading…
Reference in a new issue