diff --git a/src/components/three/ModelViewer.tsx b/src/components/three/ModelViewer.tsx index e1fbb1a..42cb13b 100644 --- a/src/components/three/ModelViewer.tsx +++ b/src/components/three/ModelViewer.tsx @@ -11,7 +11,7 @@ import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'; import { mainCameraRef, mainControlsRef, viewAnim, calibration, pushModelFaceNormal, computeCalibrationQuaternion, subscribeCalibration } from './viewCubeBus'; import { toast } from 'sonner'; -function getColorForMaterialName(name: string): string { +export function getColorForMaterialName(name: string): string { if (!name) return '#a1a1aa'; let hash = 0; for (let i = 0; i < name.length; i++) { @@ -208,7 +208,7 @@ function GLBModel({ sceneModel, isActive }: { sceneModel: SceneModel; isActive: const setActive = useModelStore((s) => s.setActiveModel); const selectionMode = useModelStore((s) => s.selectionMode); const measureMode = useModelStore((s) => s.measureMode); - const ifcMaterialColors = useModelStore((s) => s.ifcMaterialColors); + const ifcColorMode = useModelStore((s) => s.ifcColorMode); const fineTuning = sceneModel.fineTuning; // Re-render when calibration state changes (so calQuat resets to identity during the flow). @@ -277,8 +277,11 @@ function GLBModel({ sceneModel, isActive }: { sceneModel: SceneModel; isActive: } else { const root = findElementRoot(child); const matName = root?.userData?.materialName; - if (ifcMaterialColors && matName) { + const descName = root?.userData?.properties?.description || root?.userData?.description; + if (ifcColorMode === 'material' && matName) { mat.color.set(getColorForMaterialName(matName)); + } else if (ifcColorMode === 'description' && descName) { + mat.color.set(getColorForMaterialName(descName)); } else { // Tint with the per-model color (subtle) mat.color.set(sceneModel.color); @@ -304,7 +307,7 @@ function GLBModel({ sceneModel, isActive }: { sceneModel: SceneModel; isActive: } } }); - }, [scene, opacity, renderMode, checklist, wireframeColor, wireframeThickness, edgeThresholdAngle, sceneModel.color, measureMode, ifcMaterialColors]); + }, [scene, opacity, renderMode, checklist, wireframeColor, wireframeThickness, edgeThresholdAngle, sceneModel.color, measureMode, ifcColorMode]); const rotXRad = (fineTuning.rotX * Math.PI) / 180; const rotYRad = (fineTuning.rotY * Math.PI) / 180; diff --git a/src/pages/Index.tsx b/src/pages/Index.tsx index 3ede712..5dd701a 100644 --- a/src/pages/Index.tsx +++ b/src/pages/Index.tsx @@ -24,7 +24,7 @@ const Index = () => { const [showLanding, setShowLanding] = useState(false); const [converting, setConverting] = useState(false); - const { model, addModel, models, maxModels, xrSupported, setXrSupported, ifcMaterialColors, setIfcMaterialColors } = useModelStore(); + const { model, addModel, models, maxModels, xrSupported, setXrSupported, ifcColorMode, setIfcColorMode } = useModelStore(); // --- Painel de Logs Remotos em Tempo Real --- const [debugLogs, setDebugLogs] = useState<{ level: 'info' | 'warn' | 'error'; message: string; timestamp: string; data?: unknown }[]>([]); @@ -265,21 +265,48 @@ const Index = () => { /> - {/* Toggle IFC Material Colors */} -
+ {/* Escolha do Filtro de Cores (IFC) */} +
-
- +
+ + + +
{/* Demo buttons */} diff --git a/src/pages/XRSession.tsx b/src/pages/XRSession.tsx index e97185c..0c14e66 100644 --- a/src/pages/XRSession.tsx +++ b/src/pages/XRSession.tsx @@ -23,7 +23,7 @@ import { ControllerLocomotion } from '@/components/three/ControllerLocomotion'; import { useDevKit } from '@/devkit/useDevKit'; import { DevPanel } from '@/devkit/DevPanel'; import { FakeControllers } from '@/devkit/FakeControllers'; -import { VisibilityApplier } from '@/components/three/ModelViewer'; +import { VisibilityApplier, findElementRoot, getColorForMaterialName } from '@/components/three/ModelViewer'; import { registerModelLocalGroup, unregisterModelLocalGroup } from '@/lib/modelTransforms'; import { parseIFCtoThree } from '@/lib/convertIFC'; import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'; @@ -104,6 +104,7 @@ function XRModel({ sceneModel }: { sceneModel: import('@/stores/useModelStore'). const edgeThresholdAngle = useModelStore((s) => s.edgeThresholdAngle); const checklist = useModelStore((s) => s.checklist); const measureMode = useModelStore((s) => s.measureMode); + const ifcColorMode = useModelStore((s) => s.ifcColorMode); const modelInfo = useMemo(() => { if (!scene) return { size: new THREE.Vector3(), center: new THREE.Vector3() }; @@ -159,7 +160,16 @@ function XRModel({ sceneModel }: { sceneModel: import('@/stores/useModelStore'). } else if (allApproved) { mat.color.setHSL(0.38, 0.7, 0.45); } else { - mat.color.set(sceneModel.color); + const root = findElementRoot(child); + const matName = root?.userData?.materialName; + const descName = root?.userData?.properties?.description || root?.userData?.description; + if (ifcColorMode === 'material' && matName) { + mat.color.set(getColorForMaterialName(matName)); + } else if (ifcColorMode === 'description' && descName) { + mat.color.set(getColorForMaterialName(descName)); + } else { + mat.color.set(sceneModel.color); + } } } mat.needsUpdate = true; @@ -181,7 +191,7 @@ function XRModel({ sceneModel }: { sceneModel: import('@/stores/useModelStore'). } } }); - }, [scene, opacity, renderMode, checklist, wireframeColor, wireframeThickness, edgeThresholdAngle, sceneModel.color, measureMode]); + }, [scene, opacity, renderMode, checklist, wireframeColor, wireframeThickness, edgeThresholdAngle, sceneModel.color, measureMode, ifcColorMode]); const rotXRad = (fineTuning.rotX * Math.PI) / 180; const rotYRad = (fineTuning.rotY * Math.PI) / 180; diff --git a/src/stores/useModelStore.ts b/src/stores/useModelStore.ts index edd9d27..1f2988a 100644 --- a/src/stores/useModelStore.ts +++ b/src/stores/useModelStore.ts @@ -95,6 +95,9 @@ function loadVisibility(fileName: string): { hidden: Set; isolated: Set< } const IFC_MAT_COLORS_KEY = 'tsxr_ifc_mat_colors_v1'; +const IFC_COLOR_MODE_KEY = 'tsxr_ifc_color_mode_v1'; +export type IFCColorMode = 'none' | 'material' | 'description'; + function loadIfcMaterialColors(): boolean { try { const raw = localStorage.getItem(IFC_MAT_COLORS_KEY); @@ -106,6 +109,21 @@ function saveIfcMaterialColors(enabled: boolean) { try { localStorage.setItem(IFC_MAT_COLORS_KEY, String(enabled)); } catch (e) { /* ignore */ } } +function loadIfcColorMode(): IFCColorMode { + try { + const raw = localStorage.getItem(IFC_COLOR_MODE_KEY); + if (raw === 'none' || raw === 'material' || raw === 'description') { + return raw as IFCColorMode; + } + const legacy = localStorage.getItem(IFC_MAT_COLORS_KEY); + if (legacy === 'false') return 'none'; + } catch (e) { /* ignore */ } + return 'material'; +} +function saveIfcColorMode(mode: IFCColorMode) { + try { localStorage.setItem(IFC_COLOR_MODE_KEY, mode); } catch (e) { /* ignore */ } +} + export interface ScaleRatio { @@ -372,6 +390,8 @@ interface ModelStore { ifcMaterialColors: boolean; setIfcMaterialColors: (enabled: boolean) => void; + ifcColorMode: IFCColorMode; + setIfcColorMode: (mode: IFCColorMode) => void; xrRig: THREE.Group | null; setXRRig: (rig: THREE.Group | null) => void; @@ -815,7 +835,18 @@ export const useModelStore = create((set, get) => ({ ifcMaterialColors: loadIfcMaterialColors(), setIfcMaterialColors: (enabled: boolean) => { saveIfcMaterialColors(enabled); - set({ ifcMaterialColors: enabled }); + set((state) => { + const mode = enabled ? 'material' : 'none'; + saveIfcColorMode(mode); + return { ifcMaterialColors: enabled, ifcColorMode: mode }; + }); + }, + ifcColorMode: loadIfcColorMode(), + setIfcColorMode: (mode: IFCColorMode) => { + saveIfcColorMode(mode); + const enabled = mode === 'material'; + saveIfcMaterialColors(enabled); + set({ ifcColorMode: mode, ifcMaterialColors: enabled }); }, }));