UNPKG

@polygonjs/polygonjs

Version:

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

408 lines (407 loc) 14.2 kB
"use strict"; import { TypedEventNode } from "./_Base"; import { NodeContext } from "../../poly/NodeContext"; import { RaycastCPUController } from "./utils/raycast/CPUController"; import { CPUIntersectWith, CPU_INTERSECT_WITH_OPTIONS } from "./utils/raycast/CpuConstants"; import { RaycastGPUController } from "./utils/raycast/GPUController"; import { AttribType, ATTRIBUTE_TYPES, AttribTypeMenuEntries } from "../../../core/geometry/Constant"; import { EventConnectionPoint, EventConnectionPointType } from "../utils/io/connections/Event"; import { ParamType } from "../../poly/ParamType"; const TIMESTAMP = 1e3 / 60; var RaycastMode = /* @__PURE__ */ ((RaycastMode2) => { RaycastMode2["CPU"] = "cpu"; RaycastMode2["GPU"] = "gpu"; return RaycastMode2; })(RaycastMode || {}); const RAYCAST_MODES = ["cpu" /* CPU */, "gpu" /* GPU */]; function visible_for_cpu(options = {}) { options["mode"] = RAYCAST_MODES.indexOf("cpu" /* CPU */); return { visibleIf: options }; } function visible_for_cpu_geometry(options = {}) { options["mode"] = RAYCAST_MODES.indexOf("cpu" /* CPU */); options["intersectWith"] = CPU_INTERSECT_WITH_OPTIONS.indexOf(CPUIntersectWith.GEOMETRY); return { visibleIf: options }; } function visible_for_cpu_plane(options = {}) { options["mode"] = RAYCAST_MODES.indexOf("cpu" /* CPU */); options["intersectWith"] = CPU_INTERSECT_WITH_OPTIONS.indexOf(CPUIntersectWith.PLANE); return { visibleIf: options }; } function visible_for_gpu(options = {}) { options["mode"] = RAYCAST_MODES.indexOf("gpu" /* GPU */); return { visibleIf: options }; } export var TargetType = /* @__PURE__ */ ((TargetType2) => { TargetType2["SCENE_GRAPH"] = "scene graph"; TargetType2["NODE"] = "node"; return TargetType2; })(TargetType || {}); export const TARGET_TYPES = ["scene graph" /* SCENE_GRAPH */, "node" /* NODE */]; import { NodeParamsConfig, ParamConfig } from "../utils/params/ParamsConfig"; import { Poly } from "../../Poly"; class RaycastParamsConfig extends NodeParamsConfig { constructor() { super(...arguments); /** @param defines if the ray detection is done on the CPU or GPU (GPU being currently experimental) */ this.mode = ParamConfig.INTEGER(RAYCAST_MODES.indexOf("cpu" /* CPU */), { menu: { entries: RAYCAST_MODES.map((name, value) => { return { name, value }; }) } }); // // // COMMON // // /** @param defines if the mouse parameter is update when the cursor screen position changes */ this.tmouse = ParamConfig.BOOLEAN(1, { cook: false }); /** @param mouse coordinates (0,0) being the center of the screen, (-1,-1) being the bottom left corner and (1,1) being the top right corner */ this.mouse = ParamConfig.VECTOR2([0, 0], { cook: false, visibleIf: { tmouse: 1 } }); /** @param by default the ray is sent from the current camera, but this allows to set another camera */ // overrideCamera = ParamConfig.BOOLEAN(0, visible_for_cpu()); /** @param by default the ray is sent from the current camera, but this allows to set a custom ray */ // overrideRay = ParamConfig.BOOLEAN(0, { // visibleIf: { // mode: RAYCAST_MODES.indexOf(RaycastMode.CPU), // overrideCamera: 1, // }, // }); /** @param the camera to override to */ // camera = ParamConfig.NODE_PATH('', { // nodeSelection: { // context: NodeContext.OBJ, // types: CAMERA_TYPES, // }, // dependentOnFoundNode: false, // ...visible_for_cpu({ // overrideCamera: 1, // overrideRay: 0, // }), // }); /** @param the ray origin */ // rayOrigin = ParamConfig.VECTOR3([0, 0, 0], { // visibleIf: { // overrideCamera: 1, // overrideRay: 1, // }, // }); /** @param the ray direction */ // rayDirection = ParamConfig.VECTOR3([0, 0, 1], { // visibleIf: { // overrideCamera: 1, // overrideRay: 1, // }, // }); // // // GPU // // /** @param the material to use on the scene for GPU detection */ this.overrideMaterial = ParamConfig.BOOLEAN(0, { callback: (node, param) => { RaycastGPUController.PARAM_CALLBACK_updateMaterial(node); }, ...visible_for_gpu() }); /** @param the material to use on the scene for GPU detection */ this.material = ParamConfig.NODE_PATH("", { nodeSelection: { context: NodeContext.MAT }, dependentOnFoundNode: false, computeOnDirty: true, callback: (node, param) => { RaycastGPUController.PARAM_CALLBACK_updateMaterial(node); }, ...visible_for_gpu({ overrideMaterial: 1 }) }); /** @param the current pixel color being read */ this.pixelColor = ParamConfig.COLOR([0, 0, 0], { cook: false, ...visible_for_gpu() }); this.pixelAlpha = ParamConfig.FLOAT(0, { range: [0, 1], cook: false, ...visible_for_gpu() }); /** @param the value threshold for which a hit is detected */ this.hitThreshold = ParamConfig.FLOAT(0.5, { cook: false, ...visible_for_gpu() }); // // // CPU // // /** @param defines the hit it tested against geometry or just a plane */ this.intersectWith = ParamConfig.INTEGER(CPU_INTERSECT_WITH_OPTIONS.indexOf(CPUIntersectWith.GEOMETRY), { menu: { entries: CPU_INTERSECT_WITH_OPTIONS.map((name, value) => { return { name, value }; }) }, ...visible_for_cpu() }); /** @param threshold used to test hit with points */ this.pointsThreshold = ParamConfig.FLOAT(1, { range: [0, 100], rangeLocked: [true, false], ...visible_for_cpu() }); // // // CPU PLANE // // /** @param plane direction if the hit is tested against a plane */ this.planeDirection = ParamConfig.VECTOR3([0, 1, 0], { ...visible_for_cpu_plane() }); /** @param plane offset if the hit is tested against a plane */ this.planeOffset = ParamConfig.FLOAT(0, { ...visible_for_cpu_plane() }); // // // CPU GEOMETRY // // this.targetType = ParamConfig.INTEGER(0, { menu: { entries: TARGET_TYPES.map((name, value) => { return { name, value }; }) }, ...visible_for_cpu_geometry() }); /** @param node whose objects to test hit against, when testing against geometries */ this.targetNode = ParamConfig.NODE_PATH("", { nodeSelection: { context: NodeContext.OBJ }, dependentOnFoundNode: false, callback: (node, param) => { RaycastCPUController.PARAM_CALLBACK_updateTarget(node); }, ...visible_for_cpu_geometry({ targetType: TARGET_TYPES.indexOf("node" /* NODE */) }) }); /** @param objects to test hit against, when testing against geometries */ this.objectMask = ParamConfig.STRING("*geo1*", { callback: (node, param) => { RaycastCPUController.PARAM_CALLBACK_updateTarget(node); }, objectMask: true, ...visible_for_cpu_geometry({ targetType: TARGET_TYPES.indexOf("scene graph" /* SCENE_GRAPH */) }) }); /** @param toggle to hit if tested against children */ this.traverseChildren = ParamConfig.BOOLEAN(true, { callback: (node, param) => { RaycastCPUController.PARAM_CALLBACK_updateTarget(node); }, ...visible_for_cpu_geometry(), separatorAfter: true }); // // // POSITION (common between plane and geo intersection) // // /** @param toggle on to update hit position */ this.tposition = ParamConfig.BOOLEAN(1, { cook: false, ...visible_for_cpu() }); /** @param toggle on to set the param to the hit position */ this.tpositionTarget = ParamConfig.BOOLEAN(0, { cook: false, ...visible_for_cpu({ tposition: 1 }) }); /** @param this will be set to the hit position */ this.position = ParamConfig.VECTOR3([0, 0, 0], { cook: false, ...visible_for_cpu({ tposition: 1, tpositionTarget: 0 }) }); /** @param this parameter will be set to the hit position */ this.positionTarget = ParamConfig.PARAM_PATH("", { // positionTarget param should not be dependent // on found Param, otherwise, as soon as the target param is change, // this param would have to cook dependentOnFoundParam: false, cook: false, ...visible_for_cpu({ tposition: 1, tpositionTarget: 1 }), paramSelection: ParamType.VECTOR3, computeOnDirty: true }); /** @param toggle on to set the param to the mouse velocity (experimental) */ this.tvelocity = ParamConfig.BOOLEAN(0, { cook: false, ...visible_for_cpu() // callback: (node: BaseNodeType, param: BaseParamType) => { // RaycastCPUVelocityController.PARAM_CALLBACK_update_timer(node as RaycastEventNode); // }, }); /** @param toggle on to set the param to the mouse velocity */ this.tvelocityTarget = ParamConfig.BOOLEAN(0, { cook: false, ...visible_for_cpu({ tvelocity: 1 }) }); /** @param this will be set to the mouse velocity */ this.velocity = ParamConfig.VECTOR3([0, 0, 0], { cook: false, ...visible_for_cpu({ tvelocity: 1, tvelocityTarget: 0 }) }); /** @param this will be set to the mouse velocity */ this.velocityTarget = ParamConfig.PARAM_PATH("", { dependentOnFoundParam: false, cook: false, ...visible_for_cpu({ tvelocity: 1, tvelocityTarget: 1 }), paramSelection: ParamType.VECTOR3, computeOnDirty: true }); // // // GEO ATTRIB // // /** @param for geometry hit tests, a vertex attribute can be read */ this.geoAttribute = ParamConfig.BOOLEAN(0, visible_for_cpu_geometry()); /** @param geometry vertex attribute to read */ this.geoAttributeName = ParamConfig.STRING("id", { cook: false, ...visible_for_cpu_geometry({ geoAttribute: 1 }) }); /** @param type of attribute */ this.geoAttributeType = ParamConfig.INTEGER(ATTRIBUTE_TYPES.indexOf(AttribType.NUMERIC), { menu: { entries: AttribTypeMenuEntries }, ...visible_for_cpu_geometry({ geoAttribute: 1 }) }); /** @param attribute value for float */ this.geoAttributeValue1 = ParamConfig.FLOAT(0, { cook: false, ...visible_for_cpu_geometry({ geoAttribute: 1, geoAttributeType: ATTRIBUTE_TYPES.indexOf(AttribType.NUMERIC) }) }); /** @param attribute value for string */ this.geoAttributeValues = ParamConfig.STRING("", { ...visible_for_cpu_geometry({ geoAttribute: 1, geoAttributeType: ATTRIBUTE_TYPES.indexOf(AttribType.STRING) }) }); } } const ParamsConfig = new RaycastParamsConfig(); const _RaycastEventNode = class extends TypedEventNode { constructor() { super(...arguments); this.paramsConfig = ParamsConfig; this.cpuController = new RaycastCPUController(this); this.gpuController = new RaycastGPUController(this); this._lastEventProcessedAt = -1; } static type() { return "raycast"; } initializeNode() { this.io.inputs.setNamedInputConnectionPoints([ new EventConnectionPoint( _RaycastEventNode.INPUT_TRIGGER, EventConnectionPointType.BASE, this._processTriggerEventThrottled.bind(this) ), new EventConnectionPoint( _RaycastEventNode.INPUT_MOUSE, EventConnectionPointType.MOUSE, this._processMouseEvent.bind(this) ), new EventConnectionPoint( _RaycastEventNode.INPUT_UPDATE_OBJECTS, EventConnectionPointType.BASE, this._processTriggerUpdateObjects.bind(this) ), new EventConnectionPoint( _RaycastEventNode.INPUT_TRIGGER_VEL_RESET, EventConnectionPointType.BASE, this._processTriggerVelReset.bind(this) ) ]); this.io.outputs.setNamedOutputConnectionPoints([ new EventConnectionPoint(_RaycastEventNode.OUTPUT_HIT, EventConnectionPointType.BASE), new EventConnectionPoint(_RaycastEventNode.OUTPUT_MISS, EventConnectionPointType.BASE) ]); } triggerHit(context) { this.dispatchEventToOutput(_RaycastEventNode.OUTPUT_HIT, context); } triggerMiss(context) { this.dispatchEventToOutput(_RaycastEventNode.OUTPUT_MISS, context); } _processMouseEvent(context) { if (this.pv.mode == RAYCAST_MODES.indexOf("cpu" /* CPU */)) { this.cpuController.updateMouse(context); } else { this.gpuController.updateMouse(context); } } _processTriggerEventThrottled(context) { const now = Poly.performance.performanceManager().now(); const getDelta = (now2) => { const previous = this._lastEventProcessedAt; const delta2 = now2 - previous; return delta2; }; const delta = getDelta(now); if (delta < TIMESTAMP) { setTimeout(() => { const delta2 = getDelta(now); if (delta2 < TIMESTAMP) { this._processTriggerEvent(context); } }, TIMESTAMP - delta); } else { this._processTriggerEvent(context); } } _processTriggerEvent(context) { this._processMouseEvent(context); this._lastEventProcessedAt = Poly.performance.performanceManager().now(); if (this.pv.mode == RAYCAST_MODES.indexOf("cpu" /* CPU */)) { this.cpuController.processEvent(context); } else { this.gpuController.processEvent(context); } } _processTriggerUpdateObjects(context) { if (this.pv.mode == RAYCAST_MODES.indexOf("cpu" /* CPU */)) { this.cpuController.updateTarget(); } } _processTriggerVelReset(context) { if (this.pv.mode == RAYCAST_MODES.indexOf("cpu" /* CPU */)) { this.cpuController.velocityController.reset(); } } }; export let RaycastEventNode = _RaycastEventNode; RaycastEventNode.INPUT_TRIGGER = "trigger"; RaycastEventNode.INPUT_MOUSE = "mouse"; RaycastEventNode.INPUT_UPDATE_OBJECTS = "updateObjects"; RaycastEventNode.INPUT_TRIGGER_VEL_RESET = "triggerVelReset"; RaycastEventNode.OUTPUT_HIT = "hit"; RaycastEventNode.OUTPUT_MISS = "miss";