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