UNPKG

@polygonjs/polygonjs

Version:

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

235 lines (215 loc) 7.86 kB
import {Number2, Number3} from '../../../../../types/GlobalTypes'; import {RaycastEventNode, TargetType, TARGET_TYPES} from '../../Raycast'; import {Object3D} from 'three'; import {Intersection} from 'three'; import {NodeContext} from '../../../../poly/NodeContext'; import {BaseObjNodeType} from '../../../obj/_Base'; import {GeoObjNode} from '../../../obj/Geo'; import {TypeAssert} from '../../../../poly/Assert'; import {Plane} from 'three'; import {Vector3} from 'three'; import {ParamType} from '../../../../poly/ParamType'; import {AttribType, ATTRIBUTE_TYPES} from '../../../../../core/geometry/Constant'; import {Vector3Param} from '../../../../params/Vector3'; import {RaycastCPUVelocityController} from './VelocityController'; import {isString} from '../../../../../core/Type'; import {CPUIntersectWith, CPU_INTERSECT_WITH_OPTIONS} from './CpuConstants'; import {isBooleanTrue} from '../../../../../core/BooleanValue'; import {IntersectDataEventNode} from '../../IntersectData'; import {BaseRaycastController} from './BaseRaycastController'; import {resolveIntersectGeometryAttribute} from '../../../../../core/geometry/intersect/CoreIntersect'; import {EventContext} from '../../../../../core/event/EventContextType'; export class RaycastCPUController extends BaseRaycastController { private _cursorArray: Number2 = [0, 0]; private _resolvedTargets: Object3D[] | undefined; public readonly velocityController: RaycastCPUVelocityController; constructor(private _node: RaycastEventNode) { super(); this.velocityController = new RaycastCPUVelocityController(this._node); } updateMouse(eventContext: EventContext<MouseEvent | DragEvent | PointerEvent | TouchEvent>) { const viewer = eventContext.viewer; if (!viewer) { return; } const camera = viewer.camera(); if (!camera) { return; } this._cursorHelper.setCursorForCPU(eventContext, this._cursor); if (isBooleanTrue(this._node.pv.tmouse)) { this._cursor.toArray(this._cursorArray); this._node.p.mouse.set(this._cursorArray); } // this._updateFromCursor(canvas); viewer.raycastersController.raycaster0().setFromCamera(this._cursor, camera); } processEvent(context: EventContext<MouseEvent>) { this._prepareRaycaster(context); const type = CPU_INTERSECT_WITH_OPTIONS[this._node.pv.intersectWith]; switch (type) { case CPUIntersectWith.GEOMETRY: { return this._intersectGeometry(context); } case CPUIntersectWith.PLANE: { return this._intersectPlane(context); } } TypeAssert.unreachable(type); } private _plane = new Plane(); private _plane_intersect_target = new Vector3(); private _intersectPlane(eventContext: EventContext<MouseEvent>) { const viewer = eventContext.viewer; if (!viewer) { return; } this._plane.normal.copy(this._node.pv.planeDirection); this._plane.constant = this._node.pv.planeOffset; viewer.raycastersController.raycaster0().ray.intersectPlane(this._plane, this._plane_intersect_target); this._setPositionParam(this._plane_intersect_target); this._node.triggerHit(eventContext); } private _intersections: Intersection[] = []; private _intersectGeometry(eventContext: EventContext<MouseEvent>) { const viewer = eventContext.viewer; if (!viewer) { return; } if (!this._resolvedTargets) { this.updateTarget(); } if (this._resolvedTargets) { // clear array before this._intersections.length = 0; const intersections = viewer.raycastersController .raycaster0() .intersectObjects( this._resolvedTargets, isBooleanTrue(this._node.pv.traverseChildren), this._intersections ); const intersection = intersections[0]; if (intersection) { this._node.scene().batchUpdates(() => { if (isBooleanTrue(this._node.pv.tposition)) { this._setPositionParam(intersection.point); } if (isBooleanTrue(this._node.pv.geoAttribute)) { this._resolveIntersectAttribute(intersection); } }); eventContext.value = {intersect: intersection}; this._node.triggerHit(eventContext); } else { this._node.triggerMiss(eventContext); } } } private _resolveIntersectAttribute(intersection: Intersection) { const attribType = ATTRIBUTE_TYPES[this._node.pv.geoAttributeType]; let attribValue: string | number | null | undefined = IntersectDataEventNode.resolveObjectAttribute( intersection, this._node.pv.geoAttributeName ); if (attribValue == null) { attribValue = resolveIntersectGeometryAttribute(intersection, this._node.pv.geoAttributeName, attribType); } if (attribValue != null) { switch (attribType) { case AttribType.NUMERIC: { this._node.p.geoAttributeValue1.set(attribValue); return; } case AttribType.STRING: { if (isString(attribValue)) { this._node.p.geoAttributeValues.set(attribValue); } return; } } TypeAssert.unreachable(attribType); } } private _foundPositionTargetParam: Vector3Param | undefined; private _hitPositionArray: Number3 = [0, 0, 0]; private _setPositionParam(hitPosition: Vector3) { hitPosition.toArray(this._hitPositionArray); if (isBooleanTrue(this._node.pv.tpositionTarget)) { const targetParam = this._node.pv.positionTarget; if (this._foundPositionTargetParam == null || isBooleanTrue(this._foundPositionTargetParam.disposed())) { this._foundPositionTargetParam = targetParam.paramWithType(ParamType.VECTOR3); } this._foundPositionTargetParam?.set(this._hitPositionArray); // } } else { this._node.p.position.set(this._hitPositionArray); } this.velocityController.process(hitPosition); } private _prepareRaycaster(eventContext: EventContext<MouseEvent>) { const viewer = eventContext.viewer; if (!viewer) { return; } const pointsParam = viewer.raycastersController.raycaster0().params.Points; if (pointsParam) { pointsParam.threshold = this._node.pv.pointsThreshold; } // let camera = context.camera; // if (isBooleanTrue(this._node.pv.overrideCamera)) { // if (isBooleanTrue(this._node.pv.overrideRay)) { // this._raycaster.ray.origin.copy(this._node.pv.rayOrigin); // this._raycaster.ray.direction.copy(this._node.pv.rayDirection); // } else { // const foundCameraNode = this._node.pv.camera.nodeWithContext(NodeContext.OBJ, this._node.states.error); // if (foundCameraNode) { // if ((CAMERA_TYPES as string[]).includes(foundCameraNode.type())) { // cameraNode = (<unknown>foundCameraNode) as Readonly<BaseCameraObjNodeType>; // } // } // } // } // if (cameraNode && !isBooleanTrue(this._node.pv.overrideRay)) { // cameraNode.prepareRaycaster(this._cursor, this._raycaster); // } } updateTarget() { const targetType = TARGET_TYPES[this._node.pv.targetType]; switch (targetType) { case TargetType.NODE: { return this._updateTargetFromNode(); } case TargetType.SCENE_GRAPH: { return this._updateTargetFromSceneGraph(); } } TypeAssert.unreachable(targetType); } private _updateTargetFromNode() { const node = this._node.p.targetNode.value.nodeWithContext(NodeContext.OBJ) as BaseObjNodeType; if (node) { const found_obj = isBooleanTrue(this._node.pv.traverseChildren) ? node.object : (node as GeoObjNode).childrenDisplayController.sopGroup(); if (found_obj) { this._resolvedTargets = [found_obj]; } else { this._resolvedTargets = undefined; } } else { this._node.states.error.set('node is not an object'); } } private _updateTargetFromSceneGraph() { const objects: Object3D[] = this._node.scene().objectsByMask(this._node.pv.objectMask) as Object3D[]; if (objects.length > 0) { this._resolvedTargets = objects; } else { this._resolvedTargets = undefined; } } static PARAM_CALLBACK_updateTarget(node: RaycastEventNode) { node.cpuController.updateTarget(); } }