Skip to content

105. 实战:酷家乐装修编辑器(九)

Published:

我们墙、门、窗、地板、天花板都绘制完成后,这节来绘制一些具体的户型:

2025-06-19 06.41.08.gif

比如酷家乐里这个三室一厅一厨一卫的户型:

image.png

在酷家乐里,点击户型库,可以搜索各种小区的户型:

2025-06-19 06.48.41.gif

选择一个户型后,会清空当前设计,替换成新户型:

2025-06-19 06.51.14.gif

2025-06-19 06.56.40.gif

当然,我们不会内置那么多户型,写这两个户型就好了:

image.png

你可以从酷家乐的户型库里搜一下杭州 > 梦琴湾 > 一室 的这俩户型。

我们写这两个简单的:

首先是这个一室的户型:

2025-06-24 17.39.13.gif

然后是这个户型:

2025-06-24 17.40.21.gif

首先重构下代码:

创建 store/house1.ts

import type { State } from ".";

const data: State['data'] = {
    walls: [
        {
            position: { x: 0, y: 0, z: 0},
            width: 800,
            height: 500,
            depth: 30,
            windows: [
                {
                    leftBottomPosition: {
                        left: 100,
                        bottom: 50
                    },
                    width: 300,
                    height: 300
                }
            ]
        },
        {
            position: { x: 0, y: 0, z: 800},
            width: 800,
            height: 500,
            depth: 30,
            windows: [
                {
                    leftBottomPosition: {
                        left: 100,
                        bottom: 100
                    },
                    width: 600,
                    height: 300
                }
            ]
        },
        {
            position: { x: 0, y: 0, z: 0},
            width: 800,
            height: 500,
            depth: 30,
            rotationY: -Math.PI / 2,
            windows: [
            ]
        },
        {
            position: { x: 800, y: 0, z: 0},
            width: 800,
            height: 500,
            depth: 30,
            rotationY: -Math.PI / 2,
            windows: [
            ],
            doors: [
                {
                    leftBottomPosition: {
                        left: 200,
                        bottom: 20
                    },
                    width: 300,
                    height: 400
                }
            ]
        }
    ],
    floors: [
        {
            points: [
                { x: 0, z: 0},
                { x: 0, z: 800},
                { x: 800, z: 800},
                { x: 800, z: 0},
                { x: 0, z: 0 }
            ]
        }
    ],
    ceilings: [
        {
            points: [
                { x: 0, z: 0},
                { x: 0, z: 800},
                { x: 800, z: 800},
                { x: 800, z: 0},
                { x: 0, z: 0 }
            ],
            height: 500
        }
    ]
};

export default data;

我们把数据从 store/index.ts 转移到了这里。

这里用到的 State 类型需要导出:

image.png

然后把 data 换成从 store/house1.ts 导入的。

看下效果:

2025-06-24 17.49.09.gif

可以看到,渲染是正常的。

不过应该是房子的中心点在坐标原点。

我们用包围盒 Box3 来计算下:

image.png

image.png

首先,我们之前是把 floor、wall、ceiling 单独添加到 scene 的。

这次我们先把它添加到一个 group 再添加到 scene。

然后来计算这个 group 的包围盒大小:

image.png

const box3 = new THREE.Box3();
box3.expandByObject(house);

const center = box3.getCenter(new THREE.Vector3());
house.position.set(-center.x, -center.y, -center.z);

用 Box3 包围这个 group,拿到 center,做一下反向位移。

这样,房子的中心就移到坐标原点了:

2025-06-24 17.57.27.gif

然后我们来观察下这个户型:

image.png

客餐厅是 28.89 平方米。

尺寸的单位是毫米,7380 毫米就是 7.38 米。

如果按照这个单位的话,那一般的数值都是好几千的数量级。

我们现在都是按照几百的数量级来绘制的。

先把数值改一下:

比如高度是 3000 毫米也就是 3 米。

两面的墙宽度 5 米:

image.png

高 3 米宽 5 米,厚度 0.2 米:

image.png

import type { State } from ".";

const data: State['data'] = {
    walls: [
        {
            position: { x: 0, y: 0, z: 0},
            width: 5000,
            height: 3000,
            depth: 200,
            windows: []
        }
    ],
    floors: [
    ],
    ceilings: [
    ]
};

export default data;

绘制出来是这样的:

2025-06-24 18.06.13.gif

数量级大了,那相机的位置也应该调一下:

image.png

const camera = new THREE.PerspectiveCamera(60, width / height, 1, 100000);
camera.position.set(8000, 8000, 5000);

差不多是大一个数量级。

2025-06-24 18.10.21.gif

这样视野就好多了。

继续画另一面墙:

image.png

image.png

{
    position: { x: 0, y: 0, z: 7000},
    width: 5000,
    height: 3000,
    depth: 200,
    windows: []
},

间隔 7 米。

然后画另外两面:

image.png

门窗先不画。

image.png

{
    position: { x: 0, y: 0, z: 0},
    width: 7000,
    height: 3000,
    depth: 200,
    rotationY: -Math.PI / 2,
    windows: []
},
{
    position: { x: 5000, y: 0, z: 0},
    width: 7000,
    height: 3000,
    depth: 200,
    rotationY: -Math.PI / 2,
    windows: []
},

2025-06-24 18.15.03.gif

这样,四面墙就画完了。

然后加上窗户:

image.png

宽度、高度、离地、距离左侧距离,都有具体的数据。

image.png

{
    leftBottomPosition: {
        left: 2867,
        bottom: 900
    },
    width: 2100,
    height: 1620
}

把数据填上,就可以画出窗户来:

2025-06-24 18.20.34.gif

另一边的门也是一样:

image.png

image.png

{
    leftBottomPosition: {
        left: 6084,
        bottom: 0
    },
    width: 856,
    height: 2152
}

数据都是从户型数据里来的,你可以对比下。

2025-06-24 18.23.33.gif

这样,门就画好了。

有个小问题:

image.png

墙这里因为内部是 7 米,但还有个墙的厚度没加上,所以这两面墙宽度要多加 0.2 米:

image.png

这样就好了:

2025-06-24 18.27.19.gif

加上地板、天花板:

image.png

floors: [
    {
        points: [
            { x: 0, z: 0},
            { x: 0, z: 7000},
            { x: 5000, z: 7000},
            { x: 5000, z: 0},
            { x: 0, z: 0 }
        ]
    }
],
ceilings: [
    {
        points: [
            { x: 0, z: 0},
            { x: 0, z: 7000},
            { x: 5000, z: 7000},
            { x: 5000, z: 0},
            { x: 0, z: 0 }
        ],
        height: 3000
    }
]

2025-06-24 18.29.56.gif

和酷家乐的对比下:

2025-06-24 18.31.43.gif

一样的。

案例代码上传了小册仓库

总结

这节我们绘制了第一个户型。

从酷家乐里招了两个户型图,它的单位是毫米,我们根据数据绘制出了 4 面墙、一个窗、一个门,还有天花板和地板。

户型绘制出来效果和酷家乐的是一样的。

并且,我们还用 Box3 包围盒计算出了户型的中心位置,把它移到了坐标原点。

下节我们继续绘制几个户型。

评论