Spaces:
Sleeping
Sleeping
| 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 }), | |
| })); |