Hey I ended up implementing this in babylonjs, however, I’m having one issue, the cropping works perfectly when done without any transformations on the mesh but if I rotate, move etc (change the transformation) it does weird stuff to the mesh, I am applying the transformation by bakeCurrentTransformIntoVertices to mesh and all its children which are meshes
import {
AbstractMesh,
Mesh,
Scene,
VertexData,
Vector3,
Vector2,
VertexBuffer,
Plane,
Matrix,
} from '@babylonjs/core';
export function removeVerticesOutsideCube(
abstractMesh: AbstractMesh,
cubeMesh: Mesh,
scene: Scene
): void {
let mesh: Mesh | undefined;
var meshParent = abstractMesh as Mesh;
// console.log(meshParent.rotation)
// meshParent.bakeCurrentTransformIntoVertices();
// console.log(meshParent.rotation)
abstractMesh.getChildMeshes(false).forEach((childMesh) => {
if (childMesh instanceof Mesh) {
mesh = childMesh as Mesh;
}
});
if (!mesh) {
console.error('No child mesh found in the provided abstract mesh.');
return;
}
const parentMatrix = mesh.computeWorldMatrix(true);
const vertexData = VertexData.ExtractFromMesh(mesh);
const vertexPositions = vertexData.positions;
if (!vertexPositions) {
console.error('No vertex positions found in the abstract mesh');
return;
}
const cubeBoundingBox = cubeMesh.getBoundingInfo().boundingBox;
const cubeMin = cubeBoundingBox.minimumWorld;
const cubeMax = cubeBoundingBox.maximumWorld;
const updatedVertexData = removeVertices(
vertexData,
cubeMin,
cubeMax,
parentMatrix
);
if (!updatedVertexData) {
console.error('Failed to update vertex data');
return;
}
// Apply the updated vertex data back to the mesh
updatedVertexData.applyToMesh(mesh, true);
// cubeMesh.dispose();
}
function removeVertices(
vertexData: VertexData,
cubeMin: Vector3,
cubeMax: Vector3,
parentMatrix: Matrix
): VertexData | undefined {
if (!vertexData.positions || !vertexData.indices) {
console.error('Mesh vertex data is missing positions or indices');
return;
}
const positions = Array.from(vertexData.positions);
const indices = Array.from(vertexData.indices);
const uvs = vertexData.uvs ? Array.from(vertexData.uvs) : [];
const normals = vertexData.normals ? Array.from(vertexData.normals) : [];
const newPositions: number[] = [];
const newIndices: number[] = [];
const newUVs: number[] = [];
const newNormals: number[] = [];
function transformVertex(vertex: Vector3): Vector3 {
return Vector3.TransformCoordinates(vertex, parentMatrix);
}
function getVertex(index: number): Vector3 {
return transformVertex(
new Vector3(
positions[index * 3],
positions[index * 3 + 1],
positions[index * 3 + 2]
)
);
}
function getUV(index: number): Vector2 {
return new Vector2(uvs[index * 2], uvs[index * 2 + 1]);
}
function getNormal(index: number): Vector3 {
return new Vector3(
normals[index * 3],
normals[index * 3 + 1],
normals[index * 3 + 2]
);
}
function addVertex(
vertex: Vector3,
uv?: Vector2,
normal?: Vector3
): number {
const index = newPositions.length / 3;
newPositions.push(vertex.x, vertex.y, vertex.z);
if (uv) {
newUVs.push(uv.x, uv.y);
}
if (normal) {
newNormals.push(normal.x, normal.y, normal.z);
}
return index;
}
function getIntersectionPoint(
p1: Vector3,
p2: Vector3,
plane: Plane
): Vector3 | null {
const lineDir = p2.subtract(p1);
const denominator = Vector3.Dot(plane.normal, lineDir);
if (Math.abs(denominator) < 1e-6) {
return null; // The line is parallel to the plane
}
const t = -(Vector3.Dot(plane.normal, p1) + plane.d) / denominator;
if (t < 0 || t > 1) {
return null; // The intersection point is not within the segment
}
return p1.add(lineDir.scale(t));
}
const planes = [
new Plane(1, 0, 0, -cubeMin.x), // Left
new Plane(-1, 0, 0, cubeMax.x), // Right
new Plane(0, 1, 0, -cubeMin.y), // Bottom
new Plane(0, -1, 0, cubeMax.y), // Top
new Plane(0, 0, 1, -cubeMin.z), // Front
new Plane(0, 0, -1, cubeMax.z), // Back
];
function isInsideCube(
vertex: Vector3,
min: Vector3,
max: Vector3
): boolean {
return (
vertex.x >= min.x &&
vertex.x <= max.x &&
vertex.y >= min.y &&
vertex.y <= max.y &&
vertex.z >= min.z &&
vertex.z <= max.z
);
}
function getSignedDistanceToPlane(point: Vector3, plane: Plane): number {
return Vector3.Dot(plane.normal, point) + plane.d;
}
function clipPolygonWithPlane(
polygon: [Vector3, Vector2, Vector3][],
plane: Plane
): [Vector3, Vector2, Vector3][] {
const result: [Vector3, Vector2, Vector3][] = [];
for (let i = 0; i < polygon.length; i++) {
const currentVertex = polygon[i];
const nextVertex = polygon[(i + 1) % polygon.length];
const currentInside =
getSignedDistanceToPlane(currentVertex[0], plane) >= 0;
const nextInside =
getSignedDistanceToPlane(nextVertex[0], plane) >= 0;
if (currentInside) {
result.push(currentVertex);
}
if (currentInside !== nextInside) {
const intersectionPoint = getIntersectionPoint(
currentVertex[0],
nextVertex[0],
plane
);
if (intersectionPoint) {
const t =
Vector3.Distance(currentVertex[0], intersectionPoint) /
Vector3.Distance(currentVertex[0], nextVertex[0]);
const intersectionUV = Vector2.Lerp(
currentVertex[1],
nextVertex[1],
t
);
const intersectionNormal = Vector3.Lerp(
currentVertex[2],
nextVertex[2],
t
).normalize();
result.push([
intersectionPoint,
intersectionUV,
intersectionNormal,
]);
}
}
}
return result;
}
for (let i = 0; i < indices.length; i += 3) {
const [i0, i1, i2] = [indices[i], indices[i + 1], indices[i + 2]];
const [v0, v1, v2] = [getVertex(i0), getVertex(i1), getVertex(i2)];
const [uv0, uv1, uv2] = [getUV(i0), getUV(i1), getUV(i2)];
const [n0, n1, n2] = [getNormal(i0), getNormal(i1), getNormal(i2)];
let polygon: [Vector3, Vector2, Vector3][] = [
[v0, uv0, n0],
[v1, uv1, n1],
[v2, uv2, n2],
];
planes.forEach((plane) => {
polygon = clipPolygonWithPlane(polygon, plane);
});
if (polygon.length < 3) {
continue; // Ignore degenerate polygons
}
const baseIndex = newPositions.length / 3;
polygon.forEach(([position, uv, normal]) => {
newPositions.push(position.x, position.y, position.z);
newUVs.push(uv.x, uv.y);
newNormals.push(normal.x, normal.y, normal.z);
});
for (let j = 1; j < polygon.length - 1; j++) {
newIndices.push(baseIndex, baseIndex + j, baseIndex + j + 1);
}
}
const updatedVertexData = new VertexData();
updatedVertexData.positions = new Float32Array(newPositions);
updatedVertexData.indices = new Uint32Array(newIndices);
if (newUVs.length > 0) {
updatedVertexData.uvs = new Float32Array(newUVs);
}
if (newNormals.length > 0) {
updatedVertexData.normals = new Float32Array(newNormals);
} else {
updatedVertexData.normals = new Float32Array(newPositions.length);
VertexData.ComputeNormals(
updatedVertexData.positions,
updatedVertexData.indices,
updatedVertexData.normals
);
}
return updatedVertexData;
}