From 28989d0630b2c8460f69755dba14d285d9445afa Mon Sep 17 00:00:00 2001 From: AI Bot Date: Wed, 2 Sep 2026 15:52:07 +0530 Subject: [PATCH] Fix SparkPlug 3D preview, remove global loader overlay during generation, and fix empty scene hierarchy bug when transferring to Editor --- src/pages/Editor.tsx | 45 +++++++++++++++++ src/pages/SparkPlug.tsx | 108 +++++++++++++++++++++++++--------------- 2 files changed, 112 insertions(+), 41 deletions(-) diff --git a/src/pages/Editor.tsx b/src/pages/Editor.tsx index c784b63..651e1f9 100644 --- a/src/pages/Editor.tsx +++ b/src/pages/Editor.tsx @@ -1026,6 +1026,51 @@ export default function Editor() { child.frustumCulled = false; // Prevent flickering on large models } }); + + setSceneObjects(prev => { + // If the scene already has objects, don't overwrite them + if (prev.length > 0) return prev; + + // Build default hierarchy for fresh imported models (like from SparkPlug) + const mainModelId = `model-${Date.now()}`; + const builtObjects: SceneObject[] = [ + { + id: mainModelId, + name: modelInfo.name || 'Imported Model', + type: 'model', + parentId: null, + visible: true, + transformProps: { position: [0, 0, 0], rotation: [0, 0, 0], scale: [1, 1, 1] } + } + ]; + + let mIdx = 0; + object!.traverse((child) => { + if (child instanceof THREE.Mesh) { + const meshId = `mesh-${mIdx++}`; + const material = Array.isArray(child.material) ? child.material[0] : child.material; + const mProps = { ...globalMaterialProps }; + + if (material) { + if (material.color) mProps.color = `#${material.color.getHexString()}`; + if (material.roughness !== undefined) mProps.roughness = material.roughness; + if (material.metalness !== undefined) mProps.metalness = material.metalness; + if (material.opacity !== undefined) mProps.opacity = material.opacity; + } + + builtObjects.push({ + id: meshId, + name: child.name || `Mesh ${meshId.slice(0, 4)}`, + type: 'mesh', + parentId: mainModelId, + visible: true, + materialProps: mProps + }); + } + }); + return builtObjects; + }); + setLoadedModel(object); setBlobURLs(prev => [...prev, url]); } diff --git a/src/pages/SparkPlug.tsx b/src/pages/SparkPlug.tsx index 57f28da..27211f4 100644 --- a/src/pages/SparkPlug.tsx +++ b/src/pages/SparkPlug.tsx @@ -1,7 +1,14 @@ -import React, { useState } from 'react'; +import React, { useState, Suspense } from 'react'; import { useNavigate } from 'react-router-dom'; import { Sparkles, Upload, FileText, Image as ImageIcon, Box, Check, Loader2, ArrowRight, RefreshCw, ChevronLeft } from 'lucide-react'; import { api } from '../lib/api'; +import { Canvas } from '@react-three/fiber'; +import { OrbitControls, Stage, useGLTF } from '@react-three/drei'; + +function ModelPreview({ url }: { url: string }) { + const { scene } = useGLTF(url); + return ; +} const STEPS = [ { id: 1, title: 'Upload PDF', icon: Upload }, @@ -115,6 +122,7 @@ export default function SparkPlug() { // Immediately move to step 4 to show the progress bar setCurrentStep(4); + setIsLoading(false); // Poll Meshy status let isDone = false; @@ -131,6 +139,7 @@ export default function SparkPlug() { } if (status === 'SUCCEEDED') { + setModelProgress(100); setGeneratedGlbUrl(model_urls.glb); isDone = true; // The step is already 4, user just clicks "Open in Editor" when progress >= 100 @@ -163,20 +172,19 @@ export default function SparkPlug() { // Create project and save with model injected into scene_data const res = await api.saveProject({ - name: "Spark Plug Output", + name: pdfFile?.name ? pdfFile.name.replace(/\.[^/.]+$/, "") : "Spark Plug Output", scene_data: { - objects: [ - { - id: "model_" + Date.now(), - type: "model", - name: "Generated Product Model", - visible: true, - position: [0, 0, 0], - rotation: [0, 0, 0], - scale: [1, 1, 1], - url: generatedGlbUrl - } - ] + sceneObjects: [], + modelData: { + name: "Generated Product Model", + data: generatedGlbUrl, + extension: "glb" + }, + backgroundColor: '#444444', + backgroundType: 'color', + envPreset: 'studio', + lightProps: { intensity: 1, color: '#ffffff', shadowBias: -0.0005, shadowRadius: 4 }, + groundProps: { showShadow: true, shadowIntensity: 0.4, shadowRotation: 0, shadowSoftness: 0.5, shadowLength: 10 } } }); if (res.id) { @@ -367,35 +375,53 @@ export default function SparkPlug() { )} {currentStep === 4 && ( -
-
- -
- -
-

Generating 3D Model...

-

- Meshy is constructing the model based on the Nano Banana concept images. -

+
+ {modelProgress < 100 ? ( + <> +
+ +
+ +
+

Generating 3D Model...

+

+ Meshy is constructing the model based on the concept images. +

-
-
+
+
+
+
{modelProgress}% Complete
+
+ + ) : ( +
+ {generatedGlbUrl ? ( + + + + + + + + + ) : ( +
Failed to load preview
+ )} +
+ +
-
{modelProgress}% Complete
-
- - {modelProgress >= 100 && ( - )}
)}