实体Entity
实体Entity 是模块的容器,用于连接实体模块EntityModuleBase。它和一个Object3D相关联,通过连接的模块功能,来实现对Object3D的各种操作。

创建新的实体
当直接new一个Entity时,会自动创建一个新的Object3D与之相关联
let entity = new Entity();
entity.addTo(app);
如果往此实体上面加一个子对象时,其对象树状结构如下
let entity = new Entity();
entity.addTo(app);
let child = new Object3D();
entity.addObjects(child);
// └─object3d <-- 实体对应的对象
// └─child 增加的子对象
可以在创建实体时,直接通过参数node指定相关联的Object3D
let obj = new Object3D();
let entity = new Entity({
node: obj
});
entity.addTo(app);
通过一个或多个object3d对象创建一个实体, 上面的示例也可以用下面的语句代码
let obj = new Object3D();
let entity = Entity.fromObject3d(obj);
entity.addTo(app);
如果实体在场景中已存在,可通过attchObject给一个已加入场景中的对象关联一个实体
let obj = mesh; // 场景中的一个实体对象
let entity = Entity.attchObject(obj); // 不用再调用 addTo(app)了
实体移除
通过remove进行移除
entity.remove();
实体属性
基本属性
通过实体的node对象可获取实体相关联的Object3D对象。
调用实体的position、rotation,quaternion,scale,matrix,matrixWorld,visible属性,等同于调用其相关联实体的相关属性。如
entity.visible = false 执行结果与 entity.node.visible = false 相同
其主要区别是通过实体设置的属性是响应式对象,可获取此属性的响应式对象进行监听位置的变化.
// 监听visible响应式对象变化
vjmap3d.reactivity.watch(entity.visibleRef, (v) => app.logInfo(v ? "可见" : "隐藏", 1000))
let svc = new vjmap3d.Service(env.serviceUrl, env.accessToken);
let app = new vjmap3d.App(svc, {
container: "map", // 容器id
})
const geometry = new THREE.BoxGeometry( 1, 1, 1 );
const material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
const cube = new THREE.Mesh( geometry, material );
let entity = vjmap3d.Entity.fromObject3d(cube); // 创建一个来源于object3d的实体
entity.addTo(app);// 把实体加进应用中
const ui = await app.getConfigPane(); // 获取配置面板
let cfg = entity.uiConfig; // 获取实体属性配置
if (cfg) {
ui.appendChild(cfg);
}
pointerEvents
设置实体是否可响应事件
rayIntersectRecursive
拾取时是否遍历子对象 默认为true
RayQueryType
射线查询类型
效果属性
- selected 是否选中
- outline 是否描边效果 (与
selected效果只能二选一) - bloom 是否辉光
- boxHelper 是否显示包围盒
let svc = new vjmap3d.Service(env.serviceUrl, env.accessToken);
let app = new vjmap3d.App(svc, {
container: "map", // 容器id
})
const geometry = new THREE.ConeGeometry(1, 4);
const material = new THREE.MeshBasicMaterial( { color: 0x00ffff } );
const cube = new THREE.Mesh( geometry, material );
let entity = vjmap3d.Entity.fromObject3d(cube); // 创建一个来源于object3d的实体
entity.addTo(app);// 把实体加进应用中
const ui = await app.getConfigPane();
let cfg = {
type: 'folder',
expanded: true,
label: "实体效果属性",
children: vjmap3d.generateUiConfig(entity, [
{
prop: "selected"
},
{
prop: "bloom"
},
{
prop: "boxHelper"
},
{
prop: "outline"
}
])
}
ui.appendChild(cfg)
如果需修改属性效果的颜色等参数,selected、outline、bloom属于后期处理效果,不能单独设置,需要全局统一设置。
boxHelper可以直接通过属性单独进行设置。
let svc = new vjmap3d.Service(env.serviceUrl, env.accessToken);
let app = new vjmap3d.App(svc, {
container: "map", // 容器id
postProcess: {
bloomPass: {
luminanceThreshold: 0.01,
luminanceSmoothing: 0.1,
mipmapBlur: true,
intensity: 3.0,
},
outlinePass: {
edgeStrength: 4.0,
pulseSpeed: 0.4,
visibleEdgeColor: 0x00ffff,
hiddenEdgeColor: 0x634ab0,
},
selectedPass: {
edgeStrength: 8.0,
pulseSpeed: 1,
visibleEdgeColor: 0xff0000,
hiddenEdgeColor: 0xb0634a,
}
}
})
const geometry = new THREE.ConeGeometry(1, 4);
const material = new THREE.MeshBasicMaterial( { color: 0x00ffff } );
const cube = new THREE.Mesh( geometry, material );
let entity = vjmap3d.Entity.fromObject3d(cube); // 创建一个来源于object3d的实体
entity.addTo(app);// 把实体加进应用中
entity.selected = true;
entity.bloom = true;
entity.boxHelper = {
lineColor: "#ff0",
fillColor: "#f0f",
fillOpacity: 0.01
}
设置高亮
- isHighlight 是否高亮
- setHighlight 设置高亮
- clearHighlight 清除高亮
替换材质
- replaceMaterial 替换材质
- resetMaterial 恢复材质
⚠️ 坑:
setOpacity内部也走replaceMaterial的克隆路径——先setOpacity(a)再resetMaterial()恢复的是克隆体而非最初材质引用(对象 identity 变了)。要精确还原本引用,用纯净路径replaceMaterial(new) → resetMaterial(),别混进setOpacity。
模型边框线
- addEdges 增加模型边框线
- clearEdges 移除模型边框线
- getEdges 获取模型边框线
设置透明度
- setOpacity 设置透明度
模块
- addModule 或 add 增加模块
- getModule 获取模块
- removeModule 移除模块
通过ResManagerloadModel加载的资源如果模型的动画,会默认加一个动画模块AnimatorModule
let svc = new vjmap3d.Service(env.serviceUrl, env.accessToken);
let app = new vjmap3d.App(svc, {
container: "map", // 容器id
control:{
initState: { // 相机初始位置
cameraTarget: new THREE.Vector3(-15, 0, -10),
cameraPosition: new THREE.Vector3(-30, 30, -10)
}
},
ui: {
configPane: {
title: "设置",
style: { width: "240px", overflow: "hidden"}
}
}
})
let ent = await vjmap3d.ResManager.loadModel(vjmap3d.ResManager.svrUrl("models/Stork.glb"), {
splitSubEntity: true,
scale: 0.1,
anchor: "front-bottom-right"
});
ent.addTo(app);
//
const ui = await app.getConfigPane();
let cfg = [
{
type: 'slider',
label: '透明度',
value: 1.0,
bounds: [0, 1],
onChange: ({ value } )=> {
ent.setOpacity(value)
}
},
{
type: 'checkbox',
label: '开启动画',
value: false,
onChange: ({ value } )=> {
let mod = ent.getModule(vjmap3d.AnimatorModule);
if (!mod) return
let animator = mod.getAnimator();
if (value) { // 开启动画
animator.play(0)
} else { // 停止动画
animator.stop(0)
}
}
},
{
type: 'checkbox',
label: '高亮',
getValue: () => ent.isHighlight(),
setValue: v => {
if (v) { // 设置高亮
ent.setHighlight("#0ff", 0.2)
} else { // 清除高亮
ent.clearHighlight()
}
}
},
{
type: 'checkbox',
label: '替换材质',
value: false,
onChange: ({ value } )=> {
if (value) { // 替换材质
const material = new THREE.MeshPhongMaterial({
color: '#f00'
});
ent.replaceMaterial(material)
} else { // 恢复材质
ent.resetMaterial()
}
}
},
{
type: 'checkbox',
label: '模型边框线',
value: false,
onChange: ({ value } )=> {
if (value) { // 增加模型边框线
ent.addEdges({
color: 0x00ffff,
}, true)
} else { // 移除模型边框线
ent.clearEdges()
}
}
}
]
cfg.forEach(c => ui.appendChild(c));
常见方法
⚠️
traverseVisible的可见性过滤只在传了filter时生效:traverseVisible(cb)无第二参会遍历全部后代(含visible=false),与"只遍历可见"的字面描述不符;traverseVisible(cb, ()=>true)才会先按visible门控再走回调。要"仅可见且无条件"就传一个恒真 filter。
⚠️
getEntityByName/getEntityById/getEntityByUuid查的是底层 Object3D 属性:实现是getEntityByProperty → 遍历 object.name/id/uuid → 反查实体。给实体包装层赋name只改实体字段、不传播到其 node 的name,查不到;要命中需设置 mesh/Object3D 的.name。同理getObjectByUuid匹配的是object3d.uuid。而getEntitiesByProperty("name", v)也走对象属性——实体自身字段要用别的方式筛。
⚠️ 本节
getEntityByXxx/getObjectByXxx是 Entity 树上的方法,不是 App 上的:App 实例上没有这两个方法(运行时均为undefined);要全局查找请从场景根实体(或app.scene对应的实体入口)调用,查询范围是该实体的后代子树。