working on simManage

This commit is contained in:
STEINNI
2026-06-21 21:09:21 +00:00
parent 06a7868882
commit e2f8766b9f
17 changed files with 484 additions and 51 deletions
+134 -8
View File
@@ -9,6 +9,16 @@ export class Threetobus{
this._curEventsMapping = []
this._stagedEventsMapping = options.eventsMapping
this.sceneSize = options.sceneSize
this._observerConfig = {
actionsChannel: 'system:requests:observer',
subscribeFrequencyMs: 1000,
cameraDebounceMs: 600,
...options.observer,
}
this._frustumRenderEngine = null
this._frustumDebounceTimer = null
this._frustumCameraChangeHandler = null
this._frustumWatching = false
this.commitConfig()
this.cameras = {}
@@ -19,8 +29,24 @@ export class Threetobus{
}
busReconnect(){
this.commitConfig() // To resubscribe...
//TODO : Not ideal because if we're in the middle of non-commited changes...
this.commitConfig()
if(this._frustumWatching) this._scheduleFrustumResubscribe()
}
resolveSubscriberChan(chanTemplate) {
if(typeof(chanTemplate) !== 'string') return(null)
const uid = app.User?.identity?.uuid
if(!uid) return(null)
return(chanTemplate.replace(/\[UID\]/g, uid).replace(/\{uid\}/g, uid))
}
_resolvedEventsMapping() {
if(!Array.isArray(this._stagedEventsMapping)) return([])
return(this._stagedEventsMapping.map(chanObj => {
const chan = this.resolveSubscriberChan(chanObj.chan)
if(!chan) return(null)
return({ ...chanObj, chan })
}).filter(Boolean))
}
get EventsMapping() { return this._stagedEventsMapping }
@@ -28,15 +54,21 @@ export class Threetobus{
set EventsMapping(newConfig) { this._stagedEventsMapping = newConfig }
async commitConfig(){
const resolvedMapping = this._resolvedEventsMapping()
const chansToAdd = []
const chansToKeep = []
for(const chanObj of this._stagedEventsMapping){
for(const chanObj of resolvedMapping){
if(this._curEventsMapping.map(item => item.chan).includes(chanObj.chan)) chansToKeep.push(chanObj)
else chansToAdd.push(chanObj)
}
const chansToDel = this._curEventsMapping.filter(item => (!chansToKeep.map(c=>c.chan).includes(item.chan) && !chansToAdd.map(c=>c.chan).includes(item.chan)))
await app.MessageBus.subscribe(chansToAdd.map(item => item.chan))
await app.MessageBus.unSubscribe(chansToDel.map(item => item.chan))
const chansToDel = this._curEventsMapping.filter(item => (
!chansToKeep.map(c => c.chan).includes(item.chan)
&& !chansToAdd.map(c => c.chan).includes(item.chan)
))
if(app.MessageBus?.connected) {
if(chansToAdd.length) await app.MessageBus.subscribe(chansToAdd.map(item => item.chan))
if(chansToDel.length) await app.MessageBus.unSubscribe(chansToDel.map(item => item.chan))
}
// console.log('subscribe:', chansToAdd.map(item => item.chan))
// console.log('unSubscribe:', chansToDel)
@@ -59,7 +91,7 @@ export class Threetobus{
app.MessageBus.removeBusListener(eventToDel.eventName, this.processBusEvent.bind(this, eventToDel.eventName), 'threetobus')
}
this._curEventsMapping = this.deepClone(this._stagedEventsMapping)
this._curEventsMapping = this.deepClone(resolvedMapping)
}
deepClone(obj) { // Needed because structuredClone doesn't take functions (and we have transformers)
@@ -88,7 +120,8 @@ export class Threetobus{
// if yes : how to discriminate static value from event-mapping definition ?
if(mapping.child) id += '_'+mapping.child
if(id){
const obj3D = this.scene.getObjectByName(id)
let obj3D = this.scene.getObjectByName(id)
if(!obj3D) obj3D = this._ensurePlaceholderAgent(id)
if(obj3D){
this.assignFromConfig(payload, mapping, obj3D)
}
@@ -161,6 +194,99 @@ export class Threetobus{
return(path.split('.').reduce((acc, key) => acc?.[key], obj))
}
watchCameraFrustum(renderEngine) {
if(!renderEngine || renderEngine.mode !== '3D') return
if(!app.MessageBus?.config?.enabled) {
console.warn('[Threetobus] MessageBus disabled — camera frustum watch skipped')
return
}
this._frustumRenderEngine = renderEngine
this._frustumWatching = true
if(!this._frustumCameraChangeHandler) {
this._frustumCameraChangeHandler = () => this._scheduleFrustumResubscribe()
renderEngine.controls.addEventListener('change', this._frustumCameraChangeHandler)
}
app.MessageBus.whenConnected(() => {
this.commitConfig()
this._scheduleFrustumResubscribe()
})
}
stopWatchingCameraFrustum() {
this._frustumWatching = false
if(this._frustumDebounceTimer) {
clearTimeout(this._frustumDebounceTimer)
this._frustumDebounceTimer = null
}
if(this._frustumRenderEngine?.controls && this._frustumCameraChangeHandler) {
this._frustumRenderEngine.controls.removeEventListener('change', this._frustumCameraChangeHandler)
}
this._frustumRenderEngine = null
this._frustumCameraChangeHandler = null
}
_scheduleFrustumResubscribe() {
if(!this._frustumWatching) return
if(this._frustumDebounceTimer) clearTimeout(this._frustumDebounceTimer)
this._frustumDebounceTimer = setTimeout(() => {
this._frustumDebounceTimer = null
this._resubscribeCameraFrustum()
}, this._observerConfig.cameraDebounceMs)
}
async _resubscribeCameraFrustum() {
if(!this._frustumWatching || !this._frustumRenderEngine) return
if(!app.MessageBus?.connected) return
const camera = this._frustumRenderEngine.camera
if(!camera?.isPerspectiveCamera) return
const planes = this._cameraFrustumPlanes(camera)
const chan = this._observerConfig.actionsChannel
try {
await app.MessageBus.requestBusAction(
chan,
'SUBSCRIBEFRUSTUM',
{
planes,
frequency: this._observerConfig.subscribeFrequencyMs,
},
10000
)
} catch(err) {
console.warn('[Threetobus] SUBSCRIBEFRUSTUM failed:', err)
}
}
_cameraFrustumPlanes(camera) {
camera.updateMatrixWorld(true)
const matrix = new THREE.Matrix4().multiplyMatrices(
camera.projectionMatrix,
camera.matrixWorldInverse
)
const frustum = new THREE.Frustum().setFromProjectionMatrix(matrix)
return(frustum.planes.map(plane => ({
nx: plane.normal.x,
ny: plane.normal.y,
nz: plane.normal.z,
d: plane.constant,
})))
}
_ensurePlaceholderAgent(agentId) {
const geo = new THREE.BoxGeometry(0.4, 0.4, 0.4)
const mat = new THREE.MeshStandardMaterial({ color: 0x44aa88 })
const obj3D = new THREE.Mesh(geo, mat)
obj3D.name = agentId
this.tweensRegistry[agentId] = { move: null, rotate: null }
this.scene.add(obj3D)
return(obj3D)
}
initScene(options){
// Scene
this.scene = new THREE.Scene()