diff --git a/app/src/ar_scenes/AR.jsx b/app/src/ar_scenes/AR.jsx index 65c1e2788be0831f768840f578f29677f603adb6..d1bfe4b39ad3c5c78fc3308edb2fa9adbd9f5547 100644 --- a/app/src/ar_scenes/AR.jsx +++ b/app/src/ar_scenes/AR.jsx @@ -6,14 +6,23 @@ import "../styles/ar.css"; const AR = () => { const [stage, setStage] = useState("scene1"); + const [buildings, setBuildings] = useState([]); + const [activeIndex, setActiveIndex] = useState(0); + // Cargar edificios desde la API + useEffect(() => { + fetch("https://serverm3d.onrender.com/buildings") + .then((res) => res.json()) + .then((data) => setBuildings(data)) + .catch((err) => console.error("Error cargando edificios:", err)); + }, []); + + // Navegación por scroll useEffect(() => { const handleScroll = (e) => { if (e.deltaY < 0) { - // Scroll Up: go to the previous stage previusStage(); } else if (e.deltaY > 0) { - // Scroll Down: go to the next stage nextStage(); } }; @@ -31,7 +40,7 @@ const AR = () => { } }; - const previusStage = () => { + const previusStage = () => { if (stage === "scene4") { setStage("scene3top"); setTimeout(() => setStage("scene2"), 700); @@ -40,16 +49,34 @@ const AR = () => { } }; + const goToNextBuilding = () => { + setActiveIndex((prev) => (prev + 1) % buildings.length); + }; + + const goToPrevBuilding = () => { + setActiveIndex((prev) => (prev - 1 + buildings.length) % buildings.length); + }; + + const currentBuilding = buildings[activeIndex]; + return ( <div className="ar-container"> - {stage === "scene1" && <BuildingScene />} - {stage === "scene2" && <BuildingScene shrink />} + {(stage === "scene1" || stage === "scene2") && buildings.length > 0 && ( + <BuildingScene + building={currentBuilding} + shrink={stage === "scene2"} + onNext={goToNextBuilding} + onPrev={goToPrevBuilding} + showNavigation={buildings.length > 1} + /> + )} {stage === "scene4" && <Floor />} - + <FloatingBox stage={stage} onTopClick={previusStage} onBottomClick={nextStage} + building={currentBuilding} /> </div> ); diff --git a/app/src/ar_scenes/BuildingScene.jsx b/app/src/ar_scenes/BuildingScene.jsx index de762ac29cc07f15603fe448c3957f174f5d9bb0..f5cb9edae5c958bf69c97db6db3de1515271702e 100644 --- a/app/src/ar_scenes/BuildingScene.jsx +++ b/app/src/ar_scenes/BuildingScene.jsx @@ -2,30 +2,77 @@ import React, { Suspense } from "react"; import { Canvas } from "@react-three/fiber"; import { OrbitControls, useGLTF } from "@react-three/drei"; -const Model = ({ shrink }) => { - const gltf = useGLTF("/nexus/EdificiNexus.glb"); // 🔁 Asegúrate de ajustar el path - +const Model = ({ url, scale = 0.02 }) => { + const gltf = useGLTF(url); return ( <primitive object={gltf.scene} - scale={shrink ? 0.012 : 0.023} - position={[0, shrink ? 1.3 : -0.3, 0]} + scale={[scale, scale, scale]} + position={[0, 0, 0]} /> ); }; -const BuildingScene = ({ shrink }) => { +const BuildingScene = ({ building, shrink = false, onNext, onPrev, showNavigation }) => { + if (!building) return null; + + const scaleValue = building.scale ? parseFloat(building.scale) : 0.02; + const adjustedScale = shrink ? scaleValue * 0.6 : scaleValue; + return ( - <Canvas camera={{ position: [0, 1, 5] }}> - <ambientLight /> - <directionalLight position={[5, 5, 5]} /> - - <Suspense fallback={null}> - <Model shrink={shrink} /> - </Suspense> + <> + <Canvas camera={{ position: [0, 1, 5] }}> + <ambientLight /> + <directionalLight position={[5, 5, 5]} /> + <Suspense fallback={null}> + <Model url={building.urlModelo} scale={adjustedScale} /> + </Suspense> + <OrbitControls enableZoom={false} /> + </Canvas> - <OrbitControls enableZoom={false} /> - </Canvas> + {showNavigation && ( + <> + <button + onClick={onPrev} + style={{ + position: "absolute", + top: "50%", + left: "10px", + zIndex: 10, + fontSize: "2rem", + background: "rgba(0,0,0,0.5)", + color: "white", + border: "none", + borderRadius: "50%", + width: "40px", + height: "40px", + cursor: "pointer", + }} + > + ◀ + </button> + <button + onClick={onNext} + style={{ + position: "absolute", + top: "50%", + right: "10px", + zIndex: 10, + fontSize: "2rem", + background: "rgba(0,0,0,0.5)", + color: "white", + border: "none", + borderRadius: "50%", + width: "40px", + height: "40px", + cursor: "pointer", + }} + > + ▶ + </button> + </> + )} + </> ); }; diff --git a/app/src/ar_scenes/FloorScene.jsx b/app/src/ar_scenes/FloorScene.jsx index e57d7ebb74292269bf4bf0124a42dee74e1e20e6..0fb730a5480a904d7da54148636b20d28604c682 100644 --- a/app/src/ar_scenes/FloorScene.jsx +++ b/app/src/ar_scenes/FloorScene.jsx @@ -39,17 +39,18 @@ const Floor = ({ floorData, activeOffice, setActiveOffice }) => { ); }; -const App = () => { +const App = ({ buildingId }) => { const [floors, setFloors] = useState([]); const [activeFloorIndex, setActiveFloorIndex] = useState(0); const [activeOffice, setActiveOffice] = useState(null); useEffect(() => { - fetch("https://serverm3d.onrender.com/floors/building/1") + if (!buildingId) return; + fetch(`https://serverm3d.onrender.com/floors/building/${buildingId}`) .then((res) => res.json()) .then((data) => setFloors(data)) .catch((err) => console.error("Error fetching floors:", err)); - }, []); + }, [buildingId]); const options = [{ value: null, label: "Eliga su oficina" }]; const currentFloor = floors[activeFloorIndex]; diff --git a/app/src/components/FloatingBox.jsx b/app/src/components/FloatingBox.jsx index 1041b30c98fbbebfb46aad3a6e886b6e03e4cb4e..5e903e3ca9c53a8c1515714598b543fc30811465 100644 --- a/app/src/components/FloatingBox.jsx +++ b/app/src/components/FloatingBox.jsx @@ -1,15 +1,14 @@ import { ChevronUp, ChevronDown } from "lucide-react"; import { useState } from "react"; import { collection, addDoc } from "firebase/firestore"; -import firebase from "firebase/compat/app"; import { db } from "../firebase"; -const FloatingBox = ({ stage, onTopClick, onBottomClick }) => { +const FloatingBox = ({ stage, onTopClick, onBottomClick, building }) => { const [showForm, setShowForm] = useState(false); const [formData, setFormData] = useState({ nombre: "", email: "", - texto: "" + texto: "", }); const handleChange = (e) => { @@ -36,36 +35,38 @@ const FloatingBox = ({ stage, onTopClick, onBottomClick }) => { return ( <div - className={`floating-box-container ${stage === "scene2" - ? "expanded" - : stage === "scene3top" - ? "full-expanded-top" - : stage === "scene3bottom" - ? "full-expanded-bottom" - : stage === "scene4" - ? "reduced-top" - : "reduced-bottom" - }`} + className={`floating-box-container ${ + stage === "scene2" + ? "expanded" + : stage === "scene3top" + ? "full-expanded-top" + : stage === "scene3bottom" + ? "full-expanded-bottom" + : stage === "scene4" + ? "reduced-top" + : "reduced-bottom" + }`} > <div onClick={onBottomClick}> - <ChevronUp style={{ height: '6vh', width: '100vw' }} /> + <ChevronUp style={{ height: "6vh", width: "100vw" }} /> </div> <div - className={`text-box ${stage === "scene2" - ? "expanded" - : stage === "scene3top" + className={`text-box ${ + stage === "scene2" + ? "expanded" + : stage === "scene3top" ? "full-expanded-top" : stage === "scene3bottom" - ? "full-expanded-bottom" - : stage === "scene4" - ? "reduced-top" - : "reduced-bottom" - }`} + ? "full-expanded-bottom" + : stage === "scene4" + ? "reduced-top" + : "reduced-bottom" + }`} > - <h2>Edificio Nexus 1</h2> + <h2>{building?.nombre || "Nombre del edificio"}</h2> <p className="text-sm mt-2"> - Nexus 1 es un centro de oficinas inteligente con 8 pisos, equipado con sistemas automatizados de climatización, acceso y seguridad. Actualmente aloja 12 empresas tecnológicas. Ofrece espacios de coworking, zonas comunes con domótica, y una azotea con paneles solares que alimentan parcialmente el consumo energético del edificio. + {building?.descripcion || "Descripción no disponible."} </p> <button className="form-button" onClick={() => setShowForm(true)}> Más información @@ -99,14 +100,16 @@ const FloatingBox = ({ stage, onTopClick, onBottomClick }) => { /> <div className="form-actions"> <button type="submit">Enviar</button> - <button type="button" onClick={() => setShowForm(false)}>Cancelar</button> + <button type="button" onClick={() => setShowForm(false)}> + Cancelar + </button> </div> </form> </div> )} <div onClick={onTopClick}> - <ChevronDown style={{ height: '6vh', width: '100vw' }} /> + <ChevronDown style={{ height: "6vh", width: "100vw" }} /> </div> </div> );