feat: export glb
This commit is contained in:
parent
cfc19a42a2
commit
f7ebf0c7a0
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { create } from "zustand";
|
||||||
|
|
||||||
|
type ActionStore = {
|
||||||
|
action: boolean;
|
||||||
|
|
||||||
|
setAction: (action: boolean) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useActionStore = create<ActionStore>((set) => ({
|
||||||
|
action: false,
|
||||||
|
setAction: (action) => set(() => ({ action: action })),
|
||||||
|
}));
|
||||||
|
|
@ -1,8 +1,13 @@
|
||||||
import React, { useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { Canvas } from "@react-three/fiber";
|
import { Canvas, useThree } 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 { useActionStore } from "@/state/exportStore";
|
||||||
|
import { GLTFExporter } from "three/examples/jsm/Addons.js";
|
||||||
|
|
||||||
function Building({
|
function Building({
|
||||||
shape,
|
shape,
|
||||||
|
|
@ -121,7 +126,7 @@ export function Space() {
|
||||||
const buildingsData = areaData();
|
const buildingsData = areaData();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Canvas camera={{ fov: 90, near: 0.1, far: 4000 }}>
|
<Canvas camera={{ fov: 90, near: 0.1, far: 7000 }}>
|
||||||
<ambientLight intensity={Math.PI / 2} />
|
<ambientLight intensity={Math.PI / 2} />
|
||||||
<spotLight
|
<spotLight
|
||||||
position={[10, 10, 10]}
|
position={[10, 10, 10]}
|
||||||
|
|
@ -140,15 +145,69 @@ export function Space() {
|
||||||
))}
|
))}
|
||||||
<pointLight position={[-10, -10, -10]} decay={0} intensity={Math.PI} />
|
<pointLight position={[-10, -10, -10]} decay={0} intensity={Math.PI} />
|
||||||
<OrbitControls />
|
<OrbitControls />
|
||||||
|
<Export />
|
||||||
</Canvas>
|
</Canvas>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function Floor() {
|
// function Floor() {
|
||||||
return (
|
// return (
|
||||||
<mesh rotation={[-Math.PI / 2, 0, 0]} receiveShadow>
|
// <mesh rotation={[-Math.PI / 2, 0, 0]} receiveShadow>
|
||||||
<planeGeometry args={[50, 50]} />
|
// <planeGeometry args={[50, 50]} />
|
||||||
<meshStandardMaterial color="#ffffff" />
|
// <meshStandardMaterial color="#ffffff" />
|
||||||
</mesh>
|
// </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 <></>;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { css } from "@emotion/react";
|
import { css } from "@emotion/react";
|
||||||
import { Space } from "../three/Space";
|
import { ExportButton, Space } from "../three/Space";
|
||||||
import { FullscreenModal } from "../components/FullscreenModal";
|
import { FullscreenModal } from "../components/FullscreenModal";
|
||||||
import { Title } from "@/components/text/Title";
|
import { Title } from "@/components/text/Title";
|
||||||
import { Description } from "@/components/text/Description";
|
import { Description } from "@/components/text/Description";
|
||||||
|
|
@ -8,8 +8,9 @@ import { MapComponent } from "@/components/map/SelectMap";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { NextButton, PrevButton } from "@/components/button/BottomButton";
|
import { NextButton, PrevButton } from "@/components/button/BottomButton";
|
||||||
import { BuildingHeights } from "@/components/map/Processing";
|
import { BuildingHeights } from "@/components/map/Processing";
|
||||||
import { ChevronLeft, ChevronRight } from "lucide-react";
|
import { ChevronLeft, ChevronRight, Download } from "lucide-react";
|
||||||
import { useAreaStore } from "@/state/areaStore";
|
import { useAreaStore } from "@/state/areaStore";
|
||||||
|
import { useActionStore } from "@/state/exportStore";
|
||||||
|
|
||||||
const IconSize = css({
|
const IconSize = css({
|
||||||
width: "14px",
|
width: "14px",
|
||||||
|
|
@ -22,6 +23,7 @@ function App() {
|
||||||
const [steps, setSteps] = useState(["front", "processing"]);
|
const [steps, setSteps] = useState(["front", "processing"]);
|
||||||
const [step, setStep] = useState(0);
|
const [step, setStep] = useState(0);
|
||||||
const setCenter = useAreaStore((state) => state.setCenter);
|
const setCenter = useAreaStore((state) => state.setCenter);
|
||||||
|
const setAction = useActionStore((state) => state.setAction);
|
||||||
|
|
||||||
const handleDone = (data) => {
|
const handleDone = (data) => {
|
||||||
setAreaData(data);
|
setAreaData(data);
|
||||||
|
|
@ -43,6 +45,10 @@ function App() {
|
||||||
setStep(step - 1);
|
setStep(step - 1);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleClickExport = () => {
|
||||||
|
setAction(true);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div css={css({ height: "100%", width: "100%" })}>
|
<div css={css({ height: "100%", width: "100%" })}>
|
||||||
<FullscreenModal isOpen={steps[step] == "front"}>
|
<FullscreenModal isOpen={steps[step] == "front"}>
|
||||||
|
|
@ -78,14 +84,18 @@ function App() {
|
||||||
</PrevButton>
|
</PrevButton>
|
||||||
|
|
||||||
<NextButton
|
<NextButton
|
||||||
isShow={true}
|
isShow={step != 2}
|
||||||
disabled={isNextButtonDisabled}
|
disabled={isNextButtonDisabled}
|
||||||
onClick={handleClickNextStep}
|
onClick={handleClickNextStep}
|
||||||
>
|
>
|
||||||
Next Step <ChevronRight css={IconSize} />
|
Next Step <ChevronRight css={IconSize} />
|
||||||
</NextButton>
|
</NextButton>
|
||||||
|
|
||||||
<Space />
|
<NextButton isShow={step == 2} onClick={handleClickExport}>
|
||||||
|
Export GLB <Download css={IconSize} />
|
||||||
|
</NextButton>
|
||||||
|
|
||||||
|
<Space></Space>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue