@polygonjs/polygonjs
Version:
node-based WebGL 3D engine https://polygonjs.com
171 lines (170 loc) • 7 kB
JavaScript
"use strict";
import { BaseSopOperation } from "./_Base";
import { InputCloneMode } from "../../../engine/poly/InputCloneMode";
import { Matrix4, Triangle, Vector2, Vector3 } from "three";
import { isBooleanTrue } from "../../../core/BooleanValue";
import { MatDoubleSideTmpSetter } from "../../../core/render/MatDoubleSideTmpSetter";
import { TypeAssert } from "../../poly/Assert";
import { ThreeMeshBVHHelper } from "../../../core/geometry/bvh/ThreeMeshBVHHelper";
import { createRaycaster } from "../../../core/RaycastHelper";
import { corePointClassFactory } from "../../../core/geometry/CoreObjectFactory";
export var RaySopMode = /* @__PURE__ */ ((RaySopMode2) => {
RaySopMode2["PROJECT_RAY"] = "project rays";
RaySopMode2["MIN_DIST"] = "minimum distance";
return RaySopMode2;
})(RaySopMode || {});
export const RAY_SOP_MODES = ["project rays" /* PROJECT_RAY */, "minimum distance" /* MIN_DIST */];
const DIST_ATTRIB_NAME = "dist";
const objectWorldMat = new Matrix4();
const objectWorldMatInverse = new Matrix4();
const _points = [];
const _uv0 = new Vector2();
const _uv1 = new Vector2();
const _uv2 = new Vector2();
const _uv = new Vector2();
export class RaySopOperation extends BaseSopOperation {
constructor() {
super(...arguments);
this._matDoubleSideTmpSetter = new MatDoubleSideTmpSetter();
this._raycaster = createRaycaster();
this._pointPos = new Vector3();
this._pointNormal = new Vector3();
this._hitPointInfo = {
point: new Vector3(),
distance: -1,
faceIndex: -1
};
this._triangle = new Triangle();
this._faceNormal = new Vector3();
}
static type() {
return "ray";
}
cook(inputCoreGroups, params) {
const coreGroupToRay = inputCoreGroups[0];
const coreGroupToRayOnto = inputCoreGroups[1];
const coreGroup = this._ray(coreGroupToRay, coreGroupToRayOnto, params);
return coreGroup;
}
_ray(coreGroup, coreGroupCollision, params) {
const mode = RAY_SOP_MODES[params.mode];
switch (mode) {
case "project rays" /* PROJECT_RAY */: {
return this._computeWithProjectRay(coreGroup, coreGroupCollision, params);
}
case "minimum distance" /* MIN_DIST */: {
return this._computeWithMinDist(coreGroup, coreGroupCollision, params);
}
}
TypeAssert.unreachable(mode);
}
_computeWithProjectRay(coreGroup, coreGroupCollision, params) {
this._matDoubleSideTmpSetter.setCoreGroupMaterialDoubleSided(coreGroupCollision);
this._addDistAttribute(coreGroup, params);
let direction, firstIntersect;
coreGroup.points(_points);
for (const point of _points) {
point.position(this._pointPos);
direction = params.direction;
if (isBooleanTrue(params.useNormals)) {
point.normal(this._pointNormal);
direction = this._pointNormal;
}
this._raycaster.set(this._pointPos, direction);
firstIntersect = this._raycaster.intersectObjects(coreGroupCollision.threejsObjects(), true)[0];
if (firstIntersect) {
if (isBooleanTrue(params.transformPoints)) {
point.setPosition(firstIntersect.point);
}
if (isBooleanTrue(params.addDistAttribute)) {
const dist = this._pointPos.distanceTo(firstIntersect.point);
point.setAttribValue(DIST_ATTRIB_NAME, dist);
}
if (isBooleanTrue(params.transferFaceNormals) && firstIntersect.face) {
point.setNormal(firstIntersect.face.normal);
}
}
}
this._matDoubleSideTmpSetter.restoreMaterialSideProperty(coreGroupCollision);
return coreGroup;
}
_computeWithMinDist(coreGroup, coreGroupCollision, params) {
var _a, _b;
this._addDistAttribute(coreGroup, params);
const coreGroupCollisionObject = coreGroupCollision.threejsObjectsWithGeo()[0];
const collisionGeometry = coreGroupCollisionObject.geometry;
const indexArray = (_a = collisionGeometry.getIndex()) == null ? void 0 : _a.array;
if (!indexArray) {
(_b = this.states) == null ? void 0 : _b.error.set("the collision geo requires an index");
return coreGroup;
}
let bvh = collisionGeometry.boundsTree;
if (!bvh) {
ThreeMeshBVHHelper.assignDefaultBVHIfNone(coreGroupCollisionObject);
bvh = collisionGeometry.boundsTree;
}
coreGroupCollisionObject.updateMatrixWorld(true);
objectWorldMat.copy(coreGroupCollisionObject.matrixWorld);
objectWorldMatInverse.copy(objectWorldMat).invert();
const position = collisionGeometry.getAttribute("position");
const uv = collisionGeometry.getAttribute("uv");
coreGroup.points(_points);
const pointsCount = _points.length;
for (let i = 0; i < pointsCount; i++) {
const point = _points[i];
point.position(this._pointPos);
this._pointPos.applyMatrix4(objectWorldMatInverse);
bvh.closestPointToPoint(this._pointPos, this._hitPointInfo);
if (isBooleanTrue(params.transformPoints)) {
this._hitPointInfo.point.applyMatrix4(objectWorldMat);
point.setPosition(this._hitPointInfo.point);
}
if (isBooleanTrue(params.addDistAttribute)) {
point.setAttribValue(DIST_ATTRIB_NAME, this._hitPointInfo.distance);
}
if (isBooleanTrue(params.transferFaceNormals) || isBooleanTrue(params.transferUVs)) {
this._triangle.setFromAttributeAndIndices(
position,
indexArray[3 * this._hitPointInfo.faceIndex],
indexArray[3 * this._hitPointInfo.faceIndex + 1],
indexArray[3 * this._hitPointInfo.faceIndex + 2]
);
if (isBooleanTrue(params.transferFaceNormals)) {
this._triangle.getNormal(this._faceNormal);
point.setNormal(this._faceNormal);
}
if (isBooleanTrue(params.transferUVs) && uv) {
_uv0.fromBufferAttribute(uv, indexArray[3 * this._hitPointInfo.faceIndex]);
_uv1.fromBufferAttribute(uv, indexArray[3 * this._hitPointInfo.faceIndex + 1]);
_uv2.fromBufferAttribute(uv, indexArray[3 * this._hitPointInfo.faceIndex + 2]);
this._triangle.getInterpolation(this._hitPointInfo.point, _uv0, _uv1, _uv2, _uv);
point.setAttribValue("uv", _uv);
}
}
}
return coreGroup;
}
_addDistAttribute(coreGroup, params) {
if (isBooleanTrue(params.addDistAttribute) == false) {
return;
}
if (coreGroup.hasPointAttrib(DIST_ATTRIB_NAME) == true) {
return;
}
const allObjects = coreGroup.allObjects();
for (const object of allObjects) {
const corePointClass = corePointClassFactory(object);
corePointClass.addNumericAttribute(object, DIST_ATTRIB_NAME, 1, -1);
}
}
}
RaySopOperation.DEFAULT_PARAMS = {
mode: RAY_SOP_MODES.indexOf("project rays" /* PROJECT_RAY */),
useNormals: true,
direction: new Vector3(0, -1, 0),
transformPoints: true,
transferFaceNormals: true,
transferUVs: false,
addDistAttribute: false
};
RaySopOperation.INPUT_CLONED_STATE = [InputCloneMode.FROM_NODE, InputCloneMode.NEVER];