back merge tts, ui stable now

This commit is contained in:
wil committed 2024-04-09 10:31:51 -04:00
commit 6434c18d3f
48 files changed
+760 -540

No files matched your search

+4 -1
View File
@@ -5,7 +5,7 @@ import { twMerge } from "tailwind-merge";
export interface ButtonPropsI
extends React.ButtonHTMLAttributes<HTMLButtonElement> {
icon?: IconDefinition;
variant?: "primary" | "secondary";
variant?: "primary" | "secondary" | "action";
}
export const Button = ({
@@ -25,6 +25,9 @@ export const Button = ({
case "secondary": {
return " bg-brand-secondary hover:bg-brand-secondary-900 text-white focus-visible:outline-brand-secondary";
}
case "action": {
return " bg-action hover:bg-action-500 text-white focus-visible:outline-action";
}
case "primary":
default: {
return " bg-brand-primary hover:bg-brand-primary-400 text-white focus-visible:outline-brand-primary-600";
+3 -6
View File
@@ -22,12 +22,9 @@ export const PageEnigma = () => {
useSignals();
useEffect(() => {
function setPage() {
const pxRatioWidth =
window.screen.availWidth / document.documentElement.clientWidth;
const pxRatioHeight =
window.screen.availHeight / document.documentElement.clientHeight;
pageHeight.value = window.outerHeight / pxRatioHeight;
pageWidth.value = window.outerWidth / pxRatioWidth;
// TODO address this issue with zooming
pageHeight.value = window.innerHeight;
pageWidth.value = window.outerWidth;
}
timelineHeight.value = window.innerHeight * 0.25;
sidePanelWidth.value = 443;
@@ -54,7 +54,6 @@ export const PageEnigmaComponent = () => {
const width = sidePanelVisible.value
? pageWidth.value - dndWidth - 66
: pageWidth.value - 66;
const height =
dndTimelineHeight.value > -1
? pageHeight.value - dndTimelineHeight.value - 68
+2 -1
View File
@@ -12,9 +12,10 @@ import {
import { toTimelineActions } from "./toTimelineActions";
type UnionedActionTypes = fromEngineActions | toEngineActions | toTimelineActions | toInferenceActions;
type UnionedDataTypes = QueueClip | UpdateTime | QueueKeyframe | ClipUI[] | InferenceJob;
type UnionedDataTypes = QueueClip | UpdateTime | QueueKeyframe | ClipUI[] | MediaItem | InferenceJob;
import { ClipUI } from "../datastructures/clips/clip_ui";
import { MediaItem } from "~/pages/PageEnigma/models";
class Queue {
private _queue: Record<
string,
@@ -1,5 +1,6 @@
export enum fromEngineActions {
UPDATE_TIME = "update_time",
UPDATE_TIME_LINE = "update_timeline",
UPDATE_CHARACTER_ID = "update_character_id",
// data={currentTime: number}
}
@@ -2,6 +2,11 @@ export enum toEngineActions {
ADD_CLIP = "add_clip",
// data=QueueClip
ADD_KEYFRAME = "add_keyframe",
ADD_CHARACTER = "add_character",
ADD_OBJECT = "add_object",
// data= QueueKeyframe
DELETE_CLIP = "delete_clip",
// data=QueueClip
@@ -2,6 +2,9 @@ import { audioFilter, audioItems } from "~/pages/PageEnigma/store";
import { AssetFilterOption } from "~/pages/PageEnigma/models";
import { useSignals } from "@preact/signals-react/runtime";
import { ItemElements } from "~/pages/PageEnigma/comps/SidePanelTabs/itemTabs/ItemElements";
import { Button } from "~/components";
import { faCirclePlus } from "@fortawesome/pro-solid-svg-icons";
import { twMerge } from "tailwind-merge";
export const AudioTabComponent = () => {
useSignals();
@@ -9,39 +12,36 @@ export const AudioTabComponent = () => {
return (
<>
<div className="w-full overflow-x-auto">
<div className="mb-4 mt-2 flex justify-start gap-2 px-2">
<div className="mb-4 mt-4 flex justify-start gap-2 px-4">
<button
className={[
"bg-assets-background rounded-lg px-3 py-1 text-sm",
audioFilter.value === AssetFilterOption.ALL
? "border-2 border-brand-primary"
: "",
].join(" ")}
className={twMerge(
"filter-tab",
audioFilter.value === AssetFilterOption.ALL ? "active" : "",
"disabled",
)}
onClick={() => (audioFilter.value = AssetFilterOption.ALL)}
>
All
</button>
<button
className={[
"bg-assets-background text-nowrap rounded-lg px-3 py-1 text-sm",
"disabled:text-gray-500",
audioFilter.value === AssetFilterOption.MINE
? "border-2 border-brand-primary"
: "",
].join(" ")}
className={twMerge(
"filter-tab",
audioFilter.value === AssetFilterOption.MINE ? "active" : "",
"disabled",
)}
onClick={() => (audioFilter.value = AssetFilterOption.MINE)}
disabled={!audioItems.value.some((item) => item.isMine)}
>
My Audios
</button>
<button
className={[
"bg-assets-background rounded-lg px-3 py-1 text-sm",
"disabled:text-gray-500",
className={twMerge(
"filter-tab",
audioFilter.value === AssetFilterOption.BOOKMARKED
? "border-2 border-brand-primary"
? "active"
: "",
].join(" ")}
"disabled",
)}
onClick={() => (audioFilter.value = AssetFilterOption.BOOKMARKED)}
disabled={!audioItems.value.some((item) => item.isBookmarked)}
>
@@ -49,12 +49,16 @@ export const AudioTabComponent = () => {
</button>
</div>
</div>
<div className="px-2">
<button className="bg-assets-background w-full rounded-lg py-2">
Upload Audio
</button>
<div className="w-full px-4">
<Button
icon={faCirclePlus}
variant="action"
className="w-full py-3 text-sm font-medium"
>
Generate Audio
</Button>
</div>
<div className="overflow-y-auto px-2 pt-2">
<div className="h-full w-full overflow-y-auto px-4 pt-4">
<ItemElements
items={audioItems.value}
assetFilter={audioFilter.value}
@@ -62,120 +66,4 @@ export const AudioTabComponent = () => {
</div>
</>
);
};
// import { useContext, useEffect, useState } from "react";
// import { faArrowsRotate } from "@fortawesome/pro-solid-svg-icons";
// import { TrackContext } from "~/pages/PageEnigma/contexts/TrackContext/TrackContext";
// import { APPUI_ACTION_TYPES } from "../../../reducers";
// import { AppUiContext } from "../../../contexts/AppUiContext";
// import { AuthenticationContext } from "~/contexts/Authentication";
// import { useSignals } from "@preact/signals-react/runtime";
// // import { timelineHeight } from "~/pages/PageEnigma/store";
// import { Button, ButtonIcon, Label, P } from "~/components";
// import { ClipType } from "../../../models/track";
// import { AudioElement } from "./AudioElement";
// import { ListAudioByUser, MediaFile } from "./listAudioByUser";
// export const TabAudio = () => {
// const { audioClips } = useContext(TrackContext);
// const { authState } = useContext(AuthenticationContext);
// const [, dispatchAppUiState] = useContext(AppUiContext);
// const [ userAudioClips, setUserAudioClips] = useState<MediaFile[]>([]);
// useSignals();
// useEffect(() => {
// if(authState.userInfo && authState.userInfo.username && userAudioClips.length === 0)
// ListAudioByUser(authState.userInfo.username)
// .then(res=>{
// setUserAudioClips([...res]);
// });
// }, [authState]);
// const openTTSPanel = () =>{
// dispatchAppUiState({
// type: APPUI_ACTION_TYPES.OPEN_DIALOGUE_TTS
// })
// };
// const handleRefreshUserMedia = ()=>{
// if(authState.userInfo && authState.userInfo.username)
// ListAudioByUser(authState.userInfo.username)
// .then(res=>{
// setUserAudioClips([...res]);
// });
// }
// return (
// <div
// className="flex flex-col gap-3"
// >
// <div className="flex flex-col">
// <Label>Generate</Label>
// <Button
// onClick={openTTSPanel}
// variant="secondary"
// >
// Create TTS Audio
// </Button>
// <span className="w-full h-3" />
// <Button
// disabled
// variant="secondary"
// >
// Create Voice to Voice Audio
// </Button>
// </div>
// <div className="flex flex-col mt-1">
// <Label>Preset Dialogues</Label>
// <div className="flex flex-wrap gap-2">
// {audioClips.map((clip) => {
// return (
// <AudioElement
// key={clip.media_id}
// clip={clip}
// type={ClipType.AUDIO}
// />
// );
// })}
// </div>
// </div>
// <div className="flex flex-col mt-1">
// <div className="flex justify-between">
// <Label>My Dialogues</Label>
// <ButtonIcon icon={faArrowsRotate} onClick={handleRefreshUserMedia}/>
// </div>
// {userAudioClips.length === 0 &&
// <div className="flex justify-center items-center text-center w-full h-40">
// <P className="text-brand-secondary-300"> No audio generated yet</P>
// </div>
// }
// { userAudioClips.length >= 0 &&
// <div className="flex flex-wrap gap-1">
// {userAudioClips.map(item =>
// <AudioElement
// key={item.token}
// clip={{
// version: 0,
// type: ClipType.AUDIO,
// media_id: item.token,
// name: item.origin['maybe_model'].title || "Unknown",
// length: 0,
// }}
// type={ClipType.AUDIO}
// />
// )}
// </div>
// }
// </div>
// <Button className="w-fit m-auto px-6 py-2"> Add to Lip Sync Track</Button>
// </div>
// );
// };
};
@@ -1,7 +1,6 @@
import { useEffect } from "react";
import { useSignals } from "@preact/signals-react/runtime";
import {
lastSelectedTab,
selectedTab,
sidePanelHeight,
sidePanelVisible,
@@ -18,7 +17,7 @@ export const SidePanelMenu = () => {
return (
<div
className={[
"bg-assets-background fixed",
"fixed bg-assets-background",
"px-2 py-4",
"overflow-y-auto",
].join(" ")}
@@ -1,10 +1,5 @@
import { useSignals } from "@preact/signals-react/runtime";
import {
dndSidePanelWidth,
selectedTab,
sidePanelHeight,
sidePanelWidth,
} from "~/pages/PageEnigma/store";
import { selectedTab, sidePanelHeight } from "~/pages/PageEnigma/store";
import { tabList } from "~/pages/PageEnigma/comps/SidePanelTabs/tabList";
import { useMouseEventsSidePanel } from "~/pages/PageEnigma/comps/Timeline/utils/useMouseEventsSidePanel";
@@ -18,7 +13,9 @@ export const SidePanelTabs = () => {
{tabList.map((tab) => (
<div
key={tab.value}
className={tab.value === selectedTab.value?.value ? "" : "hidden"}
className={
tab.value === selectedTab.value?.value ? "h-full" : "hidden"
}
>
{tab.component}
</div>
@@ -1,6 +1,6 @@
import { useContext, useState } from "react";
import { Button, ItemPicker, Label, H4, Textarea } from "~/components";
import { Button, ItemPicker, Label, Textarea } from "~/components";
import { AppUiContext } from "../../contexts/AppUiContext";
import { APPUI_ACTION_TYPES, APPUI_VIEW_MODES } from "../../reducers";
@@ -60,9 +60,9 @@ export const TabStylization = () => {
};
return (
<div className="flex h-full w-full flex-col gap-3">
<div className="flex h-full w-full flex-col gap-3 p-4">
<div className="flex flex-col">
<H4>Select Base Style</H4>
<Label>Select Base Style</Label>
<div className="grid grid-cols-4 gap-2">
<ItemPicker
label="Anime"
@@ -2,6 +2,9 @@ import { animationFilter, animationItems } from "~/pages/PageEnigma/store";
import { AssetFilterOption } from "~/pages/PageEnigma/models";
import { useSignals } from "@preact/signals-react/runtime";
import { ItemElements } from "~/pages/PageEnigma/comps/SidePanelTabs/itemTabs/ItemElements";
import { Button } from "~/components";
import { faCirclePlus } from "@fortawesome/pro-solid-svg-icons";
import { twMerge } from "tailwind-merge";
export const AnimationTab = () => {
useSignals();
@@ -9,39 +12,36 @@ export const AnimationTab = () => {
return (
<>
<div className="w-full overflow-x-auto">
<div className="mb-4 mt-2 flex justify-start gap-2 px-2">
<div className="mb-4 mt-4 flex justify-start gap-2 px-4">
<button
className={[
"bg-assets-background rounded-lg px-3 py-1 text-sm",
animationFilter.value === AssetFilterOption.ALL
? "border-2 border-brand-primary"
: "",
].join(" ")}
className={twMerge(
"filter-tab",
animationFilter.value === AssetFilterOption.ALL ? "active" : "",
"disabled",
)}
onClick={() => (animationFilter.value = AssetFilterOption.ALL)}
>
All
</button>
<button
className={[
"bg-assets-background text-nowrap rounded-lg px-3 py-1 text-sm",
"disabled:text-gray-500",
animationFilter.value === AssetFilterOption.MINE
? "border-2 border-brand-primary"
: "",
].join(" ")}
className={twMerge(
"filter-tab",
animationFilter.value === AssetFilterOption.MINE ? "active" : "",
"disabled",
)}
onClick={() => (animationFilter.value = AssetFilterOption.MINE)}
disabled={!animationItems.value.some((item) => item.isMine)}
>
My Animations
</button>
<button
className={[
"bg-assets-background rounded-lg px-3 py-1 text-sm",
"disabled:text-gray-500",
className={twMerge(
"filter-tab",
animationFilter.value === AssetFilterOption.BOOKMARKED
? "border-2 border-brand-primary"
? "active"
: "",
].join(" ")}
"disabled",
)}
onClick={() =>
(animationFilter.value = AssetFilterOption.BOOKMARKED)
}
@@ -51,12 +51,16 @@ export const AnimationTab = () => {
</button>
</div>
</div>
<div className="w-full px-2">
<button className="bg-assets-background w-full rounded-lg py-2">
<div className="w-full px-4">
<Button
icon={faCirclePlus}
variant="action"
className="w-full py-3 text-sm font-medium"
>
Upload Animation
</button>
</Button>
</div>
<div className="w-full overflow-y-auto px-2 pt-2">
<div className="h-full w-full overflow-y-auto px-4 pt-4">
<ItemElements
items={animationItems.value}
assetFilter={animationFilter.value}
@@ -1 +1,2 @@
export { AudioTabComponent as AudioTab } from "../AudioTabComponent";
export { AudioTabComponent as AudioTab } from "../AudioTabComponent";
@@ -2,6 +2,9 @@ import { cameraFilter, cameraItems } from "~/pages/PageEnigma/store";
import { AssetFilterOption } from "~/pages/PageEnigma/models";
import { useSignals } from "@preact/signals-react/runtime";
import { ItemElements } from "~/pages/PageEnigma/comps/SidePanelTabs/itemTabs/ItemElements";
import { Button } from "~/components";
import { faCirclePlus } from "@fortawesome/pro-solid-svg-icons";
import { twMerge } from "tailwind-merge";
export const CameraTab = () => {
useSignals();
@@ -9,39 +12,36 @@ export const CameraTab = () => {
return (
<>
<div className="w-full overflow-x-auto">
<div className="mb-4 mt-2 flex justify-start gap-2 px-2">
<div className="mb-4 mt-4 flex justify-start gap-2 px-4">
<button
className={[
"bg-assets-background rounded-lg px-3 py-1 text-sm",
cameraFilter.value === AssetFilterOption.ALL
? "border-2 border-brand-primary"
: "",
].join(" ")}
className={twMerge(
"filter-tab",
cameraFilter.value === AssetFilterOption.ALL ? "active" : "",
"disabled",
)}
onClick={() => (cameraFilter.value = AssetFilterOption.ALL)}
>
All
</button>
<button
className={[
"bg-assets-background text-nowrap rounded-lg px-3 py-1 text-sm",
"disabled:text-gray-500",
cameraFilter.value === AssetFilterOption.MINE
? "border-2 border-brand-primary"
: "",
].join(" ")}
className={twMerge(
"filter-tab",
cameraFilter.value === AssetFilterOption.MINE ? "active" : "",
"disabled",
)}
onClick={() => (cameraFilter.value = AssetFilterOption.MINE)}
disabled={!cameraItems.value.some((item) => item.isMine)}
>
My Cameras
</button>
<button
className={[
"bg-assets-background rounded-lg px-3 py-1 text-sm",
"disabled:text-gray-500",
className={twMerge(
"filter-tab",
cameraFilter.value === AssetFilterOption.BOOKMARKED
? "border-2 border-brand-primary"
? "active"
: "",
].join(" ")}
"disabled",
)}
onClick={() => (cameraFilter.value = AssetFilterOption.BOOKMARKED)}
disabled={!cameraItems.value.some((item) => item.isBookmarked)}
>
@@ -49,12 +49,16 @@ export const CameraTab = () => {
</button>
</div>
</div>
<div className="px-2">
<button className="bg-assets-background w-full rounded-lg py-2">
Create Camera
</button>
<div className="w-full px-4">
<Button
icon={faCirclePlus}
variant="action"
className="w-full py-3 text-sm font-medium"
>
Create Camera Movement
</Button>
</div>
<div className="mt-2 overflow-y-auto px-2 pt-2">
<div className="h-full w-full overflow-y-auto px-4 pt-4">
<ItemElements
items={cameraItems.value}
assetFilter={cameraFilter.value}
@@ -63,11 +63,7 @@ export const ItemElement = ({ item }: Props) => {
);
return (
<div
className="relative rounded-lg"
style={{ width: 91, height: 114 }}
onPointerDown={onPointerDown}
>
<div className="relative w-full rounded-lg" onPointerDown={onPointerDown}>
<img src={item.thumbnail} alt={item.name} className="rounded-t-lg" />
<div
className="w-full rounded-b-lg py-1 text-center text-sm"
@@ -1,5 +1,6 @@
import { ItemElement } from "./ItemElement";
import { AssetFilterOption, MediaItem } from "~/pages/PageEnigma/models";
import { dndSidePanelWidth, sidePanelWidth } from "~/pages/PageEnigma/store";
interface Props {
items: MediaItem[];
@@ -7,6 +8,11 @@ interface Props {
}
export const ItemElements = ({ items, assetFilter }: Props) => {
const displayWidth =
dndSidePanelWidth.value > -1
? dndSidePanelWidth.value
: sidePanelWidth.value;
const displayItems = items.filter((item) => {
if (assetFilter === AssetFilterOption.ALL) {
return true;
@@ -16,8 +22,23 @@ export const ItemElements = ({ items, assetFilter }: Props) => {
}
return item.isBookmarked;
});
function getGridColumnsClass(displayWidth: number): string {
if (displayWidth <= 280) {
return "grid-cols-2";
} else if (displayWidth <= 360) {
return "grid-cols-3";
} else if (displayWidth <= 440) {
return "grid-cols-4";
} else {
return "grid-cols-4";
}
}
const gridColumnsClass = getGridColumnsClass(displayWidth);
return (
<div className="flex flex-wrap gap-3">
<div className={`grid ${gridColumnsClass} gap-3`}>
{displayItems.map((item) => (
<ItemElement key={item.media_id} item={item} />
))}
@@ -7,6 +7,9 @@ import {
objectItems,
} from "~/pages/PageEnigma/store";
import { ItemElements } from "~/pages/PageEnigma/comps/SidePanelTabs/itemTabs/ItemElements";
import { Button } from "~/components";
import { faCirclePlus } from "@fortawesome/pro-solid-svg-icons";
import { twMerge } from "tailwind-merge";
interface Props {
type: AssetType;
@@ -22,39 +25,36 @@ export const ObjectsTab = ({ type }: Props) => {
return (
<>
<div className="w-full overflow-x-auto">
<div className="mb-4 mt-2 flex justify-start gap-2 px-2">
<div className="mb-4 mt-4 flex justify-start gap-2 px-4">
<button
className={[
"bg-assets-background rounded-lg px-3 py-1 text-sm",
assetFilter.value === AssetFilterOption.ALL
? "border-2 border-brand-primary"
: "",
].join(" ")}
className={twMerge(
"filter-tab",
assetFilter.value === AssetFilterOption.ALL ? "active" : "",
"disabled",
)}
onClick={() => (assetFilter.value = AssetFilterOption.ALL)}
>
All
</button>
<button
className={[
"bg-assets-background text-nowrap rounded-lg px-3 py-1 text-sm",
"disabled:text-gray-500",
assetFilter.value === AssetFilterOption.MINE
? "border-2 border-brand-primary"
: "",
].join(" ")}
className={twMerge(
"filter-tab",
assetFilter.value === AssetFilterOption.MINE ? "active" : "",
"disabled",
)}
onClick={() => (assetFilter.value = AssetFilterOption.MINE)}
disabled={!items.value.some((item) => item.isMine)}
>
My {type === AssetType.CHARACTER ? "Characters" : "Objects"}
</button>
<button
className={[
"bg-assets-background rounded-lg px-3 py-1 text-sm",
"disabled:text-gray-500",
className={twMerge(
"filter-tab",
assetFilter.value === AssetFilterOption.BOOKMARKED
? "border-2 border-brand-primary"
? "active"
: "",
].join(" ")}
"disabled",
)}
onClick={() => (assetFilter.value = AssetFilterOption.BOOKMARKED)}
disabled={!items.value.some((item) => item.isBookmarked)}
>
@@ -62,12 +62,16 @@ export const ObjectsTab = ({ type }: Props) => {
</button>
</div>
</div>
<div className="px-2">
<button className="bg-assets-background w-full rounded-lg py-2">
<div className="w-full px-4">
<Button
icon={faCirclePlus}
variant="action"
className="w-full py-3 text-sm font-medium"
>
Upload {type === AssetType.CHARACTER ? "Character" : "Object"}
</button>
</Button>
</div>
<div className="mt-2 overflow-y-auto px-2 pt-2">
<div className="h-full w-full overflow-y-auto px-4 pt-4">
<ItemElements items={items.value} assetFilter={assetFilter.value} />
</div>
</>
@@ -7,8 +7,8 @@ export const ShapeTab = () => {
return (
<>
<div className="p-2 text-base font-bold">Shapes</div>
<div className="mt-2 w-full overflow-y-auto px-2 pt-2">
<div className="px-4 pt-4 text-base font-bold">Shapes</div>
<div className="w-full overflow-y-auto px-4 pt-4">
<ItemElements
items={shapeItems.value}
assetFilter={shapeFilter.value}
@@ -43,9 +43,9 @@ export const tabList = [
component: <AudioTab />,
},
{
icon: "resources/icons/characters.png",
icon: "resources/icons/objects.png",
title: "Style (Temporary being moved)",
value: AssetType.STYLE,
component: <TabStylization/>,
}
component: <TabStylization />,
},
];
@@ -1,26 +1,43 @@
import { fromEngineActions } from "~/pages/PageEnigma/Queue/fromEngineActions";
import { useSignals } from "@preact/signals-react/runtime";
import { useCallback, useContext, useEffect } from "react";
import { currentTime } from "~/pages/PageEnigma/store";
import {
addNewCharacter,
currentTime,
loadAudioData,
loadCameraData,
loadCharacterData,
loadObjectData,
} from "~/pages/PageEnigma/store";
import Queue from "~/pages/PageEnigma/Queue/Queue";
import { QueueNames } from "~/pages/PageEnigma/Queue/QueueNames";
import { toEngineActions } from "~/pages/PageEnigma/Queue/toEngineActions";
import {
ClipGroup,
MediaItem,
QueueClip,
QueueKeyframe,
UpdateTime,
} from "~/pages/PageEnigma/models";
import { TrackContext } from "~/pages/PageEnigma/contexts/TrackContext/TrackContext";
import { toTimelineActions } from "~/pages/PageEnigma/Queue/toTimelineActions";
import { ClipUI } from "~/pages/PageEnigma/datastructures/clips/clip_ui";
interface Arguments {
action: fromEngineActions | toEngineActions | toTimelineActions;
data: QueueClip | UpdateTime | QueueKeyframe;
data: QueueClip | UpdateTime | QueueKeyframe | ClipUI[] | MediaItem;
}
const LOADING_FUNCTIONS: Record<ClipGroup, (item: ClipUI) => void> = {
[ClipGroup.CHARACTER]: loadCharacterData,
[ClipGroup.OBJECT]: loadObjectData,
[ClipGroup.CAMERA]: loadCameraData,
[ClipGroup.GLOBAL_AUDIO]: loadAudioData,
};
export function useQueueHandler() {
useSignals();
const { addKeyframe } = useContext(TrackContext);
const { addKeyframe, clearExistingData } = useContext(TrackContext);
const handleFromEngineActions = useCallback(({ action, data }: Arguments) => {
console.log("FROM ENGINE", action, data);
@@ -29,22 +46,33 @@ export function useQueueHandler() {
currentTime.value = (data as UpdateTime).currentTime;
break;
case fromEngineActions.UPDATE_TIME_LINE:
console.log(action)
clearExistingData();
(data as ClipUI[]).forEach((item) => {
LOADING_FUNCTIONS[item.group](item);
});
break;
case fromEngineActions.UPDATE_CHARACTER_ID: {
addNewCharacter(data as MediaItem);
break;
}
default:
throw new Error(`Unknown action ${action}`);
}
}, []);
const handleToTimelineActions = useCallback(({ action, data }: Arguments) => {
console.log("TO TIMELINE", action, data);
switch (action) {
case toTimelineActions.ADD_KEYFRAME:
addKeyframe(data as QueueKeyframe, currentTime.value);
break;
default:
throw new Error(`Unknown action ${action}`);
}
}, []);
const handleToTimelineActions = useCallback(
({ action, data }: Arguments) => {
console.log("TO TIMELINE", action, data);
switch (action) {
case toTimelineActions.ADD_KEYFRAME:
addKeyframe(data as QueueKeyframe, currentTime.value);
break;
default:
throw new Error(`Unknown action ${action}`);
}
},
[addKeyframe],
);
useEffect(() => {
Queue.subscribe(QueueNames.FROM_ENGINE, handleFromEngineActions);
@@ -6,6 +6,9 @@ export const TrackContext = createContext<{
addKeyframe: (keyframe: QueueKeyframe, offset: number) => void;
deleteKeyframe: (keyframe: Keyframe) => void;
// misc
clearExistingData: () => void;
// drag and drop
startDrag: (item: MediaItem) => void;
endDrag: () => void;
@@ -13,6 +16,8 @@ export const TrackContext = createContext<{
addKeyframe: () => {},
deleteKeyframe: () => {},
clearExistingData: () => {},
startDrag: () => {},
endDrag: () => {},
});
@@ -6,10 +6,16 @@ import {
addCharacterAnimation,
addCharacterAudio,
addGlobalAudio,
addCharacter,
canDrop,
dragItem,
dropId,
dropOffset,
addObject,
characterGroups,
cameraGroup,
audioGroup,
objectGroup,
} from "~/pages/PageEnigma/store";
import useUpdateKeyframe from "~/pages/PageEnigma/contexts/TrackContext/utils/useUpdateKeyframe";
@@ -24,52 +30,75 @@ export const TrackProvider = ({ children }: Props) => {
// cross group functions
const dropClip = useCallback(() => {
if (dragItem.value) {
const mediaItem = dragItem.value;
if (mediaItem.type === AssetType.CHARACTER) {
addCharacter(dragItem.value);
}
// if (dragItem.value.type === AssetType.CAMERA) {
// console.log("Dragged In Camera Type")
// }
if (dragItem.value.type === AssetType.OBJECT) {
addObject(dragItem.value);
}
}
// if (dragItem.value.type === AssetType.SHAPE) {
// console.log("Dragged In Shape Type")
// }
console.log(`${canDrop.value} ${dragItem.value} ${dragItem.value.type}`)
if (canDrop.value && dragItem.value) {
if (dragItem.value.type === AssetType.ANIMATION) {
addCharacterAnimation({
dragItem: dragItem.value!,
dragItem: dragItem.value,
characterId: dropId.value,
offset: dropOffset.value,
});
}
if (dragItem.value.type === AssetType.AUDIO) {
console.log("add audio", dropId.value);
addCharacterAudio({
dragItem: dragItem.value!,
dragItem: dragItem.value,
characterId: dropId.value,
offset: dropOffset.value,
});
addGlobalAudio({
dragItem: dragItem.value!,
dragItem: dragItem.value,
audioId: dropId.value,
offset: dropOffset.value,
});
}
if (dragItem.value.type === AssetType.CHARACTER) {
console.log("Dragged In Character Type")
}
if (dragItem.value.type === AssetType.CAMERA) {
console.log("Dragged In Camera Type")
}
if (dragItem.value.type === AssetType.OBJECT) {
console.log("Dragged In Object Type")
}
if (dragItem.value.type === AssetType.SHAPE) {
console.log("Dragged In Shape Type")
}
}
endDrag();
}, [endDrag]);
const clearExistingData = useCallback(() => {
characterGroups.value = [];
cameraGroup.value = {
id: "CG1",
keyframes: [],
};
audioGroup.value = {
id: "AG-1",
clips: [],
muted: false,
};
objectGroup.value = {
id: "OG1",
objects: [],
};
}, []);
const values = useMemo(() => {
return {
...keyframes,
clearExistingData,
...dragDrop,
endDrag: dropClip,
};
}, [keyframes, dragDrop, dropClip]);
}, [keyframes, dragDrop, dropClip, clearExistingData]);
return (
<TrackContext.Provider value={values}>{children}</TrackContext.Provider>
);
@@ -28,6 +28,7 @@ const DELETE_KEYFRAME: Record<ClipGroup, (keyframe: Keyframe) => void> = {
export default function useUpdateKeyframe() {
const addKeyframe = useCallback((keyframe: QueueKeyframe, offset: number) => {
console.log("k", keyframe);
ADD_KEYFRAME[keyframe.group](keyframe, offset);
}, []);
@@ -9,9 +9,12 @@ export class ClipUI {
name: string;
media_id: string;
object_uuid: string;
object_name: string;
offset: number; // in frames
length: number; // in frames
should_play: boolean;
clip_uuid: string;
keyframe_offset: number;
constructor(
version: number,
@@ -19,21 +22,28 @@ export class ClipUI {
group: ClipGroup,
name: string,
media_id: string,
clip_uuid: string,
object_uuid: string,
object_name: string,
offset: number,
length: number,
keyframe_offset: number = 0,
) {
this.version = version;
this.group = group; // Only needed for UI
this.clip_uuid = clip_uuid;
this.name = name; // UI
this.type = type; // UI and Animation / Audio / Lipsync / : Engine
this.object_uuid = object_uuid; // Animation / Audio / Lipsync / : Engine
this.object_name = object_name;
this.media_id = media_id; // Animation / Audio / Lipsync / : Engine
this.offset = offset;
this.length = length;
this.should_play = true;
this.keyframe_offset = keyframe_offset;
}
toJSON(): any {
@@ -42,10 +52,13 @@ export class ClipUI {
group: this.group,
name: this.name,
type: this.type,
clip_uuid: this.clip_uuid,
object_uuid: this.object_uuid,
object_name: this.object_name,
media_id: this.media_id,
start_offset: this.offset,
ending_offset: this.length,
keyframe_offset: this.keyframe_offset,
};
}
}
+1 -2
View File
@@ -311,7 +311,7 @@ class Editor {
this.timeline.scene = this.activeScene;
this._test_demo();
//this._test_demo();
this.renderer.domElement.addEventListener(
"mousedown",
@@ -1076,7 +1076,6 @@ class Editor {
this.raycaster.setFromCamera(this.mouse, this.camera);
const interactable: any[] = [];
this.activeScene.scene.children.forEach((child: THREE.Object3D) => {
// console.log(child);
if (child.name != "") {
if (child.type == "Mesh" || child.type == "Object3D" || child.type == "Group") {
interactable.push(child);
+64 -95
View File
@@ -42,6 +42,7 @@ export class TimeLine {
animation_engine: AnimationEngine;
// lip sync engine
lipSync_engine: LipSyncEngine;
characters: {[key: string]: string};
scene: Scene;
@@ -55,6 +56,7 @@ export class TimeLine {
scene: Scene,
) {
this.timeline_items = [];
this.characters = {};
this.absolute_end = 60 * 12;
this.timeline_limit = 0; // 5 seconds
@@ -124,11 +126,47 @@ export class TimeLine {
case toEngineActions.UNMUTE:
await this.mute(data, true);
break;
case toEngineActions.ADD_CHARACTER:
await this.addCharacter(data);
break;
case toEngineActions.ADD_OBJECT:
await this.addObject(data);
break;
default:
console.log("Action Not Wired", action);
}
}
public async addCharacter(data: any) {
let media_id = data.data['media_id'];
let name = data.data['name'];
let type = data.data['type'];
let obj = await this.scene.load_glb(media_id);
obj.userData['name'] = name;
obj.name = name;
let object_uuid = obj.uuid;
console.log("data.data", data.data)
this.characters[object_uuid] = type;
data.data['object_uuid'] = object_uuid;
Queue.publish({
queueName: QueueNames.FROM_ENGINE,
action: fromEngineActions.UPDATE_CHARACTER_ID,
data: data.data,
});
}
public async addObject(data: any) {
let media_id = data.data['media_id'];
let name = data.data['name'];
let obj = await this.scene.load_glb(media_id);
obj.userData['name'] = name;
obj.name = name;
}
public async addKeyFrame(data: any) {
// KeyFrame Object
// version: number;
@@ -142,12 +180,21 @@ export class TimeLine {
// selected?: boolean;
let data_json = data['data'];
let uuid = data_json['object_uuid'];
let keyframe_uuid = data_json['keyframe_uuid'];
let object_name = this.scene.get_object_by_uuid(uuid)?.name;
if (object_name == undefined) {
object_name = "undefined"
}
for (let key in this.characters) {
let element = this.characters[key];
if (key == uuid) {
data_json['group'] = element;
break;
}
}
let new_item = this.transform_engine.addFrame(uuid,
this.absolute_end,
data_json['position'],
@@ -162,9 +209,12 @@ export class TimeLine {
data_json['group'],
object_name,
"",
keyframe_uuid,
uuid,
object_name,
0,
this.absolute_end))
this.absolute_end,
data_json['offset']))
}
this.scene.createPoint(data_json['position'], data_json['keyframe_uuid']);
@@ -184,6 +234,13 @@ export class TimeLine {
const type = data["data"]["type"];
const offset = data["data"]["offset"];
const end_offset = data["data"]["length"] + offset;
let object_name = this.scene.get_object_by_uuid(object_uuid)?.name;
const clip_uuid = data["data"]['clip_uuid'];
console.log(data);
if(object_name == undefined) {
object_name = "Undefined."
}
switch (type) {
case "animation":
@@ -199,11 +256,13 @@ export class TimeLine {
this.addPlayableClip(
new ClipUI(
version,
"lipsync",// TODO potiential bug ..
"lipsync", // TODO potiential bug ..
group,
name,
media_id,
clip_uuid,
object_uuid,
object_name,
offset,
end_offset));
return;
@@ -221,7 +280,9 @@ export class TimeLine {
group,
name,
media_id,
clip_uuid,
object_uuid,
object_name,
offset,
end_offset,
),
@@ -286,6 +347,7 @@ export class TimeLine {
}
public async addPlayableClip(clip: ClipUI): Promise<void> {
console.log("clip", clip)
this.timeline_items.push(clip);
}
@@ -449,96 +511,3 @@ export class TimeLine {
}
}
// How much timeline precision we have
//const percision = 100 // using a fake update loop mock
//
//class AudioEngineMock {
// async play(media_id:string) {
// console.log("Audio Playing {media_id}")
// }
//}
//
//// Visual verification tests.
//function CheckIfBasicAudioClipWorks() {
// const timeline = new TimeLine()
// timeline.addPlayableClip(new ClipUI(1.0,'audio',1,0))
// timeline.play()
//}
//
//function CheckIfBasicTransformClipWorks() {
// const timeline = new TimeLine()
// timeline.addPlayableClip(new ClipUI(1.0,'transform',2,0))
// timeline.play()
//}
//
//function CheckIfBasicClipWorks() {
// const timeline = new TimeLine()
// timeline.addPlayableClip(new ClipUI(1.0,'animation',3,0))
// timeline.play()
//}
// function CheckIfBasicClipWorks3SecondsAfterTimelineStops() {
// const timeline = new TimeLine()
// timeline.addPlayableClip(new ClipUI("clip1", 0, 10000))
// timeline.play()
// }
// function CheckIfTwoClipsAtTheSameTimeWorks() {
// const timeline = new TimeLine()
// timeline.addPlayableClip(new ClipUI("clip1", 0, 1000))
// timeline.play()
// }
// function CheckIfTwoClipsOneAfterAnotherWorks() {
// const timeline = new TimeLine()
// timeline.addPlayableClip(new ClipUI("clip1", 0, 1000))
// timeline.addPlayableClip(new ClipUI("clip2", 0, 2000))
// timeline.play()
// }
// function CheckIfTimeLineStopBeforeClipPlays() {
// const timeline = new TimeLine()
// timeline.addPlayableClip(new ClipUI("clip3",0,1000))
// timeline.play()
// setInterval(async ()=> {
// timeline.stop()
// },1100)
// console.log("Stopped")
// }
// function CheckIfTimeLineStartAfterClipPlays() {
// const timeline = new TimeLine()
// timeline.addPlayableClip(new ClipUI("clip3",0,1000))
// timeline.play()
// setInterval(async ()=> {
// timeline.stop()
// },500)
// console.log("Stopped")
// }
// function CheckIfClipsPlayAllTogetherConcurrently() {
// const timeline = new TimeLine()
// timeline.addPlayableClip(new ClipUI("clip1",0,1000))
// timeline.addPlayableClip(new ClipUI("clip2",0,1000))
// timeline.addPlayableClip(new ClipUI("clip3",0,1000))
// timeline.addPlayableClip(new ClipUI("clip4",0,1000))
// timeline.play()
// }
// function CheckIfClipsPlayAllTogether() {
// const timeline = new TimeLine()
// timeline.addPlayableClip(new TrackClip("clip1",0,1000))
// timeline.addPlayableClip(new TrackClip("clip2",0,1000))
// timeline.addPlayableClip(new TrackClip("clip3",0,1000))
// timeline.addPlayableClip(new TrackClip("clip4",0,1000))
// timeline.play()
// }
//CheckIfBasicClipWorks()
//CheckIfTwoClipsAtTheSameTimeWorks()
// CheckIfTwoClipsOneAfterAnotherWorks()
// CheckIfTimeLineStopBeforeClipPlays()
// CheckIfTimeLineStartAfterClipPlays()
// CheckIfClipsPlayAllTogetherConcurrently()
// CheckIfClipsPlayAllTogether()
+3 -2
View File
@@ -2,9 +2,10 @@ export interface MediaItem {
version: number;
type: AssetType;
media_id: string;
object_uuid?: string;
name: string;
length?: number;
thumbnail: string;
thumbnail?: string;
isMine?: boolean;
isBookmarked?: boolean;
}
@@ -16,7 +17,7 @@ export enum AssetType {
ANIMATION = "animation",
CAMERA = "camera",
SHAPE = "shape",
STYLE = "style" // TODO Remove
STYLE = "style", // TODO Remove
}
export enum AssetFilterOption {
+1 -1
View File
@@ -17,7 +17,6 @@ export enum ClipType {
TRANSFORM = "transform",
AUDIO = "audio",
ANIMATION = "animation",
LIPSYNC = "lipsync"
}
export enum ClipGroup {
@@ -41,6 +40,7 @@ export interface Keyframe {
export interface CharacterGroup {
id: string;
name: string;
muted: boolean;
animationClips: Clip[];
positionKeyframes: Keyframe[];
@@ -53,6 +53,8 @@ export class StoryTellerProxyTimeline {
audio: await this.getItemsDict(this.audio_engine.clips),
lipsync: await this.getItemsDict(this.lipsync_engine.clips),
}
console.log(timeline_json)
return timeline_json;
}
@@ -64,8 +66,11 @@ export class StoryTellerProxyTimeline {
let clip = new ClipUI(
element.version, element.type,
element.group, element.name,
element.media_id, element.object_uuid,
element.start_offset, element.ending_offset);
element.media_id, element.clip_uuid,
element.object_uuid,
element.object_name,
element.start_offset, element.ending_offset,
element.keyframe_offset);
new_clips.push(clip);
}
}
@@ -124,6 +129,7 @@ export class StoryTellerProxyTimeline {
this.lipsync_engine.clips = await this.loadLipsyncClips(timeline['lipsync']);
this.animation_engine.clips = await this.loadAnimationClips(timeline['animation']);
console.log(this.timeline.timeline_items)
this.timeline.updateUI()
}
+18
View File
@@ -10,6 +10,7 @@ import { QueueNames } from "~/pages/PageEnigma/Queue/QueueNames";
import { toEngineActions } from "~/pages/PageEnigma/Queue/toEngineActions";
import * as uuid from "uuid";
import { signal } from "@preact/signals-core";
import { ClipUI } from "~/pages/PageEnigma/datastructures/clips/clip_ui";
export const audioGroup = signal<AudioGroup>({
id: "AG-1",
@@ -140,3 +141,20 @@ export function deleteAudioClip(clip: Clip) {
],
};
}
export function loadAudioData(item: ClipUI) {
const existingAudio = audioGroup.value;
const newItem = {
version: item.version,
clip_uuid: item.clip_uuid,
type: item.type,
group: item.group,
object_uuid: item.object_uuid,
media_id: item.media_id,
name: item.name,
offset: item.offset,
length: item.length,
} as Clip;
existingAudio.clips.push(newItem);
existingAudio.clips.sort((clipA, clipB) => clipA.offset - clipB.offset);
}
+16
View File
@@ -8,6 +8,7 @@ import { QueueNames } from "~/pages/PageEnigma/Queue/QueueNames";
import { toEngineActions } from "~/pages/PageEnigma/Queue/toEngineActions";
import * as uuid from "uuid";
import { signal } from "@preact/signals-core";
import { ClipUI } from "~/pages/PageEnigma/datastructures/clips/clip_ui";
// import { toast } from "react-hot-toast";
export const cameraGroup = signal<CameraGroup>({ id: "CG1", keyframes: [] });
@@ -112,3 +113,18 @@ export function deleteCameraKeyframe(deleteKeyframe: Keyframe) {
],
};
}
export function loadCameraData(item: ClipUI) {
const existingCamera = cameraGroup.value;
const newKeyframe = {
version: item.version,
keyframe_uuid: item.clip_uuid,
group: item.group,
object_uuid: item.object_uuid,
offset: item.keyframe_offset,
} as Keyframe;
existingCamera.keyframes.push(newKeyframe);
existingCamera.keyframes.sort(
(keyframeA, keyframeB) => keyframeA.offset - keyframeB.offset,
);
}
@@ -0,0 +1,28 @@
import { CharacterGroup, MediaItem } from "~/pages/PageEnigma/models";
import Queue from "~/pages/PageEnigma/Queue/Queue";
import { QueueNames } from "~/pages/PageEnigma/Queue/QueueNames";
import { toEngineActions } from "~/pages/PageEnigma/Queue/toEngineActions";
import { characterGroups } from "~/pages/PageEnigma/store";
export function addCharacter(character: MediaItem) {
Queue.publish({
queueName: QueueNames.TO_ENGINE,
action: toEngineActions.ADD_CHARACTER,
data: character,
});
}
export function addNewCharacter(data: MediaItem) {
const newCharacter = {
id: data.object_uuid,
name: data.name,
muted: false,
animationClips: [],
positionKeyframes: [],
lipSyncClips: [],
} as CharacterGroup;
characterGroups.value = [...characterGroups.value, newCharacter].sort(
(charA, charB) => (charA.id < charB.id ? -1 : 1),
);
}
@@ -13,104 +13,7 @@ import { QueueNames } from "~/pages/PageEnigma/Queue/QueueNames";
import { toEngineActions } from "~/pages/PageEnigma/Queue/toEngineActions";
import * as uuid from "uuid";
export const characterGroups = signal<CharacterGroup[]>([
{
id: "CH1",
muted: false,
animationClips: [],
positionKeyframes: [],
lipSyncClips: [],
},
]);
export function updateCharacters({
type,
id,
offset,
length,
}: {
type: ClipType;
id: string;
length?: number;
offset: number;
}) {
const oldCharacterGroups = characterGroups.value;
if (type === ClipType.ANIMATION) {
characterGroups.value = oldCharacterGroups.map((character) => {
const newAnimationClips = [...character.animationClips];
const clipIndex = newAnimationClips.findIndex(
(row) => row.clip_uuid === id,
);
if (clipIndex === -1) {
return { ...character };
}
const clip = newAnimationClips[clipIndex];
clip.offset = offset;
clip.length = length!;
Queue.publish({
queueName: QueueNames.TO_ENGINE,
action: toEngineActions.UPDATE_CLIP,
data: clip,
});
return {
...character,
animationClips: newAnimationClips,
};
});
}
if (type === ClipType.TRANSFORM) {
characterGroups.value = oldCharacterGroups.map((character) => {
const newPositionKeyframes = [...character.positionKeyframes];
const keyframeIndex = newPositionKeyframes.findIndex(
(row) => row.keyframe_uuid === id,
);
if (keyframeIndex === -1) {
return { ...character };
}
const keyframe = newPositionKeyframes[keyframeIndex];
keyframe.offset = offset;
Queue.publish({
queueName: QueueNames.TO_ENGINE,
action: toEngineActions.UPDATE_CLIP,
data: keyframe,
});
return {
...character,
positionKeyframes: newPositionKeyframes,
};
});
}
if (type === ClipType.AUDIO) {
characterGroups.value = oldCharacterGroups.map((character) => {
const newLipSyncClips = [...character.lipSyncClips];
const clipIndex = newLipSyncClips.findIndex(
(row) => row.clip_uuid === id,
);
if (clipIndex === -1) {
return { ...character };
}
const clip = newLipSyncClips[clipIndex];
clip.offset = offset;
clip.length = length!;
Queue.publish({
queueName: QueueNames.TO_ENGINE,
action: toEngineActions.UPDATE_CLIP,
data: clip,
});
return {
...character,
lipSyncClips: newLipSyncClips,
};
});
}
}
export const characterGroups = signal<CharacterGroup[]>([]);
export function addCharacterAnimation({
dragItem,
@@ -181,6 +84,13 @@ export function addCharacterAudio({
if (character.id !== characterId) {
return { ...character };
}
Queue.publish({
queueName: QueueNames.TO_ENGINE,
action: toEngineActions.ADD_CLIP,
data: newClip,
});
return {
...character,
lipSyncClips: [...character.lipSyncClips, newClip].sort(
@@ -188,12 +98,6 @@ export function addCharacterAudio({
),
};
});
Queue.publish({
queueName: QueueNames.TO_ENGINE,
action: toEngineActions.ADD_CLIP,
data: newClip,
});
}
export function addCharacterKeyframe(keyframe: QueueKeyframe, offset: number) {
@@ -0,0 +1,4 @@
export * from "./addCharacter";
export * from "./characterGroups";
export * from "./loadCharacterData";
export * from "./updateCharacters";
@@ -0,0 +1,89 @@
import { ClipUI } from "~/pages/PageEnigma/datastructures/clips/clip_ui";
import {
CharacterGroup,
Clip,
ClipType,
Keyframe,
} from "~/pages/PageEnigma/models";
import { characterGroups } from "~/pages/PageEnigma/store";
function getAddCharacter(item: ClipUI) {
const existingCharacter = characterGroups.value.find(
(character) => character.id === item.object_uuid,
);
if (existingCharacter) {
return existingCharacter;
}
const newCharacter = {
id: item.object_uuid,
name: item.object_name,
muted: false,
animationClips: [],
positionKeyframes: [],
lipSyncClips: [],
} as CharacterGroup;
characterGroups.value = [
...characterGroups.value.filter(
(character) => character.id !== item.object_uuid,
),
newCharacter,
].sort((charA, charB) => (charA.id < charB.id ? -1 : 1));
return characterGroups.value.find(
(character) => character.id === item.object_uuid,
) as CharacterGroup;
}
export function loadCharacterData(item: ClipUI) {
const existingCharacter = getAddCharacter(item);
if (item.type === ClipType.ANIMATION) {
const newItem = {
version: item.version,
clip_uuid: item.clip_uuid,
type: item.type,
group: item.group,
object_uuid: item.object_uuid,
media_id: item.media_id,
name: item.name,
offset: item.offset,
length: item.length,
} as Clip;
existingCharacter.animationClips.push(newItem);
existingCharacter.animationClips.sort(
(clipA, clipB) => clipA.offset - clipB.offset,
);
}
if (item.type === ClipType.TRANSFORM) {
const newKeyframe = {
version: item.version,
keyframe_uuid: item.clip_uuid,
group: item.group,
object_uuid: item.object_uuid,
offset: item.keyframe_offset,
} as Keyframe;
existingCharacter.positionKeyframes.push(newKeyframe);
existingCharacter.positionKeyframes.sort(
(keyframeA, keyframeB) => keyframeA.offset - keyframeB.offset,
);
}
if (item.type === ClipType.AUDIO) {
const newItem = {
version: item.version,
clip_uuid: item.clip_uuid,
type: item.type,
group: item.group,
object_uuid: item.object_uuid,
media_id: item.media_id,
name: item.name,
offset: item.offset,
length: item.length,
} as Clip;
existingCharacter.lipSyncClips.push(newItem);
existingCharacter.lipSyncClips.sort(
(clipA, clipB) => clipA.offset - clipB.offset,
);
}
}
@@ -0,0 +1,94 @@
import { ClipType } from "~/pages/PageEnigma/models";
import Queue from "~/pages/PageEnigma/Queue/Queue";
import { QueueNames } from "~/pages/PageEnigma/Queue/QueueNames";
import { toEngineActions } from "~/pages/PageEnigma/Queue/toEngineActions";
import { characterGroups } from "~/pages/PageEnigma/store";
export function updateCharacters({
type,
id,
offset,
length,
}: {
type: ClipType;
id: string;
length?: number;
offset: number;
}) {
const oldCharacterGroups = characterGroups.value;
if (type === ClipType.ANIMATION) {
characterGroups.value = oldCharacterGroups.map((character) => {
const newAnimationClips = [...character.animationClips];
const clipIndex = newAnimationClips.findIndex(
(row) => row.clip_uuid === id,
);
if (clipIndex === -1) {
return { ...character };
}
const clip = newAnimationClips[clipIndex];
clip.offset = offset;
clip.length = length!;
Queue.publish({
queueName: QueueNames.TO_ENGINE,
action: toEngineActions.UPDATE_CLIP,
data: clip,
});
return {
...character,
animationClips: newAnimationClips,
};
});
}
if (type === ClipType.TRANSFORM) {
characterGroups.value = oldCharacterGroups.map((character) => {
const newPositionKeyframes = [...character.positionKeyframes];
const keyframeIndex = newPositionKeyframes.findIndex(
(row) => row.keyframe_uuid === id,
);
if (keyframeIndex === -1) {
return { ...character };
}
const keyframe = newPositionKeyframes[keyframeIndex];
keyframe.offset = offset;
Queue.publish({
queueName: QueueNames.TO_ENGINE,
action: toEngineActions.UPDATE_CLIP,
data: keyframe,
});
return {
...character,
positionKeyframes: newPositionKeyframes,
};
});
}
if (type === ClipType.AUDIO) {
characterGroups.value = oldCharacterGroups.map((character) => {
const newLipSyncClips = [...character.lipSyncClips];
const clipIndex = newLipSyncClips.findIndex(
(row) => row.clip_uuid === id,
);
if (clipIndex === -1) {
return { ...character };
}
const clip = newLipSyncClips[clipIndex];
clip.offset = offset;
clip.length = length!;
Queue.publish({
queueName: QueueNames.TO_ENGINE,
action: toEngineActions.UPDATE_CLIP,
data: clip,
});
return {
...character,
lipSyncClips: newLipSyncClips,
};
});
}
}
+4 -4
View File
@@ -94,7 +94,7 @@ export const cameraItems = signal<MediaItem[]>([
export const characterItems = signal<MediaItem[]>([
{
version: 1,
media_id: "",
media_id: "m_fmxy8wjnep1hdaz7qdg4n7y15d2bsp",
type: AssetType.CHARACTER,
name: "Shrek",
thumbnail: "resources/placeholders/placeholder.png",
@@ -122,7 +122,7 @@ export const characterItems = signal<MediaItem[]>([
},
{
version: 1,
media_id: "m_fmxy8wjnep1hdaz7qdg4n7y15d2bsp ",
media_id: "m_4wva09qznapzk5rcvbxy671d1qx2pr ",
type: AssetType.CHARACTER,
name: "Story Girl",
thumbnail: "resources/characters/img13.png",
@@ -132,9 +132,9 @@ export const characterItems = signal<MediaItem[]>([
export const objectItems = signal<MediaItem[]>([
{
version: 1,
media_id: "m_w5nn3kjh1fbkmjrdac5b2qaba0pmyt",
media_id: "m_0xfrmekc56satjxn66wt6c9dkw7dxe",
type: AssetType.OBJECT,
name: "Item ( Place Holder D.N.W )",
name: "Pikachu Statue",
thumbnail: "resources/placeholders/placeholder.png",
},
]);
@@ -0,0 +1,3 @@
export * from "./loadObjectData";
export * from "./objectGroup";
export * from "./updateObjectGroup";
@@ -0,0 +1,48 @@
import { ClipUI } from "~/pages/PageEnigma/datastructures/clips/clip_ui";
import { Keyframe, ObjectTrack } from "~/pages/PageEnigma/models";
import { objectGroup } from "~/pages/PageEnigma/store";
function getAddObject(item: ClipUI) {
const existingObject = objectGroup.value.objects.find(
(obj) => obj.object_uuid === item.object_uuid,
);
if (existingObject) {
return existingObject;
}
const newObject = {
object_uuid: item.object_uuid,
name: item.object_name,
keyframes: [] as Keyframe[],
} as ObjectTrack;
objectGroup.value = {
id: "OB1",
objects: [
...objectGroup.value.objects.filter(
(obj) => obj.object_uuid !== item.object_uuid,
),
newObject,
].sort((objA, objB) => (objA.object_uuid < objB.object_uuid ? -1 : 1)),
};
return objectGroup.value.objects.find(
(obj) => obj.object_uuid === item.object_uuid,
) as ObjectTrack;
}
export function loadObjectData(item: ClipUI) {
const existingObject = getAddObject(item);
const newKeyframe = {
version: item.version,
keyframe_uuid: item.clip_uuid,
group: item.group,
object_uuid: item.object_uuid,
offset: item.keyframe_offset,
} as Keyframe;
existingObject.keyframes.push(newKeyframe);
existingObject.keyframes.sort(
(keyframeA, keyframeB) => keyframeA.offset - keyframeB.offset,
);
}
@@ -1,13 +1,16 @@
import { signal } from "@preact/signals-core";
import {
Keyframe,
MediaItem,
ObjectGroup,
ObjectTrack,
QueueKeyframe,
} from "~/pages/PageEnigma/models";
import * as uuid from "uuid";
import Queue from "~/pages/PageEnigma/Queue/Queue";
import { QueueNames } from "~/pages/PageEnigma/Queue/QueueNames";
import { toEngineActions } from "~/pages/PageEnigma/Queue/toEngineActions";
import { ClipUI } from "~/pages/PageEnigma/datastructures/clips/clip_ui";
// import { toast } from "react-hot-toast";
export const objectGroup = signal<ObjectGroup>({
@@ -15,61 +18,6 @@ export const objectGroup = signal<ObjectGroup>({
objects: [],
});
export function updateObject({
id,
offset,
}: {
id: string;
offset: number;
}): void {
const oldObjectGroup = objectGroup.value;
const obj = oldObjectGroup.objects.find((objectTrack) =>
objectTrack.keyframes.some((row) => row.keyframe_uuid === id),
);
if (!obj) {
return;
}
const existingKeyframe = obj.keyframes.some((row) => {
return row.offset === offset && row.keyframe_uuid !== id;
});
if (existingKeyframe) {
//toast.error("There can only be one keyframe at this offset.");
return;
}
objectGroup.value = {
id: oldObjectGroup.id,
objects: oldObjectGroup.objects.map((object) => ({
object_uuid: object.object_uuid,
name: object.name,
keyframes: object.keyframes.map((keyframe) => {
if (keyframe.keyframe_uuid !== id) {
return {
...keyframe,
};
}
Queue.publish({
queueName: QueueNames.TO_ENGINE,
action: toEngineActions.UPDATE_KEYFRAME,
data: {
...keyframe,
offset,
},
});
return {
...keyframe,
offset,
};
}),
})),
};
}
export function addObjectKeyframe(keyframe: QueueKeyframe, offset: number) {
const oldObjectGroup = objectGroup.value;
const obj = oldObjectGroup.objects.find(
@@ -166,3 +114,11 @@ export function deleteObjectKeyframe(keyframe: Keyframe) {
],
};
}
export function addObject(object: MediaItem) {
Queue.publish({
queueName: QueueNames.TO_ENGINE,
action: toEngineActions.ADD_OBJECT,
data: object,
});
}
@@ -0,0 +1,59 @@
import Queue from "~/pages/PageEnigma/Queue/Queue";
import { QueueNames } from "~/pages/PageEnigma/Queue/QueueNames";
import { toEngineActions } from "~/pages/PageEnigma/Queue/toEngineActions";
import { objectGroup } from "~/pages/PageEnigma/store";
export function updateObject({
id,
offset,
}: {
id: string;
offset: number;
}): void {
const oldObjectGroup = objectGroup.value;
const obj = oldObjectGroup.objects.find((objectTrack) =>
objectTrack.keyframes.some((row) => row.keyframe_uuid === id),
);
if (!obj) {
return;
}
const existingKeyframe = obj.keyframes.some((row) => {
return row.offset === offset && row.keyframe_uuid !== id;
});
if (existingKeyframe) {
//toast.error("There can only be one keyframe at this offset.");
return;
}
objectGroup.value = {
id: oldObjectGroup.id,
objects: oldObjectGroup.objects.map((object) => ({
object_uuid: object.object_uuid,
name: object.name,
keyframes: object.keyframes.map((keyframe) => {
if (keyframe.keyframe_uuid !== id) {
return {
...keyframe,
};
}
Queue.publish({
queueName: QueueNames.TO_ENGINE,
action: toEngineActions.UPDATE_KEYFRAME,
data: {
...keyframe,
offset,
},
});
return {
...keyframe,
offset,
};
}),
})),
};
}
+14
View File
@@ -1,3 +1,17 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer components {
.filter-tab {
@apply text-nowrap rounded-lg border-2 border-assets-background bg-assets-background px-3 py-2 text-sm font-medium transition-all duration-150 hover:border-assets-background/0 hover:bg-assets-background/80;
}
.filter-tab.active {
@apply border-2 border-brand-primary hover:border-brand-primary hover:bg-assets-background;
}
.filter-tab.disabled {
@apply hover:bg-assets-background disabled:text-white/50;
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 621 B

After

Width:  |  Height:  |  Size: 657 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 562 B

After

Width:  |  Height:  |  Size: 608 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 617 B

After

Width:  |  Height:  |  Size: 666 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 610 B

After

Width:  |  Height:  |  Size: 723 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 610 B

After

Width:  |  Height:  |  Size: 631 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 520 B

After

Width:  |  Height:  |  Size: 542 B

+16
View File
@@ -94,6 +94,22 @@ export default {
"950": "#0c270f",
},
action: {
//comet
DEFAULT: "#55556b", //600
"50": "#f7f7f8",
"100": "#eeedf1",
"200": "#d8d8df",
"300": "#b5b5c4",
"400": "#8d8da3",
"500": "#6f7088",
"600": "#55556b",
"700": "#49495b",
"800": "#3f3f4d",
"900": "#373743",
"950": "#26252c",
},
character: {
unselected: "#46527C",
selected: "#6384F4",