UNPKG

@polygonjs/polygonjs

Version:

node-based WebGL 3D engine https://polygonjs.com

233 lines (232 loc) 8.98 kB
"use strict"; import { ref } from "@vue/reactivity"; import { Vector2, Vector3, Vector4 } from "three"; import { ACTOR_COMPILATION_CONTROLLER_DUMMY_OBJECT } from "../../../../../core/actor/ActorCompilationController"; import { RenderPixelController, coreCursorToUv } from "../../../../../core/render/renderPixel/RenderPixelController"; import { coreGetDefaultCamera } from "../../../../../core/render/renderPixel/CoreGetDefautCamera"; import { pushOnArrayAtEntry } from "../../../../../core/MapUtils"; import { hasCPUOptions, hasGPUOptions, CPUOptionsEqual, CPUOptionsMax, GPUOptionsDepthBufferRequired } from "./Common"; import { arrayCopy } from "../../../../../core/ArrayUtils"; const RAYCAST_UPDATE_OPTIONS = { pointsThreshold: 0.1, lineThreshold: 0.1 }; const TMP_CPU_OPTIONS = { traverseChildren: false, pointsThreshold: 0.1, lineThreshold: 0.1, intersectionRef: ref(null) }; function intersectsSort(a, b) { return a.distance - b.distance; } const pixelRenderUv = new Vector2(); const pixelRenderTarget = new Vector4(); const raycasterDirNormalised = new Vector3(); const gpuCameraRayAtNearPlane = new Vector3(); const gpuCameraRayAtFarPlane = new Vector3(); const gpuHitPos = new Vector3(); export class BaseRayObjectIntersectionsController { constructor(actorsManager) { this.actorsManager = actorsManager; this._objects = []; this._propertiesListByObject = /* @__PURE__ */ new Map(); this._intersectsByObject = /* @__PURE__ */ new WeakMap(); this._closestIntersects = /* @__PURE__ */ new Map(); this._objectByClosestIntersect = /* @__PURE__ */ new Map(); this._closestIntersectsSorted = []; this._renderPixelController = new RenderPixelController(); this._scene = actorsManager.scene; } objects(target) { arrayCopy(this._objects, target); } _setIntersectedState(objects, intersectedStateByObject) { if (objects.length == 0) { return; } this._closestIntersects.clear(); this._objectByClosestIntersect.clear(); const pointerEventsController = this._scene.eventsDispatcher.pointerEventsController; const raycaster = pointerEventsController.raycaster().value; pointerEventsController.updateRaycast(RAYCAST_UPDATE_OPTIONS); const gpuObjectsPresent = this._gpuObjectsPresent(); const camera = gpuObjectsPresent == true ? coreGetDefaultCamera(this._scene) : null; if (gpuObjectsPresent == true && camera) { const cursor = pointerEventsController.cursor().value; coreCursorToUv(cursor, pixelRenderUv); if (this._gpuDepthBufferReadRequired()) { raycasterDirNormalised.copy(raycaster.ray.direction).normalize(); gpuCameraRayAtNearPlane.set(cursor.x, cursor.y, -1).unproject(camera); gpuCameraRayAtFarPlane.set(cursor.x, cursor.y, 1).unproject(camera); } } for (const object of objects) { intersectedStateByObject.set( object, false /*we reset to false here*/ ); const propertiesList = this._propertiesListByObject.get(object); const intersects = this._intersectsByObject.get(object); if (propertiesList && intersects) { intersects.length = 0; if (hasCPUOptions(propertiesList)) { const cpuOptions = CPUOptionsEqual(propertiesList) ? propertiesList[0].cpu : CPUOptionsMax(propertiesList, TMP_CPU_OPTIONS); RAYCAST_UPDATE_OPTIONS.pointsThreshold = cpuOptions.pointsThreshold; RAYCAST_UPDATE_OPTIONS.lineThreshold = cpuOptions.lineThreshold; raycaster.intersectObject(object, cpuOptions.traverseChildren, intersects); const closestIntersect = intersects[0]; this._closestIntersects.set(object, closestIntersect); if (closestIntersect) { this._objectByClosestIntersect.set(closestIntersect, object); } } if (hasGPUOptions(propertiesList)) { const gpuOptions = propertiesList[0].gpu; if (gpuOptions && camera) { const worldPosMaterial = gpuOptions.worldPosMaterial; if (worldPosMaterial != null) { this._renderPixelController.renderColor( this._scene, object, gpuOptions.worldPosMaterial, camera, null, //necessary to have alpha=0 when no object is hit pixelRenderUv, pixelRenderTarget ); } else { this._renderPixelController.renderDepth( this._scene, object, // gpuOptions.worldPosMaterial, camera, null, //necessary to have alpha=0 when no object is hit pixelRenderUv, pixelRenderTarget ); } if (pixelRenderTarget.w > 0) { if (worldPosMaterial) { gpuHitPos.set(pixelRenderTarget.x, pixelRenderTarget.y, pixelRenderTarget.z); } else { gpuHitPos.copy(gpuCameraRayAtNearPlane).lerp(gpuCameraRayAtFarPlane, pixelRenderTarget.x); } const distance = gpuHitPos.distanceTo(raycaster.ray.origin); const gpuIntersect = { distance }; this._closestIntersects.set(object, gpuIntersect); if (gpuIntersect) { this._objectByClosestIntersect.set(gpuIntersect, object); } } else { this._closestIntersects.set(object, void 0); } } } } } this._closestIntersectsSorted.length = 0; for (const object of objects) { const closestIntersect = this._closestIntersects.get(object); if (closestIntersect) { this._closestIntersectsSorted.push(closestIntersect); } const propertiesList = this._propertiesListByObject.get(object); if (propertiesList) { for (const properties of propertiesList) { const cpuOptions = properties.cpu; if (cpuOptions) { cpuOptions.intersectionRef.value = closestIntersect || null; } else { const gpuOptions = properties.gpu; if (gpuOptions) { gpuOptions.distanceRef.value = closestIntersect ? closestIntersect.distance : -1; } } } } } this._closestIntersectsSorted.sort(intersectsSort); let blockingObjectProcessed = false; for (const intersect of this._closestIntersectsSorted) { const object = this._objectByClosestIntersect.get(intersect); if (object) { const propertiesList = this._propertiesListByObject.get(object); if (propertiesList) { let blockObjectsBehind = false; for (const properties of propertiesList) { if (blockingObjectProcessed == false || properties.priority.skipIfObjectsInFront == true) { intersectedStateByObject.set(object, true); } if (properties.priority.blockObjectsBehind == true) { blockObjectsBehind = true; } } blockingObjectProcessed = blockObjectsBehind; } } } this._objectByClosestIntersect.clear(); } // protected _postProcess() { // this._objectByClosestIntersect.clear(); // } _gpuObjectsPresent() { const objects = this._objects; for (const object of objects) { const propertiesList = this._propertiesListByObject.get(object); if (propertiesList && hasGPUOptions(propertiesList)) { return true; } } return false; } _gpuDepthBufferReadRequired() { const objects = this._objects; for (const object of objects) { const propertiesList = this._propertiesListByObject.get(object); if (propertiesList && GPUOptionsDepthBufferRequired(propertiesList)) { return true; } } return false; } addPropertiesForObject(object, properties) { if (object == ACTOR_COMPILATION_CONTROLLER_DUMMY_OBJECT) { return; } pushOnArrayAtEntry(this._propertiesListByObject, object, properties); const index = this._objects.indexOf(object); if (index < 0) { this._objects.push(object); this._intersectsByObject.set(object, []); } } removePropertiesForObject(object, properties) { if (object == ACTOR_COMPILATION_CONTROLLER_DUMMY_OBJECT) { return; } const propertiesForObject = this._propertiesListByObject.get(object); if (propertiesForObject) { const propertyIndex = propertiesForObject.indexOf(properties); propertiesForObject.splice(propertyIndex, 1); if (propertiesForObject.length == 0) { const objectIndex = this._objects.indexOf(object); if (objectIndex >= 0) { this._objects.splice(objectIndex, 1); this._intersectsByObject.delete(object); this._propertiesListByObject.delete(object); } } } } }