UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

392 lines (253 loc) • 11.5 kB
import { StreamGeometryBuilder } from "../../../src/engine/graphics/ecs/path/tube/build/StreamGeometryBuilder.js"; import { make_ring_faces } from "../../../src/engine/graphics/ecs/path/tube/build/make_ring_faces.js"; import { BufferGeometry } from "three/src/core/BufferGeometry.js"; import { Float32BufferAttribute } from "three/src/core/BufferAttribute.js"; import { Vector2, Vector3 } from "three"; // Modified from https://github.com/mrdoob/three.js/blob/master/src/geometries/CylinderBufferGeometry.js class ArrowBufferGeometry extends BufferGeometry { constructor( {radiusTop = 1/7, radiusBottom = 1/20, height = 1, heightTop = 0.6, radialSegments = 8, heightSegments = 1, openEnded = false, heightIncludesHead = true} = {} ) { super(); this.type = 'ArrowBufferGeometry'; this.parameters = { radiusTop: radiusTop, radiusBottom: radiusBottom, height: height, heightTop: heightTop, radialSegments: radialSegments, heightSegments: heightSegments, openEnded: openEnded, heightIncludesHead: heightIncludesHead, }; const scope = this; const thetaStart = 0, thetaLength = 2 * Math.PI; radialSegments = Math.floor( radialSegments ); heightSegments = Math.floor( heightSegments ); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables let index = 0; const indexArray = []; const halfHeight = height / 2; const tubeHeight = heightIncludesHead ? height - heightTop : height; let groupStart = 0; // generate geometry generateTorso(); generateCap( true ); generateCap( false, true ); if ( openEnded === false ) { if ( radiusBottom > 0 ) generateCap( false ); } // 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 ) ); function generateTorso() { const normal = new Vector3(); const vertex = new Vector3(); let groupCount = 0; // this will be used to calculate the normal const slope = 0; // generate vertices, normals and uvs for ( let y = 0; y <= heightSegments; y ++ ) { const indexRow = []; const v = y / heightSegments; // calculate the radius of the current row const radius = radiusBottom; for ( let x = 0; x <= radialSegments; x ++ ) { const u = x / radialSegments; const theta = u * Math.PI * 2; const sinTheta = Math.sin( theta ); const cosTheta = Math.cos( theta ); // vertex vertex.x = radius * cosTheta; vertex.y = radius * sinTheta; vertex.z = v * tubeHeight; vertices.push( vertex.x, vertex.y, vertex.z ); // normal normal.set( cosTheta, sinTheta, 0 ).normalize(); normals.push( normal.x, normal.y, normal.z ); // uv uvs.push( u, 1 - v ); // save index of vertex in respective row indexRow.push( index ++ ); } // now save vertices of the row in our index array indexArray.push( indexRow ); } // generate indices for ( let x = 0; x < radialSegments; x ++ ) { for ( let y = 0; y < heightSegments; y ++ ) { // we use the index array to access the correct indices const a = indexArray[ y ][ x ]; const b = indexArray[ y + 1 ][ x ]; const c = indexArray[ y + 1 ][ x + 1 ]; const d = indexArray[ y ][ x + 1 ]; // faces indices.push( b, a, d ); indices.push( c, b, d ); // update group counter groupCount += 6; } } // add a group to the geometry. this will ensure multi material support scope.addGroup( groupStart, groupCount, 0 ); // calculate new start value for groups groupStart += groupCount; } function generateCap( top, headBase = false ) { // save the index of the first center vertex const centerIndexStart = index; const uv = new Vector2(); const vertex = new Vector3(); let groupCount = 0; const radius = ( top || headBase ) ? radiusTop : radiusBottom; const sign = ( top ) ? 1 : -1; // first we generate the center vertex data of the cap. // because the geometry needs one set of uvs per face, // we must generate a center vertex per face/segment for ( let x = 1; x <= radialSegments; x ++ ) { // vertex vertices.push( 0, 0, top ? tubeHeight + heightTop : (headBase ? tubeHeight : 0) ); // normal if (top) { const theta = (x - 1/2) / radialSegments * Math.PI * 2; const sinTheta = Math.sin(theta), cosTheta = Math.cos(theta); const normal = new Vector3( cosTheta, sinTheta, radiusTop / heightTop ); normal.normalize(); normals.push( normal.x, normal.y, normal.z); } else { normals.push( 0, 0, sign ); } // uv uvs.push( 0.5, 0.5 ); // increase index index ++; } // save the index of the last center vertex const centerIndexEnd = index; // now we generate the surrounding vertices, normals and uvs for ( let x = 0; x <= radialSegments; x ++ ) { const u = x / radialSegments; const theta = u * thetaLength + thetaStart; const cosTheta = Math.cos( theta ); const sinTheta = Math.sin( theta ); // vertex vertex.x = radius * cosTheta; vertex.y = radius * sinTheta; vertex.z = top || headBase ? tubeHeight : 0; vertices.push( vertex.x, vertex.y, vertex.z ); // normal if (top) { const normal = new Vector3(cosTheta, sinTheta, radiusTop / heightTop ); normal.normalize(); normals.push( normal.x, normal.y, normal.z); } else { normals.push( 0, 0, sign ); } // uv uv.x = ( cosTheta * 0.5 ) + 0.5; uv.y = ( sinTheta * 0.5 * sign ) + 0.5; uvs.push( uv.x, uv.y ); // increase index index ++; } // generate indices for ( let x = 0; x < radialSegments; x ++ ) { const c = centerIndexStart + x; const i = centerIndexEnd + x; if ( top === true ) { // face top indices.push( i, i + 1, c ); } else { // face bottom indices.push( i + 1, i, c ); } groupCount += 3; } // add a group to the geometry. this will ensure multi material support scope.addGroup( groupStart, groupCount, top === true ? 1 : 2 ); // calculate new start value for groups groupStart += groupCount; } } } /** * * @param {StreamGeometryBuilder} out * @param {number} count * @param {number} radius * @param {number} z */ function make_ring_vertices(out, count, radius, z) { const positions = out.positions; for (let i = 0; i < count; i++) { const vertex_index = out.cursor_vertices++; const fraction = i / count; const angle = Math.PI * 2 * fraction; const vertex_offset = vertex_index * 3; positions[vertex_offset] = Math.cos(angle) * radius; positions[vertex_offset + 1] = Math.sin(angle) * radius; positions[vertex_offset + 2] = z; } } /** * * @param {StreamGeometryBuilder} out * @param {number} count * @param {number} vertex_offset * @param {number} tip_vertex_index */ function make_cone_indices(out, count, vertex_offset, tip_vertex_index) { const indices = out.indices; for (let i = 0; i < count; i++) { const index_0 = i + vertex_offset; const index_1 = tip_vertex_index; const index_2 = ((i + 1) % count) + vertex_offset; const triangle_index = out.cursor_indices++; const triangle_offset = triangle_index * 3; indices[triangle_offset] = index_1; indices[triangle_offset + 1] = index_2; indices[triangle_offset + 2] = index_0; } } export function makeSolidArrowGeometry(){ return new ArrowBufferGeometry(); } function makeSolidArrowGeometry2( radial_resolution = 16, pointer_length = 0.5, pointer_width = 0.2, stem_width = 0.1 ) { const Z_OFFSET = -0.5; const vertex_count = radial_resolution * 3 + 1; const triangle_count = radial_resolution //arrow cap + radial_resolution * 2 // under-arrow ring + radial_resolution * 2 // stem + radial_resolution //cap for base of the stem ; const builder = new StreamGeometryBuilder(); builder.allocate(vertex_count, triangle_count); const positions = builder.positions; // write tip const tip_vertex_index = builder.cursor_vertices++; positions[tip_vertex_index * 3] = 0; positions[tip_vertex_index * 3 + 1] = 0; positions[tip_vertex_index * 3 + 2] = Z_OFFSET + 1; const ring_0_start = builder.cursor_vertices; // make cone ring - arrow cap make_ring_vertices(builder, radial_resolution, pointer_width, Z_OFFSET + 1 - pointer_length); make_cone_indices(builder, radial_resolution, ring_0_start, tip_vertex_index); const ring_1_start = builder.cursor_vertices; // make ring 1 make_ring_vertices(builder, radial_resolution, stem_width, Z_OFFSET + 1 - pointer_length); make_ring_faces(builder, ring_1_start, 1, radial_resolution); const ring_2_start = builder.cursor_vertices; make_ring_vertices(builder, radial_resolution, stem_width, Z_OFFSET + 0); make_ring_faces(builder, ring_2_start, 1, radial_resolution); return builder.build(); }