UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

203 lines (154 loc) • 5.91 kB
import { BufferGeometry, Vector3 } from "three"; import { assert } from "../../../../../../core/assert.js"; import { v3_angle_cos_between } from "../../../../../../core/geom/vec3/v3_angle_cos_between.js"; import { v3_length } from "../../../../../../core/geom/vec3/v3_length.js"; import { CapType } from "../CapType.js"; import { append_compute_cap_geometry_size, make_cap } from "./make_cap.js"; import { make_ring_faces } from "./make_ring_faces.js"; import { make_ring_vertices } from "./make_ring_vertices.js"; import { StreamGeometryBuilder } from "./StreamGeometryBuilder.js"; const v4_array = new Float32Array(4); /** * @see https://github.com/mrdoob/three.js/blob/master/src/geometries/TubeGeometry.js * @see https://github.com/hofk/THREEg.js/blob/488f1128a25321a76888aa1fa19db64750318444/THREEg.js#L3483 * @param {Float32Array|number[]} in_positions * @param {Vector3[]} in_normals * @param {Vector3[]} in_binormals * @param {Vector3[]} in_tangents * @param {number[]} shape * @param {number[]|Float32Array} shape_normal * @param {number} shape_length * @param {number[]|Float32Array} shape_transform * @param {boolean} [closed] * @param {CapType} [cap_type] * @returns {BufferGeometry} */ export function makeTubeGeometry( in_positions, in_normals, in_binormals, in_tangents, shape, shape_normal, shape_length, shape_transform, closed = false, cap_type = CapType.Round ) { assert.enum(cap_type, CapType, 'cap_type'); assert.isBoolean(closed, 'closed'); assert.isNumber(shape_length, 'shape_length'); assert.isArrayLike(shape, 'shape'); const out = new StreamGeometryBuilder(); // helper variables const point_count = in_positions.length / 3; const tubular_segments = point_count - 1; const geometry_size = { vertex_count: (tubular_segments + 1) * (shape_length + 1), polygon_count: tubular_segments * shape_length * 2 }; if (!closed) { append_compute_cap_geometry_size(2, geometry_size, shape_length, cap_type); } out.allocate( geometry_size.vertex_count, geometry_size.polygon_count ); // create buffer data if (!closed) { // start cap make_cap( out, 0, in_positions, in_normals, in_binormals, in_tangents, shape, shape_normal, shape_length, shape_transform, 1, cap_type ); } const index_offset = out.cursor_vertices; for (let i = 0; i < tubular_segments; 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) ? tubular_segments : 0); // finally create faces make_ring_faces(out, index_offset, tubular_segments, shape_length); if (!closed) { // end cap make_cap( out, point_count - 1, in_positions, in_normals, in_binormals, in_tangents, shape, shape_normal, shape_length, shape_transform, -1, cap_type ); } /** * * @param {number} i */ function generateSegment(i) { // we use getPointAt to sample evenly distributed points from the given path const i3 = i * 3; const Px = in_positions[i3]; const Py = in_positions[i3 + 1]; const Pz = in_positions[i3 + 2]; // retrieve corresponding normal and binormal const N = in_normals[i]; const B = in_binormals[i]; // generate normals and vertices for the current segment compute_bend_normal(v4_array, i, tubular_segments, in_positions); make_ring_vertices( out, Px, Py, Pz, N, B, in_tangents[i], i / tubular_segments, v4_array, shape, shape_normal, shape_length, shape_transform ); } return out.build(); } /** * * @param {number[]|Float32Array} out * @param {number} index * @param {number} index_count * @param {number[]|Float32Array} positions */ function compute_bend_normal( out, index, index_count, positions ) { if (index <= 0 || index >= index_count - 1) { // end points, no bending out[0] = 0; out[1] = 1; out[2] = 0; out[3] = 0; return; } const index_next = index + 1; const index_prev = index - 1; const address_current = index * 3; const address_next = index_next * 3; const address_prev = index_prev * 3; const i0_x = positions[address_prev]; const i0_y = positions[address_prev + 1]; const i0_z = positions[address_prev + 2]; const i1_x = positions[address_current]; const i1_y = positions[address_current + 1]; const i1_z = positions[address_current + 2]; const i2_x = positions[address_next]; const i2_y = positions[address_next + 1]; const i2_z = positions[address_next + 2]; const d0_x = i0_x - i1_x; const d0_y = i0_y - i1_y; const d0_z = i0_z - i1_z; const d1_x = i1_x - i2_x; const d1_y = i1_y - i2_y; const d1_z = i1_z - i2_z; // compute rotation axis const cross_x = d0_y * d1_z - d0_z * d1_y; const cross_y = d0_z * d1_x - d0_x * d1_z; const cross_z = d0_x * d1_y - d0_y * d1_x; const angle = v3_angle_cos_between(d0_x, d0_y, d0_z, d1_x, d1_y, d1_z); const length_inv = 1 / v3_length(cross_x, cross_y, cross_z); out[0] = cross_x * length_inv; out[1] = cross_y * length_inv; out[2] = cross_z * length_inv; // bend amount out[3] = (1 - Math.abs(angle)) * 0.5; }