Skip to content

Commit

Permalink
distanceToFace, pointOnFace, ...
Browse files Browse the repository at this point in the history
  • Loading branch information
Bela Bohlender committed Jul 4, 2023
1 parent 320cca3 commit bfcd4b0
Show file tree
Hide file tree
Showing 12 changed files with 415 additions and 60 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@coconut-xr/xinteraction",
"version": "0.0.11",
"version": "0.1.0",
"homepage": "https://coconut-xr.github.io/xinteraction",
"license": "SEE LICENSE IN LICENSE",
"description": "interactions for three.js",
Expand Down
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ export type XIntersection = Intersection & {
inputDevicePosition: Vector3;
inputDeviceRotation: Quaternion;
capturedObject?: Object3D;
pointOnFace: Vector3;
localPoint: Vector3;
};

export function isXIntersection(val: Intersection): val is XIntersection {
Expand Down
17 changes: 17 additions & 0 deletions src/intersections/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Intersection, Mesh, Object3D, Plane } from "three";
import { XIntersection } from "../index.js";

export function traverseUntilInteractable<T, R>(
object: Object3D,
Expand Down Expand Up @@ -39,6 +40,22 @@ export function isIntersectionNotClipped(intersection: Intersection): boolean {
return true;
}

export function computeIntersectionWorldPlane(
plane: Plane,
intersection: XIntersection,
object: Object3D
): boolean {
if (intersection.face == null) {
return false;
}
plane.setFromNormalAndCoplanarPoint(
intersection.face.normal,
intersection.localPoint
);
plane.applyMatrix4(object.matrixWorld);
return true;
}

export * from "./lines.js";
export * from "./ray.js";
export * from "./sphere.js";
54 changes: 53 additions & 1 deletion src/intersections/lines.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
import { Line3, Object3D, Quaternion, Raycaster, Vector3 } from "three";
import {
Line3,
Matrix4,
Object3D,
Plane,
Quaternion,
Ray,
Raycaster,
Vector3,
} from "three";
import { EventDispatcher, XIntersection } from "../index.js";
import {
isIntersectionNotClipped,
computeIntersectionWorldPlane,
traverseUntilInteractable,
} from "./index.js";
import { ThreeEvent } from "@react-three/fiber";
Expand All @@ -15,6 +25,7 @@ export type XLinesIntersection = XIntersection & {

const directionHelper = new Vector3();
const lineHelper = new Line3();
const planeHelper = new Plane();

export function intersectLinesFromCapturedEvents(
from: Object3D,
Expand All @@ -33,8 +44,14 @@ export function intersectLinesFromCapturedEvents(
.applyMatrix4(from.matrixWorld);

const point = lineHelper.at(intersection.distanceOnLine, new Vector3());
computeIntersectionWorldPlane(planeHelper, intersection, capturedObject);
const pointOnFace =
backwardsIntersectionLinesWithPlane(from, linePoints, planeHelper) ??
point;

return {
...intersection,
pointOnFace,
point,
inputDevicePosition: fromPosition.clone(),
inputDeviceRotation: fromRotation.clone(),
Expand All @@ -43,6 +60,33 @@ export function intersectLinesFromCapturedEvents(
});
}

const vectorHelper = new Vector3();
const rayHelper = new Ray();

function backwardsIntersectionLinesWithPlane(
from: Object3D,
linePoints: Array<Vector3>,
plane: Plane
): Vector3 | undefined {
for (let i = linePoints.length - 1; i > 0; i--) {
const start = linePoints[i - 1];
const end = linePoints[i];
rayHelper.origin.copy(start).applyMatrix4(from.matrixWorld);
rayHelper.direction
.copy(end)
.applyMatrix4(from.matrixWorld)
.sub(raycaster.ray.origin)
.normalize();
const point = rayHelper.intersectPlane(plane, vectorHelper);
if (point != null) {
return vectorHelper.clone();
}
}
return undefined;
}

const invertedMatrixHelper = new Matrix4();

export function intersectLinesFromObject(
from: Object3D,
fromPosition: Vector3,
Expand Down Expand Up @@ -92,6 +136,14 @@ export function intersectLinesFromObject(
inputDeviceRotation: fromRotation.clone(),
lineIndex: i - 1,
distanceOnLine,
pointOnFace: newIntersection.point,
localPoint: newIntersection.point
.clone()
.applyMatrix4(
invertedMatrixHelper
.copy(newIntersection.object.matrixWorld)
.invert()
),
})
);
}
Expand Down
46 changes: 36 additions & 10 deletions src/intersections/ray.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
import {
Camera,
Euler,

Check warning on line 3 in src/intersections/ray.ts

View workflow job for this annotation

GitHub Actions / publish-npm-package

'Euler' is defined but never used

Check warning on line 3 in src/intersections/ray.ts

View workflow job for this annotation

GitHub Actions / publish-github-package

'Euler' is defined but never used
Matrix4,
Object3D,
Plane,
Quaternion,
Ray,
Raycaster,
Vector2,
Vector3,
} from "three";
import { EventDispatcher, XIntersection } from "../index.js";
import {
computeIntersectionWorldPlane,
isIntersectionNotClipped,
traverseUntilInteractable,
} from "./index.js";
Expand All @@ -23,6 +26,7 @@ export type XCameraRayIntersection = XIntersection & {

const directionHelper = new Vector3();
const planeHelper = new Plane();
const rayHelper = new Ray();

export function intersectRayFromCapturedEvents(
fromPosition: Vector3,
Expand All @@ -32,8 +36,14 @@ export function intersectRayFromCapturedEvents(
): Array<XIntersection> {
directionHelper.copy(direction).applyQuaternion(fromRotation);
return Array.from(capturedEvents).map(([capturedObject, intersection]) => {
rayHelper.set(fromPosition, directionHelper);
computeIntersectionWorldPlane(planeHelper, intersection, capturedObject);
const pointOnFace =
rayHelper.intersectPlane(planeHelper, new Vector3()) ??
intersection.point;
return {
...intersection,
pointOnFace,
point: directionHelper
.clone()
.multiplyScalar(intersection.distance)
Expand Down Expand Up @@ -67,18 +77,24 @@ export function intersectRayFromCameraCapturedEvents(
planeHelper.constant -= intersection.distanceViewPlane;

//find captured intersection point by intersecting the ray to the plane of the camera
const point = new Vector3();
raycaster.ray.intersectPlane(planeHelper, point);
const point = raycaster.ray.intersectPlane(planeHelper, new Vector3())!;

Check warning on line 80 in src/intersections/ray.ts

View workflow job for this annotation

GitHub Actions / publish-npm-package

Forbidden non-null assertion

Check warning on line 80 in src/intersections/ray.ts

View workflow job for this annotation

GitHub Actions / publish-github-package

Forbidden non-null assertion

computeIntersectionWorldPlane(planeHelper, intersection, capturedObject);
const pointOnFace =
raycaster.ray.intersectPlane(planeHelper, new Vector3()) ?? point;
return {
...intersection,
point,
pointOnFace,
inputDevicePosition: worldPositionTarget.clone(),
inputDeviceRotation: worldQuaternionTarget.clone(),
capturedObject,
};
});
}

const invertedMatrixHelper = new Matrix4();

export function intersectRayFromObject(
fromPosition: Vector3,
fromRotation: Quaternion,
Expand All @@ -96,12 +112,17 @@ export function intersectRayFromObject(
on,
dispatcher.hasEventHandlers.bind(dispatcher),
(object) =>
raycaster.intersectObject(object, true).map((intersection) =>
Object.assign(intersection, {
raycaster.intersectObject(object, true).map((intersection) => {
invertedMatrixHelper.copy(object.matrixWorld).invert();
return Object.assign(intersection, {
inputDevicePosition: fromPosition.clone(),
inputDeviceRotation: fromRotation.clone(),
})
),
pointOnFace: intersection.point,
localPoint: intersection.point
.clone()
.applyMatrix4(invertedMatrixHelper),
});
}),
(prev, cur) => prev.concat(cur),
[]
);
Expand Down Expand Up @@ -138,13 +159,18 @@ export function intersectRayFromCamera(
on,
dispatcher.hasEventHandlers.bind(dispatcher),
(object) =>
raycaster.intersectObject(object, true).map((intersection) =>
Object.assign(intersection, {
raycaster.intersectObject(object, true).map((intersection) => {
invertedMatrixHelper.copy(object.matrixWorld).invert();
return Object.assign(intersection, {
pointOnFace: intersection.point,
inputDevicePosition: worldPositionTarget.clone(),
inputDeviceRotation: worldQuaternionTarget.clone(),
distanceViewPlane: planeHelper.distanceToPoint(intersection.point),
})
),
localPoint: intersection.point
.clone()
.applyMatrix4(invertedMatrixHelper),
});
}),
(prev, cur) => prev.concat(cur),
[]
);
Expand Down
64 changes: 23 additions & 41 deletions src/intersections/sphere.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,26 @@ import {
Sphere,
Quaternion,
Intersection,
Matrix3,
Box3,
Plane,
} from "three";
import { EventDispatcher, XIntersection } from "../index.js";
import {
computeIntersectionWorldPlane,
isIntersectionNotClipped,
traverseUntilInteractable,
} from "./index.js";
import { ThreeEvent } from "@react-three/fiber";

const oldInputDevicePointOffset = new Vector3();
const inputDeviceQuaternionOffset = new Quaternion();
const planeHelper = new Plane();

export type XSphereIntersection = XIntersection & {
/**
* set when the event is captured because the "distance" property is only the distance to a "expected intersection"
*/
actualDistance?: number;
distanceToFace?: number;
};

export function intersectSphereFromCapturedEvents(
Expand All @@ -50,53 +52,27 @@ export function intersectSphereFromCapturedEvents(
.clone()
.applyQuaternion(inputDeviceQuaternionOffset)
.add(fromPosition);

computeIntersectionWorldPlane(planeHelper, intersection, capturedObject);

const pointOnFace = planeHelper.projectPoint(fromPosition, new Vector3());

return {
distance: intersection.distance,
inputDevicePosition: fromPosition.clone(),
inputDeviceRotation: fromRotation.clone(),
object: intersection.object,
point,
pointOnFace,
face: intersection.face,
capturedObject,
actualDistance: computeActualDistance(fromPosition, intersection),
distanceToFace: pointOnFace.distanceTo(fromPosition),
localPoint: intersection.localPoint,
};
}
);
}

function computeActualDistance(
fromPosition: Vector3,
intersection: Intersection
): number {
const object = intersection.object;
if (intersection.instanceId != null && object instanceof InstancedMesh) {
if (object.geometry.boundingBox == null) {
object.geometry.computeBoundingBox();
}
object.getMatrixAt(intersection.instanceId, matrixHelper);
matrixHelper.premultiply(object.matrixWorld);
invertedMatrixHelper.copy(matrixHelper).invert();
vectorHelper.copy(fromPosition).applyMatrix4(invertedMatrixHelper);
object.geometry.boundingBox!.clampPoint(vectorHelper, vectorHelper);
vectorHelper.applyMatrix4(matrixHelper);
return vectorHelper.distanceTo(fromPosition);
}

if (object instanceof Mesh) {
if (object.geometry.boundingBox == null) {
object.geometry.computeBoundingBox();
}
invertedMatrixHelper.copy(object.matrixWorld).invert();
vectorHelper.copy(fromPosition).applyMatrix4(invertedMatrixHelper);
object.geometry.boundingBox!.clampPoint(vectorHelper, vectorHelper);
vectorHelper.applyMatrix4(object.matrixWorld);
return vectorHelper.distanceTo(fromPosition);
}

//not lösung - emergency solution
return object.getWorldPosition(vectorHelper).distanceTo(fromPosition);
}

const collisionSphere = new Sphere();

export function intersectSphereFromObject(
Expand Down Expand Up @@ -162,8 +138,14 @@ function intersectSphere(
object.spherecast(collisionSphere, intersections);
return intersections.map((intersection) => ({
...intersection,
pointOnFace: intersection.point,
inputDevicePosition: collisionSphere.center.clone(),
inputDeviceRotation: inputDeviceRotation.clone(),
localPoint: intersection.point
.clone()
.applyMatrix4(
invertedMatrixHelper.copy(intersection.object.matrixWorld).invert()
),
}));
}
if (object instanceof InstancedMesh) {
Expand Down Expand Up @@ -275,6 +257,8 @@ function intersectSphereBox(
normal.divide(boxSizeHelper);
maximizeAxisVector(normal);

const point = vectorHelper.clone();

return {
distance: Math.sqrt(distanceToSphereCenterSquared),
object,
Expand All @@ -285,10 +269,12 @@ function intersectSphereBox(
materialIndex: 0,
normal,
},
point: vectorHelper.clone(),
pointOnFace: point,
point,
instanceId,
inputDevicePosition: inputDevicePosition.clone(),
inputDeviceRotation: inputDeviceRotation.clone(),
localPoint: point.clone().applyMatrix4(invertedMatrixWorld),
};
}

Expand All @@ -311,7 +297,3 @@ function maximizeAxisVector(vec: Vector3): void {
//z biggest
vec.set(0, 0, vec.z < 0 ? -1 : 1);
}

function replaceZero(value: number, newValue: number): number {
return value === 0 ? newValue : 0;
}
Loading

0 comments on commit bfcd4b0

Please sign in to comment.