import {BufferGeometry, Float32BufferAttribute, Vector3} from 'three'; /** * Map node geometry is a geometry used to represent the map nodes. * * Consists of a XZ plane with normals facing +Y. * * The geometry points start in XZ plane that can be manipulated for example for height adjustment. * * Geometry can also include skirts to mask off missalignments between tiles. */ export class MapNodeGeometry extends BufferGeometry { /** * Map node geometry constructor. * * @param width - Width of the node. * @param height - Height of the node. * @param widthSegments - Number of subdivisions along the width. * @param heightSegments - Number of subdivisions along the height. * @param skirt - Skirt around the plane to mask gaps between tiles. */ public constructor(width: number = 1.0, height: number = 1.0, widthSegments: number = 1.0, heightSegments: number = 1.0, skirt: boolean = false, skirtDepth: number = 10.0) { super(); // Buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // Build plane MapNodeGeometry.buildPlane(width, height, widthSegments, heightSegments, indices, vertices, normals, uvs); // Generate the skirt if (skirt) { MapNodeGeometry.buildSkirt(width, height, widthSegments, heightSegments, skirtDepth, indices, vertices, normals, uvs); } this.setIndex(indices); this.setAttribute('position', new Float32BufferAttribute(vertices, 3)); this.setAttribute('normal', new Float32BufferAttribute(normals, 3)); this.setAttribute('uv', new Float32BufferAttribute(uvs, 2)); } public static buildPlane(width: number = 1.0, height: number = 1.0, widthSegments: number = 1.0, heightSegments: number = 1.0, indices: number[], vertices: number[], normals: number[], uvs: number[]): void { // Half width X const widthHalf = width / 2; // Half width Z const heightHalf = height / 2; // Size of the grid in X const gridX = widthSegments + 1; // Size of the grid in Z const gridZ = heightSegments + 1; // Width of each segment X const segmentWidth = width / widthSegments; // Height of each segment Z const segmentHeight = height / heightSegments; // Generate vertices, normals and uvs for (let iz = 0; iz < gridZ; iz++) { const z = iz * segmentHeight - heightHalf; for (let ix = 0; ix < gridX; ix++) { const x = ix * segmentWidth - widthHalf; vertices.push(x, 0, z); normals.push(0, 1, 0); uvs.push(ix / widthSegments, 1 - iz / heightSegments); } } // Indices for (let iz = 0; iz < heightSegments; iz++) { for (let ix = 0; ix < widthSegments; ix++) { const a = ix + gridX * iz; const b = ix + gridX * (iz + 1); const c = ix + 1 + gridX * (iz + 1); const d = ix + 1 + gridX * iz; // Faces indices.push(a, b, d, b, c, d); } } } public static buildSkirt(width: number = 1.0, height: number = 1.0, widthSegments: number = 1.0, heightSegments: number = 1.0, skirtDepth: number, indices: number[], vertices: number[], normals: number[], uvs: number[]): void { // Half width X const widthHalf = width / 2; // Half width Z const heightHalf = height / 2; // Size of the grid in X const gridX = widthSegments + 1; // Size of the grid in Z const gridZ = heightSegments + 1; // Width of each segment X const segmentWidth = width / widthSegments; // Height of each segment Z const segmentHeight = height / heightSegments; let start = vertices.length / 3; // Down X for (let ix = 0; ix < gridX; ix++) { const x = ix * segmentWidth - widthHalf; const z = -heightHalf; vertices.push(x, -skirtDepth, z); normals.push(0, 1, 0); uvs.push(ix / widthSegments, 1); } // Indices for (let ix = 0; ix < widthSegments; ix++) { const a = ix; const d = ix + 1; const b = ix + start; const c = ix + start + 1; indices.push(d, b, a, d, c, b); } start = vertices.length / 3; // Up X for (let ix = 0; ix < gridX; ix++) { const x = ix * segmentWidth - widthHalf; const z = heightSegments * segmentHeight - heightHalf; vertices.push(x, -skirtDepth, z); normals.push(0, 1, 0); uvs.push(ix / widthSegments, 0); } // Index of the beginning of the last X row let offset = gridX * gridZ - widthSegments - 1; for (let ix = 0; ix < widthSegments; ix++) { const a = offset + ix; const d = offset + ix + 1; const b = ix + start; const c = ix + start + 1; indices.push(a, b, d, b, c, d); } start = vertices.length / 3; // Down Z for (let iz = 0; iz < gridZ; iz++) { const z = iz * segmentHeight - heightHalf; const x = - widthHalf; vertices.push(x, -skirtDepth, z); normals.push(0, 1, 0); uvs.push(0, 1 - iz / heightSegments); } for (let iz = 0; iz < heightSegments; iz++) { const a = iz * gridZ; const d = (iz + 1) * gridZ; const b = iz + start; const c = iz + start + 1; indices.push(a, b, d, b, c, d); } start = vertices.length / 3; // Up Z for (let iz = 0; iz < gridZ; iz++) { const z = iz * segmentHeight - heightHalf; const x = widthSegments * segmentWidth - widthHalf; vertices.push(x, -skirtDepth, z); normals.push(0, 1, 0); uvs.push(1.0, 1 - iz / heightSegments); } for (let iz = 0; iz < heightSegments; iz++) { const a = iz * gridZ + heightSegments; const d = (iz + 1) * gridZ + heightSegments; const b = iz + start; const c = iz + start + 1; indices.push(d, b, a, d, c, b); } } }