import * as THREE from '/app/thirdparty/Three/three.module.js' import { OrbitControls } from '/app/thirdparty/Three/OrbitControls.module.js' import * as TWEEN from '/app/thirdparty/Three/tween.module.js' export class kfArena{ constructor(canvasEl, agentSprites){ Object.assign(this, app.helpers.helpers3D) this.agentSprites = app.Assets.Store.json.agentSprites this.canvasEl = canvasEl this.agentSprites = agentSprites this.renderer = null this.mode = '3D' this.sceneSize = app.Assets.Store.json.arenaConfig.arenaSize this.initScene() this.raycaster = new THREE.Raycaster() this.agents = {} this.onclickAgent = null } initScene(){ // Scene this.scene = new THREE.Scene() // Camera this.camera = new THREE.PerspectiveCamera(75, this.canvasEl.clientWidth / this.canvasEl.clientHeight, 0.1, 1000) this.camera.position.set(3, 3, 5) this.camera.lookAt(0, 0, 0) this.camera.layers.enable(1) this.camera.layers.enable(2) // Lights const light = new THREE.DirectionalLight(0xffffff, 1) light.position.set(5, 5, 5) light.intensity = 2 this.scene.add(light) this.scene.add(new THREE.AmbientLight(0xffffff, 0.4)) this.grid = new THREE.GridHelper(this.sceneSize.x, this.sceneSize.x, 0x8888AA, 0x8888AA) this.grid.layers.set(1) this.scene.add(this.grid) // Base plane const planeGeo = new THREE.PlaneGeometry(100, 100) const planeMat = new THREE.MeshBasicMaterial({ color: 0xaaaacc, opacity: 0.3, transparent: true, // needed for opacity < 1 to take effect side: THREE.DoubleSide }) this.basePlane = new THREE.Mesh(planeGeo, planeMat) this.basePlane.rotation.x = -Math.PI / 2 // lay it flat (like the grid) this.basePlane.position.y=-0.01 // to avoid artefacts on objets bases this.scene.add(this.basePlane) this.axes = new THREE.AxesHelper(this.sceneSize.x/2) this.axes.layers.set(2) this.scene.add(this.axes) this.renderer = new THREE.WebGLRenderer({ antialias: true, canvas: this.canvasEl }) this.init3DHighlighter({ edgeStrength: 3, visibleEdgeColor: 0xffff00, edgeGlow: 1, edgeThickness: 4, pulsePeriod: 2, }) this.canvasEl.addEventListener('click', this.onSceneClick.bind(this)) } startRendering(){ this.addControls() this.render() } render() { TWEEN.update() this.resizeRendererToDisplaySize() if(this.composer) this.composer.render() else this.renderer.render(this.scene, this.camera) requestAnimationFrame(this.render.bind(this)) } resizeRendererToDisplaySize() { const width = this.canvasEl.clientWidth const height = this.canvasEl.clientHeight if (this.canvasEl.width !== width || this.canvasEl.height !== height) { this.renderer.setSize(width, height, false) return true } return false } addControls(){ this.controls = new OrbitControls(this.camera, this.canvasEl) if(this.mode=='2D'){ this.controls.maxPolarAngle = 0 // Math.PI / 2 this.controls.minPolarAngle = 0 // Math.PI / 2 } else if(this.mode=='3D'){ } this.controls.mouseButtons = { LEFT: THREE.MOUSE.ROTATE, // keep orbit on left MIDDLE: THREE.MOUSE.PAN, // pan with middle-click RIGHT: THREE.MOUSE.DOLLY // zoom with right-click } } onSceneClick(event){ // ray from the mouse, through the camera lens, to find the first (most foreground) object if(typeof(this.onclickAgent) != 'function') return const normalizedPointer = new THREE.Vector2() const rect = this.canvasEl.getBoundingClientRect() normalizedPointer.x = ((event.clientX - rect.left) / rect.width) * 2 - 1 normalizedPointer.y = -((event.clientY - rect.top) / rect.height) * 2 + 1 this.raycaster.setFromCamera(normalizedPointer, this.camera) const intersects = this.raycaster.intersectObjects(this.scene.children, true) if (intersects.length > 0) { const hit = this.getNamedParent(intersects[0].object) if(hit) this.onclickAgent(hit) } } addAgent(typeId, aid, properties, values){ const agentSprite = this.agentSprites.find(item => item.atp_id==typeId) if(!agentSprite) return const agentObj = this.agentFromJSON(aid, agentSprite.asp_3d) agentObj.position.set(values.position.x, values.position.z, values.position.y ) //TODO Speed vector this.scene.add(agentObj) this.agents[aid] = { type: typeId, props: properties, values: values, } } removeAgent(aid){ const obj3d = scene.getObjectByName(aid) this.scene.remove(obj3d) if(aid in this.agents) delete(this.agents[aid]) } // getAllAgents(){ // const agents = [] // scene.traverse(o => o.name && names.push(o.name)) } // Make this module available to common JS if(!app.LoadedModules) app.LoadedModules = {} app.LoadedModules.kfArena = kfArena