Skip to content

案例展示

改变模型材质

模型:
颜色:
粗糙度:
0.2
金属度:
0.2
<template>
    <div class="vp-px2vw">
        <div class="demo-wrap">
            <div id="TO"></div>
            <div id="formContainer">
                <ModelMaterialForm
                    :mesh-cascader-options="meshCascaderOptions"
                    @apply-material="handleMaterialChange" />
            </div>
        </div>
    </div>
</template>

<script setup lang="ts">
// @ts-ignore
declare const ThingOrigin: any
import { ref, onMounted } from 'vue'
import ModelMaterialForm, {
    type MaterialApplyPayload,
    type MeshCascaderLeaf,
    type MeshCascaderOption
} from './ModelMaterialForm.vue'

const meshCascaderOptions = ref<MeshCascaderOption[]>([])

let TO: any = null
let modelList = [
    {
        modelType: 'gltf',
        modelName: 'ABB机器人-1',
        id: 3012,
        position: {
            x: 0,
            y: 0,
            z: 0
        },
        base: {
            modelUrl: 'https://124.70.30.193:8443/model2/loadFileAsId/56201403'
        }
    },
    {
        modelType: 'gltf',
        modelName: '非皮带传送带-1',
        id: 3021,
        position: {
            x: -24.90687943165758,
            y: 0,
            z: 19.922502148523417
        },
        base: {
            modelUrl: 'https://124.70.30.193:8443/model2/loadFileAsId/258642254'
        }
    },
    {
        modelType: 'gltf',
        modelName: '非皮带传送带-2',
        id: 3021,
        position: {
            x: 40.90687943165758,
            y: 0,
            z: 19.922502148523417
        },
        base: {
            modelUrl: 'https://124.70.30.193:8443/model2/loadFileAsId/258642254'
        }
    },
    {
        modelType: 'gltf',
        modelName: '精雕机床-1',
        id: 3016,
        position: {
            x: -6,
            y: 0,
            z: -23
        },
        rotation: {
            x: 0,
            y: 3.14,
            z: 0
        },
        base: {
            modelUrl: 'https://124.70.30.193:8443/model2/loadFileAsId/701730674'
        }
    },
    {
        modelType: 'gltf',
        modelName: '数字机床-1',
        id: 3014,
        position: {
            x: 46,
            y: 0,
            z: -4
        },
        rotation: {
            x: 3.14,
            y: 0,
            z: 3.14
        },
        base: {
            modelUrl: 'https://124.70.30.193:8443/model2/loadFileAsId/878966042'
        }
    }
]
onMounted(async () => {
    initScene()
})

function initScene() {
    //初始化场景
    TO = new ThingOrigin(
        'fileModel',
        document.getElementById('TO'),
        {
            scene: {
                ground: {
                    //设置地面
                    active: false
                },
                background: {
                    type: 'sky'
                },
                renderQuality: {
                    toneMapping: {
                        exposure: 0.8 //曝光度
                    }
                }
            },
            camera: {
                //相机参数
                lookAt: {
                    x: 25.62454680418967,
                    y: -2.3106037456621856,
                    z: -66.99965038950995
                },
                position: {
                    x: 41.87976418982612,
                    y: 78.50564268979477,
                    z: 117.31701183506453
                }
            },
            modelList: modelList
        },
        (TO: any) => {
            console.log('模型加载完成')
            buildMeshCascaderOptions()
        }
    )
}

//级联项文案:仅当前节点名称,无名则用类型 + uuid 前缀
function nodeDisplayLabel(obj: any): string {
    const n = obj.name?.trim()
    if (n) return n
    const t = obj.type ?? 'Object3D'
    return `(${t})_${obj.uuid?.slice(0, 8) ?? '?'}`
}

//构建材质级联选项
function buildMeshCascaderOptions() {
    if (!TO) return
    const options: MeshCascaderOption[] = []
    for (const item of modelList) {
        const root = getModel(item.modelName)
        const children: MeshCascaderLeaf[] = []
        if (root) {
            root.traverse((child: any) => {
                if (child.isMesh) {
                    children.push({
                        value: child.uuid,
                        label: nodeDisplayLabel(child)
                    })
                }
            })
        }
        options.push({
            value: item.modelName,
            label: item.modelName,
            children
        })
    }
    meshCascaderOptions.value = options
}

//查找模型中的Mesh
function findMeshInModel(modelName: string, meshUuid: string): any | null {
    const root = getModel(modelName)
    if (!root) return null
    let found: any = null
    root.traverse((child: any) => {
        if (child.isMesh && child.uuid === meshUuid) found = child
    })
    return found
}

// 应用材质到 Mesh(数值由子组件 emit 的 payload 提供)
function applyMaterialToMesh(mesh: any, payload: MaterialApplyPayload) {
    const mats = Array.isArray(mesh.material) ? mesh.material : [mesh.material]
    for (const mat of mats) {
        if (!mat) continue
        if (mat.color !== undefined) mat.color.set(payload.color)
        if (mat.metalness !== undefined) mat.metalness = payload.metalness
        if (mat.roughness !== undefined) mat.roughness = payload.roughness
    }
}

// 只对级联选中的 Mesh 生效(两级:[模型名, meshUuid])
function handleMaterialChange(payload: MaterialApplyPayload) {
    const path = payload.cascaderPath
    if (!path || path.length !== 2) {
        return
    }
    const [modelName, meshUuid] = path
    const mesh = findMeshInModel(modelName, meshUuid)
    if (mesh) {
        applyMaterialToMesh(mesh, payload)
    } else {
        console.log('未找到 Mesh', modelName, meshUuid)
    }
}

/*
 * 根据模型名称获取场景中的模型对象
 */
function getModel(modelName: string) {
    if (!TO) {
        return undefined
    }
    return TO.scene.getObjectByName(modelName)
}
</script>

<style scoped lang="scss">
.vp-px2vw {
    .demo-wrap {
        position: relative;
    }

    #TO {
        width: 100%;
        height: 400px;
        position: relative;
    }

    #formContainer {
        position: absolute;
        top: 10px;
        right: 10px;
        font-size: 14px;
    }
}
</style>