mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-05 01:38:44 +00:00
Saving some api manager work and beginning to serialize the timeline
This commit is contained in:
1 parent
2d00e0055a
commit
dcc8bd7194
4 files changed
+126
-106
No files matched your search
@@ -1,107 +1,142 @@
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { Scene } from '../datastructures/scene/scene_object'
|
||||
import * as THREE from 'three'
|
||||
import { GLTFExporter } from 'three/addons/exporters/GLTFExporter.js'
|
||||
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'
|
||||
import { TimelineCurrentReactState } from './timeline'
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { Scene } from "../datastructures/scene/scene_object";
|
||||
import * as THREE from "three";
|
||||
import { GLTFExporter } from "three/addons/exporters/GLTFExporter.js";
|
||||
import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
|
||||
import { TimelineCurrentReactState } from "./timeline";
|
||||
|
||||
// Basically the data will exist on the database
|
||||
// Basically the data will exist on the database
|
||||
class APIManager {
|
||||
baseUrl:String
|
||||
baseUrl: String;
|
||||
|
||||
constructor() {
|
||||
this.baseUrl = "https://api.fakeyou.com"
|
||||
this.baseUrl = "https://api.fakeyou.com";
|
||||
//this.baseUrl = "http://localhost:12345"
|
||||
}
|
||||
|
||||
|
||||
// Function to save or update the scene
|
||||
async saveSceneState(scene:THREE.Scene,
|
||||
timeLine:TimelineCurrentReactState,
|
||||
scene_id:string | null =null):Promise<string> {
|
||||
if (scene_id == null) {
|
||||
// it will always return a boolean success and the media_file_token
|
||||
// they take an optional media_file_token as a parameter for the file to overwrite, which must belong to the user.
|
||||
//If it's an anonymous user, we check the anonymous visitor token/cookie
|
||||
async saveSceneState(
|
||||
scene: THREE.Scene,
|
||||
timeline: TimelineCurrentReactState,
|
||||
media_file_token: string | null = null,
|
||||
): Promise<string> {
|
||||
if (media_file_token == null) {
|
||||
// create new scene.
|
||||
let file = await this.gltfExport(scene)
|
||||
let response = await this.uploadGLB(file)
|
||||
console.log(response)
|
||||
let file = await this.gltfExport(scene);
|
||||
let response = await this.uploadGLB(file);
|
||||
|
||||
let success = response["success"];
|
||||
if (success) {
|
||||
let media_file_token = response["media_file_token"];
|
||||
await this._saveSceneAndTimelineToJSONSpec(media_file_token, timeline);
|
||||
} else {
|
||||
throw Error("Saving the Scene Resulted in an Error Success False");
|
||||
}
|
||||
} else {
|
||||
// update scene or overwrite glb scene.
|
||||
}
|
||||
return ""
|
||||
return "";
|
||||
}
|
||||
|
||||
async _saveSceneAndTimelineToJSONSpec() {
|
||||
|
||||
// which will create or update/replace scene descriptor JSON files
|
||||
async _saveSceneAndTimelineToJSONSpec(
|
||||
media_file_token: string,
|
||||
timeline: TimelineCurrentReactState,
|
||||
): Promise<string> {
|
||||
const url = `${this.baseUrl}/v1/media_files/write/scene_file`;
|
||||
let uuid = uuidv4();
|
||||
return "";
|
||||
}
|
||||
|
||||
async loadScene(scene_token:[]):Promise<string> {
|
||||
|
||||
return ""
|
||||
async loadScene(media_file_token: string): Promise<string> {
|
||||
let json_response = `{
|
||||
|
||||
}`
|
||||
return ""
|
||||
}
|
||||
|
||||
// Provides bucket path to the actual file to download from the media file
|
||||
async getMediaFile(media_file_token: string): Promise<string> {
|
||||
let api_base_url = "https://api.fakeyou.com";
|
||||
let url = `${api_base_url}/v1/media_files/file/${media_file_token}`
|
||||
let response = await fetch(url);
|
||||
let json = await JSON.parse(await response.text());
|
||||
let bucketPath = json["media_file"]["public_bucket_path"];
|
||||
let media_base_url = "https://storage.googleapis.com/vocodes-public"
|
||||
let media_url = `${media_base_url}${bucketPath}` // gets you a bucket path
|
||||
// TODO get it as binary.
|
||||
return media_url
|
||||
}
|
||||
|
||||
async batchGetMedia(media_tokens: []): Promise<string> {
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
async batchMedia(media_tokens:[]):Promise<string> {
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
async gltfExport(scene:THREE.Scene) {
|
||||
let gltfExporter = new GLTFExporter()
|
||||
let uuid = uuidv4()
|
||||
// save the scene to keep ids positions etc
|
||||
let result = await gltfExporter.parseAsync(scene)
|
||||
const file = new File([JSON.stringify(result)], `${uuid}.glb`, {type: 'application/json'})
|
||||
return file
|
||||
async gltfExport(scene: THREE.Scene) {
|
||||
let gltfExporter = new GLTFExporter();
|
||||
let uuid = uuidv4();
|
||||
// save the scene to keep ids positions etc
|
||||
let result = await gltfExporter.parseAsync(scene);
|
||||
const file = new File([JSON.stringify(result)], `${uuid}.glb`, {
|
||||
type: "application/json",
|
||||
});
|
||||
return file;
|
||||
}
|
||||
// two core functions to upload things to the server and get id's
|
||||
async uploadMedia(blob:any, fileName:string):Promise<string> {
|
||||
const url = `${this.baseUrl}/v1/media_uploads/upload`
|
||||
let uuid = uuidv4()
|
||||
const formData = new FormData()
|
||||
formData.append('uuid_idempotency_token', uuid)
|
||||
formData.append('file', blob, fileName)
|
||||
formData.append('source', 'file')
|
||||
formData.append('type', 'video')
|
||||
formData.append('source', 'file')
|
||||
async uploadMedia(blob: any, fileName: string): Promise<string> {
|
||||
const url = `${this.baseUrl}/v1/media_uploads/upload`;
|
||||
let uuid = uuidv4();
|
||||
const formData = new FormData();
|
||||
formData.append("uuid_idempotency_token", uuid);
|
||||
formData.append("file", blob, fileName);
|
||||
formData.append("source", "file");
|
||||
formData.append("type", "video");
|
||||
formData.append("source", "file");
|
||||
const response = await fetch(url, {
|
||||
method: 'POST',
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
headers: {
|
||||
"Accept": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
body: formData,
|
||||
})
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to Send Data')
|
||||
throw new Error("Failed to Send Data");
|
||||
}
|
||||
|
||||
return response.json() // or handle the response as appropriate
|
||||
return response.json(); // or handle the response as appropriate
|
||||
}
|
||||
|
||||
// to save to a file then upload the scene.
|
||||
async uploadGLB(file:File):Promise<string> {
|
||||
const url = `${this.baseUrl}/v1/media_files/upload/engine_asset`
|
||||
let uuid = uuidv4()
|
||||
const formData = new FormData()
|
||||
formData.append('uuid_idempotency_token', uuid)
|
||||
formData.append('file', file)
|
||||
formData.append('source','file')
|
||||
formData.append('media_file_subtype', 'scene_import')
|
||||
formData.append('media_file_class', 'scene')
|
||||
async uploadGLB(file: File): Promise<string> {
|
||||
// /v1/media_files/write/engine_asset REPLACE with this api endpoint
|
||||
const url = `${this.baseUrl}/v1/media_files/upload/engine_asset`;
|
||||
let uuid = uuidv4();
|
||||
const formData = new FormData();
|
||||
formData.append("uuid_idempotency_token", uuid);
|
||||
formData.append("file", file);
|
||||
formData.append("source", "file");
|
||||
formData.append("media_file_subtype", "scene_import");
|
||||
formData.append("media_file_class", "scene");
|
||||
const response = await fetch(url, {
|
||||
method: 'POST',
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
headers: {
|
||||
"Accept": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
body: formData,
|
||||
})
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to Send Data')
|
||||
throw new Error("Failed to Send Data");
|
||||
}
|
||||
return response.json() // or handle the response as appropriate
|
||||
return response.json(); // or handle the response as appropriate
|
||||
}
|
||||
}
|
||||
|
||||
export default APIManager
|
||||
export default APIManager;
|
||||
@@ -1,34 +0,0 @@
|
||||
import * as THREE from "three";
|
||||
|
||||
class AudioClip {
|
||||
|
||||
constructor(listener, audioURL) {
|
||||
this.audioLoader = new THREE.AudioLoader();
|
||||
this.sound = new THREE.Audio(listener);
|
||||
this.audioURL = audioURL;
|
||||
this.isLoaded = false;
|
||||
|
||||
// Load the audio file asynchronously
|
||||
this.audioLoader.load(audioURL, (buffer) => {
|
||||
this.sound.setBuffer(buffer);
|
||||
this.isLoaded = true;
|
||||
});
|
||||
}
|
||||
|
||||
play() {
|
||||
if (this.isLoaded) {
|
||||
this.sound.play();
|
||||
this.sound.setLoop(true);
|
||||
this.sound.setVolume(1);
|
||||
this.sound.play();
|
||||
} else {
|
||||
console.warn(`AudioClip: Sound not loaded yet (${this.audioURL}).`);
|
||||
}
|
||||
}
|
||||
|
||||
stop() {
|
||||
if (this.isLoaded) {
|
||||
this.sound.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -165,10 +165,30 @@ class Editor {
|
||||
|
||||
// This is the default scene.
|
||||
// "m_189p8hj0eyypbg74kkhcpehwpjhnkz" scene with the fox.
|
||||
let result = await this.api_manager.saveSceneState(this.activeScene.scene)
|
||||
//let result = await this.api_manager.saveSceneState(this.activeScene.scene)
|
||||
let result = await this.api_manager.getMediaFile("m_189p8hj0eyypbg74kkhcpehwpjhnkz")
|
||||
console.log(result)
|
||||
}
|
||||
|
||||
async _serialize_timeline() {
|
||||
// note the database from the server is the source of truth for all the data.
|
||||
// Test code here
|
||||
let object = await this.activeScene.load_glb("./resources/models/fox/fox.glb")
|
||||
|
||||
// load object into the engine for lip syncing
|
||||
this.lipsync_engine.load_object(object.uuid, "m_f1jxx4zwy4da2zn0cvdqhha7kqkj72");
|
||||
|
||||
// create the clip with the same id for a reference to the media
|
||||
this.timeline.addPlayableClip(new ClipUI(1.0, "lipsync", "clip1", "m_f1jxx4zwy4da2zn0cvdqhha7kqkj72", object.uuid, 150, 400));
|
||||
|
||||
// media id for this is up in the air but when a path is created you should be able to store and delete it
|
||||
this.timeline.addPlayableClip(new ClipUI(1.0, "transform", "clip2", object.uuid, object.uuid, 0, 150));
|
||||
|
||||
// media id for this as well it can be downloaded
|
||||
this.timeline.addPlayableClip(new ClipUI(1.0, "animation", "clip3", "/resources/models/fox/fox_idle.glb", object.uuid, 0, 400));
|
||||
this.animation_engine.load_object(object.uuid, "/resources/models/fox/fox_idle.glb", "clip3");
|
||||
}
|
||||
|
||||
async _test_demo() {
|
||||
// note the database from the server is the source of truth for all the data.
|
||||
// Test code here
|
||||
@@ -283,7 +303,6 @@ class Editor {
|
||||
//this.transform_engine.clips[this.test_box_uuid].reset(this.activeScene.get_object_by_uuid(this.test_box_uuid));
|
||||
}
|
||||
|
||||
|
||||
// Basicly Unity 3D's update loop.
|
||||
update_loop(time) {
|
||||
// Updates debug stats.
|
||||
@@ -307,10 +326,6 @@ class Editor {
|
||||
this.timeline.isPlaying = true;
|
||||
}
|
||||
|
||||
// _save_to_cloud(blob) {
|
||||
// this.api_manager.uploadGLB(blob, "test.glb");
|
||||
// }
|
||||
|
||||
change_mode(type) {
|
||||
this.control.mode = type;
|
||||
this.transform_interaction = true;
|
||||
|
||||
@@ -9,7 +9,7 @@ import AnimationEngine from "./animation_engine";
|
||||
|
||||
// Every object uuid / entity has a track.
|
||||
export class TimelineCurrentReactState {
|
||||
isEditable: boolean
|
||||
isEditable: boolean
|
||||
selectedObjectID: number
|
||||
|
||||
constructor() {
|
||||
@@ -19,6 +19,8 @@ export class TimelineCurrentReactState {
|
||||
}
|
||||
|
||||
export class TimeLine {
|
||||
|
||||
|
||||
timelineItems: ClipUI[]
|
||||
runningClips: ClipUI[]
|
||||
|
||||
@@ -45,6 +47,7 @@ export class TimeLine {
|
||||
lipsyncEngine: LipSyncEngine,
|
||||
animationEngine: AnimationEngine,
|
||||
scene: Scene) {
|
||||
|
||||
this.timelineItems = []
|
||||
this.timeLineLimit = 60 * 10 // 10 seconds
|
||||
this.runningClips = []
|
||||
@@ -67,8 +70,9 @@ export class TimeLine {
|
||||
|
||||
// when given a media id item it will create the clip.
|
||||
// Then the clip will be loaded by the engines, if they come from outside of the loaded scene.
|
||||
async createClipOffset(media_id: string, type: string): Promise<void> {
|
||||
async createClipOffset(media_id: string, object_uuid:string, type: string): Promise<void> {
|
||||
// use engine to load based off media id and type animation | transform |
|
||||
|
||||
}
|
||||
|
||||
// this will update the state of the clips based off uuid easing?
|
||||
|
||||
Reference in new issue
Block a user