Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reinstate original version of projectOnPlaneToRef with small amendment #12831

Merged
merged 6 commits into from Aug 3, 2022
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
58 changes: 22 additions & 36 deletions packages/dev/core/src/Maths/math.vector.ts
Expand Up @@ -1130,12 +1130,12 @@ export class Vector3 {
}

/**
* Projects the current vector3 to a plane along a ray starting from a specified origin and directed towards the point.
* Projects the current point Vector3 to a plane along a ray starting from a specified origin and passing through the current point Vector3.
* @param plane defines the plane to project to
* @param origin defines the origin of the projection ray
* @returns the projected vector3
*/
public projectOnPlane(plane: Plane, origin: Vector3): Vector3 {
public projectOnPlane(plane: Plane, origin: Vector3): Vector3 {
const result = Vector3.Zero();

this.projectOnPlaneToRef(plane, origin, result);
Expand All @@ -1144,48 +1144,34 @@ export class Vector3 {
}

/**
* Projects the current vector3 to a plane along a ray starting from a specified origin and directed towards the point.
* Projects the current point Vector3 to a plane along a ray starting from a specified origin and passing through the current point Vector3.
* @param plane defines the plane to project to
* @param origin defines the origin of the projection ray
* @param result defines the Vector3 where to store the result
*/
public projectOnPlaneToRef(plane: Plane, origin: Vector3, result: Vector3): void {
// Use the normal scaled to the plane offset as the origin for the formula
const planeOrigin = MathTmp.Vector3[0];
plane.normal.scaleToRef(-plane.d, planeOrigin);

// Since the normal in Babylon should point toward the viewer, invert it for the dot product
const inverseNormal = MathTmp.Vector3[1];
plane.normal.negateToRef(inverseNormal);

// This vector is the direction
const { x, y, z } = this;

// Calculate how close the direction is to the normal of the plane
const dotProduct = Vector3.Dot(inverseNormal, this);

/*
* Early out in case the direction will never hit the plane.
*
* Epsilon is used to avoid issues with rays very near to parallel with the
* plane, and squared because as of writing the value is not sufficiently
* small for this use case.
*/
if (dotProduct <= Epsilon * Epsilon) {
// No good option for setting the result vector here, so just take the origin of the ray
result.copyFrom(origin);
return;
}
const n = plane.normal;
const d = plane.d;

const V = MathTmp.Vector3[0];

// ray direction
this.subtractToRef(origin, V);

// Calculate the offset
const relativeOrigin = MathTmp.Vector3[2];
planeOrigin.subtractToRef(origin, relativeOrigin);
V.normalize();

// Calculate the length along the direction vector to the hit point
const hitDistance = Vector3.Dot(relativeOrigin, inverseNormal) / dotProduct;
const denom = Vector3.Dot(V, n);

// Apply the hit point by adding the direction scaled by the distance to the origin
result.set(origin.x + x * hitDistance, origin.y + y * hitDistance, origin.z + z * hitDistance);
//When the ray is close to parallel to the plane return infinity vector
if (Math.abs(denom) < Math.pow(10, -10)) {
origin.addToRef(new Vector3(Infinity, Infinity, Infinity), result);
} else {
const t = -(Vector3.Dot(origin, n) + d) / denom;

// P = P0 + t*V
const scaledV = V.scaleInPlace(t);
origin.addToRef(scaledV, result);
}
}

/**
Expand Down
23 changes: 10 additions & 13 deletions packages/dev/core/test/unit/Math/babylon.math.vector.test.ts
Expand Up @@ -5,39 +5,36 @@ import { Plane, Vector3 } from 'core/Maths'
*/
describe("Babylon Vectors", () => {
describe("#Vector3", () => {
it("can project a direction vector onto a plane", () => {
it("can project from an origin onto a plane", () => {
// A ground plane at origin
const simplePlane = Plane.FromPositionAndNormal(
Vector3.Zero(),
Vector3.Up(),
);

const rayOrigin = new Vector3(0, 10, 0);
const rayAngle = new Vector3(0.5, -0.5, 0);
const rayGoingThrough = new Vector3(1, 8, 0);

/*
* At 45 degrees this should form a perfect right triangle,
* so the result should be the same as the distance to the origin.
*/
const expected = new Vector3(10, 0, 0);
// Going left 1 unit for each 2 units downs
const expected = new Vector3(5, 0, 0);

expect(rayAngle.projectOnPlane(simplePlane, rayOrigin)).toEqual(expected)
expect(rayGoingThrough.projectOnPlane(simplePlane, rayOrigin)).toEqual(expected)
})

it("can project a direction vector onto an offset plane", () => {
it("can project from an origin onto an offset plane", () => {
// A ground plane 10 units below origin
const simplePlane = Plane.FromPositionAndNormal(
new Vector3(0, -10, 0),
Vector3.Up(),
);

const rayOrigin = new Vector3(0, 10, 0);
const rayAngle = new Vector3(0.5, -0.5, 0);
const rayGoingThrough = new Vector3(1, 8, 0);

// This is also a right triangle, but the plane is offset so the hit point is offset and the distance increases
const expected = new Vector3(20, -10, 0);
// Going left 1 unit for each 2 units downs
const expected = new Vector3(10, -10, 0);

expect(rayAngle.projectOnPlane(simplePlane, rayOrigin)).toEqual(expected)
expect(rayGoingThrough.projectOnPlane(simplePlane, rayOrigin)).toEqual(expected)
})
})
})