@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.
312 lines • 14.6 kB
JavaScript
import { Vector3 } from "../../Maths/math.vector.js";
import { Mesh } from "../mesh.js";
import { Buffer, VertexBuffer } from "../../Buffers/buffer.js";
import { PickingInfo } from "../../Collisions/pickingInfo.js";
import { DeepCopier } from "../../Misc/deepCopier.js";
import { GreasedLineTools } from "../../Misc/greasedLineTools.js";
import { GreasedLineBaseMesh } from "./greasedLineBaseMesh.js";
Mesh._GreasedLineMeshParser = (parsedMesh, scene) => {
return GreasedLineMesh.Parse(parsedMesh, scene);
};
/**
* GreasedLineMesh
* Use the GreasedLineBuilder.CreateGreasedLine function to create an instance of this class.
*/
export class GreasedLineMesh extends GreasedLineBaseMesh {
/**
* GreasedLineMesh
* @param name name of the mesh
* @param scene the scene
* @param _options mesh options
*/
constructor(name, scene, _options) {
super(name, scene, _options);
this.name = name;
/**
* Treshold used to pick the mesh
*/
this.intersectionThreshold = 0.1;
this._previousAndSide = [];
this._nextAndCounters = [];
if (_options.points) {
this.addPoints(GreasedLineTools.ConvertPoints(_options.points));
}
}
/**
* "GreasedLineMesh"
* @returns "GreasedLineMesh"
*/
getClassName() {
return "GreasedLineMesh";
}
_updateColorPointers() {
if (this._options.colorPointers) {
return;
}
let colorPointer = 0;
this._colorPointers = [];
for (const p of this._points) {
for (let jj = 0; jj < p.length; jj += 3) {
this._colorPointers.push(colorPointer);
this._colorPointers.push(colorPointer++);
}
}
}
_updateWidths() {
// intentionally left blank
}
_setPoints(points) {
this._points = points;
this._options.points = points;
this._initGreasedLine();
let indiceOffset = 0;
let vertexPositionsLen = 0, indicesLength = 0, uvLength = 0, previousAndSideLength = 0;
for (const p of points) {
vertexPositionsLen += p.length * 2;
indicesLength += (p.length - 3) * 2;
uvLength += (p.length * 4) / 3;
previousAndSideLength += (p.length * 8) / 3;
}
const vertexPositionsArr = new Float32Array(vertexPositionsLen);
const indicesArr = vertexPositionsLen > 65535 ? new Uint32Array(indicesLength) : new Uint16Array(indicesLength);
const uvArr = new Float32Array(uvLength);
const previousAndSide = new Float32Array(previousAndSideLength);
// it's the same length here
const nextAndCounters = new Float32Array(previousAndSideLength);
let vertexPositionsOffset = 0, indicesOffset = 0, uvOffset = 0, previousAndSideOffset = 0, nextAndCountersOffset = 0;
for (const p of points) {
const lengthArray = GreasedLineTools.GetLineLengthArray(p);
const totalLength = lengthArray[lengthArray.length - 1];
for (let j = 0, jj = 0; jj < p.length; j++, jj += 3) {
const baseOffset = vertexPositionsOffset + jj * 2;
vertexPositionsArr[baseOffset + 0] = p[jj + 0];
vertexPositionsArr[baseOffset + 1] = p[jj + 1];
vertexPositionsArr[baseOffset + 2] = p[jj + 2];
vertexPositionsArr[baseOffset + 3] = p[jj + 0];
vertexPositionsArr[baseOffset + 4] = p[jj + 1];
vertexPositionsArr[baseOffset + 5] = p[jj + 2];
if (jj < p.length - 3) {
const n = j * 2 + indiceOffset;
const baseIndicesOffset = indicesOffset + jj * 2;
indicesArr[baseIndicesOffset + 0] = n;
indicesArr[baseIndicesOffset + 1] = n + 1;
indicesArr[baseIndicesOffset + 2] = n + 2;
indicesArr[baseIndicesOffset + 3] = n + 2;
indicesArr[baseIndicesOffset + 4] = n + 1;
indicesArr[baseIndicesOffset + 5] = n + 3;
}
}
indiceOffset += (p.length / 3) * 2;
const currVertexPositionsOffsetLength = p.length * 2;
const positions = vertexPositionsArr.subarray(vertexPositionsOffset, vertexPositionsOffset + currVertexPositionsOffsetLength);
vertexPositionsOffset += currVertexPositionsOffsetLength;
indicesOffset += (p.length - 3) * 2;
const previous = new Float32Array(positions.length);
const next = new Float32Array(positions.length);
const l = positions.length / 6;
let v;
if (GreasedLineMesh._CompareV3(0, l - 1, positions)) {
v = positions.subarray((l - 2) * 6, (l - 1) * 6);
}
else {
v = positions.subarray(0, 6);
}
previous.set(v);
previous.set(positions.subarray(0, positions.length - 6), 6);
next.set(positions.subarray(6));
if (GreasedLineMesh._CompareV3(l - 1, 0, positions)) {
v = positions.subarray(6, 12);
}
else {
v = positions.subarray((l - 1) * 6, l * 6);
}
next.set(v, next.length - 6);
for (let i = 0, sidesLength = positions.length / 3; i < sidesLength; i++) {
previousAndSide[previousAndSideOffset++] = previous[i * 3];
previousAndSide[previousAndSideOffset++] = previous[i * 3 + 1];
previousAndSide[previousAndSideOffset++] = previous[i * 3 + 2];
// side[i] = i % 2 ? -1 : 1;
// side[i] = 1 - ((i & 1) << 1);
previousAndSide[previousAndSideOffset++] = 1 - ((i & 1) << 1);
nextAndCounters[nextAndCountersOffset++] = next[i * 3];
nextAndCounters[nextAndCountersOffset++] = next[i * 3 + 1];
nextAndCounters[nextAndCountersOffset++] = next[i * 3 + 2];
// counters[i] = lengthArray[i >> 1] / totalLength;
nextAndCounters[nextAndCountersOffset++] = lengthArray[i >> 1] / totalLength;
}
if (this._options.uvs) {
for (let i = 0; i < this._options.uvs.length; i++) {
uvArr[uvOffset++] = this._options.uvs[i];
}
}
else {
for (let j = 0; j < l; j++) {
// uvs
const lengthRatio = lengthArray[j] / totalLength;
const uvOffsetBase = uvOffset + j * 4;
uvArr[uvOffsetBase + 0] = lengthRatio;
uvArr[uvOffsetBase + 1] = 0;
uvArr[uvOffsetBase + 2] = lengthRatio;
uvArr[uvOffsetBase + 3] = 1;
}
}
}
this._vertexPositions = vertexPositionsArr;
this._indices = indicesArr;
this._uvs = uvArr;
this._previousAndSide = previousAndSide;
this._nextAndCounters = nextAndCounters;
if (!this._lazy) {
if (!this._options.colorPointers) {
this._updateColorPointers();
}
this._createVertexBuffers();
!this.doNotSyncBoundingInfo && this.refreshBoundingInfo();
}
}
/**
* Clones the GreasedLineMesh.
* @param name new line name
* @param newParent new parent node
* @returns cloned line
*/
clone(name = `${this.name}-cloned`, newParent) {
const lineOptions = this._createLineOptions();
const deepCopiedLineOptions = {};
DeepCopier.DeepCopy(lineOptions, deepCopiedLineOptions, ["instance"], undefined, true);
const cloned = new GreasedLineMesh(name, this._scene, deepCopiedLineOptions);
if (newParent) {
cloned.parent = newParent;
}
cloned.material = this.material;
return cloned;
}
/**
* Serializes this GreasedLineMesh
* @param serializationObject object to write serialization to
*/
serialize(serializationObject) {
super.serialize(serializationObject);
serializationObject.type = this.getClassName();
serializationObject.lineOptions = this._createLineOptions();
}
/**
* Parses a serialized GreasedLineMesh
* @param parsedMesh the serialized GreasedLineMesh
* @param scene the scene to create the GreasedLineMesh in
* @returns the created GreasedLineMesh
*/
static Parse(parsedMesh, scene) {
const lineOptions = parsedMesh.lineOptions;
const name = parsedMesh.name;
const result = new GreasedLineMesh(name, scene, lineOptions);
return result;
}
_initGreasedLine() {
super._initGreasedLine();
this._previousAndSide = [];
this._nextAndCounters = [];
}
/**
* Checks whether a ray is intersecting this GreasedLineMesh
* @param ray ray to check the intersection of this mesh with
* @param fastCheck not supported
* @param trianglePredicate not supported
* @param onlyBoundingInfo defines a boolean indicating if picking should only happen using bounding info (false by default)
* @param worldToUse not supported
* @param skipBoundingInfo a boolean indicating if we should skip the bounding info check
* @returns the picking info
*/
intersects(ray, fastCheck, trianglePredicate, onlyBoundingInfo = false, worldToUse, skipBoundingInfo = false) {
const pickingInfo = new PickingInfo();
const intersections = this.findAllIntersections(ray, fastCheck, trianglePredicate, onlyBoundingInfo, worldToUse, skipBoundingInfo, true);
if (intersections?.length === 1) {
const intersection = intersections[0];
pickingInfo.hit = true;
pickingInfo.distance = intersection.distance;
pickingInfo.ray = ray;
pickingInfo.pickedMesh = this;
pickingInfo.pickedPoint = intersection.point;
}
return pickingInfo;
}
/**
* Gets all intersections of a ray and the line
* @param ray Ray to check the intersection of this mesh with
* @param _fastCheck not supported
* @param _trianglePredicate not supported
* @param onlyBoundingInfo defines a boolean indicating if picking should only happen using bounding info (false by default)
* @param _worldToUse not supported
* @param skipBoundingInfo a boolean indicating if we should skip the bounding info check
* @param firstOnly If true, the first and only intersection is immediatelly returned if found
* @returns intersection(s)
*/
findAllIntersections(ray, _fastCheck, _trianglePredicate, onlyBoundingInfo = false, _worldToUse, skipBoundingInfo = false, firstOnly = false) {
if (onlyBoundingInfo && !skipBoundingInfo && ray.intersectsSphere(this._boundingSphere, this.intersectionThreshold) === false) {
return;
}
const indices = this.getIndices();
const positions = this.getVerticesData(VertexBuffer.PositionKind);
const widths = this._widths;
const lineWidth = this.greasedLineMaterial?.width ?? 1;
const intersects = [];
if (indices && positions && widths) {
let i = 0, l = 0;
for (i = 0, l = indices.length - 1; i < l; i += 3) {
const a = indices[i];
const b = indices[i + 1];
GreasedLineMesh._V_START.fromArray(positions, a * 3);
GreasedLineMesh._V_END.fromArray(positions, b * 3);
if (this._offsets) {
GreasedLineMesh._V_OFFSET_START.fromArray(this._offsets, a * 3);
GreasedLineMesh._V_OFFSET_END.fromArray(this._offsets, b * 3);
GreasedLineMesh._V_START.addInPlace(GreasedLineMesh._V_OFFSET_START);
GreasedLineMesh._V_END.addInPlace(GreasedLineMesh._V_OFFSET_END);
}
const iFloored = Math.floor(i / 3);
const width = widths[iFloored] !== undefined ? widths[iFloored] : 1;
const precision = (this.intersectionThreshold * (lineWidth * width)) / 2;
const distance = ray.intersectionSegment(GreasedLineMesh._V_START, GreasedLineMesh._V_END, precision);
if (distance !== -1) {
intersects.push({
distance: distance,
point: ray.direction.normalize().multiplyByFloats(distance, distance, distance).add(ray.origin),
});
if (firstOnly) {
return intersects;
}
}
}
i = l;
}
return intersects;
}
get _boundingSphere() {
return this.getBoundingInfo().boundingSphere;
}
static _CompareV3(positionIdx1, positionIdx2, positions) {
const arrayIdx1 = positionIdx1 * 6;
const arrayIdx2 = positionIdx2 * 6;
return positions[arrayIdx1] === positions[arrayIdx2] && positions[arrayIdx1 + 1] === positions[arrayIdx2 + 1] && positions[arrayIdx1 + 2] === positions[arrayIdx2 + 2];
}
_createVertexBuffers() {
const vertexData = super._createVertexBuffers();
const engine = this._scene.getEngine();
const previousAndSideBuffer = new Buffer(engine, this._previousAndSide, false, 4);
this.setVerticesBuffer(previousAndSideBuffer.createVertexBuffer("grl_previousAndSide", 0, 4));
const nextAndCountersBuffer = new Buffer(engine, this._nextAndCounters, false, 4);
this.setVerticesBuffer(nextAndCountersBuffer.createVertexBuffer("grl_nextAndCounters", 0, 4));
const widthBuffer = new Buffer(engine, this._widths, this._updatable, 1);
this.setVerticesBuffer(widthBuffer.createVertexBuffer("grl_widths", 0, 1));
this._widthsBuffer = widthBuffer;
const colorPointersBuffer = new Buffer(engine, this._colorPointers, this._updatable, 1);
this.setVerticesBuffer(colorPointersBuffer.createVertexBuffer("grl_colorPointers", 0, 1));
this._colorPointersBuffer = colorPointersBuffer;
return vertexData;
}
}
GreasedLineMesh._V_START = new Vector3();
GreasedLineMesh._V_END = new Vector3();
GreasedLineMesh._V_OFFSET_START = new Vector3();
GreasedLineMesh._V_OFFSET_END = new Vector3();
//# sourceMappingURL=greasedLineMesh.js.map