feat: implement duplicate scene functionality with builder, controls, and post-processing components

This commit is contained in:
Jerald-Golden-B 2025-06-13 15:22:03 +05:30
parent ae20cf1437
commit d0538ccfae
18 changed files with 775 additions and 16 deletions

View File

@ -82,7 +82,7 @@ function ComparisonScene() {
{comparisonProduct && !isPlaying &&
<div className="initial-selectLayout-wrapper">
<RegularDropDown
header={selectedProduct.productName}
header={'Product 1'}
options={products.map((l) => l.productName)} // Pass layout names as options
onSelect={handleSelectLayout}
search={false}

View File

@ -14,10 +14,10 @@ import { useProductStore } from "../../../store/simulation/useProductStore";
import Scene from "../../../modules/scene/scene";
import { useComparisonProduct } from "../../../store/simulation/useSimulationStore";
import { usePauseButtonStore, usePlayButtonStore } from "../../../store/usePlayButtonStore";
import DuplicateScene from "../../../modules/duplicate/sceneDuplicate";
const CompareLayOut = () => {
const { comparisonProduct, setComparisonProduct, clearComparisonProduct } =
useComparisonProduct();
const { comparisonProduct, setComparisonProduct, clearComparisonProduct } = useComparisonProduct();
const { products } = useProductStore();
const { setLoadingProgress } = useLoadingProgress();
const [width, setWidth] = useState("50vw");
@ -127,7 +127,7 @@ const CompareLayOut = () => {
ref={wrapperRef}
style={{ width }}
>
{loadingProgress == 0 && (
{loadingProgress == 0 && comparisonProduct && (
<button
title="resize-canvas"
id="compare-resize-slider-btn"
@ -141,7 +141,8 @@ const CompareLayOut = () => {
{comparisonProduct && (
<div className="compare-layout-canvas-container">
<Suspense fallback={null}>
<Scene layout="Comparison Layout" />
{/* <Scene layout="Comparison Layout" /> */}
<DuplicateScene />
</Suspense>
</div>
)}
@ -163,7 +164,7 @@ const CompareLayOut = () => {
{showLayoutDropdown && (
<div className="displayLayouts-container">
<div className="header">Layouts</div>
<Search onChange={() => {}} />
<Search onChange={() => { }} />
<div className="layouts-container">
{products.map((layout) => (
<button

View File

@ -26,8 +26,6 @@ function AisleInstances() {
return points;
}, [aisles]);
return (
<>
{toggleView &&

View File

@ -21,7 +21,7 @@ const WallsMeshComponent = ({ lines }: any) => {
const email = localStorage.getItem("email");
const organization = email!.split("@")[1].split(".")[0];
getLines(organization,projectId).then((data) => {
getLines(organization, projectId).then((data) => {
const Lines: Types.Lines = objectLinesToArray(data);
localStorage.setItem("Lines", JSON.stringify(Lines));

View File

@ -0,0 +1,48 @@
////////// Three and React Three Fiber Imports //////////
import * as THREE from "three";
import { useRef } from "react";
import * as Types from "../../../types/world/worldTypes";
import Ground from "../../scene/environment/ground";
import { Bvh } from "@react-three/drei";
import AssetsGroupDuplicate from "./duplicateAsset/assetsGroupDuplicate";
import FloorGroupDuplicate from "./duplicateFloor/floorGroupDuplicate";
import WallsDuplicate from "./duplicateWall/wallsDuplicate";
import ZoneDuplicate from "./duplicateZone/zoneDuplicate";
import AislesDuplicate from "./duplicateAisle/aislesDuplicate";
export default function BuilderDuplicate({ projectId }: { projectId: string }) {
const plane = useRef<THREE.Mesh>(null); // Reference for a plane object for raycaster reference.
const grid = useRef() as any; // Reference for a grid object for raycaster reference.
const lines = useRef([]) as Types.RefLines; // Reference for lines which stores all the lines that are ever drawn.
const floorGroup = useRef() as Types.RefGroup; // Reference to the THREE.Group that has the roofs and the floors.
////////// Return //////////
return (
<>
<Ground grid={grid} plane={plane} />
<WallsDuplicate
lines={lines}
projectId={projectId}
/>
<Bvh firstHitOnly>
<FloorGroupDuplicate
floorGroup={floorGroup}
lines={lines}
projectId={projectId}
/>
<ZoneDuplicate projectId={projectId} />
<AssetsGroupDuplicate projectId={projectId} />
<AislesDuplicate projectId={projectId} />
</Bvh>
</>
);
}

View File

@ -0,0 +1,27 @@
import { useEffect, useState } from 'react'
import { getAisleApi } from '../../../../services/factoryBuilder/aisle/getAisleApi';
import AisleInstance from '../../../builder/aisle/Instances/instance/aisleInstance';
function AislesDuplicate({ projectId }: { projectId: string }) {
const [aisles, setAisles] = useState<Aisles>([]);
useEffect(() => {
const fetchAisle = async () => {
if (projectId) {
const aisles = await getAisleApi(projectId);
setAisles(aisles);
}
}
fetchAisle()
}, [])
return (
<group name='Aisles-Group'>
{aisles.map((aisle) => (
<AisleInstance aisle={aisle} key={aisle.aisleUuid} />
))}
</group>
)
}
export default AislesDuplicate

View File

@ -0,0 +1,123 @@
import * as THREE from "three"
import { useEffect, useState } from 'react'
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";
import ModelDuplicate from "./modelDuplicate";
import { useLoadingProgress } from "../../../../store/builder/store";
import { getFloorAssets } from "../../../../services/factoryBuilder/assest/floorAsset/getFloorItemsApi";
import { FloorItems } from "../../../../types/world/worldTypes";
const gltfLoaderWorker = new Worker(
new URL(
"../../../../services/factoryBuilder/webWorkers/gltfLoaderWorker.js",
import.meta.url
)
);
function AssetsGroupDuplicate({ projectId }: { projectId: string }) {
const { setLoadingProgress } = useLoadingProgress();
const email = localStorage.getItem("email");
const organization = email!.split("@")[1].split(".")[0];
const [assetsDuplicates, setAssetsDuplicates] = useState<Assets>([]);
const loader = new GLTFLoader();
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath(
"https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/"
);
loader.setDRACOLoader(dracoLoader);
useEffect(() => {
let totalAssets = 0;
let loadedAssets = 0;
const updateLoadingProgress = (progress: number) => {
if (progress < 100) {
setLoadingProgress(progress);
} else if (progress === 100) {
setTimeout(() => {
setLoadingProgress(100);
setTimeout(() => {
setLoadingProgress(0);
}, 1500);
}, 1000);
}
};
getFloorAssets(organization, projectId).then((data) => {
if (data.length > 0) {
const uniqueItems = (data as FloorItems).filter((item, index, self) => index === self.findIndex((t) => t.assetId === item.assetId));
totalAssets = uniqueItems.length;
if (totalAssets === 0) {
updateLoadingProgress(100);
return;
}
gltfLoaderWorker.postMessage({ floorItems: uniqueItems });
} else {
gltfLoaderWorker.postMessage({ floorItems: [] });
updateLoadingProgress(100);
}
});
gltfLoaderWorker.onmessage = async (event) => {
if (event.data.message === "gltfLoaded" && event.data.modelBlob) {
const blobUrl = URL.createObjectURL(event.data.modelBlob);
loader.load(blobUrl, (gltf) => {
URL.revokeObjectURL(blobUrl);
THREE.Cache.remove(blobUrl);
THREE.Cache.add(event.data.modelID, gltf);
loadedAssets++;
const progress = Math.round((loadedAssets / totalAssets) * 100);
updateLoadingProgress(progress);
if (loadedAssets === totalAssets) {
getFloorAssets(organization, projectId).then((data: FloorItems) => {
const newAssets: Assets = data.map((item) => {
if (item.eventData) {
return {
modelUuid: item.modelUuid,
modelName: item.modelName,
assetId: item.assetId,
position: item.position,
rotation: [item.rotation.x, item.rotation.y, item.rotation.z],
isLocked: item.isLocked,
isCollidable: false,
isVisible: item.isVisible,
opacity: 1,
eventData: item.eventData
};
} else {
return {
modelUuid: item.modelUuid,
modelName: item.modelName,
assetId: item.assetId,
position: item.position,
rotation: [item.rotation.x, item.rotation.y, item.rotation.z],
isLocked: item.isLocked,
isCollidable: false,
isVisible: item.isVisible,
opacity: 1,
};
}
});
setAssetsDuplicates(newAssets);
});
updateLoadingProgress(100);
}
});
}
};
}, []);
return (
<>
{assetsDuplicates.map((asset) =>
<ModelDuplicate key={`${asset.modelUuid}_Duplicate`} asset={asset} />
)}
</>
)
}
export default AssetsGroupDuplicate;

View File

@ -0,0 +1,123 @@
import * as THREE from 'three';
import { useEffect, useRef, useState } from 'react';
import { GLTF, GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader';
import { useFrame, useThree } from '@react-three/fiber';
import { useRenderDistance } from '../../../../store/builder/store';
import { AssetBoundingBox } from '../../../builder/asset/functions/assetBoundingBox';
import { retrieveGLTF, storeGLTF } from '../../../../utils/indexDB/idbUtils';
function ModelDuplicate({ asset }: { readonly asset: Asset }) {
const { camera } = useThree();
const { renderDistance } = useRenderDistance();
const [isRendered, setIsRendered] = useState(false);
const url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`;
const [gltfScene, setGltfScene] = useState<GLTF["scene"] | null>(null);
const [boundingBox, setBoundingBox] = useState<THREE.Box3 | null>(null);
const groupRef = useRef<THREE.Group>(null);
useEffect(() => {
const loader = new GLTFLoader();
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath('https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/');
loader.setDRACOLoader(dracoLoader);
const loadModel = async () => {
try {
// Check Cache
const assetId = asset.assetId;
const cachedModel = THREE.Cache.get(assetId);
if (cachedModel) {
setGltfScene(cachedModel.scene.clone());
calculateBoundingBox(cachedModel.scene);
return;
}
// Check IndexedDB
const indexedDBModel = await retrieveGLTF(assetId);
if (indexedDBModel) {
const blobUrl = URL.createObjectURL(indexedDBModel);
loader.load(blobUrl, (gltf) => {
URL.revokeObjectURL(blobUrl);
THREE.Cache.remove(blobUrl);
THREE.Cache.add(assetId, gltf);
setGltfScene(gltf.scene.clone());
calculateBoundingBox(gltf.scene);
},
undefined,
(error) => {
echo.error(`[IndexedDB] Error loading ${asset.modelName}:`);
URL.revokeObjectURL(blobUrl);
}
);
return;
}
// Fetch from Backend
const modelUrl = `${url_Backend_dwinzo}/api/v2/AssetFile/${assetId}`;
const handleBackendLoad = async (gltf: GLTF) => {
try {
const response = await fetch(modelUrl);
const modelBlob = await response.blob();
await storeGLTF(assetId, modelBlob);
THREE.Cache.add(assetId, gltf);
setGltfScene(gltf.scene.clone());
calculateBoundingBox(gltf.scene);
} catch (error) {
console.error(`[Backend] Error storing/loading ${asset.modelName}:`, error);
}
};
loader.load(
modelUrl,
handleBackendLoad,
undefined,
(error) => {
echo.error(`[Backend] Error loading ${asset.modelName}:`);
}
);
} catch (err) {
console.error("Failed to load model:", asset.assetId, err);
}
};
const calculateBoundingBox = (scene: THREE.Object3D) => {
const box = new THREE.Box3().setFromObject(scene);
setBoundingBox(box);
};
loadModel();
}, []);
useFrame(() => {
const assetPosition = new THREE.Vector3(...asset.position);
if (!isRendered && assetPosition.distanceTo(camera.position) <= renderDistance) {
setIsRendered(true);
} else if (isRendered && assetPosition.distanceTo(camera.position) > renderDistance) {
setIsRendered(false);
}
})
return (
<group
key={asset.modelUuid}
name='Asset Model'
ref={groupRef}
uuid={asset.modelUuid}
position={asset.position}
rotation={asset.rotation}
visible={asset.isVisible}
userData={asset}
>
{gltfScene && (
isRendered ? (
<primitive object={gltfScene} />
) : (
<AssetBoundingBox boundingBox={boundingBox} />
)
)}
</group>
);
}
export default ModelDuplicate;

View File

@ -0,0 +1,47 @@
import {
useRoofVisibility,
useToggleView,
useWallVisibility,
} from "../../../../store/builder/store";
import { useEffect } from "react";
import loadFloor from "../../../builder/geomentries/floors/loadFloor";
import { getLines } from "../../../../services/factoryBuilder/lines/getLinesApi";
import objectLinesToArray from "../../../builder/geomentries/lines/lineConvertions/objectLinesToArray";
import { useFrame, useThree } from "@react-three/fiber";
import hideRoof from "../../../builder/geomentries/roofs/hideRoof";
import hideWalls from "../../../builder/geomentries/walls/hideWalls";
const FloorGroupDuplicate = ({
floorGroup,
lines,
projectId
}: any) => {
const { scene, camera } = useThree();
const { toggleView } = useToggleView();
const { roofVisibility } = useRoofVisibility();
const { wallVisibility } = useWallVisibility();
useEffect(() => {
const email = localStorage.getItem('email')
const organization = (email!.split("@")[1]).split(".")[0];
getLines(organization, projectId).then((data) => {
const Lines = objectLinesToArray(data);
if (Lines) {
lines.current = Lines;
loadFloor(lines, floorGroup);
}
})
}, []);
useFrame(() => {
hideRoof(roofVisibility, floorGroup, camera);
hideWalls(wallVisibility, scene, camera);
});
return (
<group ref={floorGroup} visible={!toggleView} name="floorGroup"></group>
);
};
export default FloorGroupDuplicate;

View File

@ -0,0 +1,66 @@
import * as THREE from "three";
import * as Types from "../../../../types/world/worldTypes";
import * as CONSTANTS from "../../../../types/world/worldConstants";
import { Base } from "@react-three/csg";
import { MeshDiscardMaterial } from "@react-three/drei";
import { useEffect } from "react";
import objectLinesToArray from "../../../builder/geomentries/lines/lineConvertions/objectLinesToArray";
import loadWalls from "../../../builder/geomentries/walls/loadWalls";
import texturePath from "../../../../assets/textures/floor/wall-tex.png";
import { getLines } from "../../../../services/factoryBuilder/lines/getLinesApi";
const WallsMeshDuplicate = ({ projectId, walls, setWalls, lines }: any) => {
useEffect(() => {
const email = localStorage.getItem("email");
const organization = email!.split("@")[1].split(".")[0];
getLines(organization, projectId).then((data) => {
const Lines: Types.Lines = objectLinesToArray(data);
if (Lines) {
lines.current = Lines;
loadWalls(lines, setWalls);
}
});
}, []);
const textureLoader = new THREE.TextureLoader();
const wallTexture = textureLoader.load(texturePath);
wallTexture.wrapS = wallTexture.wrapT = THREE.RepeatWrapping;
wallTexture.repeat.set(0.1, 0.1);
wallTexture.colorSpace = THREE.SRGBColorSpace;
return (
<>
{walls.map((wall: Types.Wall, index: number) => (
<mesh key={index} renderOrder={1}>
<Base
name={`Wall${index + 1}`}
geometry={wall[0]}
rotation={wall[1]}
position={wall[2]}
userData={{ WallType: wall[3], Layer: wall[4] }}
>
<meshStandardMaterial
side={THREE.DoubleSide}
color={CONSTANTS.wallConfig.defaultColor}
map={wallTexture}
/>
</Base>
<mesh
castShadow
geometry={wall[0]}
rotation={wall[1]}
position={wall[2]}
name={`WallRaycastReference_${index + 1}`}
>
<MeshDiscardMaterial />
</mesh>
</mesh>
))}
</>
);
};
export default WallsMeshDuplicate;

View File

@ -0,0 +1,29 @@
import { Geometry } from "@react-three/csg";
import {
useToggleView,
} from "../../../../store/builder/store";
import WallsMeshDuplicate from "./wallMeshDuplicate";
import { useState } from "react";
const WallsDuplicate = ({
lines,
projectId
}: any) => {
const { toggleView } = useToggleView();
const [walls, setWalls] = useState([]);
return (
<mesh
name="Walls"
key={walls.length}
receiveShadow
visible={!toggleView}
>
<Geometry computeVertexNormals useGroups>
<WallsMeshDuplicate projectId={projectId} setWalls={setWalls} walls={walls} lines={lines} />
</Geometry>
</mesh>
);
};
export default WallsDuplicate;

View File

@ -0,0 +1,182 @@
import { useEffect, useMemo, useRef, useState } from "react";
import { Html } from "@react-three/drei";
import * as THREE from "three";
import {
useToggleView,
useZonePoints,
} from "../../../../store/builder/store";
import { getZonesApi } from "../../../../services/factoryBuilder/zones/getZonesApi";
import * as CONSTANTS from "../../../../types/world/worldConstants";
import * as turf from "@turf/turf";
import { useSelectedZoneStore } from "../../../../store/visualization/useZoneStore";
const ZoneDuplicate = ({ projectId }: { projectId: string }) => {
const [zones, setZones] = useState([]);
const { setZonePoints } = useZonePoints();
const { selectedZone } = useSelectedZoneStore();
const { toggleView } = useToggleView();
const groupsRef = useRef<any>();
const zoneMaterial = useMemo(
() =>
new THREE.ShaderMaterial({
side: THREE.DoubleSide,
vertexShader: `
varying vec2 vUv;
void main(){
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
vUv = uv;
}
`,
fragmentShader: `
varying vec2 vUv;
uniform vec3 uOuterColor;
void main(){
float alpha = 1.0 - vUv.y;
gl_FragColor = vec4(uOuterColor, alpha);
}
`,
uniforms: {
uOuterColor: { value: new THREE.Color(CONSTANTS.zoneConfig.color) },
},
transparent: true,
depthWrite: false,
}),
[]
);
useEffect(() => {
const fetchZones = async () => {
const email = localStorage.getItem("email");
if (!email) return;
const organization = email.split("@")[1].split(".")[0];
const data = await getZonesApi(organization, projectId);
if (data.length > 0) {
const fetchedZones = data.map((zone: any) => ({
zoneUuid: zone.zoneUuid,
zoneName: zone.zoneName,
points: zone.points,
viewPortCenter: zone.viewPortCenter,
viewPortposition: zone.viewPortposition,
layer: zone.layer,
}));
setZones(fetchedZones);
const fetchedPoints = data.flatMap((zone: any) =>
zone.points.slice(0, 4).map((point: [number, number, number]) => new THREE.Vector3(...point))
);
setZonePoints(fetchedPoints);
}
};
fetchZones();
}, []);
return (
<group ref={groupsRef} name="zoneGroup">
<group name="zones" visible={!toggleView}>
{zones.map((zone: any) => (
<group
key={zone.zoneUuid}
name={zone.zoneName}
visible={zone.zoneUuid === selectedZone.zoneUuid}
>
{zone.points
.slice(0, -1)
.map((point: [number, number, number], index: number) => {
const nextPoint = zone.points[index + 1];
const point1 = new THREE.Vector3(point[0], point[1], point[2]);
const point2 = new THREE.Vector3(
nextPoint[0],
nextPoint[1],
nextPoint[2]
);
const planeWidth = point1.distanceTo(point2);
const planeHeight = CONSTANTS.zoneConfig.height;
const midpoint = new THREE.Vector3(
(point1.x + point2.x) / 2,
CONSTANTS.zoneConfig.height / 2 +
(zone.layer - 1) * CONSTANTS.zoneConfig.height,
(point1.z + point2.z) / 2
);
const angle = Math.atan2(
point2.z - point1.z,
point2.x - point1.x
);
return (
<mesh
key={index}
position={midpoint}
rotation={[0, -angle, 0]}
>
<planeGeometry args={[planeWidth, planeHeight]} />
<primitive
object={zoneMaterial.clone()}
attach="material"
/>
</mesh>
);
})}
{!toggleView &&
(() => {
const points3D = zone.points || [];
const coords2D = points3D.map((p: any) => [p[0], p[2]]);
// Ensure the polygon is closed
if (
coords2D.length >= 4 &&
(coords2D[0][0] !== coords2D[coords2D.length - 1][0] ||
coords2D[0][1] !== coords2D[coords2D.length - 1][1])
) {
coords2D.push(coords2D[0]);
}
if (coords2D.length < 4) return null;
const polygon = turf.polygon([coords2D]);
const center2D = turf.center(polygon).geometry.coordinates;
// Calculate the average Y value
const sumY = points3D.reduce(
(sum: number, p: any) => sum + p[1],
0
);
const avgY = points3D.length > 0 ? sumY / points3D.length : 0;
const htmlPosition: [number, number, number] = [
center2D[0],
avgY + (CONSTANTS.zoneConfig.height || 0) + 1.5,
center2D[1],
];
return (
<Html
// data
key={zone.zoneUuid}
position={htmlPosition}
// class
className="zone-name-wrapper"
// others
center
>
<div className="zone-name">{zone.zoneName}</div>
</Html>
);
})()}
</group>
))}
</group>
</group>
);
};
export default ZoneDuplicate;

View File

@ -0,0 +1,54 @@
import { CameraControls } from "@react-three/drei";
import { useRef, useEffect } from "react";
import { useThree } from "@react-three/fiber";
import * as CONSTANTS from '../../../types/world/worldConstants';
import { useToggleView } from "../../../store/builder/store";
import { getCamera } from "../../../services/factoryBuilder/camera/getCameraApi";
export default function ControlsDuplicate({ projectId }: { projectId: string }) {
const controlsRef = useRef<CameraControls>(null);
const { toggleView } = useToggleView();
const state = useThree();
useEffect(() => {
if (controlsRef.current) {
(controlsRef.current as any).mouseButtons.left = CONSTANTS.thirdPersonControls.leftMouse;
(controlsRef.current as any).mouseButtons.right = CONSTANTS.thirdPersonControls.rightMouse;
}
const email = localStorage.getItem("email");
const organization = email!.split("@")[1].split(".")[0];
const userId = localStorage.getItem("userId")!;
getCamera(organization, userId, projectId).then((data) => {
if (data && data.position && data.target) {
controlsRef.current?.setPosition(data.position.x, data.position.y, data.position.z);
controlsRef.current?.setTarget(data.target.x, data.target.y, data.target.z);
} else {
controlsRef.current?.setPosition(...CONSTANTS.threeDimension.defaultPosition);
controlsRef.current?.setTarget(...CONSTANTS.threeDimension.defaultTarget);
}
})
.catch((error) => console.error("Failed to fetch camera data:", error));
}, []);
return (
<>
<CameraControls
makeDefault
ref={controlsRef}
minDistance={toggleView ? CONSTANTS.twoDimension.minDistance : CONSTANTS.threeDimension.minDistance}
maxDistance={CONSTANTS.thirdPersonControls.maxDistance}
minZoom={CONSTANTS.thirdPersonControls.minZoom}
maxZoom={CONSTANTS.thirdPersonControls.maxZoom}
maxPolarAngle={CONSTANTS.thirdPersonControls.maxPolarAngle}
camera={state.camera}
verticalDragToForward={true}
boundaryEnclosesCamera={true}
dollyToCursor={toggleView}
>
</CameraControls>
</>
);
}

View File

@ -0,0 +1,18 @@
import { EffectComposer, N8AO } from "@react-three/postprocessing";
export default function PostProcessingDuplicate() {
return (
<EffectComposer autoClear={false}>
<N8AO
color="black"
aoRadius={20}
intensity={7}
distanceFalloff={4}
aoSamples={32}
denoiseRadius={6}
denoiseSamples={16}
/>
</EffectComposer>
);
}

View File

@ -0,0 +1,44 @@
import { Canvas } from "@react-three/fiber";
import BuilderDuplicate from "./duplicateBuilder/builderDuplicate";
import { Environment } from "@react-three/drei";
import Shadows from "../scene/environment/shadow";
import Sun from "../scene/environment/sky";
import background from "../../assets/textures/hdr/mudroadpuresky2k.hdr";
import ControlsDuplicate from "./duplicateSetup/controlsDuplicate";
import PostProcessingDuplicate from "./duplicateSetup/postProcessingDuplicate";
import { Color } from "three";
export default function DuplicateScene() {
const projectId = "684bcd620a64bc2a815a88d6";
return (
<Canvas
id="sceneCanvas"
shadows
color="#aaaa"
eventPrefix="client"
onContextMenu={(e) => {
e.preventDefault();
}}
onCreated={(e) => {
e.scene.background = new Color(0x19191d);
}}
gl={{ powerPreference: "high-performance", antialias: true, preserveDrawingBuffer: true }}
>
<Sun />
<Shadows />
<ControlsDuplicate projectId={projectId} />
<PostProcessingDuplicate />
<Environment files={background} environmentIntensity={1.5} />
<BuilderDuplicate projectId={projectId} />
</Canvas>
);
}

View File

@ -22,7 +22,6 @@ export default function Controls() {
const state = useThree();
const { projectId } = useParams();
useEffect(() => {
if (controlsRef.current) {
(controlsRef.current as any).mouseButtons.left = CONSTANTS.thirdPersonControls.leftMouse;

View File

@ -14,6 +14,7 @@ import { getAllProjects } from "../../services/dashboard/getAllProjects";
import { getUserData } from "../../components/Dashboard/functions/getUserData";
import { useLoadingProgress, useSocketStore } from "../../store/builder/store";
import { useAssetsStore } from "../../store/builder/useAssetStore";
import { Color } from "three";
export default function Scene({ layout }: { readonly layout: 'Main Layout' | 'Comparison Layout' }) {
const map = useMemo(() => [
@ -28,6 +29,7 @@ export default function Scene({ layout }: { readonly layout: 'Main Layout' | 'Co
const { projectId } = useParams();
const { projectSocket } = useSocketStore();
const { loadingProgress } = useLoadingProgress();
const handleUpdatingProject = async () => {
if (!projectId) return;
try {
@ -36,7 +38,6 @@ export default function Scene({ layout }: { readonly layout: 'Main Layout' | 'Co
(val: any) => val.projectUuid === projectId || val._id === projectId
);
if (activeModule === "builder" && loadingProgress !== 1) {
const canvas =
document.getElementById("sceneCanvas")?.children[0]?.children[0];
@ -64,8 +65,6 @@ export default function Scene({ layout }: { readonly layout: 'Main Layout' | 'Co
projectName: projectUuid.projectName,
thumbnail: screenshotDataUrl,
};
// console.log('screenshotDataUrl: ', screenshotDataUrl);
// console.log('updateProjects: ', updateProjects);
if (projectSocket) {
projectSocket.emit("v1:project:update", updateProjects);
}
@ -89,7 +88,7 @@ export default function Scene({ layout }: { readonly layout: 'Main Layout' | 'Co
e.preventDefault();
}}
onCreated={(e) => {
e.scene.background = null;
e.scene.background = layout === 'Main Layout' ? null : new Color(0x19191d);
}}
gl={{ powerPreference: "high-performance", antialias: true, preserveDrawingBuffer: true }}
>

View File

@ -23,7 +23,7 @@ import { useLogger } from "../../components/ui/log/LoggerContext";
import { useComparisonProduct } from "../../store/simulation/useSimulationStore";
const KeyPressListener: React.FC = () => {
const { clearComparisonProduct } = useComparisonProduct();
const { comparisonProduct, clearComparisonProduct } = useComparisonProduct();
const { activeModule, setActiveModule } = useModuleStore();
const { setActiveSubTool } = useActiveSubTool();
const { toggleUILeft, toggleUIRight, setToggleUI } = useToggleStore();
@ -195,7 +195,7 @@ const KeyPressListener: React.FC = () => {
handleBuilderShortcuts(keyCombination);
// Shortcut to enter play mode
if (keyCombination === "Ctrl+P" && !toggleView) {
if (keyCombination === "Ctrl+P" && !toggleView && !comparisonProduct) {
setIsPlaying(true);
}
@ -242,6 +242,7 @@ const KeyPressListener: React.FC = () => {
hidePlayer,
selectedFloorItem,
isRenameMode,
comparisonProduct
]);
return null;