@babylonjs/core
Version:
Getting started? Play directly with the Babylon.js API using our [playground](https://playground.babylonjs.com/). It also contains a lot of samples to learn how to use it.
225 lines • 8.27 kB
JavaScript
import { Vector3 } from "../Maths/math.vector.js";
import { VertexBuffer } from "./buffer.js";
import { Clamp, OutsideRange } from "../Maths/math.scalar.functions.js";
import { Epsilon } from "../Maths/math.constants.js";
/**
* Class used to represent a lattice
* @see [Moving lattice bounds](https://playground.babylonjs.com/#MDVD75#18)
* @see [Twist](https://playground.babylonjs.com/#MDVD75#23)
*/
export class Lattice {
/**
* @returns the string "Lattice"
*/
getClassName() {
return "Lattice";
}
/**
* Gets the resolution on x axis
*/
get resolutionX() {
return this._resolutionX;
}
/**
* Gets the resolution on y axis
*/
get resolutionY() {
return this._resolutionY;
}
/**
* Gets the resolution on z axis
*/
get resolutionZ() {
return this._resolutionZ;
}
/**
* Gets the size of the lattice along each axis in object space
* Updating the size requires you to call update afterwards
*/
get size() {
return this._size;
}
/**
* Gets the lattice position in object space
*/
get position() {
return this._position;
}
/**
* Gets the data of the lattice
*/
get data() {
return this._data;
}
/**
* Gets the size of each cell in the lattice
*/
get cellSize() {
return this._cellSize;
}
/**
* Gets the min bounds of the lattice
*/
get min() {
return this._min;
}
/**
* Gets the max bounds of the lattice
*/
get max() {
return this._max;
}
/**
* Creates a new Lattice
* @param options options for creating
*/
constructor(options) {
this._cellSize = new Vector3();
// Cache
this._min = new Vector3(-0.5, -0.5, -0.5);
this._max = new Vector3(0.5, 0.5, 0.5);
this._localPos = new Vector3();
this._tmpVector = new Vector3();
this._lerpVector0 = new Vector3();
this._lerpVector1 = new Vector3();
this._lerpVector2 = new Vector3();
this._lerpVector3 = new Vector3();
this._lerpVector4 = new Vector3();
this._lerpVector5 = new Vector3();
const localOptions = {
resolutionX: 3,
resolutionY: 3,
resolutionZ: 3,
position: Vector3.Zero(),
size: Vector3.One(),
...options,
};
this._resolutionX = localOptions.resolutionX;
this._resolutionY = localOptions.resolutionY;
this._resolutionZ = localOptions.resolutionZ;
this._position = localOptions.position;
this._size = localOptions.autoAdaptToMesh ? localOptions.autoAdaptToMesh.getBoundingInfo().boundingBox.extendSize.scale(2) : localOptions.size;
// Allocate data
this._allocateData();
this.update();
}
_allocateData() {
this._data = new Array(this.resolutionX);
for (let i = 0; i < this.resolutionX; i++) {
this._data[i] = new Array(this.resolutionY);
for (let j = 0; j < this.resolutionY; j++) {
this._data[i][j] = new Array(this.resolutionZ);
for (let k = 0; k < this.resolutionZ; k++) {
this._data[i][j][k] = Vector3.Zero();
}
}
}
}
/**
* Update of the lattice data
*/
update() {
for (let i = 0; i < this.resolutionX; i++) {
for (let j = 0; j < this.resolutionY; j++) {
for (let k = 0; k < this.resolutionZ; k++) {
const x = -this.size.x / 2 + this.size.x * (i / (this.resolutionX - 1));
const y = -this.size.y / 2 + this.size.y * (j / (this.resolutionY - 1));
const z = -this.size.z / 2 + this.size.z * (k / (this.resolutionZ - 1));
this._data[i][j][k].set(x, y, z);
}
}
}
}
/**
* Apply the lattice to a mesh
* @param mesh mesh to deform
*/
deformMesh(mesh) {
const positions = mesh.getVerticesData(VertexBuffer.PositionKind);
if (!positions) {
return;
}
// Apply the lattice
this.deform(positions);
// Update back the mesh
mesh.setVerticesData(VertexBuffer.PositionKind, positions, true);
}
/**
* Update the lattice internals (like min, max and cell size)
*/
updateInternals() {
const nx = this._resolutionX;
const ny = this._resolutionY;
const nz = this._resolutionZ;
// Calculate the size of each cell in the lattice
this._cellSize.set(this.size.x / (nx - 1), this.size.y / (ny - 1), this.size.z / (nz - 1));
// Calculate the lattice bounds
this._min.set(this.position.x - this.size.x / 2, this.position.y - this.size.y / 2, this.position.z - this.size.z / 2);
this._min.addToRef(this._size, this._max);
}
/**
* Apply the lattice to a set of points
* @param positions vertex data to deform
* @param target optional target array to store the result (operation will be done in place in not defined)
*/
deform(positions, target) {
const nx = this._resolutionX;
const ny = this._resolutionY;
const nz = this._resolutionZ;
this.updateInternals();
const min = this._min;
const max = this._max;
// Loop over each vertex
for (let i = 0; i < positions.length; i += 3) {
const vertex = this._tmpVector.fromArray(positions, i);
// Check we are inside
if (OutsideRange(vertex.x, min.x, max.x, Epsilon) || OutsideRange(vertex.y, min.y, max.y, Epsilon) || OutsideRange(vertex.z, min.z, max.z, Epsilon)) {
if (target) {
vertex.toArray(target, i);
}
continue;
}
// Map vertex position to lattice local coordinates
const localPos = this._localPos.set((vertex.x - min.x) / this._cellSize.x, (vertex.y - min.y) / this._cellSize.y, (vertex.z - min.z) / this._cellSize.z);
// Get integer lattice indices
const i0 = Math.floor(localPos.x);
const j0 = Math.floor(localPos.y);
const k0 = Math.floor(localPos.z);
const i1 = Math.min(i0 + 1, nx - 1);
const j1 = Math.min(j0 + 1, ny - 1);
const k1 = Math.min(k0 + 1, nz - 1);
// Compute interpolation weights
const tx = localPos.x - i0;
const ty = localPos.y - j0;
const tz = localPos.z - k0;
// Ensure indices are within bounds
const ii0 = Clamp(i0, 0, nx - 1);
const jj0 = Clamp(j0, 0, ny - 1);
const kk0 = Clamp(k0, 0, nz - 1);
const ii1 = Clamp(i1, 0, nx - 1);
const jj1 = Clamp(j1, 0, ny - 1);
const kk1 = Clamp(k1, 0, nz - 1);
// Get lattice control points
const p000 = this._data[ii0][jj0][kk0];
const p100 = this._data[ii1][jj0][kk0];
const p010 = this._data[ii0][jj1][kk0];
const p110 = this._data[ii1][jj1][kk0];
const p001 = this._data[ii0][jj0][kk1];
const p101 = this._data[ii1][jj0][kk1];
const p011 = this._data[ii0][jj1][kk1];
const p111 = this._data[ii1][jj1][kk1];
// Trilinear interpolation
const p00 = Vector3.LerpToRef(p000, p100, tx, this._lerpVector0);
const p01 = Vector3.LerpToRef(p001, p101, tx, this._lerpVector1);
const p10 = Vector3.LerpToRef(p010, p110, tx, this._lerpVector2);
const p11 = Vector3.LerpToRef(p011, p111, tx, this._lerpVector3);
const p0 = Vector3.LerpToRef(p00, p10, ty, this._lerpVector4);
const p1 = Vector3.LerpToRef(p01, p11, ty, this._lerpVector5);
const deformedPos = Vector3.LerpToRef(p0, p1, tz, this._lerpVector0);
deformedPos.addInPlace(this.position);
// Apply deformation to the vertex
deformedPos.toArray(target || positions, i);
}
}
}
//# sourceMappingURL=lattice.js.map