案例展示
在场景中导入外部模型
模型格式
支持 gltf、glb、fbx、obj、stl、json、zip、text、assemble 等格式。
<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 |