@polygonjs/polygonjs
Version:
node-based WebGL 3D engine https://polygonjs.com
408 lines (407 loc) • 14.2 kB
JavaScript
"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";