feat(materials): add campaign materials overlay for sessions
Let GMs manage and show images over the scene from the editor and control panel, with zoom tools, help docs, and full ru/en i18n. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -14,7 +14,7 @@ import type {
|
||||
SceneId,
|
||||
} from '../types';
|
||||
import type { AssetId, ProjectId } from '../types/ids';
|
||||
import { asAssetId, asGraphNodeId, asProjectId, asSceneId } from '../types/ids';
|
||||
import { asAssetId, asGraphNodeId, asMaterialId, asProjectId, asSceneId } from '../types/ids';
|
||||
|
||||
export type StorylineKind = 'main' | 'side';
|
||||
|
||||
@@ -273,6 +273,7 @@ export function buildPartialExportProject(
|
||||
...draft,
|
||||
assets,
|
||||
campaignAudios: source.campaignAudios.map((a) => ({ ...a })),
|
||||
materials: (source.materials ?? []).map((m) => ({ ...m })),
|
||||
currentSceneId: mainStart?.sceneId ?? firstSide?.sceneId ?? null,
|
||||
currentGraphNodeId: mainStart?.id ?? firstSide?.id ?? null,
|
||||
};
|
||||
@@ -287,6 +288,7 @@ function collectReferencedAssetIdsForProject(p: Project): Set<AssetId> {
|
||||
for (const au of sc.media.audios) refs.add(au.assetId);
|
||||
}
|
||||
for (const au of p.campaignAudios) refs.add(au.assetId);
|
||||
for (const m of p.materials ?? []) refs.add(m.assetId);
|
||||
return refs;
|
||||
}
|
||||
|
||||
@@ -373,6 +375,7 @@ export function mergeStorylinesIntoProject(
|
||||
for (const au of sc.media.audios) neededAssetIds.add(au.assetId);
|
||||
}
|
||||
for (const au of source.campaignAudios) neededAssetIds.add(au.assetId);
|
||||
for (const m of source.materials ?? []) neededAssetIds.add(m.assetId);
|
||||
|
||||
const assetMap = new Map<AssetId, AssetId>();
|
||||
const targetSha = new Map<string, AssetId>();
|
||||
@@ -483,11 +486,35 @@ export function mergeStorylinesIntoProject(
|
||||
}
|
||||
}
|
||||
|
||||
const materials = [...(target.materials ?? [])];
|
||||
const materialNameKeys = new Set(materials.map((m) => m.name.trim().toLowerCase()));
|
||||
const materialAssetIds = new Set(materials.map((m) => m.assetId));
|
||||
for (const m of source.materials ?? []) {
|
||||
const mapped = assetMap.get(m.assetId) ?? m.assetId;
|
||||
if (materialAssetIds.has(mapped)) continue;
|
||||
let name = m.name.trim();
|
||||
const baseKey = name.toLowerCase();
|
||||
if (materialNameKeys.has(baseKey)) {
|
||||
let n = 2;
|
||||
while (materialNameKeys.has(`${baseKey} (${String(n)})`)) n += 1;
|
||||
name = `${name} (${String(n)})`;
|
||||
}
|
||||
materials.push({
|
||||
id: asMaterialId(`mat_${generateId()}`),
|
||||
name,
|
||||
assetId: mapped,
|
||||
rotationDeg: m.rotationDeg ?? 0,
|
||||
});
|
||||
materialNameKeys.add(name.toLowerCase());
|
||||
materialAssetIds.add(mapped);
|
||||
}
|
||||
|
||||
let merged: Project = {
|
||||
...target,
|
||||
scenes,
|
||||
assets,
|
||||
campaignAudios,
|
||||
materials,
|
||||
sceneGraphNodes: [...target.sceneGraphNodes, ...newGraphNodes],
|
||||
sceneGraphEdges: [...target.sceneGraphEdges, ...newEdges],
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user