UNPKG

@vci/quick-three

Version:

quick three

124 lines (117 loc) 4.14 kB
import { BufferGeometry, CatmullRomCurve3, Curve, Float32BufferAttribute, Vector2, Vector3 } from "three"; export default class LineGeometry extends BufferGeometry { constructor(path = new Curve(), tubularSegments = 64, radius = 1, closed = false) { super(); this.type = "LineGeometry"; this.parameters = { path, tubularSegments: tubularSegments, radius: radius, closed: closed }; const frames = path.computeFrenetFrames(tubularSegments, closed); // expose internals this.tangents = frames.tangents; this.normals = frames.normals; this.binormals = frames.binormals; // helper variables const vertex = new Vector3(); const normal = new Vector3(); const uv = new Vector2(); let P = new Vector3(); // buffer const vertices = []; const normals = []; const uvs = []; const indices = []; // create buffer data generateBufferData(); // build geometry this.setIndex(indices); this.setAttribute("position", new Float32BufferAttribute(vertices, 3)); this.setAttribute("normal", new Float32BufferAttribute(normals, 3)); this.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); // functions function generateBufferData() { for (let i = 0; i < tubularSegments; i++) { generateSegment(i); } // if the geometry is not closed, generate the last row of vertices and normals // at the regular position on the given path // // if the geometry is closed, duplicate the first row of vertices and normals (uvs will differ) generateSegment((closed === false) ? tubularSegments : 0); // uvs are generated in a separate function. // this makes it easy compute correct values for closed geometries generateUVs(); // finally create faces generateIndices(); } function generateSegment(i) { // we use getPointAt to sample evenly distributed points from the given path P = path.getPointAt(i / tubularSegments, P); // retrieve corresponding normal and binormal const N = frames.normals[i]; const B = frames.binormals[i]; // generate normals and vertices for the current segment for (let j = 0; j <= 2; j++) { const v = j / 2 * Math.PI * 2; const sin = Math.sin(v); const cos = -Math.cos(v); // normal normal.x = cos * B.x + sin * N.x; normal.y = cos * B.y + sin * N.y; normal.z = cos * B.z + sin * N.z; normal.normalize(); normals.push(normal.x, normal.y, normal.z); // vertex vertex.x = P.x + radius * normal.x; vertex.y = P.y + radius * normal.y; vertex.z = P.z + radius * normal.z; vertices.push(vertex.x, vertex.y, vertex.z); } } function generateIndices() { for (let j = 1; j <= tubularSegments; j++) { for (let i = 1; i <= 2; i++) { const a = (2 + 1) * (j - 1) + (i - 1); const b = (2 + 1) * j + (i - 1); const c = (2 + 1) * j + i; const d = (2 + 1) * (j - 1) + i; // faces indices.push(a, b, d); indices.push(b, c, d); } } } function generateUVs() { for (let i = 0; i <= tubularSegments; i++) { for (let j = 0; j <= 2; j++) { uv.x = i / tubularSegments; uv.y = j / 2; uvs.push(uv.x, uv.y); } } } } static fromJSON(data) { // This only works for built-in curves (e.g. CatmullRomCurve3). // User defined curves or instances of CurvePath will not be deserialized. return new LineGeometry( new CatmullRomCurve3().fromJSON(data.path), data.tubularSegments, data.radius, data.closed ); } copy(source) { super.copy(source); this.parameters = Object.assign({}, source.parameters); return this; } toJSON() { const data = super.toJSON(); data.path = this.parameters.path.toJSON(); return data; } }