polygonjs-engine
Version:
node-based webgl 3D engine https://polygonjs.com
259 lines (258 loc) • 10.1 kB
JavaScript
import {TargetType, TARGET_TYPES} from "../../Raycast";
import {Vector2 as Vector22} from "three/src/math/Vector2";
import {Raycaster as Raycaster2} from "three/src/core/Raycaster";
import {NodeContext as NodeContext2} from "../../../../poly/NodeContext";
import {TypeAssert} from "../../../../poly/Assert";
import {Plane as Plane2} from "three/src/math/Plane";
import {Vector3 as Vector32} from "three/src/math/Vector3";
import {ParamType as ParamType2} from "../../../../poly/ParamType";
import {AttribType, ATTRIBUTE_TYPES} from "../../../../../core/geometry/Constant";
import {objectTypeFromConstructor, ObjectType} from "../../../../../core/geometry/Constant";
import {CoreGeometry} from "../../../../../core/geometry/Geometry";
import {Triangle as Triangle2} from "three/src/math/Triangle";
import {Poly as Poly2} from "../../../../Poly";
import {RaycastCPUVelocityController} from "./VelocityController";
import {CoreType} from "../../../../../core/Type";
import {CPUIntersectWith, CPU_INTERSECT_WITH_OPTIONS} from "./CpuConstants";
const RaycastCPUController2 = class {
constructor(_node) {
this._node = _node;
this._mouse = new Vector22();
this._mouse_array = [0, 0];
this._raycaster = new Raycaster2();
this._plane = new Plane2();
this._plane_intersect_target = new Vector32();
this._hit_position_array = [0, 0, 0];
this.velocity_controller = new RaycastCPUVelocityController(this._node);
}
update_mouse(context) {
const canvas = context.viewer?.canvas();
if (!(canvas && context.cameraNode)) {
return;
}
if (context.event instanceof MouseEvent) {
this._mouse.x = context.event.offsetX / canvas.offsetWidth * 2 - 1;
this._mouse.y = -(context.event.offsetY / canvas.offsetHeight) * 2 + 1;
this._mouse.toArray(this._mouse_array);
this._node.p.mouse.set(this._mouse_array);
}
this._raycaster.setFromCamera(this._mouse, context.cameraNode.object);
}
process_event(context) {
this._prepare_raycaster(context);
const type = CPU_INTERSECT_WITH_OPTIONS[this._node.pv.intersectWith];
switch (type) {
case CPUIntersectWith.GEOMETRY: {
return this._intersect_with_geometry(context);
}
case CPUIntersectWith.PLANE: {
return this._intersect_with_plane(context);
}
}
TypeAssert.unreachable(type);
}
_intersect_with_plane(context) {
this._plane.normal.copy(this._node.pv.planeDirection);
this._plane.constant = this._node.pv.planeOffset;
this._raycaster.ray.intersectPlane(this._plane, this._plane_intersect_target);
this._set_position_param(this._plane_intersect_target);
this._node.trigger_hit(context);
}
_intersect_with_geometry(context) {
if (!this._resolved_targets) {
this.update_target();
}
if (this._resolved_targets) {
const intersections = this._raycaster.intersectObjects(this._resolved_targets, true);
const intersection = intersections[0];
if (intersection) {
this._set_position_param(intersection.point);
if (this._node.pv.geoAttribute == true) {
this._resolve_geometry_attribute(intersection);
}
context.value = {intersect: intersection};
this._node.trigger_hit(context);
} else {
this._node.trigger_miss(context);
}
}
}
_resolve_geometry_attribute(intersection) {
const attrib_type = ATTRIBUTE_TYPES[this._node.pv.geoAttributeType];
const val = RaycastCPUController2.resolve_geometry_attribute(intersection, this._node.pv.geoAttributeName, attrib_type);
if (val != null) {
switch (attrib_type) {
case AttribType.NUMERIC: {
this._node.p.geoAttributeValue1.set(val);
return;
}
case AttribType.STRING: {
if (CoreType.isString(val)) {
this._node.p.geoAttributeValues.set(val);
}
return;
}
}
TypeAssert.unreachable(attrib_type);
}
}
static resolve_geometry_attribute(intersection, attribute_name, attrib_type) {
const object_type = objectTypeFromConstructor(intersection.object.constructor);
switch (object_type) {
case ObjectType.MESH:
return this.resolve_geometry_attribute_for_mesh(intersection, attribute_name, attrib_type);
case ObjectType.POINTS:
return this.resolve_geometry_attribute_for_point(intersection, attribute_name, attrib_type);
}
}
static resolve_geometry_attribute_for_mesh(intersection, attribute_name, attrib_type) {
const geometry = intersection.object.geometry;
if (geometry) {
const attribute = geometry.getAttribute(attribute_name);
if (attribute) {
switch (attrib_type) {
case AttribType.NUMERIC: {
const position = geometry.getAttribute("position");
if (intersection.face) {
this._vA.fromBufferAttribute(position, intersection.face.a);
this._vB.fromBufferAttribute(position, intersection.face.b);
this._vC.fromBufferAttribute(position, intersection.face.c);
this._uvA.fromBufferAttribute(attribute, intersection.face.a);
this._uvB.fromBufferAttribute(attribute, intersection.face.b);
this._uvC.fromBufferAttribute(attribute, intersection.face.c);
intersection.uv = Triangle2.getUV(intersection.point, this._vA, this._vB, this._vC, this._uvA, this._uvB, this._uvC, this._hitUV);
return this._hitUV.x;
}
return;
}
case AttribType.STRING: {
const core_geometry = new CoreGeometry(geometry);
const core_point = core_geometry.points()[0];
if (core_point) {
return core_point.stringAttribValue(attribute_name);
}
return;
}
}
TypeAssert.unreachable(attrib_type);
}
}
}
static resolve_geometry_attribute_for_point(intersection, attribute_name, attrib_type) {
const geometry = intersection.object.geometry;
if (geometry && intersection.index != null) {
switch (attrib_type) {
case AttribType.NUMERIC: {
const attribute = geometry.getAttribute(attribute_name);
if (attribute) {
return attribute.array[intersection.index];
}
return;
}
case AttribType.STRING: {
const core_geometry = new CoreGeometry(geometry);
const core_point = core_geometry.points()[intersection.index];
if (core_point) {
return core_point.stringAttribValue(attribute_name);
}
return;
}
}
TypeAssert.unreachable(attrib_type);
}
}
_set_position_param(hit_position) {
hit_position.toArray(this._hit_position_array);
if (this._node.pv.tpositionTarget) {
if (Poly2.playerMode()) {
this._found_position_target_param = this._found_position_target_param || this._node.p.positionTarget.found_param_with_type(ParamType2.VECTOR3);
} else {
const target_param = this._node.p.positionTarget;
this._found_position_target_param = target_param.found_param_with_type(ParamType2.VECTOR3);
}
if (this._found_position_target_param) {
this._found_position_target_param.set(this._hit_position_array);
}
} else {
this._node.p.position.set(this._hit_position_array);
}
this.velocity_controller.process(hit_position);
}
_prepare_raycaster(context) {
const points_param = this._raycaster.params.Points;
if (points_param) {
points_param.threshold = this._node.pv.pointsThreshold;
}
let camera_node = context.cameraNode;
if (this._node.pv.overrideCamera) {
if (this._node.pv.overrideRay) {
this._raycaster.ray.origin.copy(this._node.pv.rayOrigin);
this._raycaster.ray.direction.copy(this._node.pv.rayDirection);
} else {
const found_camera_node = this._node.p.camera.found_node_with_context(NodeContext2.OBJ);
if (found_camera_node) {
camera_node = found_camera_node;
}
}
}
if (camera_node && !this._node.pv.overrideRay) {
camera_node.prepare_raycaster(this._mouse, this._raycaster);
}
}
update_target() {
const targetType = TARGET_TYPES[this._node.pv.targetType];
switch (targetType) {
case TargetType.NODE: {
return this._update_target_from_node();
}
case TargetType.SCENE_GRAPH: {
return this._update_target_from_scene_graph();
}
}
TypeAssert.unreachable(targetType);
}
_update_target_from_node() {
const node = this._node.p.targetNode.value.ensure_node_context(NodeContext2.OBJ);
if (node) {
const found_obj = this._node.pv.traverseChildren ? node.object : node.children_display_controller.sop_group;
if (found_obj) {
this._resolved_targets = [found_obj];
} else {
this._resolved_targets = void 0;
}
} else {
this._node.states.error.set("node is not an object");
}
}
_update_target_from_scene_graph() {
const objects = this._node.scene().objectsByMask(this._node.pv.objectMask);
if (objects.length > 0) {
this._resolved_targets = objects;
} else {
this._resolved_targets = void 0;
}
}
async update_position_target() {
if (this._node.p.positionTarget.isDirty()) {
await this._node.p.positionTarget.compute();
}
}
static PARAM_CALLBACK_update_target(node) {
node.cpu_controller.update_target();
}
static PARAM_CALLBACK_print_resolve(node) {
node.cpu_controller.print_resolve();
}
print_resolve() {
this.update_target();
console.log(this._resolved_targets);
}
};
export let RaycastCPUController = RaycastCPUController2;
RaycastCPUController._vA = new Vector32();
RaycastCPUController._vB = new Vector32();
RaycastCPUController._vC = new Vector32();
RaycastCPUController._uvA = new Vector22();
RaycastCPUController._uvB = new Vector22();
RaycastCPUController._uvC = new Vector22();
RaycastCPUController._hitUV = new Vector22();