123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476 |
- import {
- Box3,
- BoxGeometry,
- Color,
- DirectionalLight,
- DoubleSide,
- Mesh,
- MeshPhongMaterial,
- MeshPhysicalMaterial,
- MeshStandardMaterial,
- Object3D,
- Vector3,
- } from "three";
- import { GLTFLoader } from "three/examples/jsm/Addons.js";
- const gltfLoader = new GLTFLoader().setPath("/static/models/");
- const normalized = async (model: Object3D, pub = true) => {
- const parent = new Object3D();
- parent.add(model);
- const bbox = new Box3().setFromObject(parent);
- const size = bbox.getSize(new Vector3());
- if (pub) {
- parent.scale.set(1 / size.x, 1 / size.y, 1 / size.z);
- } else {
- const min = Math.max(size.x, size.y, size.z);
- parent.scale.set(1 / min, 1 / min, 1 / min);
- }
- model.traverse((child: any) => {
- if (child.isMesh) {
- child.receiveShadow = true;
- child.castShadow = true;
- }
- });
- const center = new Box3().setFromObject(parent).getCenter(new Vector3());
- parent.position.sub({ x: center.x, y: center.y, z: center.z });
- return parent;
- };
- const resources: Record<string, () => Promise<Object3D>> = {
- "men_l.svg": async () => {
- const gltf = await gltfLoader.loadAsync("door_with_frame/scene.gltf");
- gltf.scene.rotateY(Math.PI);
- gltf.scene.scale.setX(-1);
- return await normalized(gltf.scene);
- },
- "piaochuang.svg": async () => {
- const gltf = await gltfLoader.loadAsync("window_1/scene.gltf");
- gltf.scene.rotateY(Math.PI);
- gltf.scene.traverse((node: any) => {
- if (!node.isMesh) return;
- if (node.name.includes("Object")) {
- 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, // 可选:表面清漆层(增强反光)
- });
- } else if (node.name.includes("_111111_white_plastic")) {
- node.material = new MeshStandardMaterial({
- color: 0xffffff, // 浅灰色
- metalness: 0.9, // 高金属度
- roughness: 0.3, // 中等粗糙度
- side: DoubleSide,
- });
- } else if (
- node.name.includes("_111111_seam_0") ||
- node.name.includes("_111111__15_0") ||
- node.name.includes("_111111_Aluminium_profile_0")
- ) {
- node.material = new MeshStandardMaterial({
- color: 0xffffff,
- metalness: 0.8,
- roughness: 0.4,
- aoMapIntensity: 1.0,
- side: DoubleSide,
- });
- } else {
- node.material = new MeshPhongMaterial({
- side: DoubleSide,
- color: 0xffffff,
- });
- }
- });
- const model = await normalized(gltf.scene);
- model.scale.add({ x: 0.00015, y: 0.0001, z: 0 });
- model.position.add({ x: -0.01, y: -0.005, z: 0.02 });
- return model;
- },
- "chuang.svg": async () => {
- const gltf = await gltfLoader.loadAsync("window (3)/scene.gltf");
- return await normalized(gltf.scene);
- },
- "yimen.svg": async () => {
- const gltf = await gltfLoader.loadAsync("sliding_door/scene.gltf");
- return await normalized(gltf.scene);
- },
- "shuangkaimen.svg": async () => {
- const gltf = await gltfLoader.loadAsync(
- "white_double_windowed_door/scene.gltf"
- );
- return await normalized(gltf.scene);
- },
- "luodichuang.svg": async () => {
- const gltf = await gltfLoader.loadAsync("window2/scene.gltf");
- gltf.scene.traverse((node: any) => {
- if (node.name?.includes("glass_0")) {
- 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, // 可选:表面清漆层(增强反光)
- });
- }
- });
- return await normalized(gltf.scene);
- },
- "DoubleBed.svg": async () => {
- const gltf = await gltfLoader.loadAsync("bed/scene.gltf");
- const models: Object3D[] = [];
- const delModelName = ["Pillow_2002", "Plane002"];
- gltf.scene.traverse((child: any) => {
- if (delModelName.some((n) => n === child.name)) {
- models.push(child);
- }
- });
- models.forEach((m) => m.parent?.remove(m));
- const model = await normalized(gltf.scene);
- model.position.setY(model.position.y - 0.131);
- return model;
- },
- "SingleBed.svg": async () => {
- const gltf = await gltfLoader.loadAsync("woodbed/scene.gltf");
- const model = await normalized(gltf.scene);
- model.rotateY(Math.PI / 2);
- return model;
- },
- sf: async () => {
- const gltf = await gltfLoader.loadAsync(
- "sofa_set_-_4_type_of_sofa_lowpoly./scene.gltf"
- );
- return gltf.scene;
- },
- "ThreeSofa.svg": async () => {
- const gltf = await gltfLoader.loadAsync(
- "sofa_-_game_ready_model/scene.gltf"
- );
- const model = await normalized(gltf.scene, undefined);
- model.traverse((child: any) => {
- if (child.isMesh) {
- child.material.color = new Color(0x444444);
- }
- });
- return model;
- },
- "SingleSofa.svg": async () => {
- const scene = (await getModel("sf"))!;
- const models: Object3D[] = [];
- const pickModelName = ["Cube026"];
- scene.traverse((child: any) => {
- if (pickModelName.some((n) => n === child.name)) {
- models.push(child);
- }
- });
- const model = new Object3D().add(...models.map((item) => item.clone()));
- model.rotateY(Math.PI / 2);
- return await normalized(model);
- },
- "Desk.svg": async () => {
- const scene = (await getModel("sf"))!;
- const models: Object3D[] = [];
- const pickModelName = ["Cube004"];
- scene.traverse((child: any) => {
- if (pickModelName.some((n) => n === child.name)) {
- models.push(child);
- }
- });
- const model = new Object3D().add(...models.map((item) => item.clone()));
- model.rotateY(Math.PI / 2);
- return await normalized(model);
- },
- "TeaTable.svg": async () => {
- return (await getModel("Desk.svg"))!.clone();
- },
- "DiningTable.svg": async () => {
- const desk = new Object3D().add((await getModel("Desk.svg"))!.clone());
- const chair = (await getModel("Chair.svg"))!;
- const model = new Object3D();
- const lt = chair.clone();
- lt.position.set(-0.14, -0.5, 0.25);
- lt.scale.set(0.5, 1.2, 0.8);
- lt.rotateY(Math.PI);
- model.add(lt);
- const rt = chair.clone();
- rt.position.set(0.14, -0.5, 0.25);
- rt.scale.set(0.5, 1.2, 0.8);
- rt.rotateY(Math.PI);
- model.add(rt);
- const lb = chair.clone();
- lb.position.set(-0.14, -0.5, -0.25);
- lb.scale.set(0.5, 1.2, 0.8);
- model.add(lb);
- const rb = chair.clone();
- rb.position.set(0.14, -0.5, -0.25);
- rb.scale.set(0.5, 1.2, 0.8);
- model.add(rb);
- desk.scale.set(1.2, 1, 0.55);
- model.add(desk);
- const nModel = await normalized(model);
- nModel.position.sub({ x: 0, y: 0.075, z: 0 });
- return nModel;
- },
- "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 });
- return model;
- },
- "TV.svg": async () => {
- const gltf = await gltfLoader.loadAsync("tv_and_tv_stand/scene.gltf");
- const model = await normalized(gltf.scene, undefined);
- return model;
- },
- "Plant.svg": async () => {
- const gltf = await gltfLoader.loadAsync("pothos_plant/scene.gltf");
- const model = await normalized(gltf.scene, undefined);
- return model;
- },
- "Washstand.svg": async () => {
- const gltf = await gltfLoader.loadAsync("washbasin/scene.gltf");
- gltf.scene.rotateY(Math.PI);
- const model = await normalized(gltf.scene, undefined);
- return model;
- },
- "Closestool.svg": async () => {
- const gltf = await gltfLoader.loadAsync("toilet/scene.gltf");
- const model = await normalized(gltf.scene, undefined);
- model.traverse((child: any) => {
- if (child.isMesh) {
- child.material.color = new Color(0xffffff);
- }
- });
- return model;
- },
- "Wardrobe.svg": async () => {
- const gltf = await gltfLoader.loadAsync("wardrobe_14722-22/scene.gltf");
- const model = await normalized(gltf.scene, undefined);
- model.traverse((child: any) => {
- if (child.isMesh) {
- child.material.color = new Color(0xcbc3b3);
- }
- });
- return model;
- },
- "BedsideCupboard.svg": async () => {
- const gltf = await gltfLoader.loadAsync(
- "low_poly_bedside_table/scene.gltf"
- );
- const model = await normalized(gltf.scene, undefined);
- model.traverse((child: any) => {
- if (child.isMesh) {
- child.material.color = new Color(0xffffff);
- }
- });
- return model;
- },
- "CombinationSofa.svg": async () => {
- const tsofa = (await getModel("ThreeSofa.svg"))!.clone();
- const ssofa = (await getModel("SingleSofa.svg"))!.clone();
- const tea = (await getModel("TeaTable.svg"))!.clone();
- const model = new Object3D();
- // tsofa.rotateY(-Math.PI / 2)
- tsofa.scale.multiply({ x: 0.8, y: 1, z: 0.4 });
- tsofa.position.add({ x: -0, y: 0, z: -0.6 });
- model.add(tsofa);
- ssofa.rotateY(-Math.PI / 2);
- ssofa.scale.multiply({ x: 0.4, y: 1, z: 0.4 });
- ssofa.position.add({ x: -0.15, y: 0, z: -2.2 });
- model.add(ssofa);
- tea.scale.multiply({ x: 0.8, y: 0.5, z: 0.4 });
- tea.position.add({ x: -0, y: -0.13, z: 0 });
- model.add(tea);
- return normalized(model);
- },
- kitchen: async () => {
- const gltf = await gltfLoader.loadAsync(
- "basic_kitchen_cabinets_and_counter/scene.gltf"
- );
- gltf.scene.rotateY(-Math.PI);
- return gltf.scene;
- },
- "Cupboard.svg": async () => {
- const gltf = await gltfLoader.loadAsync("kitchen_cabinets (1)/scene.gltf");
- gltf.scene.rotateY(Math.PI / 2);
- const model = await normalized(gltf.scene);
- model.traverse((child: any) => {
- if (
- child.isMesh &&
- ["pCube1_cor_0", "pCube8_cor_0"].includes(child.name)
- ) {
- child.material.color = new Color(0xffffff);
- }
- });
- return model;
- },
- "GasStove.svg": async () => {
- const gltf = await gltfLoader.loadAsync("burner_gas_stove/scene.gltf");
- const model = await normalized(gltf.scene);
- model.traverse((child: any) => {
- if (child.isMesh) {
- child.material.emissive = new Color(0x222222)
- }
- });
- return model;
- },
- };
- export const levelResources: Record<
- string,
- {
- bottom?: number | string;
- height?: number | string | "full";
- top?: number | string;
- }
- > = {
- "SingleBed.svg": {
- height: 70,
- },
- "ThreeSofa.svg": {
- height: 90,
- },
- "SingleSofa.svg": {
- height: 90,
- },
- "CombinationSofa.svg": {
- height: 90,
- },
- "Desk.svg": {
- height: 80,
- },
- "TeaTable.svg": {
- height: 50,
- },
- "DiningTable.svg": {
- height: 100,
- },
- "Chair.svg": {
- height: 80,
- },
- "TV.svg": {
- height: 120,
- },
- "Washstand.svg": {
- height: 100,
- },
- "Closestool.svg": {
- height: 45,
- },
- "Wardrobe.svg": {
- height: "full",
- },
- "BedsideCupboard.svg": {
- height: 50,
- },
- "piaochuang.svg": {
- top: 4,
- bottom: 40,
- },
- "men_l.svg": {
- height: "full",
- },
- "yimen.svg": {
- height: "full",
- },
- "shuangkaimen.svg": {
- height: "full",
- },
- "luodichuang.svg": {
- height: "full",
- },
- "Cupboard.svg": {
- height: "full",
- },
- "GasStove.svg": {
- height: 10,
- bottom: "0.335",
- },
- };
- export const getLevel = (type: string, fullHeight: number) => {
- const ndx = type.lastIndexOf("/");
- if (~ndx) {
- type = type.substring(ndx + 1);
- }
- const transform = (data: any): Record<string, number> => {
- const tdata: Record<string, number> = {};
- for (const key of Object.keys(data)) {
- if (data[key] === "full") {
- tdata[key] = fullHeight;
- } else if (typeof data[key] === "string") {
- tdata[key] = parseFloat(data[key]) * fullHeight;
- } else {
- tdata[key] = data[key];
- }
- }
- return tdata;
- };
- if (!levelResources[type]) {
- return {};
- }
- const data = transform(levelResources[type]);
- if (!data.height && "top" in data && "bottom" in data) {
- data.height = fullHeight - data.top - data.bottom;
- }
- return data;
- };
- export const getModel = (() => {
- const typeModels: Record<string, Promise<Object3D | undefined>> = {};
- return (type: string) => {
- const ndx = type.lastIndexOf("/");
- if (~ndx) {
- type = type.substring(ndx + 1);
- }
- if (type in typeModels) {
- return typeModels[type];
- }
- if (type in resources) {
- typeModels[type] = resources[type]();
- typeModels[type].catch(() => {
- delete typeModels[type];
- });
- return typeModels[type];
- }
- };
- })();
- export const fullMesh = new Mesh(
- new BoxGeometry(1, 1, 1),
- new MeshPhongMaterial({ color: 0xffffff })
- );
- fullMesh.receiveShadow = fullMesh.castShadow = true;
|