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