({ recursionLevel, splitFn, color, scale=1 })
| 571 | |
| 572 | // Not very optimized - lots of duplicate vertices are generated. |
| 573 | function makeRecursiveCubeModel({ recursionLevel, splitFn, color, scale=1 }) { |
| 574 | const getScaleAtLevel = level => 1 / (3 ** level); |
| 575 | |
| 576 | // We can model level 0 manually. It's just a single, centered, cube. |
| 577 | let cubeOrigins = [{ x: 0, y: 0, z: 0 }]; |
| 578 | |
| 579 | // Recursively replace cubes with smaller cubes. |
| 580 | for (let i=1; i<=recursionLevel; i++) { |
| 581 | const scale = getScaleAtLevel(i) * 2; |
| 582 | const cubeOrigins2 = []; |
| 583 | cubeOrigins.forEach(origin => { |
| 584 | cubeOrigins2.push(...splitFn(origin, scale)); |
| 585 | }); |
| 586 | cubeOrigins = cubeOrigins2; |
| 587 | } |
| 588 | |
| 589 | const finalModel = { vertices: [], polys: [] }; |
| 590 | |
| 591 | // Generate single cube model and scale it. |
| 592 | const cubeModel = makeCubeModel({ scale: 1 }); |
| 593 | cubeModel.vertices.forEach(scaleVector(getScaleAtLevel(recursionLevel))); |
| 594 | |
| 595 | // Compute the max distance x, y, or z origin values will be. |
| 596 | // Same result as `Math.max(...cubeOrigins.map(o => o.x))`, but much faster. |
| 597 | const maxComponent = getScaleAtLevel(recursionLevel) * (3 ** recursionLevel - 1); |
| 598 | |
| 599 | // Place cube geometry at each origin. |
| 600 | cubeOrigins.forEach((origin, cubeIndex) => { |
| 601 | // To compute occlusion (shading), find origin component with greatest |
| 602 | // magnitude and normalize it relative to `maxComponent`. |
| 603 | const occlusion = Math.max( |
| 604 | Math.abs(origin.x), |
| 605 | Math.abs(origin.y), |
| 606 | Math.abs(origin.z) |
| 607 | ) / maxComponent; |
| 608 | // At lower iterations, occlusion looks better lightened up a bit. |
| 609 | const occlusionLighter = recursionLevel > 2 |
| 610 | ? occlusion |
| 611 | : (occlusion + 0.8) / 1.8; |
| 612 | // Clone, translate vertices to origin, and apply scale |
| 613 | finalModel.vertices.push( |
| 614 | ...cubeModel.vertices.map(v => ({ |
| 615 | x: (v.x + origin.x) * scale, |
| 616 | y: (v.y + origin.y) * scale, |
| 617 | z: (v.z + origin.z) * scale |
| 618 | })) |
| 619 | ); |
| 620 | // Clone polys, shift referenced vertex indexes, and compute color. |
| 621 | finalModel.polys.push( |
| 622 | ...cubeModel.polys.map(poly => ({ |
| 623 | vIndexes: poly.vIndexes.map(add(cubeIndex * 8)) |
| 624 | })) |
| 625 | ); |
| 626 | }); |
| 627 | |
| 628 | return finalModel; |
| 629 | } |
| 630 |
no test coverage detected