Spaces:
Sleeping
Sleeping
| import { BufferGeometry } from '../core/BufferGeometry.js'; | |
| import { Float32BufferAttribute } from '../core/BufferAttribute.js'; | |
| import { Vector3 } from '../math/Vector3.js'; | |
| import { Vector2 } from '../math/Vector2.js'; | |
| class PolyhedronGeometry extends BufferGeometry { | |
| constructor(vertices = [], indices = [], radius = 1, detail = 0) { | |
| super(); | |
| this.type = 'PolyhedronGeometry'; | |
| this.parameters = { | |
| vertices: vertices, | |
| indices: indices, | |
| radius: radius, | |
| detail: detail, | |
| }; | |
| // default buffer data | |
| const vertexBuffer = []; | |
| const uvBuffer = []; | |
| // the subdivision creates the vertex buffer data | |
| subdivide(detail); | |
| // all vertices should lie on a conceptual sphere with a given radius | |
| applyRadius(radius); | |
| // finally, create the uv data | |
| generateUVs(); | |
| // build non-indexed geometry | |
| this.setAttribute('position', new Float32BufferAttribute(vertexBuffer, 3)); | |
| this.setAttribute('normal', new Float32BufferAttribute(vertexBuffer.slice(), 3)); | |
| this.setAttribute('uv', new Float32BufferAttribute(uvBuffer, 2)); | |
| if (detail === 0) { | |
| this.computeVertexNormals(); // flat normals | |
| } else { | |
| this.normalizeNormals(); // smooth normals | |
| } | |
| // helper functions | |
| function subdivide(detail) { | |
| const a = new Vector3(); | |
| const b = new Vector3(); | |
| const c = new Vector3(); | |
| // iterate over all faces and apply a subdivison with the given detail value | |
| for (let i = 0; i < indices.length; i += 3) { | |
| // get the vertices of the face | |
| getVertexByIndex(indices[i + 0], a); | |
| getVertexByIndex(indices[i + 1], b); | |
| getVertexByIndex(indices[i + 2], c); | |
| // perform subdivision | |
| subdivideFace(a, b, c, detail); | |
| } | |
| } | |
| function subdivideFace(a, b, c, detail) { | |
| const cols = detail + 1; | |
| // we use this multidimensional array as a data structure for creating the subdivision | |
| const v = []; | |
| // construct all of the vertices for this subdivision | |
| for (let i = 0; i <= cols; i++) { | |
| v[i] = []; | |
| const aj = a.clone().lerp(c, i / cols); | |
| const bj = b.clone().lerp(c, i / cols); | |
| const rows = cols - i; | |
| for (let j = 0; j <= rows; j++) { | |
| if (j === 0 && i === cols) { | |
| v[i][j] = aj; | |
| } else { | |
| v[i][j] = aj.clone().lerp(bj, j / rows); | |
| } | |
| } | |
| } | |
| // construct all of the faces | |
| for (let i = 0; i < cols; i++) { | |
| for (let j = 0; j < 2 * (cols - i) - 1; j++) { | |
| const k = Math.floor(j / 2); | |
| if (j % 2 === 0) { | |
| pushVertex(v[i][k + 1]); | |
| pushVertex(v[i + 1][k]); | |
| pushVertex(v[i][k]); | |
| } else { | |
| pushVertex(v[i][k + 1]); | |
| pushVertex(v[i + 1][k + 1]); | |
| pushVertex(v[i + 1][k]); | |
| } | |
| } | |
| } | |
| } | |
| function applyRadius(radius) { | |
| const vertex = new Vector3(); | |
| // iterate over the entire buffer and apply the radius to each vertex | |
| for (let i = 0; i < vertexBuffer.length; i += 3) { | |
| vertex.x = vertexBuffer[i + 0]; | |
| vertex.y = vertexBuffer[i + 1]; | |
| vertex.z = vertexBuffer[i + 2]; | |
| vertex.normalize().multiplyScalar(radius); | |
| vertexBuffer[i + 0] = vertex.x; | |
| vertexBuffer[i + 1] = vertex.y; | |
| vertexBuffer[i + 2] = vertex.z; | |
| } | |
| } | |
| function generateUVs() { | |
| const vertex = new Vector3(); | |
| for (let i = 0; i < vertexBuffer.length; i += 3) { | |
| vertex.x = vertexBuffer[i + 0]; | |
| vertex.y = vertexBuffer[i + 1]; | |
| vertex.z = vertexBuffer[i + 2]; | |
| const u = azimuth(vertex) / 2 / Math.PI + 0.5; | |
| const v = inclination(vertex) / Math.PI + 0.5; | |
| uvBuffer.push(u, 1 - v); | |
| } | |
| correctUVs(); | |
| correctSeam(); | |
| } | |
| function correctSeam() { | |
| // handle case when face straddles the seam, see #3269 | |
| for (let i = 0; i < uvBuffer.length; i += 6) { | |
| // uv data of a single face | |
| const x0 = uvBuffer[i + 0]; | |
| const x1 = uvBuffer[i + 2]; | |
| const x2 = uvBuffer[i + 4]; | |
| const max = Math.max(x0, x1, x2); | |
| const min = Math.min(x0, x1, x2); | |
| // 0.9 is somewhat arbitrary | |
| if (max > 0.9 && min < 0.1) { | |
| if (x0 < 0.2) uvBuffer[i + 0] += 1; | |
| if (x1 < 0.2) uvBuffer[i + 2] += 1; | |
| if (x2 < 0.2) uvBuffer[i + 4] += 1; | |
| } | |
| } | |
| } | |
| function pushVertex(vertex) { | |
| vertexBuffer.push(vertex.x, vertex.y, vertex.z); | |
| } | |
| function getVertexByIndex(index, vertex) { | |
| const stride = index * 3; | |
| vertex.x = vertices[stride + 0]; | |
| vertex.y = vertices[stride + 1]; | |
| vertex.z = vertices[stride + 2]; | |
| } | |
| function correctUVs() { | |
| const a = new Vector3(); | |
| const b = new Vector3(); | |
| const c = new Vector3(); | |
| const centroid = new Vector3(); | |
| const uvA = new Vector2(); | |
| const uvB = new Vector2(); | |
| const uvC = new Vector2(); | |
| for (let i = 0, j = 0; i < vertexBuffer.length; i += 9, j += 6) { | |
| a.set(vertexBuffer[i + 0], vertexBuffer[i + 1], vertexBuffer[i + 2]); | |
| b.set(vertexBuffer[i + 3], vertexBuffer[i + 4], vertexBuffer[i + 5]); | |
| c.set(vertexBuffer[i + 6], vertexBuffer[i + 7], vertexBuffer[i + 8]); | |
| uvA.set(uvBuffer[j + 0], uvBuffer[j + 1]); | |
| uvB.set(uvBuffer[j + 2], uvBuffer[j + 3]); | |
| uvC.set(uvBuffer[j + 4], uvBuffer[j + 5]); | |
| centroid.copy(a).add(b).add(c).divideScalar(3); | |
| const azi = azimuth(centroid); | |
| correctUV(uvA, j + 0, a, azi); | |
| correctUV(uvB, j + 2, b, azi); | |
| correctUV(uvC, j + 4, c, azi); | |
| } | |
| } | |
| function correctUV(uv, stride, vector, azimuth) { | |
| if (azimuth < 0 && uv.x === 1) { | |
| uvBuffer[stride] = uv.x - 1; | |
| } | |
| if (vector.x === 0 && vector.z === 0) { | |
| uvBuffer[stride] = azimuth / 2 / Math.PI + 0.5; | |
| } | |
| } | |
| // Angle around the Y axis, counter-clockwise when looking from above. | |
| function azimuth(vector) { | |
| return Math.atan2(vector.z, -vector.x); | |
| } | |
| // Angle above the XZ plane. | |
| function inclination(vector) { | |
| return Math.atan2(-vector.y, Math.sqrt(vector.x * vector.x + vector.z * vector.z)); | |
| } | |
| } | |
| static fromJSON(data) { | |
| return new PolyhedronGeometry(data.vertices, data.indices, data.radius, data.details); | |
| } | |
| } | |
| export { PolyhedronGeometry, PolyhedronGeometry as PolyhedronBufferGeometry }; | |