feat: add roads
This commit is contained in:
parent
f7ebf0c7a0
commit
8fc0848369
|
|
@ -1,10 +1,8 @@
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { Canvas, useThree } from "@react-three/fiber";
|
import { Canvas, useThree, useLoader } from "@react-three/fiber";
|
||||||
import { useAreaStore } from "@/state/areaStore";
|
import { useAreaStore } from "@/state/areaStore";
|
||||||
import { OrbitControls, Html } from "@react-three/drei";
|
import { OrbitControls, Html } from "@react-three/drei";
|
||||||
import * as THREE from "three";
|
import * as THREE from "three";
|
||||||
import { NextButton } from "@/components/button/BottomButton";
|
|
||||||
import { ChevronRight } from "lucide-react";
|
|
||||||
import { css } from "@emotion/react";
|
import { css } from "@emotion/react";
|
||||||
import { useActionStore } from "@/state/exportStore";
|
import { useActionStore } from "@/state/exportStore";
|
||||||
import { GLTFExporter } from "three/examples/jsm/Addons.js";
|
import { GLTFExporter } from "three/examples/jsm/Addons.js";
|
||||||
|
|
@ -21,7 +19,6 @@ function Building({
|
||||||
const [hovered, setHovered] = useState(false);
|
const [hovered, setHovered] = useState(false);
|
||||||
const [clicked, setClicked] = useState(false);
|
const [clicked, setClicked] = useState(false);
|
||||||
const [hoverPos, setHoverPos] = useState<THREE.Vector3 | null>(null);
|
const [hoverPos, setHoverPos] = useState<THREE.Vector3 | null>(null);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<mesh
|
<mesh
|
||||||
onPointerOver={(e) => {
|
onPointerOver={(e) => {
|
||||||
|
|
@ -76,20 +73,114 @@ function Building({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Space() {
|
function Roads({ area }: { area: any }) {
|
||||||
const areas = useAreaStore((state) => state.areas);
|
const [roads, setRoads] = useState<any[]>([]);
|
||||||
const center = useAreaStore((state) => state.center);
|
const center = useAreaStore((state) => state.center);
|
||||||
|
|
||||||
const refLat = (center[1].lat + center[0].lat) / 2;
|
const refLat = (center[1].lat + center[0].lat) / 2;
|
||||||
const refLng = (center[1].lng + center[0].lng) / 2;
|
const refLng = (center[1].lng + center[0].lng) / 2;
|
||||||
const scale = 111000;
|
const scale = 51000;
|
||||||
|
|
||||||
function project(lat: number, lng: number) {
|
function project(lat: number, lng: number) {
|
||||||
const x = (lng - refLng) * scale;
|
const x = (lng - refLng) * scale;
|
||||||
const y = (lat - refLat) * scale;
|
const y = (lat - refLat) * scale;
|
||||||
return new THREE.Vector2(x, y);
|
return new THREE.Vector2(x, y);
|
||||||
}
|
}
|
||||||
|
useEffect(() => {
|
||||||
|
const south = area[1].lat;
|
||||||
|
const west = area[1].lng;
|
||||||
|
const north = area[0].lat;
|
||||||
|
const east = area[0].lng;
|
||||||
|
const query = `[out:json][timeout:25];(way["highway"](${south},${west},${north},${east}););out body geom;`;
|
||||||
|
fetch("https://overpass-api.de/api/interpreter", {
|
||||||
|
method: "POST",
|
||||||
|
body: query,
|
||||||
|
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
||||||
|
})
|
||||||
|
.then((response) => response.json())
|
||||||
|
.then((data) => {
|
||||||
|
setRoads(data.elements);
|
||||||
|
})
|
||||||
|
.catch((err) => console.error(err));
|
||||||
|
}, [area]);
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{roads.map((road, index) => {
|
||||||
|
if (!road.geometry || road.geometry.length < 2) return null;
|
||||||
|
const points = road.geometry.map((pt: any) => {
|
||||||
|
const v = project(pt.lat, pt.lon);
|
||||||
|
return new THREE.Vector3(v.x, 0.1, -v.y);
|
||||||
|
});
|
||||||
|
const lineGeometry = new THREE.BufferGeometry().setFromPoints(points);
|
||||||
|
return (
|
||||||
|
<line key={index} geometry={lineGeometry}>
|
||||||
|
<lineBasicMaterial
|
||||||
|
attach="material"
|
||||||
|
color="#34f516"
|
||||||
|
linewidth={4}
|
||||||
|
/>
|
||||||
|
</line>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
export function Export() {
|
||||||
|
const { scene } = useThree();
|
||||||
|
const action = useActionStore((state) => state.action);
|
||||||
|
const setAction = useActionStore((state) => state.setAction);
|
||||||
|
useEffect(() => {
|
||||||
|
if (action === true) {
|
||||||
|
setAction(false);
|
||||||
|
exportGLB();
|
||||||
|
}
|
||||||
|
}, [action, setAction, scene]);
|
||||||
|
const exportGLB = () => {
|
||||||
|
const sceneClone = scene.clone(true);
|
||||||
|
sceneClone.traverse((child) => {
|
||||||
|
if (child.userData && child.userData.skipExport === true) {
|
||||||
|
child.parent?.remove(child);
|
||||||
|
}
|
||||||
|
if ((child as any).isHtml === true) {
|
||||||
|
child.parent?.remove(child);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const exporter = new GLTFExporter();
|
||||||
|
const options = { binary: true, embedImages: true };
|
||||||
|
exporter.parse(
|
||||||
|
sceneClone,
|
||||||
|
(result) => {
|
||||||
|
if (result instanceof ArrayBuffer) {
|
||||||
|
const blob = new Blob([result], { type: "model/gltf-binary" });
|
||||||
|
const link = document.createElement("a");
|
||||||
|
link.style.display = "none";
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.href = URL.createObjectURL(blob);
|
||||||
|
link.download = "scene.glb";
|
||||||
|
link.click();
|
||||||
|
document.body.removeChild(link);
|
||||||
|
} else {
|
||||||
|
console.error("GLB export failed: unexpected result", result);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
console.error("An error occurred during export", error);
|
||||||
|
},
|
||||||
|
options
|
||||||
|
);
|
||||||
|
};
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Space() {
|
||||||
|
const areas = useAreaStore((state) => state.areas);
|
||||||
|
const center = useAreaStore((state) => state.center);
|
||||||
|
const refLat = (center[1].lat + center[0].lat) / 2;
|
||||||
|
const refLng = (center[1].lng + center[0].lng) / 2;
|
||||||
|
const scale = 51000;
|
||||||
|
function project(lat: number, lng: number) {
|
||||||
|
const x = (lng - refLng) * scale;
|
||||||
|
const y = (lat - refLat) * scale;
|
||||||
|
return new THREE.Vector2(x, y);
|
||||||
|
}
|
||||||
const areaData = () => {
|
const areaData = () => {
|
||||||
const result: Array<{
|
const result: Array<{
|
||||||
shape: THREE.Shape;
|
shape: THREE.Shape;
|
||||||
|
|
@ -122,9 +213,7 @@ export function Space() {
|
||||||
});
|
});
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
const buildingsData = areaData();
|
const buildingsData = areaData();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Canvas camera={{ fov: 90, near: 0.1, far: 7000 }}>
|
<Canvas camera={{ fov: 90, near: 0.1, far: 7000 }}>
|
||||||
<ambientLight intensity={Math.PI / 2} />
|
<ambientLight intensity={Math.PI / 2} />
|
||||||
|
|
@ -143,71 +232,10 @@ export function Space() {
|
||||||
tags={item.tags}
|
tags={item.tags}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
<Roads area={center} />
|
||||||
<pointLight position={[-10, -10, -10]} decay={0} intensity={Math.PI} />
|
<pointLight position={[-10, -10, -10]} decay={0} intensity={Math.PI} />
|
||||||
<OrbitControls />
|
<OrbitControls />
|
||||||
<Export />
|
<Export />
|
||||||
</Canvas>
|
</Canvas>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// function Floor() {
|
|
||||||
// return (
|
|
||||||
// <mesh rotation={[-Math.PI / 2, 0, 0]} receiveShadow>
|
|
||||||
// <planeGeometry args={[50, 50]} />
|
|
||||||
// <meshStandardMaterial color="#ffffff" />
|
|
||||||
// </mesh>
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
export function Export() {
|
|
||||||
const { scene } = useThree();
|
|
||||||
|
|
||||||
const action = useActionStore((state) => state.action);
|
|
||||||
const setAction = useActionStore((state) => state.setAction);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (action == true) {
|
|
||||||
setAction(false);
|
|
||||||
console.log(action);
|
|
||||||
exportGLB();
|
|
||||||
}
|
|
||||||
}, [action]);
|
|
||||||
|
|
||||||
const exportGLB = () => {
|
|
||||||
const sceneClone = scene.clone(true);
|
|
||||||
sceneClone.traverse((child) => {
|
|
||||||
if (child.userData && child.userData.skipExport === true) {
|
|
||||||
if (child.parent) child.parent.remove(child);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const exporter = new GLTFExporter();
|
|
||||||
const options = {
|
|
||||||
binary: true,
|
|
||||||
embedImages: true,
|
|
||||||
};
|
|
||||||
exporter.parse(
|
|
||||||
sceneClone,
|
|
||||||
(result) => {
|
|
||||||
if (result instanceof ArrayBuffer) {
|
|
||||||
const blob = new Blob([result], { type: "model/gltf-binary" });
|
|
||||||
const link = document.createElement("a");
|
|
||||||
link.style.display = "none";
|
|
||||||
document.body.appendChild(link);
|
|
||||||
link.href = URL.createObjectURL(blob);
|
|
||||||
link.download = "scene.glb";
|
|
||||||
link.click();
|
|
||||||
document.body.removeChild(link);
|
|
||||||
} else {
|
|
||||||
console.error("GLB export failed: unexpected result", result);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
function () {
|
|
||||||
console.log("An error happened");
|
|
||||||
},
|
|
||||||
options
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
return <></>;
|
|
||||||
}
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue