diff --git a/crates/desktop/artcraft/src/core/commands/enqueue/image_to_object/artcraft/handle_artcraft_object.rs b/crates/desktop/artcraft/src/core/commands/enqueue/image_to_object/artcraft/handle_artcraft_object.rs index fefafba42a..7aff545105 100644 --- a/crates/desktop/artcraft/src/core/commands/enqueue/image_to_object/artcraft/handle_artcraft_object.rs +++ b/crates/desktop/artcraft/src/core/commands/enqueue/image_to_object/artcraft/handle_artcraft_object.rs @@ -17,6 +17,8 @@ use log::{error, info}; use storyteller_client::endpoints::generate::object::generate_hunyuan_3d_2_0_image_to_3d::generate_hunyuan3d_2_0_image_to_3d; use storyteller_client::endpoints::generate::object::generate_hunyuan_3d_2_1_image_to_3d::generate_hunyuan3d_2_1_image_to_3d; use tauri::AppHandle; +use artcraft_api_defs::generate::object::multi_function::hunyuan3d_v3_multi_function_object_gen::Hunyuan3dV3MultiFunctionObjectGenRequest; +use storyteller_client::endpoints::generate::object::multi_function::hunyuan3d_v3_multi_function_object_gen::hunyuan3d_v3_multi_function_object_gen; pub async fn handle_artcraft_object( request: &EnqueueImageTo3dObjectRequest, @@ -100,6 +102,37 @@ pub async fn handle_artcraft_object( } } } + Some(EnqueueImageTo3dObjectModel::Hunyuan3d3) => { + info!("enqueue Artcraft Hunyuan 3D 3"); + used_model = Some(GenerationModel::Hunyuan3d3); + let request = Hunyuan3dV3MultiFunctionObjectGenRequest { + uuid_idempotency_token, + image_media_token: request.image_media_token.clone(), + prompt: None, + back_image_media_token: None, + left_image_media_token: None, + right_image_media_token: None, + face_count: None, + generate_type: None, + polygon_type: None, + enable_pbr: None, + }; + let result = hunyuan3d_v3_multi_function_object_gen( + &app_env_configs.storyteller_host, + Some(&creds), + request, + ).await; + match result { + Ok(enqueued) => { + info!("Successfully enqueued Artcraft Hunyuan 3D 3"); + enqueued.inference_job_token + } + Err(err) => { + error!("Failed to use Artcraft Hunyuan 3D 3: {:?}", err); + return Err(GenerateError::from(err)); + } + } + } }; info!("Successfully enqueued 3D generation"); diff --git a/crates/desktop/artcraft/src/core/commands/enqueue/image_to_object/enqueue_image_to_3d_object_command.rs b/crates/desktop/artcraft/src/core/commands/enqueue/image_to_object/enqueue_image_to_3d_object_command.rs index b1c7320003..e9cd6a0609 100644 --- a/crates/desktop/artcraft/src/core/commands/enqueue/image_to_object/enqueue_image_to_3d_object_command.rs +++ b/crates/desktop/artcraft/src/core/commands/enqueue/image_to_object/enqueue_image_to_3d_object_command.rs @@ -55,6 +55,8 @@ pub enum EnqueueImageTo3dObjectModel { Hunyuan3d2_0, #[serde(rename = "hunyuan_3d_2_1")] Hunyuan3d2_1, + #[serde(rename = "hunyuan_3d_3")] + Hunyuan3d3, } #[derive(Serialize)] diff --git a/crates/desktop/artcraft/src/core/commands/enqueue/task_enqueue_success.rs b/crates/desktop/artcraft/src/core/commands/enqueue/task_enqueue_success.rs index c484731afd..e78d8655ef 100644 --- a/crates/desktop/artcraft/src/core/commands/enqueue/task_enqueue_success.rs +++ b/crates/desktop/artcraft/src/core/commands/enqueue/task_enqueue_success.rs @@ -91,6 +91,7 @@ impl TaskEnqueueSuccess{ Some(GenerationModel::Veo3Fast) => Some(TaskModelType::Veo3Fast), Some(GenerationModel::Hunyuan3d2_0) => Some(TaskModelType::Hunyuan3d2_0), Some(GenerationModel::Hunyuan3d2_1) => Some(TaskModelType::Hunyuan3d2_1), + Some(GenerationModel::Hunyuan3d3) => Some(TaskModelType::Hunyuan3d3), Some(GenerationModel::WorldlabsMarble) => Some(TaskModelType::WorldlabsMarble), Some(GenerationModel::Midjourney) => Some(TaskModelType::Midjourney), // NB: This is a generic Midjourney model, version unknown. diff --git a/crates/desktop/artcraft/src/core/events/generation_events/common.rs b/crates/desktop/artcraft/src/core/events/generation_events/common.rs index 4d5ce55528..7cddc3091c 100644 --- a/crates/desktop/artcraft/src/core/events/generation_events/common.rs +++ b/crates/desktop/artcraft/src/core/events/generation_events/common.rs @@ -50,6 +50,8 @@ pub enum GenerationModel { Hunyuan3d2_0, #[serde(rename = "hunyuan_3d_2_1")] Hunyuan3d2_1, + #[serde(rename = "hunyuan_3d_3")] + Hunyuan3d3, #[serde(rename = "worldlabs_marble")] WorldlabsMarble, diff --git a/crates/schema/public/enums/src/tauri/tasks/task_model_type.rs b/crates/schema/public/enums/src/tauri/tasks/task_model_type.rs index 67af56a43d..fd1ecadb58 100644 --- a/crates/schema/public/enums/src/tauri/tasks/task_model_type.rs +++ b/crates/schema/public/enums/src/tauri/tasks/task_model_type.rs @@ -85,6 +85,8 @@ pub enum TaskModelType { Hunyuan3d2_0, #[serde(rename = "hunyuan_3d_2.1")] Hunyuan3d2_1, + #[serde(rename = "hunyuan_3d_3")] + Hunyuan3d3, #[serde(rename = "worldlabs_marble")] WorldlabsMarble, } @@ -134,6 +136,7 @@ impl TaskModelType { // 3D Object generation models Self::Hunyuan3d2_0 => "hunyuan_3d_2.0", Self::Hunyuan3d2_1 => "hunyuan_3d_2.1", + Self::Hunyuan3d3 => "hunyuan_3d_3", Self::WorldlabsMarble => "worldlabs_marble", } } @@ -176,6 +179,7 @@ impl TaskModelType { // 3D Object generation models "hunyuan_3d_2.0" => Ok(Self::Hunyuan3d2_0), "hunyuan_3d_2.1" => Ok(Self::Hunyuan3d2_1), + "hunyuan_3d_3" => Ok(Self::Hunyuan3d3), "worldlabs_marble" => Ok(Self::WorldlabsMarble), _ => Err(EnumError::CouldNotConvertFromString(value.to_string())), } @@ -221,6 +225,7 @@ impl TaskModelType { // 3D Object generation models Self::Hunyuan3d2_0, Self::Hunyuan3d2_1, + Self::Hunyuan3d3, Self::WorldlabsMarble, ]) } @@ -273,6 +278,7 @@ mod tests { // 3D Object generation models assert_serialization(TaskModelType::Hunyuan3d2_0, "hunyuan_3d_2.0"); assert_serialization(TaskModelType::Hunyuan3d2_1, "hunyuan_3d_2.1"); + assert_serialization(TaskModelType::Hunyuan3d3, "hunyuan_3d_3"); assert_serialization(TaskModelType::WorldlabsMarble, "worldlabs_marble"); } @@ -314,6 +320,7 @@ mod tests { // 3D Object generation models assert_eq!(TaskModelType::Hunyuan3d2_0.to_str(), "hunyuan_3d_2.0"); assert_eq!(TaskModelType::Hunyuan3d2_1.to_str(), "hunyuan_3d_2.1"); + assert_eq!(TaskModelType::Hunyuan3d3.to_str(), "hunyuan_3d_3"); assert_eq!(TaskModelType::WorldlabsMarble.to_str(), "worldlabs_marble"); } @@ -355,6 +362,7 @@ mod tests { // 3D Object generation models assert_eq!(TaskModelType::from_str("hunyuan_3d_2.0").unwrap(), TaskModelType::Hunyuan3d2_0); assert_eq!(TaskModelType::from_str("hunyuan_3d_2.1").unwrap(), TaskModelType::Hunyuan3d2_1); + assert_eq!(TaskModelType::from_str("hunyuan_3d_3").unwrap(), TaskModelType::Hunyuan3d3); assert_eq!(TaskModelType::from_str("worldlabs_marble").unwrap(), TaskModelType::WorldlabsMarble); } @@ -372,7 +380,7 @@ mod tests { #[test] fn all_variants() { let mut variants = TaskModelType::all_variants(); - assert_eq!(variants.len(), 34); + assert_eq!(variants.len(), 35); // Image models assert_eq!(variants.pop_first(), Some(TaskModelType::Flux1Dev)); assert_eq!(variants.pop_first(), Some(TaskModelType::Flux1Schnell)); @@ -409,6 +417,7 @@ mod tests { // 3D Object generation models assert_eq!(variants.pop_first(), Some(TaskModelType::Hunyuan3d2_0)); assert_eq!(variants.pop_first(), Some(TaskModelType::Hunyuan3d2_1)); + assert_eq!(variants.pop_first(), Some(TaskModelType::Hunyuan3d3)); assert_eq!(variants.pop_first(), Some(TaskModelType::WorldlabsMarble)); assert_eq!(variants.pop_first(), None); } diff --git a/crates/service/web/storyteller_web/src/http_server/endpoints/generate/object/multi_function/generate_hunyuan3d_v3_multi_function_object_handler.rs b/crates/service/web/storyteller_web/src/http_server/endpoints/generate/object/multi_function/generate_hunyuan3d_v3_multi_function_object_handler.rs index 5df73553d6..6bb0f24360 100644 --- a/crates/service/web/storyteller_web/src/http_server/endpoints/generate/object/multi_function/generate_hunyuan3d_v3_multi_function_object_handler.rs +++ b/crates/service/web/storyteller_web/src/http_server/endpoints/generate/object/multi_function/generate_hunyuan3d_v3_multi_function_object_handler.rs @@ -152,6 +152,9 @@ pub async fn generate_hunyuan3d_v3_multi_function_object_handler( .unwrap_or(false); let has_image = image_url.is_some(); + + // Defaults + let enable_pbr = request.enable_pbr.unwrap_or(true); let fal_result = match (has_prompt, has_image) { // Text-to-3D: prompt only, no image @@ -176,7 +179,7 @@ pub async fn generate_hunyuan3d_v3_multi_function_object_handler( face_count: request.face_count, generate_type, polygon_type, - enable_pbr: request.enable_pbr, + enable_pbr: Some(enable_pbr), webhook_url: &server_state.fal.webhook_url, api_key: &server_state.fal.api_key, }; @@ -215,7 +218,7 @@ pub async fn generate_hunyuan3d_v3_multi_function_object_handler( face_count: request.face_count, generate_type, polygon_type, - enable_pbr: request.enable_pbr, + enable_pbr: Some(enable_pbr), webhook_url: &server_state.fal.webhook_url, api_key: &server_state.fal.api_key, }; @@ -256,7 +259,7 @@ pub async fn generate_hunyuan3d_v3_multi_function_object_handler( face_count: request.face_count, generate_type, polygon_type, - enable_pbr: request.enable_pbr, + enable_pbr: Some(enable_pbr), webhook_url: &server_state.fal.webhook_url, api_key: &server_state.fal.api_key, }; diff --git a/frontend/apps/artcraft/app/src/components/experiences/ImageTo3DExperience.tsx b/frontend/apps/artcraft/app/src/components/experiences/ImageTo3DExperience.tsx index acefc477e6..5e53a8f058 100644 --- a/frontend/apps/artcraft/app/src/components/experiences/ImageTo3DExperience.tsx +++ b/frontend/apps/artcraft/app/src/components/experiences/ImageTo3DExperience.tsx @@ -252,7 +252,7 @@ export const ImageTo3DExperience = ({ variant === "object" ? await EnqueueImageTo3dObject({ image_media_token: uploadedMediaToken || undefined, - model: EnqueueImageTo3dObjectModel.Hunyuan3d2, + model: EnqueueImageTo3dObjectModel.Hunyuan3d3, frontend_caller: "mini_app", frontend_subscriber_id: subscriberId, }) diff --git a/frontend/libs/components/create-3d-modal/src/lib/create-3d-modal.tsx b/frontend/libs/components/create-3d-modal/src/lib/create-3d-modal.tsx index 50099ef5ac..1475d97d5e 100644 --- a/frontend/libs/components/create-3d-modal/src/lib/create-3d-modal.tsx +++ b/frontend/libs/components/create-3d-modal/src/lib/create-3d-modal.tsx @@ -183,7 +183,7 @@ export const Create3dModal = ({ onModelComplete }: Create3dModalProps = {}) => { // Enqueue the image for 3D model creation using the Hunyuan 3D 2 model const result = await EnqueueImageTo3dObject({ image_media_token: mediaToken, - model: EnqueueImageTo3dObjectModel.Hunyuan3d2, + model: EnqueueImageTo3dObjectModel.Hunyuan3d3, frontend_caller: "mini_app", }); diff --git a/frontend/libs/components/lightbox-modal/src/lib/lightbox-modal.tsx b/frontend/libs/components/lightbox-modal/src/lib/lightbox-modal.tsx index bdda31102e..953c9535df 100644 --- a/frontend/libs/components/lightbox-modal/src/lib/lightbox-modal.tsx +++ b/frontend/libs/components/lightbox-modal/src/lib/lightbox-modal.tsx @@ -735,7 +735,7 @@ export function LightboxModal({ gtagEvent("image_to_3d_clicked"); await EnqueueImageTo3dObject({ image_media_token: selectedMediaToken, - model: EnqueueImageTo3dObjectModel.Hunyuan3d2_0, + model: EnqueueImageTo3dObjectModel.Hunyuan3d3, frontend_caller: "mini_app", }); }} diff --git a/frontend/libs/tauri-api/src/lib/enqueue/EnqueueImageTo3dObject.ts b/frontend/libs/tauri-api/src/lib/enqueue/EnqueueImageTo3dObject.ts index 3f93cdafe6..e1775b1e84 100644 --- a/frontend/libs/tauri-api/src/lib/enqueue/EnqueueImageTo3dObject.ts +++ b/frontend/libs/tauri-api/src/lib/enqueue/EnqueueImageTo3dObject.ts @@ -36,6 +36,7 @@ export enum EnqueueImageTo3dObjectModel { Hunyuan3d2 = "hunyuan_3d_2", Hunyuan3d2_0 = "hunyuan_3d_2_0", Hunyuan3d2_1 = "hunyuan_3d_2_1", + Hunyuan3d3 = "hunyuan_3d_3", } export interface EnqueueImageTo3dObjectError extends CommandResult {