Skip to content

104. 实战:酷家乐装修编辑器(八)

Published:

门窗绘制好了,这节来绘制地板、天花板:

2025-06-18 18.54.45.gif

酷家乐里,除了墙、门窗、也绘制了地板、天花板:

2025-06-18 19.13.50.gif

你可以用一下酷家乐个人版 https://www.kujiale.com/ 体验下,免费用。

相机从下往上看的时候可以看到天花板:

image.png

从上往下看则是看到地板:

2025-06-18 19.16.01.gif

并且客厅、卧室、洗手间的地板材料都不同:

image.png

很明显,我们也要单独存储每个房间的地板数据。

在 store 里加一个 floors 数组:

image.png

floors: [
    {
        points: [
            { x: 0, z: 0},
            { x: 0, z: 800},
            { x: 800, z: 800},
            { x: 800, z: 0},
            { x: 0, z: 0 }
        ]
    }
]

因为地板的形状是不规则的:

image.png

所以要存储一系列的点。

y 都是 0,所以存储 x、z 就好。

类型也加一下:

image.png

interface Floor {
    points: Array<{
        x: number,
        z: number
    }>
}

interface State {
    data: {
        walls: Array<Wall>,
        floors: Array<Floor>
    }
}

然后我们把它绘制出来:

image.png

用 Shape 把所有的点依次连起来,然后用 ShapeGeometry 画出形状。

const floors = data.floors.map(item => {
    const shape = new THREE.Shape();
    shape.moveTo(item.points[0].x, item.points[0].z);
    for(let i = 1; i < item.points.length; i++) {
        shape.lineTo(item.points[i].x, item.points[i].z);
    }

    const geometry = new THREE.ShapeGeometry(shape);
    const material = new THREE.MeshPhongMaterial({
        color: 'orange'
    });
    const floor = new THREE.Mesh(geometry, material);

    return floor;
});
scene.add(...floors);

看下效果:

image.png

可以看到,形状画出来了,但是角度不对。

设置旋转角度和背面可见:

image.png

const material = new THREE.MeshPhongMaterial({
    color: 'orange',
    side: THREE.BackSide
});
const floor = new THREE.Mesh(geometry, material);
floor.rotateX(Math.PI / 2);

2025-06-19 06.13.28.gif

这样就好了。

我们找个地板的纹理图片,默认用木质地板。

image.png

image.png

放 public 目录下:

image.png

代码里加载下:

image.png

因为 ShapeGeometry、ExtrudeGeometry 这种画出来的几何体的 uv 值都特别大,所以要设置一个比较小的 repeat 才能正常展示出纹理。

const textureLoader = new THREE.TextureLoader();
const floorTexture = textureLoader.load('./floor-texture.png');
floorTexture.colorSpace = THREE.SRGBColorSpace;
floorTexture.wrapS =  THREE.RepeatWrapping;
floorTexture.wrapT =  THREE.RepeatWrapping;
floorTexture.repeat.set(0.002, 0.002);

image.png

看下效果:

image.png

没问题。

你可以打印下 geometry 看下 uv 数组就明白了:

image.png

image.png

uv 数组的值都特别大,要设置一个很小的 repeat 才能刚好是 0 到 1,也就是正常映射纹理。

image.png

地板画好后,继续来画天花板:

image.png

ceilings: [
    {
        points: [
            { x: 0, z: 0},
            { x: 0, z: 800},
            { x: 800, z: 800},
            { x: 800, z: 0},
            { x: 0, z: 0 }
        ],
        height: 500
    }
]

数据这里多一个 height,也就是天花板高度。

加下类型:

image.png

interface Ceiling {
    points: Array<{
        x: number,
        z: number
    }>,
    height: number
}

interface State {
    data: {
        walls: Array<Wall>,
        floors: Array<Floor>,
        ceilings: Array<Ceiling>
    }
}

然后画出来:

image.png

const ceilings = data.ceilings.map(item => {
    const shape = new THREE.Shape();
    shape.moveTo(item.points[0].x, item.points[0].z);
    for(let i = 1; i < item.points.length; i++) {
        shape.lineTo(item.points[i].x, item.points[i].z);
    }

    const geometry = new THREE.ShapeGeometry(shape);
    const material = new THREE.MeshPhongMaterial({
        color: '#eee',
        side: THREE.FrontSide
    });
    const ceiling = new THREE.Mesh(geometry, material);
    ceiling.rotateX(Math.PI / 2);
    ceiling.position.y = item.height;
    return ceiling;
});
scene.add(...ceilings);

颜色比墙深一点,然后设置下 position.y,其余部分一样。

2025-06-19 06.41.08.gif

这样天花板就画好了。

因为设置了单面可见,所以从上往下看,看不到天花板,从下网上看,看不到地板。

和酷家乐这个是一样的:

2025-06-18 19.13.50.gif

案例代码上传了小册仓库

总结

这节我们绘制了地板、天花板。

存储一系列的点,用 Shape 画形状,然后用 ShapeGeometry 画出来。

设置单面可见,这样就可以实现从上往下、从下往上只能看到地板或天花板。

墙、门、窗、地板、天花板都完成了,下节就可以来写两室一厅等户型了。

评论