|
@@ -119,7 +119,45 @@
|
|
|
private _areNormalsFrozen: boolean = false; // Will be used by ribbons mainly
|
|
|
|
|
|
private _sourcePositions: Float32Array; // Will be used to save original positions when using software skinning
|
|
|
- private _sourceNormals: Float32Array; // Will be used to save original normals when using software skinning
|
|
|
+ private _sourceNormals: Float32Array; // Will be used to save original normals when using software skinning
|
|
|
+
|
|
|
+ private _facetPositions: Vector3[]; // facet local positions
|
|
|
+ private _facetNormals: Vector3[]; // facet local normals
|
|
|
+ private _facetPartitioning: number[][]; // partitioning array of facet index arrays
|
|
|
+ private _facetNb: number = 0; // facet number
|
|
|
+ private _partitioningSubdivisions: number = 10; // number of subdivisions per axis in the partioning space
|
|
|
+ private _partitioningBBoxRatio: number = 1.01; // the partioning array space is by default 1% bigger than the bounding box
|
|
|
+ private _facetDataEnabled: boolean = false; // is the facet data feature enabled on this mesh ?
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Read-only : the number of facets in the mesh
|
|
|
+ */
|
|
|
+ public get facetNb(): number {
|
|
|
+ return this._facetNb;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * The number of subdivisions per axis in the partioning space
|
|
|
+ */
|
|
|
+ public get partitioningSubdivisions(): number {
|
|
|
+ return this._partitioningSubdivisions;
|
|
|
+ }
|
|
|
+
|
|
|
+ public set partitioningSubdivisions(nb: number) {
|
|
|
+ this._partitioningSubdivisions = nb;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * The ratio to apply to the bouding box size to set to the partioning space.
|
|
|
+ * Ex : 1.01 (default) the partioning space is 1% bigger than the bounding box.
|
|
|
+ */
|
|
|
+ public get partitioningBBoxRatio(): number {
|
|
|
+ return this._partitioningBBoxRatio;
|
|
|
+ }
|
|
|
+
|
|
|
+ public set partitioningBBoxRatio(ratio: number) {
|
|
|
+ this._partitioningBBoxRatio = ratio;
|
|
|
+ }
|
|
|
|
|
|
// Will be used to save a source mesh reference, If any
|
|
|
private _source: BABYLON.Mesh = null;
|
|
@@ -1754,6 +1792,212 @@
|
|
|
});
|
|
|
}
|
|
|
|
|
|
+ // facet data
|
|
|
+ /**
|
|
|
+ * Initialize the facet data arrays : facetNormals, facetPositions and facetPartitioning
|
|
|
+ */
|
|
|
+ private _initFacetData(): Mesh {
|
|
|
+ if (!this._facetNormals) {
|
|
|
+ this._facetNormals = new Array<Vector3>();
|
|
|
+ }
|
|
|
+ if (!this._facetPositions) {
|
|
|
+ this._facetPositions = new Array<Vector3>();
|
|
|
+ }
|
|
|
+ if (!this._facetPartitioning) {
|
|
|
+ this._facetPartitioning = new Array<number[]>();
|
|
|
+ }
|
|
|
+ this._facetNb = this.getIndices().length / 3;
|
|
|
+ this._partitioningSubdivisions = (this._partitioningSubdivisions) ? this._partitioningSubdivisions : 10; // default nb of partitioning subdivisions = 10
|
|
|
+ this._partitioningBBoxRatio = (this._partitioningBBoxRatio) ? this._partitioningBBoxRatio : 1.01; // default ratio 1.01 = the partitioning is 1% bigger than the bounding box
|
|
|
+ for (var f = 0; f < this._facetNb; f++) {
|
|
|
+ this._facetNormals[f] = BABYLON.Vector3.Zero();
|
|
|
+ this._facetPositions[f] = BABYLON.Vector3.Zero();
|
|
|
+ }
|
|
|
+ this._facetDataEnabled = true;
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Updates the mesh facetData arrays and the internal partitioning when the mesh is morphed or updated.
|
|
|
+ * This method can be called within the render loop.
|
|
|
+ */
|
|
|
+ public updateFacetData(): Mesh {
|
|
|
+ if (!this._facetDataEnabled) {
|
|
|
+ this._initFacetData();
|
|
|
+ }
|
|
|
+ var positions = this.getVerticesData(BABYLON.VertexBuffer.PositionKind);
|
|
|
+ var indices = this.getIndices();
|
|
|
+ var normals = this.getVerticesData(BABYLON.VertexBuffer.NormalKind);
|
|
|
+ var options = {
|
|
|
+ facetNormals: this.getFacetLocalNormals(),
|
|
|
+ facetPositions: this.getFacetLocalPositions(),
|
|
|
+ facetPartitioning: this._facetPartitioning,
|
|
|
+ bInfo: this.getBoundingInfo(),
|
|
|
+ ratio: this._partitioningBBoxRatio,
|
|
|
+ partitioningSubdivisions: this._partitioningSubdivisions
|
|
|
+ };
|
|
|
+ BABYLON.VertexData.ComputeNormals(positions, indices, normals, options);
|
|
|
+ this.updateVerticesData(BABYLON.VertexBuffer.NormalKind, normals, false, false);
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * Returns the facetLocalNormals array.
|
|
|
+ * The normals are expressed in the mesh local space.
|
|
|
+ */
|
|
|
+ public getFacetLocalNormals(): Vector3[] {
|
|
|
+ if (!this._facetNormals) {
|
|
|
+ this.updateFacetData();
|
|
|
+ }
|
|
|
+ return this._facetNormals;
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * Returns the facetLocalPositions array.
|
|
|
+ * The facet positions are expressed in the mesh local space.
|
|
|
+ */
|
|
|
+ public getFacetLocalPositions(): Vector3[] {
|
|
|
+ if (!this._facetPositions) {
|
|
|
+ this.updateFacetData();
|
|
|
+ }
|
|
|
+ return this._facetPositions;
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * Returns the i-th facet position in the world system.
|
|
|
+ * This method allocates a new Vector3 per call.
|
|
|
+ */
|
|
|
+ public getFacetPosition(i: number): Vector3 {
|
|
|
+ var pos = BABYLON.Vector3.Zero();
|
|
|
+ this.getFacetPositionToRef(i, pos);
|
|
|
+ return pos;
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * Sets the reference Vector3 with the i-th facet position in the world system.
|
|
|
+ * Returns the mesh.
|
|
|
+ */
|
|
|
+ public getFacetPositionToRef(i: number, ref: Vector3): Mesh {
|
|
|
+ var localPos = (this.getFacetLocalPositions())[i];
|
|
|
+ var world = this.getWorldMatrix();
|
|
|
+ BABYLON.Vector3.TransformCoordinatesToRef(localPos, world, ref);
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * Returns the i-th facet normal in the world system.
|
|
|
+ * This method allocates a new Vector3 per call.
|
|
|
+ */
|
|
|
+ public getFacetNormal(i: number): Vector3 {
|
|
|
+ var norm = BABYLON.Vector3.Zero();
|
|
|
+ this.getFacetNormalToRef(i, norm);
|
|
|
+ return norm;
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * Sets the reference Vector3 with the i-th facet normal in the world system.
|
|
|
+ * Returns the mesh.
|
|
|
+ */
|
|
|
+ public getFacetNormalToRef(i: number, ref: Vector3) {
|
|
|
+ var localNorm = (this.getFacetLocalNormals())[i];
|
|
|
+ var localPos = (this.getFacetLocalPositions())[i];
|
|
|
+ var world = this.getWorldMatrix();
|
|
|
+ var x = localPos.x + localNorm.x;
|
|
|
+ var y = localPos.y + localNorm.y;
|
|
|
+ var z = localPos.z + localNorm.z;
|
|
|
+ BABYLON.Vector3.TransformCoordinatesFromFloatsToRef(x, y, z, world, ref);
|
|
|
+ var worldPos = BABYLON.Tmp.Vector3[8];
|
|
|
+ this.getFacetPositionToRef(i, worldPos);
|
|
|
+ ref.subtractInPlace(worldPos);
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * Returns the facets (in an array) in the same partitioning block than the one the passed coordinates are located (expressed in the mesh local system).
|
|
|
+ */
|
|
|
+ public getFacetsAtLocalCoordinates(x: number, y: number, z: number): number[] {
|
|
|
+ var bInfo = this.getBoundingInfo();
|
|
|
+ var subRatio = this._partitioningSubdivisions * this._partitioningBBoxRatio;
|
|
|
+ var ox = Math.floor((x - bInfo.minimum.x * this._partitioningBBoxRatio) / (bInfo.maximum.x - bInfo.minimum.x) * subRatio);
|
|
|
+ var oy = Math.floor((y - bInfo.minimum.y * this._partitioningBBoxRatio) / (bInfo.maximum.y - bInfo.minimum.y) * subRatio);
|
|
|
+ var oz = Math.floor((z - bInfo.minimum.z * this._partitioningBBoxRatio) / (bInfo.maximum.z - bInfo.minimum.z) * subRatio);
|
|
|
+ if (ox < 0 || ox > this._partitioningSubdivisions || oy < 0 || oy > this._partitioningSubdivisions || oz < 0 || oz > this._partitioningSubdivisions) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ return this._facetPartitioning[ox + this._partitioningSubdivisions * oy + this._partitioningSubdivisions * this._partitioningSubdivisions * oz];
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * Returns the closest mesh facet index at (x,y,z) World coordinates, null if not found
|
|
|
+ * If the parameter projected (vector3) is passed, it is set as the (x,y,z) World projection on the facet
|
|
|
+ * If onlyFacing is true, only the facet "facing" (x,y,z) are returned : positive dot normal * (x,y,z)
|
|
|
+ */
|
|
|
+ public getClosestFacetAtCoordinates(x: number, y: number, z: number, projected?: Vector3, onlyFacing?: boolean): number {
|
|
|
+ var world = this.getWorldMatrix();
|
|
|
+ var invMat = BABYLON.Tmp.Matrix[5];
|
|
|
+ world.invertToRef(invMat);
|
|
|
+ var invVect = BABYLON.Tmp.Vector3[8];
|
|
|
+ var closest = null;
|
|
|
+ BABYLON.Vector3.TransformCoordinatesFromFloatsToRef(x, y, z, invMat, invVect); // transform (x,y,z) to coordinates in the mesh local space
|
|
|
+ closest = this.getClosestFacetAtLocalCoordinates(invVect.x, invVect.y, invVect.z, projected, onlyFacing);
|
|
|
+ if (projected) {
|
|
|
+ // tranform the local computed projected vector to world coordinates
|
|
|
+ BABYLON.Vector3.TransformCoordinatesFromFloatsToRef(projected.x, projected.y, projected.z, world, projected);
|
|
|
+ }
|
|
|
+ return closest;
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * Returns the closest mesh facet index at (x,y,z) local coordinates, null if not found
|
|
|
+ * If the parameter projected (vector3) is passed, it is set as the (x,y,z) local projection on the facet
|
|
|
+ * If onlyFacing is true, only the facet "facing" (x,y,z) are returned : positive dot normal * (x,y,z)
|
|
|
+ */
|
|
|
+ public getClosestFacetAtLocalCoordinates(x: number, y: number, z: number, projected?: Vector3, onlyFacing?: boolean) {
|
|
|
+ var closest = null;
|
|
|
+ var tmpx = 0.0;
|
|
|
+ var tmpy = 0.0;
|
|
|
+ var tmpz = 0.0;
|
|
|
+ var d = 0.0; // tmp dot facet normal * facet position
|
|
|
+ var t0 = 0.0;
|
|
|
+ var projx = 0.0;
|
|
|
+ var projy = 0.0;
|
|
|
+ var projz = 0.0;
|
|
|
+ // Get all the facets in the same partitioning block than (x, y, z)
|
|
|
+ var facetPositions = this.getFacetLocalPositions();
|
|
|
+ var facetNormals = this.getFacetLocalNormals();
|
|
|
+ var facetsInBlock = this.getFacetsAtLocalCoordinates(x, y, z);
|
|
|
+ if (!facetsInBlock) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ // Get the closest facet to (x, y, z)
|
|
|
+ var shortest = Number.MAX_VALUE; // init distance vars
|
|
|
+ var tmpDistance = shortest;
|
|
|
+ var fib; // current facet in the block
|
|
|
+ var norm; // current facet normal
|
|
|
+ var p0; // current facet barycenter position
|
|
|
+ // loop on all the facets in the current partitioning block
|
|
|
+ for (var idx = 0; idx < facetsInBlock.length; idx++) {
|
|
|
+ fib = facetsInBlock[idx];
|
|
|
+ norm = facetNormals[fib];
|
|
|
+ p0 = facetPositions[fib];
|
|
|
+
|
|
|
+ d = (x - p0.x) * norm.x + (y - p0.y) * norm.y + (z - p0.z) * norm.z;
|
|
|
+ if ( !onlyFacing || (onlyFacing && d >= 0) ) {
|
|
|
+ // compute (x,y,z) projection on the facet = (projx, projy, projz)
|
|
|
+ d = norm.x * p0.x + norm.y * p0.y + norm.z * p0.z;
|
|
|
+ t0 = -(norm.x * x + norm.y * y + norm.z * z - d) / (norm.x * norm.x + norm.y * norm.y + norm.z * norm.z);
|
|
|
+ projx = x + norm.x * t0;
|
|
|
+ projy = y + norm.y * t0;
|
|
|
+ projz = z + norm.z * t0;
|
|
|
+
|
|
|
+ tmpx = projx - x;
|
|
|
+ tmpy = projy - y;
|
|
|
+ tmpz = projz - z;
|
|
|
+ tmpDistance = tmpx * tmpx + tmpy * tmpy + tmpz * tmpz; // compute length between (x, y, z) and its projection on the facet
|
|
|
+ if (tmpDistance < shortest) { // just keep the closest facet to (x, y, z)
|
|
|
+ shortest = tmpDistance;
|
|
|
+ closest = fib;
|
|
|
+ if (projected) {
|
|
|
+ projected.x = projx;
|
|
|
+ projected.y = projy;
|
|
|
+ projected.z = projz;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return closest;
|
|
|
+ }
|
|
|
// Statics
|
|
|
/**
|
|
|
* Returns a new Mesh object what is a deep copy of the passed mesh.
|