diff options
| author | Maze <167211895+mazeincoding@users.noreply.github.com> | 2026-01-31 00:20:04 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-01-31 00:20:04 +0100 |
| commit | 7bf0984698e43b9aa2dd879f65951318ed410390 (patch) | |
| tree | ccf2cd590a47d550152afc07129cb55c71a9ec35 /apps/web/src/services/renderer/scene-builder.ts | |
| parent | 0173db99448d1c4c6415dcf15272e17551b48b04 (diff) | |
| download | opencut-7bf0984698e43b9aa2dd879f65951318ed410390.tar.gz | |
codebase overhaul (#697)
Diffstat (limited to 'apps/web/src/services/renderer/scene-builder.ts')
| -rw-r--r-- | apps/web/src/services/renderer/scene-builder.ts | 130 |
1 files changed, 130 insertions, 0 deletions
diff --git a/apps/web/src/services/renderer/scene-builder.ts b/apps/web/src/services/renderer/scene-builder.ts new file mode 100644 index 00000000..b36edda1 --- /dev/null +++ b/apps/web/src/services/renderer/scene-builder.ts @@ -0,0 +1,130 @@ +import type { TimelineTrack } from "@/types/timeline"; +import type { MediaAsset } from "@/types/assets"; +import { RootNode } from "./nodes/root-node"; +import { VideoNode } from "./nodes/video-node"; +import { ImageNode } from "./nodes/image-node"; +import { TextNode } from "./nodes/text-node"; +import { StickerNode } from "./nodes/sticker-node"; +import { ColorNode } from "./nodes/color-node"; +import { BlurBackgroundNode } from "./nodes/blur-background-node"; +import type { TBackground, TCanvasSize } from "@/types/project"; +import { DEFAULT_BLUR_INTENSITY } from "@/constants/project-constants"; +import { isMainTrack } from "@/lib/timeline"; + +export type BuildSceneParams = { + canvasSize: TCanvasSize; + tracks: TimelineTrack[]; + mediaAssets: MediaAsset[]; + duration: number; + background: TBackground; +}; + +export function buildScene(params: BuildSceneParams) { + const { tracks, mediaAssets, duration, canvasSize, background } = params; + + const rootNode = new RootNode({ duration }); + const mediaMap = new Map(mediaAssets.map((m) => [m.id, m])); + + const visibleTracks = tracks.filter( + (track) => !("hidden" in track && track.hidden), + ); + + const mainTrack = visibleTracks.find((track) => isMainTrack(track)) ?? null; + const orderedTracksTopToBottom = [ + ...visibleTracks.filter((track) => !isMainTrack(track)), + ...(mainTrack ? [mainTrack] : []), + ]; + + const orderedTracksBottomToTop = orderedTracksTopToBottom.slice().reverse(); + + const contentNodes = []; + + for (const track of orderedTracksBottomToTop) { + const elements = track.elements + .filter((element) => !("hidden" in element && element.hidden)) + .slice() + .sort((a, b) => { + if (a.startTime !== b.startTime) return a.startTime - b.startTime; + return a.id.localeCompare(b.id); + }); + + for (const element of elements) { + if (element.type === "video" || element.type === "image") { + const mediaAsset = mediaMap.get(element.mediaId); + if (!mediaAsset?.file) { + continue; + } + + if (mediaAsset.type === "video") { + contentNodes.push( + new VideoNode({ + mediaId: mediaAsset.id, + file: mediaAsset.file, + duration: element.duration, + timeOffset: element.startTime, + trimStart: element.trimStart, + trimEnd: element.trimEnd, + }), + ); + } + if (mediaAsset.type === "image") { + contentNodes.push( + new ImageNode({ + file: mediaAsset.file, + duration: element.duration, + timeOffset: element.startTime, + trimStart: element.trimStart, + trimEnd: element.trimEnd, + }), + ); + } + } + + if (element.type === "text") { + contentNodes.push( + new TextNode({ + ...element, + canvasCenter: { x: canvasSize.width / 2, y: canvasSize.height / 2 }, + textBaseline: "middle", + }), + ); + } + + if (element.type === "sticker") { + contentNodes.push( + new StickerNode({ + iconName: element.iconName, + duration: element.duration, + timeOffset: element.startTime, + trimStart: element.trimStart, + trimEnd: element.trimEnd, + transform: element.transform, + opacity: element.opacity, + color: element.color, + }), + ); + } + } + } + + if (background.type === "blur") { + rootNode.add( + new BlurBackgroundNode({ + blurIntensity: background.blurIntensity ?? DEFAULT_BLUR_INTENSITY, + contentNodes, + }), + ); + for (const node of contentNodes) { + rootNode.add(node); + } + } else { + if (background.type === "color" && background.color !== "transparent") { + rootNode.add(new ColorNode({ color: background.color })); + } + for (const node of contentNodes) { + rootNode.add(node); + } + } + + return rootNode; +} |
