|
@@ -1,14 +1,15 @@
|
|
|
+import { animation } from "@/core/hook/use-animation";
|
|
|
import {
|
|
|
Box3,
|
|
|
BoxGeometry,
|
|
|
Color,
|
|
|
- DirectionalLight,
|
|
|
DoubleSide,
|
|
|
Mesh,
|
|
|
MeshPhongMaterial,
|
|
|
MeshPhysicalMaterial,
|
|
|
MeshStandardMaterial,
|
|
|
Object3D,
|
|
|
+ Quaternion,
|
|
|
Vector3,
|
|
|
} from "three";
|
|
|
import { GLTFLoader } from "three/examples/jsm/Addons.js";
|
|
@@ -48,6 +49,58 @@ const resources: Record<string, () => Promise<Object3D>> = {
|
|
|
return await normalized(gltf.scene);
|
|
|
},
|
|
|
"piaochuang.svg": async () => {
|
|
|
+ const gltf = await gltfLoader.loadAsync("bay_window/scene.gltf");
|
|
|
+ gltf.scene.scale.setX(-1);
|
|
|
+ const names = ["01_glass_0", "02_glass_0", "03_glass_0", "04_glass_0"]
|
|
|
+ gltf.scene.traverse((node: any) => {
|
|
|
+ if (names.includes(node.name)) {
|
|
|
+ node.material = new MeshPhysicalMaterial({
|
|
|
+ color: 0xffffff, // 浅灰色(可根据需求调整,如0xcccccc)
|
|
|
+ metalness: 0.1, // 轻微金属感(增强反射)
|
|
|
+ roughness: 0.01, // 表面光滑度(0-1,越小越光滑)
|
|
|
+ transmission: 1, // 透光率(模拟玻璃透光,需环境光遮蔽和光源支持)
|
|
|
+ opacity: 1, // 透明度(与transmission配合使用)
|
|
|
+ transparent: true, // 启用透明
|
|
|
+ side: DoubleSide, // 双面渲染(玻璃通常需要)
|
|
|
+ ior: 0, // 折射率(玻璃约为1.5)
|
|
|
+ clearcoat: 0.5, // 可选:表面清漆层(增强反光)
|
|
|
+ });
|
|
|
+ }
|
|
|
+ })
|
|
|
+ let copyModel: Object3D
|
|
|
+ let parent: Object3D
|
|
|
+ gltf.scene.traverse((node: any) => {
|
|
|
+ if (node.name === "01") {
|
|
|
+ copyModel = node.clone()
|
|
|
+ parent = node.parent
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ copyModel!.scale.add({x: 0, y: 0.06, z: -0.1})
|
|
|
+ const left = copyModel!.clone()
|
|
|
+ left.name = "05"
|
|
|
+ left.rotation.y = -Math.PI / 2
|
|
|
+ left.position.set(-170, 0, 50)
|
|
|
+ parent!.add(left)
|
|
|
+
|
|
|
+
|
|
|
+ const right = copyModel!
|
|
|
+ right.name = "06"
|
|
|
+ right.rotation.y = Math.PI / 2
|
|
|
+ right.position.set(170, 0, 50)
|
|
|
+ parent!.add(right)
|
|
|
+
|
|
|
+ const model = await normalized(gltf.scene);
|
|
|
+ // model.scale.add(({x: 0.015, y: 0, z: 0}))
|
|
|
+ // model.position.add({x: 0, y: 0, z: 0})
|
|
|
+ left.scale.add({x: -0.3, y: 0, z: 0})
|
|
|
+ left.position.add({x: -7, y: 0, z: -16})
|
|
|
+
|
|
|
+ right.scale.add({x: -0.3, y: 0, z: 0})
|
|
|
+ right.position.add({x: 7, y: 0, z: -16})
|
|
|
+ return model
|
|
|
+ },
|
|
|
+ "piaochuang1.svg": async () => {
|
|
|
const gltf = await gltfLoader.loadAsync("window_1/scene.gltf");
|
|
|
gltf.scene.rotateY(Math.PI);
|
|
|
gltf.scene.traverse((node: any) => {
|
|
@@ -143,7 +196,7 @@ const resources: Record<string, () => Promise<Object3D>> = {
|
|
|
models.forEach((m) => m.parent?.remove(m));
|
|
|
|
|
|
const model = await normalized(gltf.scene);
|
|
|
- model.position.setY(model.position.y - 0.131);
|
|
|
+ model.position.setY(model.position.y);
|
|
|
return model;
|
|
|
},
|
|
|
"SingleBed.svg": async () => {
|
|
@@ -238,7 +291,7 @@ const resources: Record<string, () => Promise<Object3D>> = {
|
|
|
"Chair.svg": async () => {
|
|
|
const gltf = await gltfLoader.loadAsync("psx_chair/scene.gltf");
|
|
|
const model = await normalized(gltf.scene, undefined);
|
|
|
- model.scale.add({ x: 0, y: 0.3, z: 0 });
|
|
|
+ model.position.add({x: 0, y: -0.1, z: 0})
|
|
|
return model;
|
|
|
},
|
|
|
"TV.svg": async () => {
|
|
@@ -281,6 +334,7 @@ const resources: Record<string, () => Promise<Object3D>> = {
|
|
|
const gltf = await gltfLoader.loadAsync(
|
|
|
"low_poly_bedside_table/scene.gltf"
|
|
|
);
|
|
|
+ gltf.scene.rotateY(Math.PI);
|
|
|
const model = await normalized(gltf.scene, undefined);
|
|
|
model.traverse((child: any) => {
|
|
|
if (child.isMesh) {
|
|
@@ -338,7 +392,7 @@ const resources: Record<string, () => Promise<Object3D>> = {
|
|
|
const model = await normalized(gltf.scene);
|
|
|
model.traverse((child: any) => {
|
|
|
if (child.isMesh) {
|
|
|
- child.material.emissive = new Color(0x222222)
|
|
|
+ child.material.emissive = new Color(0x222222);
|
|
|
}
|
|
|
});
|
|
|
return model;
|
|
@@ -417,6 +471,189 @@ export const levelResources: Record<
|
|
|
},
|
|
|
};
|
|
|
|
|
|
+export type ModelSwitch = (open: boolean) => ReturnType<typeof animation>;
|
|
|
+export type SwitchResult = ReturnType<ModelSwitch>;
|
|
|
+export const switchResources: Record<
|
|
|
+ string,
|
|
|
+ (model: Object3D, render: () => void) => ModelSwitch
|
|
|
+> = {
|
|
|
+ "men_l.svg": (model, render) => {
|
|
|
+ let node: Object3D;
|
|
|
+ model.traverse((child) => {
|
|
|
+ if (child.name === "Plane001") {
|
|
|
+ node = child;
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ return (open: boolean) =>
|
|
|
+ animation(
|
|
|
+ { z: node!.rotation.z },
|
|
|
+ { z: open ? -Math.PI / 2 : 0 },
|
|
|
+ (data) => {
|
|
|
+ node.rotation.z = data.z;
|
|
|
+ render();
|
|
|
+ }
|
|
|
+ );
|
|
|
+ },
|
|
|
+
|
|
|
+ "luodichuang.svg": (model, render) => {
|
|
|
+ let nodes: Object3D[] = [];
|
|
|
+ const initVals = [121.44782257080078, -121.4478];
|
|
|
+ const finalVals = [58, -58];
|
|
|
+ model.traverse((child) => {
|
|
|
+ if (child.name === "01") {
|
|
|
+ nodes[0] = child;
|
|
|
+ } else if (child.name === "04") {
|
|
|
+ nodes[1] = child;
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ return (open: boolean) =>
|
|
|
+ animation(
|
|
|
+ nodes.map((node) => node.position.x),
|
|
|
+ open ? finalVals : initVals,
|
|
|
+ (data) => {
|
|
|
+ nodes.forEach((node, i) => (node.position.x = data[i]));
|
|
|
+ render();
|
|
|
+ }
|
|
|
+ );
|
|
|
+ },
|
|
|
+ "chuang.svg": (model, render) => {
|
|
|
+ let nodes: Object3D[] = [];
|
|
|
+ model.traverse((child) => {
|
|
|
+ if (child.name === "L") {
|
|
|
+ nodes[0] = child;
|
|
|
+ } else if (child.name === "R") {
|
|
|
+ nodes[1] = child;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ const initVals = nodes.map((item) => ({
|
|
|
+ position: item.position.clone(),
|
|
|
+ quat: item.quaternion.clone(),
|
|
|
+ }));
|
|
|
+ const changes = [
|
|
|
+ { origin: new Vector3(108, 0, 0), angle: -Math.PI / 3 },
|
|
|
+ { origin: new Vector3(-108, 0, 0), angle: Math.PI / 3 },
|
|
|
+ ];
|
|
|
+ const finalVals = initVals.map((item, i) => {
|
|
|
+ const { origin, angle } = changes[i];
|
|
|
+ const qua = new Quaternion().setFromAxisAngle(
|
|
|
+ { x: 0, y: 1, z: 0 },
|
|
|
+ angle
|
|
|
+ );
|
|
|
+ const finalPosition = item.position
|
|
|
+ .clone()
|
|
|
+ .sub(origin)
|
|
|
+ .applyQuaternion(qua)
|
|
|
+ .add(origin);
|
|
|
+ const finalQua = item.quat.clone().multiply(qua);
|
|
|
+ return {
|
|
|
+ position: finalPosition,
|
|
|
+ quat: finalQua,
|
|
|
+ };
|
|
|
+ });
|
|
|
+
|
|
|
+ return (open: boolean) =>
|
|
|
+ animation(
|
|
|
+ nodes.map((node) => ({
|
|
|
+ position: node.position,
|
|
|
+ quat: node.quaternion,
|
|
|
+ })),
|
|
|
+ open ? finalVals : initVals,
|
|
|
+ (data) => {
|
|
|
+ nodes.forEach((node, i) => {
|
|
|
+ node.position.copy(data[i].position);
|
|
|
+ node.quaternion.copy(data[i].quat);
|
|
|
+ });
|
|
|
+ render();
|
|
|
+ }
|
|
|
+ );
|
|
|
+ },
|
|
|
+ "yimen.svg": (model, render) => {
|
|
|
+ let node: Object3D;
|
|
|
+ model.traverse((child) => {
|
|
|
+ if (child.name === "16668_84x96_Slider_Door-Black_V1001_0") {
|
|
|
+ node = child;
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ const initVal = node!.position.x;
|
|
|
+ const finalVal = initVal - 100;
|
|
|
+ return (open: boolean) =>
|
|
|
+ animation(
|
|
|
+ { x: node.position.x },
|
|
|
+ { x: open ? finalVal : initVal },
|
|
|
+ (data) => {
|
|
|
+ node.position.setX(data.x);
|
|
|
+ render();
|
|
|
+ }
|
|
|
+ );
|
|
|
+ },
|
|
|
+
|
|
|
+ "piaochuang.svg": (model, render) => {
|
|
|
+ let nodes: Object3D[] = [];
|
|
|
+ const names = ["01", "02", "03", "04"]
|
|
|
+ model.traverse((child) => {
|
|
|
+ if (names.includes(child.name)) {
|
|
|
+ nodes.push(child)
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ const initVals = nodes.map((item) => ({
|
|
|
+ position: item.position.clone(),
|
|
|
+ quat: item.quaternion.clone(),
|
|
|
+ }));
|
|
|
+ const changes = [
|
|
|
+ { origin: new Vector3(80, 0, 0), angle: Math.PI / 3 },
|
|
|
+ { origin: new Vector3(80, 0, 0), angle: -Math.PI / 3 },
|
|
|
+ { origin: new Vector3(-80, 0, 0), angle: Math.PI / 3 },
|
|
|
+ { origin: new Vector3(-80, 0, 0), angle: -Math.PI / 3 },
|
|
|
+ ];
|
|
|
+ const finalVals = initVals.map((item, i) => {
|
|
|
+ const { origin, angle } = changes[i];
|
|
|
+ const qua = new Quaternion().setFromAxisAngle(
|
|
|
+ { x: 0, y: 1, z: 0 },
|
|
|
+ angle
|
|
|
+ );
|
|
|
+ const finalPosition = item.position
|
|
|
+ .clone()
|
|
|
+ .sub(origin)
|
|
|
+ .applyQuaternion(qua)
|
|
|
+ .add(origin);
|
|
|
+ const finalQua = item.quat.clone().multiply(qua);
|
|
|
+ return {
|
|
|
+ position: finalPosition,
|
|
|
+ quat: finalQua,
|
|
|
+ // quat: item.quat
|
|
|
+ };
|
|
|
+ });
|
|
|
+
|
|
|
+ return (open: boolean) =>
|
|
|
+ animation(
|
|
|
+ nodes.map((node) => ({
|
|
|
+ position: node.position,
|
|
|
+ quat: node.quaternion,
|
|
|
+ })),
|
|
|
+ open ? finalVals : initVals,
|
|
|
+ (data) => {
|
|
|
+ nodes.forEach((node, i) => {
|
|
|
+ node.position.copy(data[i].position);
|
|
|
+ node.quaternion.copy(data[i].quat);
|
|
|
+ });
|
|
|
+ render();
|
|
|
+ }
|
|
|
+ );
|
|
|
+ },
|
|
|
+};
|
|
|
+
|
|
|
+export const getModelSwitch = (type: string) => {
|
|
|
+ const ndx = type.lastIndexOf("/");
|
|
|
+ if (~ndx) {
|
|
|
+ type = type.substring(ndx + 1);
|
|
|
+ }
|
|
|
+ return switchResources[type];
|
|
|
+};
|
|
|
+
|
|
|
export const getLevel = (type: string, fullHeight: number) => {
|
|
|
const ndx = type.lastIndexOf("/");
|
|
|
if (~ndx) {
|