studio3d / src /store /useStudioStore.ts
varunm2004's picture
Update src/store/useStudioStore.ts
f6cb498 verified
import { create } from 'zustand';
import { Track, Keyframe } from '../utils/timeline';
export type Mode = 'model' | 'render' | 'animate';
export type SkyboxType = 'none' | 'gradient' | 'uploaded';
export type GizmoMode = 'translate' | 'rotate' | 'scale';
export interface AnimationClip {
name: string;
duration: number;
index: number;
}
export interface SceneObject {
id: string;
name: string;
url: string;
file: File;
position: [number, number, number];
rotation: [number, number, number];
scale: [number, number, number];
color: string;
metalness: number;
roughness: number;
textureUrl?: string;
envMapIntensity: number;
animations: AnimationClip[];
selectedAnimIndex: number;
animPlaying: boolean;
animSpeed: number;
animLoop: boolean;
}
export interface ErrorLog {
id: string;
message: string;
timestamp: number;
resolved?: string;
resolving?: boolean;
}
interface StudioStore {
// โ”€โ”€ Mode โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
mode: Mode;
setMode: (mode: Mode) => void;
// โ”€โ”€ Scene objects โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
objects: SceneObject[];
selectedId: string | null;
addObject: (obj: SceneObject) => void;
removeObject: (id: string) => void;
updateObject: (id: string, patch: Partial<SceneObject>) => void;
setSelectedId: (id: string | null) => void;
// โ”€โ”€ Gizmo โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
gizmoMode: GizmoMode;
setGizmoMode: (m: GizmoMode) => void;
// โ”€โ”€ Timeline / keyframes โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
tracks: Track[];
timelineDuration: number;
playhead: number;
timelinePlaying: boolean;
setPlayhead: (t: number) => void;
setTimelinePlaying: (v: boolean) => void;
setTimelineDuration: (d: number) => void;
addKeyframe: (objectId: string, kf: Keyframe) => void;
removeKeyframe: (objectId: string, kfId: string) => void;
getTrack: (objectId: string) => Track | undefined;
// โ”€โ”€ Skybox โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
skyboxType: SkyboxType;
skyboxUrl: string | null;
setSkybox: (type: SkyboxType, url?: string) => void;
// โ”€โ”€ Lighting / scene โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
ambientIntensity: number;
directionalIntensity: number;
bgColor: string;
showGrid: boolean;
showAxes: boolean;
postProcessing: boolean;
bloomIntensity: number;
setAmbientIntensity: (v: number) => void;
setDirectionalIntensity: (v: number) => void;
setBgColor: (c: string) => void;
setShowGrid: (v: boolean) => void;
setShowAxes: (v: boolean) => void;
setPostProcessing: (v: boolean) => void;
setBloomIntensity: (v: number) => void;
// โ”€โ”€ AI Error Resolver โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
openrouterKey: string;
setOpenrouterKey: (k: string) => void;
errorLogs: ErrorLog[];
addError: (msg: string) => void;
resolveError: (id: string, resolution: string) => void;
setErrorResolving: (id: string, v: boolean) => void;
clearErrors: () => void;
// โ”€โ”€ Recording โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
isRecording: boolean;
setIsRecording: (v: boolean) => void;
}
export const useStudioStore = create<StudioStore>((set, get) => ({
// โ”€โ”€ Mode โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
mode: 'model',
setMode: (mode) => set({ mode }),
// โ”€โ”€ Scene objects โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
objects: [],
selectedId: null,
addObject: (obj) => set((s) => ({ objects: [...s.objects, obj], selectedId: obj.id })),
removeObject: (id) => set((s) => ({
objects: s.objects.filter(o => o.id !== id),
selectedId: s.selectedId === id ? null : s.selectedId,
tracks: s.tracks.filter(t => t.objectId !== id),
})),
updateObject: (id, patch) => set((s) => ({
objects: s.objects.map(o => o.id === id ? { ...o, ...patch } : o),
})),
setSelectedId: (id) => set({ selectedId: id }),
// โ”€โ”€ Gizmo โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
gizmoMode: 'translate',
setGizmoMode: (m) => set({ gizmoMode: m }),
// โ”€โ”€ Timeline โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
tracks: [],
timelineDuration: 5,
playhead: 0,
timelinePlaying: false,
setPlayhead: (t) => set({ playhead: t }),
setTimelinePlaying: (v) => set({ timelinePlaying: v }),
setTimelineDuration:(d) => set({ timelineDuration: d }),
addKeyframe: (objectId, kf) => set((s) => {
const exists = s.tracks.find(t => t.objectId === objectId);
if (exists) {
return {
tracks: s.tracks.map(t =>
t.objectId === objectId
? { ...t, keyframes: [...t.keyframes.filter(k => Math.abs(k.time - kf.time) > 0.05), kf] }
: t
),
};
}
return { tracks: [...s.tracks, { objectId, keyframes: [kf] }] };
}),
removeKeyframe: (objectId, kfId) => set((s) => ({
tracks: s.tracks.map(t =>
t.objectId === objectId
? { ...t, keyframes: t.keyframes.filter(k => k.id !== kfId) }
: t
),
})),
getTrack: (objectId) => get().tracks.find(t => t.objectId === objectId),
// โ”€โ”€ Skybox โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
skyboxType: 'gradient',
skyboxUrl: null,
setSkybox: (type, url) => set({ skyboxType: type, skyboxUrl: url || null }),
// โ”€โ”€ Lighting โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
ambientIntensity: 0.5,
directionalIntensity: 1.0,
bgColor: '#1a1a2e',
showGrid: true,
showAxes: true,
postProcessing: false,
bloomIntensity: 0.5,
setAmbientIntensity: (v) => set({ ambientIntensity: v }),
setDirectionalIntensity:(v) => set({ directionalIntensity: v }),
setBgColor: (c) => set({ bgColor: c }),
setShowGrid: (v) => set({ showGrid: v }),
setShowAxes: (v) => set({ showAxes: v }),
setPostProcessing:(v) => set({ postProcessing: v }),
setBloomIntensity:(v) => set({ bloomIntensity: v }),
// โ”€โ”€ AI Error Resolver โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
openrouterKey: '',
setOpenrouterKey: (k) => set({ openrouterKey: k }),
errorLogs: [],
addError: (msg) => set((s) => ({
errorLogs: [...s.errorLogs, {
id: Math.random().toString(36).slice(2),
message: msg,
timestamp: Date.now(),
}],
})),
resolveError: (id, resolution) => set((s) => ({
errorLogs: s.errorLogs.map(e => e.id === id ? { ...e, resolved: resolution, resolving: false } : e),
})),
setErrorResolving: (id, v) => set((s) => ({
errorLogs: s.errorLogs.map(e => e.id === id ? { ...e, resolving: v } : e),
})),
clearErrors: () => set({ errorLogs: [] }),
// โ”€โ”€ Recording โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
isRecording: false,
setIsRecording: (v) => set({ isRecording: v }),
}));