Skip to content

案例展示

在场景中导入外部模型

模型格式

支持 gltfglbfbxobjstljsonziptextassemble 等格式。

<template>
    <div id="TO"></div>
</template>

<script setup>
import { getResourceBase, resUrl } from '../../utils/resourceUrl'
import { onMounted } from 'vue'

const modelList = [
    {
        modelType: 'glb',
        modelName: 'glb-abb',
        label: 'ABB机械臂',
        base: {
            modelUrl: resUrl('/editor/model2/loadFileAsId/56201403')
        },
        position: { x: -130, y: 0, z: 0 },
        scale: { x: 1, y: 1, z: 1 },
        rotation: { x: 0, y: 0, z: 0 }
    },
    {
        modelType: 'fbx',
        modelName: 'fbx-box',
        label: '物流车集装盒',
        base: {
            modelUrl: resUrl('/editor/model2/loadFileAsId/683898859')
        },
        position: { x: -90, y: 0, z: 0 },
        scale: { x: 0.2, y: 0.2, z: 0.2 }
    },
    {
        modelType: 'gltf',
        modelName: 'gltf-abb01',
        label: 'ABB01机械臂',
        base: {
            modelUrl: resUrl('/editor/model2/loadFileAsId/973363420')
        },
        position: { x: -50, y: 0, z: 0 },
        scale: { x: 10, y: 10, z: 10 }
    },
    {
        modelType: 'stl',
        modelName: 'stl-kettle',
        label: '水壶',
        base: {
            modelUrl: resUrl('/editor/model2/loadFileAsId/367634841')
        },
        position: { x: 0, y: 0, z: 0 },
        rotation: { x: -1.57, y: 0, z: 0 },
        scale: { x: 1.2, y: 1.2, z: 1.2 }
    },
    {
        modelType: 'obj',
        modelName: 'obj-kettle',
        label: 'Kettle',
        base: {
            modelUrl: resUrl('/editor/model2/loadFileAsId/961956159')
        },
        position: { x: 40, y: 0, z: 0 },
        scale: { x: 20, y: 20, z: 20 }
    },
    {
        modelType: 'zip',
        modelName: 'zip-line',
        label: '复合材料生产线',
        base: {
            modelUrl: resUrl('/editor/model2/loadFileAsId/809926274')
        },
        position: { x: 110, y: 0, z: 0 },
        scale: { x: 1.5, y: 1.5, z: 1.5 }
    }
]

function buildLabelHtml(info) {
    const type = info.modelType.toUpperCase()
    return `
        <div class="file-model-label__badge">${type}</div>
        <div class="file-model-label__name">${info.label}</div>
    `
}

/** 3D 场景标签:绑定模型,随模型移动,始终面向相机 */
function addModelLabel(TO, model, info) {
    if (!model || !info) return

    const size = TO.tool.getModelSize(model)
    // setMarkerPosition 已叠加 center + height/2,offset 只需额外间距
    const offsetY = Math.max(size.height * 0.07, 6)
    const markerName = `label-${info.modelName}`

    TO.marker.addMarker(model, buildLabelHtml(info), {
        modelName: markerName,
        markerName,
        type: 'text',
        width: 168,
        height: 62,
        base: {
            relative: 'model',
            offset: { x: 0, y: offsetY, z: 0 },
            ratio: 1
        },
        configs: {
            visible: true,
            html: ''
        }
    })
}

function findModelInfo(model) {
    return modelList.find((item) => item.modelName === model.name)
}

onMounted(() => {
    const TO = new ThingOrigin('fileModel', document.getElementById('TO'), {
        params: {
            resource: {
                serverAddress: getResourceBase()
            }
        },
        scene: {
            background: {
                type: 'color',
                color: {
                    alpha: 1,
                    color: '#000000'
                }
            },
            ground: {
                active: false
            }
        },
        camera: {
            position: {
                x: 0,
                y: 55,
                z: 220
            }
        }
    })

    TO.model.loadModel(modelList, (model) => {
        TO.scene.add(model)
        const info = findModelInfo(model)
        addModelLabel(TO, model, info)
    })
})
</script>

<style scoped>
#TO {
    width: 100%;
    height: 400px;
    position: relative;
}
</style>

<style>
.file-model-label {
    box-sizing: border-box;
    min-width: 120px;
    padding: 8px 14px 10px;
    text-align: center;
    background: linear-gradient(180deg, rgba(15, 23, 42, 0.94) 0%, rgba(15, 23, 42, 0.88) 100%);
    border: 1px solid rgba(125, 211, 252, 0.55);
    border-radius: 8px;
    box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.06) inset, 0 8px 24px rgba(0, 0, 0, 0.35);
    backdrop-filter: blur(4px);
    pointer-events: none;
    user-select: none;
}

.file-model-label__badge {
    display: inline-block;
    padding: 2px 8px;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.08em;
    color: #7dd3fc;
    background: rgba(125, 211, 252, 0.12);
    border: 1px solid rgba(125, 211, 252, 0.35);
    border-radius: 999px;
    line-height: 1.4;
}

.file-model-label__name {
    margin-top: 6px;
    font-size: 13px;
    font-weight: 500;
    line-height: 1.35;
    color: #f8fafc;
    letter-spacing: 0.02em;
    white-space: nowrap;
}
</style>

API 介绍

model.loadModel

方法签名返回值描述
loadModel(modelInfo: modelInfoParams, useIndexedDB: boolean = true)Promise<Object3D>导入外部三维模型

参数说明:

参数名说明类型必填默认值
modelInfo模型参数modelInfoParams--
useIndexedDB使用indexedDB存储boolean-true