@driftengine/core 4.5.0 → 4.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.json +51 -0
- package/README.md +1 -1
- package/dist/geometry/boxCsg.d.ts +28 -0
- package/dist/geometry/boxCsg.js +139 -0
- package/dist/geometry/csg.d.ts +36 -0
- package/dist/geometry/csg.js +329 -0
- package/dist/geometry/meshBuilder.js +13 -0
- package/dist/geometry/meshConcat.js +1 -0
- package/dist/geometry/pixelFont.js +2 -0
- package/dist/geometry/solid.d.ts +61 -0
- package/dist/geometry/solid.js +152 -0
- package/dist/geometry/solidFlat.d.ts +35 -0
- package/dist/geometry/solidFlat.js +428 -0
- package/dist/geometry/solidHarness.d.ts +19 -0
- package/dist/geometry/solidHarness.js +66 -0
- package/dist/geometry/solidNormals.d.ts +17 -0
- package/dist/geometry/solidNormals.js +86 -0
- package/dist/geometry/solidRound.d.ts +41 -0
- package/dist/geometry/solidRound.js +323 -0
- package/dist/geometry/solidWriter.d.ts +16 -0
- package/dist/geometry/solidWriter.js +42 -0
- package/dist/index.d.ts +23 -2
- package/dist/index.js +13 -1
- package/dist/render/backend/api.d.ts +17 -3
- package/dist/render/backend/drawFog.d.ts +26 -0
- package/dist/render/backend/drawFog.js +30 -0
- package/dist/render/backend/materialChanges.d.ts +2 -1
- package/dist/render/backend/materialChanges.js +6 -1
- package/dist/render/backend/webgl2/instanced.d.ts +24 -5
- package/dist/render/backend/webgl2/instanced.js +72 -15
- package/dist/render/backend/webgl2/renderer.d.ts +122 -8
- package/dist/render/backend/webgl2/renderer.js +238 -121
- package/dist/render/backend/webgpu/buffers.d.ts +10 -0
- package/dist/render/backend/webgpu/buffers.js +34 -3
- package/dist/render/backend/webgpu/depthPass.js +5 -1
- package/dist/render/backend/webgpu/drawCommand.d.ts +5 -0
- package/dist/render/backend/webgpu/drawCommand.js +1 -0
- package/dist/render/backend/webgpu/flatPass.d.ts +4 -1
- package/dist/render/backend/webgpu/flatPass.js +38 -9
- package/dist/render/backend/webgpu/glassTintPass.js +2 -1
- package/dist/render/backend/webgpu/instanceCullPass.d.ts +34 -0
- package/dist/render/backend/webgpu/instanceCullPass.js +144 -0
- package/dist/render/backend/webgpu/instanced.d.ts +11 -1
- package/dist/render/backend/webgpu/instanced.js +21 -3
- package/dist/render/backend/webgpu/latePass.d.ts +6 -2
- package/dist/render/backend/webgpu/latePass.js +11 -4
- package/dist/render/backend/webgpu/postPass.d.ts +1 -1
- package/dist/render/backend/webgpu/renderer.d.ts +100 -10
- package/dist/render/backend/webgpu/renderer.js +462 -201
- package/dist/render/backend/webgpu/sdfTextPass.js +6 -1
- package/dist/render/backend/webgpu/shaders/instanceCull.wgsl.d.ts +18 -0
- package/dist/render/backend/webgpu/shaders/instanceCull.wgsl.js +59 -0
- package/dist/render/backend/webgpu/surfaceTexturePass.d.ts +17 -3
- package/dist/render/backend/webgpu/surfaceTexturePass.js +77 -38
- package/dist/render/driftLight/bake.d.ts +7 -0
- package/dist/render/driftLight/bake.js +61 -56
- package/dist/render/driftLight/denseField.d.ts +46 -0
- package/dist/render/driftLight/denseField.js +109 -0
- package/dist/render/driftLight/lightField.d.ts +10 -5
- package/dist/render/driftLight/lightField.js +25 -19
- package/dist/render/driftLight/presence.d.ts +50 -0
- package/dist/render/driftLight/presence.js +43 -0
- package/dist/render/driftLight/uniforms.d.ts +3 -3
- package/dist/render/driftLight/uniforms.js +5 -6
- package/dist/render/driftLight/worldLightField.d.ts +41 -0
- package/dist/render/driftLight/worldLightField.js +66 -0
- package/dist/render/fixtureAtlas.d.ts +20 -0
- package/dist/render/fixtureAtlas.js +49 -0
- package/dist/render/instanceCull.d.ts +63 -0
- package/dist/render/instanceCull.js +160 -0
- package/dist/render/instances.d.ts +15 -0
- package/dist/render/lightBudget.d.ts +22 -33
- package/dist/render/lightBudget.js +24 -35
- package/dist/render/lightGrid.d.ts +42 -0
- package/dist/render/lightGrid.js +87 -0
- package/dist/render/mesh.d.ts +18 -24
- package/dist/render/mesh.js +51 -68
- package/dist/render/occlusion.d.ts +10 -0
- package/dist/render/occlusion.js +36 -7
- package/dist/render/pointLightSelection.d.ts +7 -0
- package/dist/render/pointLightSelection.js +29 -4
- package/dist/render/rainField.d.ts +13 -0
- package/dist/render/rainField.js +27 -5
- package/dist/render/recon/lateDepth.d.ts +11 -0
- package/dist/render/recon/lateDepth.js +70 -0
- package/dist/render/recon/resolve.d.ts +34 -4
- package/dist/render/recon/resolve.js +161 -20
- package/dist/render/rendererHarness.js +4 -0
- package/dist/render/sceneTarget.d.ts +2 -0
- package/dist/render/sceneTarget.js +2 -0
- package/dist/render/shaders/depth.js +12 -9
- package/dist/render/shaders/flat/driftLight.d.ts +1 -1
- package/dist/render/shaders/flat/driftLight.js +32 -7
- package/dist/render/shaders/flat/index.js +5 -3
- package/dist/render/shaders/flat/main.d.ts +1 -1
- package/dist/render/shaders/flat/main.js +75 -23
- package/dist/render/shaders/flat/preamble.js +75 -20
- package/dist/render/shaders/flat/surfaceEffects.d.ts +29 -0
- package/dist/render/shaders/flat/surfaceEffects.js +152 -0
- package/dist/render/shaders/generated/depth.wgsl.d.ts +8 -8
- package/dist/render/shaders/generated/depth.wgsl.js +8 -8
- package/dist/render/shaders/generated/flat.wgsl.d.ts +1320 -1320
- package/dist/render/shaders/generated/flat.wgsl.js +1359 -1345
- package/dist/render/shaders/generated/lightVolume.wgsl.js +18 -4
- package/dist/render/shaders/generated/rush.wgsl.d.ts +21 -16
- package/dist/render/shaders/generated/rush.wgsl.js +21 -16
- package/dist/render/shaders/generated/sdfText.wgsl.d.ts +2 -2
- package/dist/render/shaders/generated/sdfText.wgsl.js +2 -2
- package/dist/render/shaders/generated/text.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/text.wgsl.js +1 -1
- package/dist/render/shaders/gi/driftLight.wgsl.d.ts +1 -1
- package/dist/render/shaders/gi/driftLight.wgsl.js +27 -4
- package/dist/render/shaders/recon/lateDepth.wgsl.js +38 -13
- package/dist/render/shaders/recon/resolve.wgsl.d.ts +8 -7
- package/dist/render/shaders/recon/resolve.wgsl.js +161 -27
- package/dist/render/shaders/rush.js +11 -1
- package/dist/render/shaders/sdfText.d.ts +1 -1
- package/dist/render/shaders/sdfText.js +3 -2
- package/dist/render/shaders/text.d.ts +1 -1
- package/dist/render/shaders/text.js +18 -9
- package/dist/render/surfaceEffects.d.ts +83 -0
- package/dist/render/surfaceEffects.js +75 -0
- package/dist/render/surfaceScene.d.ts +8 -0
- package/dist/render/surfaceScene.js +8 -0
- package/dist/render/surfaceTexture.d.ts +18 -2
- package/dist/render/surfaceTexture.js +68 -15
- package/dist/render/temporalAa.d.ts +25 -6
- package/dist/render/temporalAa.js +45 -15
- package/dist/render/textureSource.d.ts +30 -0
- package/dist/render/textureSource.js +53 -0
- package/dist/render/vertexDefaults.js +2 -0
- package/dist/world/hlod.d.ts +104 -0
- package/dist/world/hlod.js +214 -0
- package/package.json +6 -6
- package/src/geometry/boxCsg.ts +186 -0
- package/src/geometry/csg.ts +384 -0
- package/src/geometry/meshBuilder.ts +15 -0
- package/src/geometry/meshConcat.ts +1 -0
- package/src/geometry/pixelFont.ts +2 -0
- package/src/geometry/solid.ts +189 -0
- package/src/geometry/solidFlat.ts +522 -0
- package/src/geometry/solidHarness.ts +83 -0
- package/src/geometry/solidNormals.ts +103 -0
- package/src/geometry/solidRound.ts +443 -0
- package/src/geometry/solidWriter.ts +58 -0
- package/src/index.ts +51 -1
- package/src/render/backend/api.ts +21 -2
- package/src/render/backend/drawFog.ts +31 -0
- package/src/render/backend/materialChanges.ts +9 -2
- package/src/render/backend/webgl2/instanced.ts +80 -14
- package/src/render/backend/webgl2/renderer.ts +300 -143
- package/src/render/backend/webgpu/buffers.ts +42 -3
- package/src/render/backend/webgpu/depthPass.ts +5 -1
- package/src/render/backend/webgpu/drawCommand.ts +6 -0
- package/src/render/backend/webgpu/flatPass.ts +41 -11
- package/src/render/backend/webgpu/glassTintPass.ts +2 -1
- package/src/render/backend/webgpu/instanceCullPass.ts +165 -0
- package/src/render/backend/webgpu/instanced.ts +21 -3
- package/src/render/backend/webgpu/latePass.ts +12 -3
- package/src/render/backend/webgpu/renderer.ts +540 -226
- package/src/render/backend/webgpu/sdfTextPass.ts +6 -1
- package/src/render/backend/webgpu/shaders/instanceCull.wgsl.ts +61 -0
- package/src/render/backend/webgpu/surfaceTexturePass.ts +90 -50
- package/src/render/driftLight/bake.ts +85 -62
- package/src/render/driftLight/denseField.ts +149 -0
- package/src/render/driftLight/lightField.ts +33 -18
- package/src/render/driftLight/presence.ts +69 -0
- package/src/render/driftLight/uniforms.ts +8 -9
- package/src/render/driftLight/worldLightField.ts +83 -0
- package/src/render/fixtureAtlas.ts +63 -0
- package/src/render/instanceCull.ts +205 -0
- package/src/render/instances.ts +16 -0
- package/src/render/lightBudget.ts +25 -36
- package/src/render/lightGrid.ts +130 -0
- package/src/render/mesh.ts +59 -72
- package/src/render/occlusion.ts +51 -7
- package/src/render/pointLightSelection.ts +72 -4
- package/src/render/rainField.ts +39 -5
- package/src/render/recon/lateDepth.ts +83 -0
- package/src/render/recon/resolve.ts +206 -20
- package/src/render/rendererHarness.ts +4 -0
- package/src/render/sceneTarget.ts +4 -0
- package/src/render/shaders/depth.ts +12 -9
- package/src/render/shaders/flat/driftLight.ts +32 -7
- package/src/render/shaders/flat/index.ts +5 -3
- package/src/render/shaders/flat/main.ts +75 -23
- package/src/render/shaders/flat/preamble.ts +75 -20
- package/src/render/shaders/flat/surfaceEffects.ts +152 -0
- package/src/render/shaders/generated/depth.wgsl.ts +8 -8
- package/src/render/shaders/generated/flat.wgsl.ts +1361 -1345
- package/src/render/shaders/generated/lightVolume.wgsl.ts +20 -4
- package/src/render/shaders/generated/rush.wgsl.ts +21 -16
- package/src/render/shaders/generated/sdfText.wgsl.ts +2 -2
- package/src/render/shaders/generated/text.wgsl.ts +1 -1
- package/src/render/shaders/gi/driftLight.wgsl.ts +27 -4
- package/src/render/shaders/recon/lateDepth.wgsl.ts +38 -13
- package/src/render/shaders/recon/resolve.wgsl.ts +161 -27
- package/src/render/shaders/rush.ts +11 -1
- package/src/render/shaders/sdfText.ts +3 -2
- package/src/render/shaders/text.ts +18 -9
- package/src/render/surfaceEffects.ts +132 -0
- package/src/render/surfaceScene.ts +16 -0
- package/src/render/surfaceTexture.ts +110 -19
- package/src/render/temporalAa.ts +78 -14
- package/src/render/textureSource.ts +72 -0
- package/src/render/vertexDefaults.ts +2 -0
- package/src/world/hlod.ts +290 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Checks a closed surface can be held to, for the solid and CSG tests.
|
|
3
|
+
*
|
|
4
|
+
* **Not a `.test.ts` file**, for the reason `rendererHarness.ts` gives: Vitest registers a test
|
|
5
|
+
* when the file declaring it is imported, so helpers living beside tests would re-run them in every
|
|
6
|
+
* file that imported them.
|
|
7
|
+
*
|
|
8
|
+
* `edgesPairOnce` welds by position and asks that every edge be shared by exactly two triangles
|
|
9
|
+
* running opposite ways, which is what closed and consistently wound means. `normalsFaceOutward`
|
|
10
|
+
* asks that every vertex normal lie on the outward side of its triangle. The matrices are
|
|
11
|
+
* column-major, as `transformSolid` takes them.
|
|
12
|
+
*/
|
|
13
|
+
import type { Solid } from './solid.ts';
|
|
14
|
+
export declare function edgesPairOnce(solid: Solid): boolean;
|
|
15
|
+
export declare function normalsFaceOutward(solid: Solid): boolean;
|
|
16
|
+
export declare function translation(x: number, y: number, z: number): number[];
|
|
17
|
+
export declare function scaling(x: number, y: number, z: number): number[];
|
|
18
|
+
export declare function rotationX(a: number): number[];
|
|
19
|
+
export declare function rotationY(a: number): number[];
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
export function edgesPairOnce(solid) {
|
|
2
|
+
const p = solid.positions;
|
|
3
|
+
const key = (i) => `${Math.round((p[i * 3] ?? 0) * 1e5)},${Math.round((p[i * 3 + 1] ?? 0) * 1e5)},${Math.round((p[i * 3 + 2] ?? 0) * 1e5)}`;
|
|
4
|
+
const directed = new Map();
|
|
5
|
+
const idx = solid.indices;
|
|
6
|
+
for (let t = 0; t < idx.length; t += 3) {
|
|
7
|
+
for (let e = 0; e < 3; e++) {
|
|
8
|
+
const a = key(idx[t + e] ?? 0);
|
|
9
|
+
const b = key(idx[t + ((e + 1) % 3)] ?? 0);
|
|
10
|
+
if (a === b)
|
|
11
|
+
continue;
|
|
12
|
+
const k = `${a}>${b}`;
|
|
13
|
+
directed.set(k, (directed.get(k) ?? 0) + 1);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
for (const [k, n] of directed) {
|
|
17
|
+
const [a, b] = k.split('>');
|
|
18
|
+
if (n !== 1 || directed.get(`${b}>${a}`) !== 1)
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
return directed.size > 0;
|
|
22
|
+
}
|
|
23
|
+
export function normalsFaceOutward(solid) {
|
|
24
|
+
const p = solid.positions;
|
|
25
|
+
const n = solid.normals;
|
|
26
|
+
const idx = solid.indices;
|
|
27
|
+
const at = (i, c) => p[i * 3 + c] ?? 0;
|
|
28
|
+
for (let t = 0; t < idx.length; t += 3) {
|
|
29
|
+
const a = idx[t] ?? 0;
|
|
30
|
+
const b = idx[t + 1] ?? 0;
|
|
31
|
+
const c = idx[t + 2] ?? 0;
|
|
32
|
+
const ux = at(b, 0) - at(a, 0);
|
|
33
|
+
const uy = at(b, 1) - at(a, 1);
|
|
34
|
+
const uz = at(b, 2) - at(a, 2);
|
|
35
|
+
const vx = at(c, 0) - at(a, 0);
|
|
36
|
+
const vy = at(c, 1) - at(a, 1);
|
|
37
|
+
const vz = at(c, 2) - at(a, 2);
|
|
38
|
+
const gx = uy * vz - uz * vy;
|
|
39
|
+
const gy = uz * vx - ux * vz;
|
|
40
|
+
const gz = ux * vy - uy * vx;
|
|
41
|
+
if (Math.hypot(gx, gy, gz) < 1e-12)
|
|
42
|
+
continue;
|
|
43
|
+
for (const v of [a, b, c]) {
|
|
44
|
+
if (gx * (n[v * 3] ?? 0) + gy * (n[v * 3 + 1] ?? 0) + gz * (n[v * 3 + 2] ?? 0) <= 0) {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
export function translation(x, y, z) {
|
|
52
|
+
return [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, x, y, z, 1];
|
|
53
|
+
}
|
|
54
|
+
export function scaling(x, y, z) {
|
|
55
|
+
return [x, 0, 0, 0, 0, y, 0, 0, 0, 0, z, 0, 0, 0, 0, 1];
|
|
56
|
+
}
|
|
57
|
+
export function rotationX(a) {
|
|
58
|
+
const c = Math.cos(a);
|
|
59
|
+
const s = Math.sin(a);
|
|
60
|
+
return [1, 0, 0, 0, 0, c, s, 0, 0, -s, c, 0, 0, 0, 0, 1];
|
|
61
|
+
}
|
|
62
|
+
export function rotationY(a) {
|
|
63
|
+
const c = Math.cos(a);
|
|
64
|
+
const s = Math.sin(a);
|
|
65
|
+
return [c, 0, -s, 0, 0, 1, 0, 0, s, 0, c, 0, 0, 0, 0, 1];
|
|
66
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Crease-angle normal smoothing: every vertex takes the average of the faces meeting at its
|
|
3
|
+
* position whose normals lie within the crease angle of its own, so a cylinder's side goes round
|
|
4
|
+
* while its rim stays sharp.
|
|
5
|
+
*
|
|
6
|
+
* **Weighted by the corner angle each face makes at the vertex, not by area.** A quad split into
|
|
7
|
+
* two triangles meets a vertex with one triangle at one corner and two at the next, so area weights
|
|
8
|
+
* favour whichever side the split fell on and lean every normal of a smooth side a degree or two off
|
|
9
|
+
* true. The corner angle a quad makes is 90° however it was split, which is what makes the average
|
|
10
|
+
* belong to the shape rather than to its triangulation.
|
|
11
|
+
*
|
|
12
|
+
* Positions are welded at 1e-5 for the lookup only; the solid keeps its vertices, indices and
|
|
13
|
+
* texture coordinates, and each vertex's incoming normal chooses which faces it joins — so a
|
|
14
|
+
* faceted solid's own face normals decide the creases.
|
|
15
|
+
*/
|
|
16
|
+
import type { Solid } from './solid.ts';
|
|
17
|
+
export declare function smoothSolidNormals(solid: Solid, creaseDeg: number): Solid;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
export function smoothSolidNormals(solid, creaseDeg) {
|
|
2
|
+
const p = solid.positions;
|
|
3
|
+
const idx = solid.indices;
|
|
4
|
+
const count = p.length / 3;
|
|
5
|
+
const cosCrease = Math.cos((creaseDeg * Math.PI) / 180);
|
|
6
|
+
const weld = new Map();
|
|
7
|
+
const group = new Int32Array(count);
|
|
8
|
+
for (let v = 0; v < count; v++) {
|
|
9
|
+
const key = `${Math.round((p[v * 3] ?? 0) * 1e5)},${Math.round((p[v * 3 + 1] ?? 0) * 1e5)},${Math.round((p[v * 3 + 2] ?? 0) * 1e5)}`;
|
|
10
|
+
let id = weld.get(key);
|
|
11
|
+
if (id === undefined) {
|
|
12
|
+
id = weld.size;
|
|
13
|
+
weld.set(key, id);
|
|
14
|
+
}
|
|
15
|
+
group[v] = id;
|
|
16
|
+
}
|
|
17
|
+
/* For each welded position, the faces meeting there: unit normal and the corner angle. */
|
|
18
|
+
const corners = Array.from({ length: weld.size }, () => []);
|
|
19
|
+
for (let t = 0; t < idx.length; t += 3) {
|
|
20
|
+
const ids = [idx[t] ?? 0, idx[t + 1] ?? 0, idx[t + 2] ?? 0];
|
|
21
|
+
const at = (i, c) => p[(ids[i] ?? 0) * 3 + c] ?? 0;
|
|
22
|
+
const ux = at(1, 0) - at(0, 0);
|
|
23
|
+
const uy = at(1, 1) - at(0, 1);
|
|
24
|
+
const uz = at(1, 2) - at(0, 2);
|
|
25
|
+
const vx = at(2, 0) - at(0, 0);
|
|
26
|
+
const vy = at(2, 1) - at(0, 1);
|
|
27
|
+
const vz = at(2, 2) - at(0, 2);
|
|
28
|
+
let nx = uy * vz - uz * vy;
|
|
29
|
+
let ny = uz * vx - ux * vz;
|
|
30
|
+
let nz = ux * vy - uy * vx;
|
|
31
|
+
const len = Math.hypot(nx, ny, nz);
|
|
32
|
+
if (len < 1e-12)
|
|
33
|
+
continue;
|
|
34
|
+
nx /= len;
|
|
35
|
+
ny /= len;
|
|
36
|
+
nz /= len;
|
|
37
|
+
for (let k = 0; k < 3; k++) {
|
|
38
|
+
const o = (k + 1) % 3;
|
|
39
|
+
const q = (k + 2) % 3;
|
|
40
|
+
const ax = at(o, 0) - at(k, 0);
|
|
41
|
+
const ay = at(o, 1) - at(k, 1);
|
|
42
|
+
const az = at(o, 2) - at(k, 2);
|
|
43
|
+
const bx = at(q, 0) - at(k, 0);
|
|
44
|
+
const by = at(q, 1) - at(k, 1);
|
|
45
|
+
const bz = at(q, 2) - at(k, 2);
|
|
46
|
+
const denom = Math.hypot(ax, ay, az) * Math.hypot(bx, by, bz) || 1;
|
|
47
|
+
const angle = Math.acos(Math.max(-1, Math.min(1, (ax * bx + ay * by + az * bz) / denom)));
|
|
48
|
+
corners[group[ids[k] ?? 0] ?? 0]?.push(nx, ny, nz, angle);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
const normals = new Float32Array(count * 3);
|
|
52
|
+
for (let v = 0; v < count; v++) {
|
|
53
|
+
const own = [
|
|
54
|
+
solid.normals[v * 3] ?? 0,
|
|
55
|
+
solid.normals[v * 3 + 1] ?? 0,
|
|
56
|
+
solid.normals[v * 3 + 2] ?? 0,
|
|
57
|
+
];
|
|
58
|
+
const list = corners[group[v] ?? 0] ?? [];
|
|
59
|
+
let sx = 0;
|
|
60
|
+
let sy = 0;
|
|
61
|
+
let sz = 0;
|
|
62
|
+
for (let c = 0; c < list.length; c += 4) {
|
|
63
|
+
const fx = list[c] ?? 0;
|
|
64
|
+
const fy = list[c + 1] ?? 0;
|
|
65
|
+
const fz = list[c + 2] ?? 0;
|
|
66
|
+
if (fx * (own[0] ?? 0) + fy * (own[1] ?? 0) + fz * (own[2] ?? 0) < cosCrease)
|
|
67
|
+
continue;
|
|
68
|
+
const w = list[c + 3] ?? 0;
|
|
69
|
+
sx += fx * w;
|
|
70
|
+
sy += fy * w;
|
|
71
|
+
sz += fz * w;
|
|
72
|
+
}
|
|
73
|
+
const len = Math.hypot(sx, sy, sz);
|
|
74
|
+
if (len < 1e-12) {
|
|
75
|
+
normals[v * 3] = own[0] ?? 0;
|
|
76
|
+
normals[v * 3 + 1] = own[1] ?? 0;
|
|
77
|
+
normals[v * 3 + 2] = own[2] ?? 0;
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
normals[v * 3] = sx / len;
|
|
81
|
+
normals[v * 3 + 1] = sy / len;
|
|
82
|
+
normals[v * 3 + 2] = sz / len;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return { positions: solid.positions, normals, uvs: solid.uvs, indices: solid.indices };
|
|
86
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The round solids: cylinder, cone, frustum, tube, sphere, hemisphere, icosphere, capsule, torus
|
|
3
|
+
* and lathe — all but the icosphere one revolve of a profile around +Y.
|
|
4
|
+
*
|
|
5
|
+
* A profile is `(r, y)` points. Revolving it, the vertex at angle θ sits at (r·cos θ, y, r·sin θ),
|
|
6
|
+
* and a quad is wound so that the profile's direction crossed with the direction of increasing θ
|
|
7
|
+
* points outward: a profile climbing on its outer side, or running outward along a bottom cap,
|
|
8
|
+
* faces out. Where the profile touches the axis a quad collapses to a triangle and only the
|
|
9
|
+
* triangle is written, so no edge is counted twice.
|
|
10
|
+
*
|
|
11
|
+
* `smooth` shares vertices around the axis with normals that vary around it; the default,
|
|
12
|
+
* faceted, gives every quad its own four vertices and its own normal. Texture u runs around (0..1,
|
|
13
|
+
* the seam duplicated) and v along the profile (0..1 by length).
|
|
14
|
+
*/
|
|
15
|
+
import type { Solid } from './solid.ts';
|
|
16
|
+
/** A cylinder of `radius` and `length` along Y, centred, capped. */
|
|
17
|
+
export declare function solidCylinder(radius: number, length: number, segments: number, smooth?: boolean): Solid;
|
|
18
|
+
/** A cone: base `radius` at −length/2, apex at +length/2. */
|
|
19
|
+
export declare function solidCone(radius: number, length: number, segments: number, smooth?: boolean): Solid;
|
|
20
|
+
/** A frustum with explicit radii at its bottom (−length/2) and top (+length/2), capped. */
|
|
21
|
+
export declare function solidFrustum(radiusBottom: number, radiusTop: number, length: number, segments: number, smooth?: boolean): Solid;
|
|
22
|
+
/** A hollow cylinder: outer `radius`, wall `thickness`, `length` along Y, centred. Four segments make a square frame. */
|
|
23
|
+
export declare function solidTube(radius: number, thickness: number, length: number, segments: number, smooth?: boolean): Solid;
|
|
24
|
+
/** A sphere of `radius`: `segments` around, half as many from pole to pole. */
|
|
25
|
+
export declare function solidSphere(radius: number, segments: number, smooth?: boolean): Solid;
|
|
26
|
+
/** A dome toward +Y on a flat base at the origin. */
|
|
27
|
+
export declare function solidHemisphere(radius: number, segments: number, smooth?: boolean): Solid;
|
|
28
|
+
/** A capsule: a cylinder of `length` (excluding the caps) with hemispherical ends, along Y. */
|
|
29
|
+
export declare function solidCapsule(radius: number, length: number, segments: number, smooth?: boolean): Solid;
|
|
30
|
+
/** A torus around +Y: ring radius `major`, tube radius `minor`, `segments` around and `sides` across. */
|
|
31
|
+
export declare function solidTorus(major: number, minor: number, segments: number, sides: number, smooth?: boolean): Solid;
|
|
32
|
+
/**
|
|
33
|
+
* A profile (`r, y` pairs, bottom to top) revolved around +Y. An end off the axis is closed with a
|
|
34
|
+
* flat cap, so any profile makes a solid. Every profile corner creases, faceted or smooth.
|
|
35
|
+
*/
|
|
36
|
+
export declare function solidLathe(profile: ArrayLike<number>, segments: number, smooth?: boolean): Solid;
|
|
37
|
+
/**
|
|
38
|
+
* An icosahedron subdivided `level` times onto a sphere of `radius`. Faceted by default — every
|
|
39
|
+
* triangle its own vertices and face normal — or smooth, sharing vertices with radial normals.
|
|
40
|
+
*/
|
|
41
|
+
export declare function solidIcosphere(radius: number, level: number, smooth?: boolean): Solid;
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
import { SolidWriter } from './solidWriter.js';
|
|
2
|
+
/**
|
|
3
|
+
* Revolve one or more strips — runs of profile points whose normals are continuous — around +Y.
|
|
4
|
+
* A strip's normals are used when smooth; faceted quads take the normal their corners imply.
|
|
5
|
+
*/
|
|
6
|
+
function revolve(strips, segments, smooth) {
|
|
7
|
+
const n = Math.max(3, Math.floor(segments));
|
|
8
|
+
const out = new SolidWriter();
|
|
9
|
+
let total = 0;
|
|
10
|
+
for (const strip of strips) {
|
|
11
|
+
for (let i = 0; i + 1 < strip.length; i++)
|
|
12
|
+
total += span(strip[i], strip[i + 1]);
|
|
13
|
+
}
|
|
14
|
+
let walked = 0;
|
|
15
|
+
for (const strip of strips) {
|
|
16
|
+
const v = [];
|
|
17
|
+
for (let i = 0; i < strip.length; i++) {
|
|
18
|
+
if (i > 0)
|
|
19
|
+
walked += span(strip[i - 1], strip[i]);
|
|
20
|
+
v.push(total > 0 ? walked / total : 0);
|
|
21
|
+
}
|
|
22
|
+
if (smooth)
|
|
23
|
+
revolveSmooth(out, strip, v, n);
|
|
24
|
+
else
|
|
25
|
+
revolveFaceted(out, strip, v, n);
|
|
26
|
+
}
|
|
27
|
+
return out.finish();
|
|
28
|
+
}
|
|
29
|
+
function span(a, b) {
|
|
30
|
+
return a && b ? Math.hypot(b.r - a.r, b.y - a.y) : 0;
|
|
31
|
+
}
|
|
32
|
+
function revolveSmooth(out, strip, v, n) {
|
|
33
|
+
const base = out.vertexCount;
|
|
34
|
+
const row = n + 1;
|
|
35
|
+
for (let i = 0; i < strip.length; i++) {
|
|
36
|
+
const p = strip[i];
|
|
37
|
+
if (!p)
|
|
38
|
+
continue;
|
|
39
|
+
for (let j = 0; j <= n; j++) {
|
|
40
|
+
const t = (2 * Math.PI * (j % n)) / n;
|
|
41
|
+
const c = Math.cos(t);
|
|
42
|
+
const s = Math.sin(t);
|
|
43
|
+
out.vertex(p.r * c, p.y, p.r * s, p.nr * c, p.ny, p.nr * s, j / n, v[i] ?? 0);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
for (let i = 0; i + 1 < strip.length; i++) {
|
|
47
|
+
const r0 = strip[i]?.r ?? 0;
|
|
48
|
+
const r1 = strip[i + 1]?.r ?? 0;
|
|
49
|
+
for (let j = 0; j < n; j++) {
|
|
50
|
+
const a = base + i * row + j;
|
|
51
|
+
const b = a + row;
|
|
52
|
+
const c = b + 1;
|
|
53
|
+
const d = a + 1;
|
|
54
|
+
emit(out, a, b, c, d, r0, r1);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function revolveFaceted(out, strip, v, n) {
|
|
59
|
+
for (let i = 0; i + 1 < strip.length; i++) {
|
|
60
|
+
const p = strip[i];
|
|
61
|
+
const q = strip[i + 1];
|
|
62
|
+
if (!p || !q)
|
|
63
|
+
continue;
|
|
64
|
+
for (let j = 0; j < n; j++) {
|
|
65
|
+
const t0 = (2 * Math.PI * j) / n;
|
|
66
|
+
const t1 = (2 * Math.PI * ((j + 1) % n)) / n;
|
|
67
|
+
const c0 = Math.cos(t0);
|
|
68
|
+
const s0 = Math.sin(t0);
|
|
69
|
+
const c1 = Math.cos(t1);
|
|
70
|
+
const s1 = Math.sin(t1);
|
|
71
|
+
const ax = p.r * c0;
|
|
72
|
+
const az = p.r * s0;
|
|
73
|
+
const bx = q.r * c0;
|
|
74
|
+
const bz = q.r * s0;
|
|
75
|
+
const cx = q.r * c1;
|
|
76
|
+
const cz = q.r * s1;
|
|
77
|
+
const dx = p.r * c1;
|
|
78
|
+
const dz = p.r * s1;
|
|
79
|
+
/* The normal from the diagonals, (c − a) × (d − b): right for a quad and for the triangle a
|
|
80
|
+
quad collapses to at the axis, where the edge-based cross product is zero. */
|
|
81
|
+
const ex = cx - ax;
|
|
82
|
+
const ey = q.y - p.y;
|
|
83
|
+
const ez = cz - az;
|
|
84
|
+
const fx = dx - bx;
|
|
85
|
+
const fy = p.y - q.y;
|
|
86
|
+
const fz = dz - bz;
|
|
87
|
+
let nx = ey * fz - ez * fy;
|
|
88
|
+
let ny = ez * fx - ex * fz;
|
|
89
|
+
let nz = ex * fy - ey * fx;
|
|
90
|
+
const len = Math.hypot(nx, ny, nz) || 1;
|
|
91
|
+
nx /= len;
|
|
92
|
+
ny /= len;
|
|
93
|
+
nz /= len;
|
|
94
|
+
const u0 = j / n;
|
|
95
|
+
const u1 = (j + 1) / n;
|
|
96
|
+
const a = out.vertex(ax, p.y, az, nx, ny, nz, u0, v[i] ?? 0);
|
|
97
|
+
const b = out.vertex(bx, q.y, bz, nx, ny, nz, u0, v[i + 1] ?? 0);
|
|
98
|
+
const c = out.vertex(cx, q.y, cz, nx, ny, nz, u1, v[i + 1] ?? 0);
|
|
99
|
+
const d = out.vertex(dx, p.y, dz, nx, ny, nz, u1, v[i] ?? 0);
|
|
100
|
+
emit(out, a, b, c, d, p.r, q.r);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/** Quad `a b c d`, or the one triangle left of it where either end of the profile step is on the axis. */
|
|
105
|
+
function emit(out, a, b, c, d, r0, r1) {
|
|
106
|
+
if (r0 <= 1e-12 && r1 <= 1e-12)
|
|
107
|
+
return;
|
|
108
|
+
if (r0 <= 1e-12)
|
|
109
|
+
out.triangle(a, b, c);
|
|
110
|
+
else if (r1 <= 1e-12)
|
|
111
|
+
out.triangle(a, b, d);
|
|
112
|
+
else
|
|
113
|
+
out.quad(a, b, c, d);
|
|
114
|
+
}
|
|
115
|
+
/** Points along a polyline profile, each segment its own strip so every corner creases. */
|
|
116
|
+
function creasedStrips(points) {
|
|
117
|
+
const strips = [];
|
|
118
|
+
for (let i = 0; i + 1 < points.length; i++) {
|
|
119
|
+
const [r0, y0] = points[i] ?? [0, 0];
|
|
120
|
+
const [r1, y1] = points[i + 1] ?? [0, 0];
|
|
121
|
+
const len = Math.hypot(r1 - r0, y1 - y0) || 1;
|
|
122
|
+
const nr = (y1 - y0) / len;
|
|
123
|
+
const ny = -(r1 - r0) / len;
|
|
124
|
+
strips.push([
|
|
125
|
+
{ r: r0, y: y0, nr, ny },
|
|
126
|
+
{ r: r1, y: y1, nr, ny },
|
|
127
|
+
]);
|
|
128
|
+
}
|
|
129
|
+
return strips;
|
|
130
|
+
}
|
|
131
|
+
/** An arc of the circle centred (cr, cy), radius `radius`, from angle `a0` to `a1` in `steps`. */
|
|
132
|
+
function arc(cr, cy, radius, a0, a1, steps) {
|
|
133
|
+
const points = [];
|
|
134
|
+
for (let s = 0; s <= steps; s++) {
|
|
135
|
+
const a = a0 + ((a1 - a0) * s) / steps;
|
|
136
|
+
const c = Math.cos(a);
|
|
137
|
+
const sn = Math.sin(a);
|
|
138
|
+
points.push({ r: Math.max(0, cr + radius * c), y: cy + radius * sn, nr: c, ny: sn });
|
|
139
|
+
}
|
|
140
|
+
return points;
|
|
141
|
+
}
|
|
142
|
+
/** A cylinder of `radius` and `length` along Y, centred, capped. */
|
|
143
|
+
export function solidCylinder(radius, length, segments, smooth = false) {
|
|
144
|
+
return solidFrustum(radius, radius, length, segments, smooth);
|
|
145
|
+
}
|
|
146
|
+
/** A cone: base `radius` at −length/2, apex at +length/2. */
|
|
147
|
+
export function solidCone(radius, length, segments, smooth = false) {
|
|
148
|
+
return solidFrustum(radius, 0, length, segments, smooth);
|
|
149
|
+
}
|
|
150
|
+
/** A frustum with explicit radii at its bottom (−length/2) and top (+length/2), capped. */
|
|
151
|
+
export function solidFrustum(radiusBottom, radiusTop, length, segments, smooth = false) {
|
|
152
|
+
const h = length / 2;
|
|
153
|
+
return revolve(creasedStrips([
|
|
154
|
+
[0, -h],
|
|
155
|
+
[radiusBottom, -h],
|
|
156
|
+
[radiusTop, h],
|
|
157
|
+
[0, h],
|
|
158
|
+
]), segments, smooth);
|
|
159
|
+
}
|
|
160
|
+
/** A hollow cylinder: outer `radius`, wall `thickness`, `length` along Y, centred. Four segments make a square frame. */
|
|
161
|
+
export function solidTube(radius, thickness, length, segments, smooth = false) {
|
|
162
|
+
const h = length / 2;
|
|
163
|
+
const inner = Math.max(0, radius - thickness);
|
|
164
|
+
return revolve(creasedStrips([
|
|
165
|
+
[inner, -h],
|
|
166
|
+
[radius, -h],
|
|
167
|
+
[radius, h],
|
|
168
|
+
[inner, h],
|
|
169
|
+
[inner, -h],
|
|
170
|
+
]), segments, smooth);
|
|
171
|
+
}
|
|
172
|
+
/** A sphere of `radius`: `segments` around, half as many from pole to pole. */
|
|
173
|
+
export function solidSphere(radius, segments, smooth = false) {
|
|
174
|
+
const rings = Math.max(2, Math.floor(segments / 2));
|
|
175
|
+
return revolve([arc(0, 0, radius, -Math.PI / 2, Math.PI / 2, rings)], segments, smooth);
|
|
176
|
+
}
|
|
177
|
+
/** A dome toward +Y on a flat base at the origin. */
|
|
178
|
+
export function solidHemisphere(radius, segments, smooth = false) {
|
|
179
|
+
const rings = Math.max(1, Math.floor(segments / 4));
|
|
180
|
+
const base = [
|
|
181
|
+
{ r: 0, y: 0, nr: 0, ny: -1 },
|
|
182
|
+
{ r: radius, y: 0, nr: 0, ny: -1 },
|
|
183
|
+
];
|
|
184
|
+
return revolve([base, arc(0, 0, radius, 0, Math.PI / 2, rings)], segments, smooth);
|
|
185
|
+
}
|
|
186
|
+
/** A capsule: a cylinder of `length` (excluding the caps) with hemispherical ends, along Y. */
|
|
187
|
+
export function solidCapsule(radius, length, segments, smooth = false) {
|
|
188
|
+
const rings = Math.max(1, Math.floor(segments / 4));
|
|
189
|
+
const h = length / 2;
|
|
190
|
+
return revolve([
|
|
191
|
+
arc(0, -h, radius, -Math.PI / 2, 0, rings),
|
|
192
|
+
[
|
|
193
|
+
{ r: radius, y: -h, nr: 1, ny: 0 },
|
|
194
|
+
{ r: radius, y: h, nr: 1, ny: 0 },
|
|
195
|
+
],
|
|
196
|
+
arc(0, h, radius, 0, Math.PI / 2, rings),
|
|
197
|
+
], segments, smooth);
|
|
198
|
+
}
|
|
199
|
+
/** A torus around +Y: ring radius `major`, tube radius `minor`, `segments` around and `sides` across. */
|
|
200
|
+
export function solidTorus(major, minor, segments, sides, smooth = false) {
|
|
201
|
+
return revolve([arc(major, 0, minor, 0, 2 * Math.PI, Math.max(3, Math.floor(sides)))], segments, smooth);
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* A profile (`r, y` pairs, bottom to top) revolved around +Y. An end off the axis is closed with a
|
|
205
|
+
* flat cap, so any profile makes a solid. Every profile corner creases, faceted or smooth.
|
|
206
|
+
*/
|
|
207
|
+
export function solidLathe(profile, segments, smooth = false) {
|
|
208
|
+
const points = [];
|
|
209
|
+
for (let i = 0; i + 1 < profile.length; i += 2)
|
|
210
|
+
points.push([profile[i] ?? 0, profile[i + 1] ?? 0]);
|
|
211
|
+
const first = points[0];
|
|
212
|
+
const last = points[points.length - 1];
|
|
213
|
+
if (first && first[0] > 1e-12)
|
|
214
|
+
points.unshift([0, first[1]]);
|
|
215
|
+
if (last && last[0] > 1e-12)
|
|
216
|
+
points.push([0, last[1]]);
|
|
217
|
+
return revolve(creasedStrips(points), segments, smooth);
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* An icosahedron subdivided `level` times onto a sphere of `radius`. Faceted by default — every
|
|
221
|
+
* triangle its own vertices and face normal — or smooth, sharing vertices with radial normals.
|
|
222
|
+
*/
|
|
223
|
+
export function solidIcosphere(radius, level, smooth = false) {
|
|
224
|
+
const t = (1 + Math.sqrt(5)) / 2;
|
|
225
|
+
const verts = [
|
|
226
|
+
[-1, t, 0],
|
|
227
|
+
[1, t, 0],
|
|
228
|
+
[-1, -t, 0],
|
|
229
|
+
[1, -t, 0],
|
|
230
|
+
[0, -1, t],
|
|
231
|
+
[0, 1, t],
|
|
232
|
+
[0, -1, -t],
|
|
233
|
+
[0, 1, -t],
|
|
234
|
+
[t, 0, -1],
|
|
235
|
+
[t, 0, 1],
|
|
236
|
+
[-t, 0, -1],
|
|
237
|
+
[-t, 0, 1],
|
|
238
|
+
].map(unit);
|
|
239
|
+
let faces = [
|
|
240
|
+
[0, 11, 5],
|
|
241
|
+
[0, 5, 1],
|
|
242
|
+
[0, 1, 7],
|
|
243
|
+
[0, 7, 10],
|
|
244
|
+
[0, 10, 11],
|
|
245
|
+
[1, 5, 9],
|
|
246
|
+
[5, 11, 4],
|
|
247
|
+
[11, 10, 2],
|
|
248
|
+
[10, 7, 6],
|
|
249
|
+
[7, 1, 8],
|
|
250
|
+
[3, 9, 4],
|
|
251
|
+
[3, 4, 2],
|
|
252
|
+
[3, 2, 6],
|
|
253
|
+
[3, 6, 8],
|
|
254
|
+
[3, 8, 9],
|
|
255
|
+
[4, 9, 5],
|
|
256
|
+
[2, 4, 11],
|
|
257
|
+
[6, 2, 10],
|
|
258
|
+
[8, 6, 7],
|
|
259
|
+
[9, 8, 1],
|
|
260
|
+
];
|
|
261
|
+
for (let l = 0; l < Math.floor(level); l++) {
|
|
262
|
+
const cache = new Map();
|
|
263
|
+
const mid = (a, b) => {
|
|
264
|
+
const key = a < b ? `${a},${b}` : `${b},${a}`;
|
|
265
|
+
const hit = cache.get(key);
|
|
266
|
+
if (hit !== undefined)
|
|
267
|
+
return hit;
|
|
268
|
+
const va = verts[a] ?? [0, 0, 0];
|
|
269
|
+
const vb = verts[b] ?? [0, 0, 0];
|
|
270
|
+
verts.push(unit([
|
|
271
|
+
(va[0] ?? 0) + (vb[0] ?? 0),
|
|
272
|
+
(va[1] ?? 0) + (vb[1] ?? 0),
|
|
273
|
+
(va[2] ?? 0) + (vb[2] ?? 0),
|
|
274
|
+
]));
|
|
275
|
+
cache.set(key, verts.length - 1);
|
|
276
|
+
return verts.length - 1;
|
|
277
|
+
};
|
|
278
|
+
const next = [];
|
|
279
|
+
for (const [a = 0, b = 0, c = 0] of faces) {
|
|
280
|
+
const ab = mid(a, b);
|
|
281
|
+
const bc = mid(b, c);
|
|
282
|
+
const ca = mid(c, a);
|
|
283
|
+
next.push([a, ab, ca], [b, bc, ab], [c, ca, bc], [ab, bc, ca]);
|
|
284
|
+
}
|
|
285
|
+
faces = next;
|
|
286
|
+
}
|
|
287
|
+
const out = new SolidWriter();
|
|
288
|
+
const uv = (p) => [
|
|
289
|
+
0.5 + Math.atan2(p[2] ?? 0, p[0] ?? 0) / (2 * Math.PI),
|
|
290
|
+
0.5 + Math.asin(Math.max(-1, Math.min(1, p[1] ?? 0))) / Math.PI,
|
|
291
|
+
];
|
|
292
|
+
if (smooth) {
|
|
293
|
+
for (const p of verts) {
|
|
294
|
+
const [u, v] = uv(p);
|
|
295
|
+
out.vertex((p[0] ?? 0) * radius, (p[1] ?? 0) * radius, (p[2] ?? 0) * radius, p[0] ?? 0, p[1] ?? 0, p[2] ?? 0, u, v);
|
|
296
|
+
}
|
|
297
|
+
/* The table's faces run counter-clockwise seen from outside, as every face here does. */
|
|
298
|
+
for (const [a = 0, b = 0, c = 0] of faces)
|
|
299
|
+
out.triangle(a, b, c);
|
|
300
|
+
return out.finish();
|
|
301
|
+
}
|
|
302
|
+
for (const [a = 0, b = 0, c = 0] of faces) {
|
|
303
|
+
const pa = verts[a] ?? [0, 0, 0];
|
|
304
|
+
const pb = verts[b] ?? [0, 0, 0];
|
|
305
|
+
const pc = verts[c] ?? [0, 0, 0];
|
|
306
|
+
const n = unit([
|
|
307
|
+
((pa[0] ?? 0) + (pb[0] ?? 0) + (pc[0] ?? 0)) / 3,
|
|
308
|
+
((pa[1] ?? 0) + (pb[1] ?? 0) + (pc[1] ?? 0)) / 3,
|
|
309
|
+
((pa[2] ?? 0) + (pb[2] ?? 0) + (pc[2] ?? 0)) / 3,
|
|
310
|
+
]);
|
|
311
|
+
const base = out.vertexCount;
|
|
312
|
+
for (const p of [pa, pb, pc]) {
|
|
313
|
+
const [u, v] = uv(p);
|
|
314
|
+
out.vertex((p[0] ?? 0) * radius, (p[1] ?? 0) * radius, (p[2] ?? 0) * radius, n[0] ?? 0, n[1] ?? 0, n[2] ?? 0, u, v);
|
|
315
|
+
}
|
|
316
|
+
out.triangle(base, base + 1, base + 2);
|
|
317
|
+
}
|
|
318
|
+
return out.finish();
|
|
319
|
+
}
|
|
320
|
+
function unit(p) {
|
|
321
|
+
const len = Math.hypot(p[0] ?? 0, p[1] ?? 0, p[2] ?? 0) || 1;
|
|
322
|
+
return [(p[0] ?? 0) / len, (p[1] ?? 0) / len, (p[2] ?? 0) / len];
|
|
323
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Solid } from './solid.ts';
|
|
2
|
+
export declare class SolidWriter {
|
|
3
|
+
private readonly positions;
|
|
4
|
+
private readonly normals;
|
|
5
|
+
private readonly uvs;
|
|
6
|
+
private readonly indices;
|
|
7
|
+
private count;
|
|
8
|
+
/** Add one vertex and return its index. The normal is taken as given; pass it unit length. */
|
|
9
|
+
vertex(px: number, py: number, pz: number, nx: number, ny: number, nz: number, u: number, v: number): number;
|
|
10
|
+
triangle(a: number, b: number, c: number): void;
|
|
11
|
+
/** Two triangles over a counter-clockwise quad `a b c d`. */
|
|
12
|
+
quad(a: number, b: number, c: number, d: number): void;
|
|
13
|
+
/** How many vertices so far, which is the index the next `vertex` returns. */
|
|
14
|
+
get vertexCount(): number;
|
|
15
|
+
finish(): Solid;
|
|
16
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The accumulator every solid primitive writes through: a vertex at a time, a triangle or a quad at
|
|
3
|
+
* a time, finished into a `Solid`.
|
|
4
|
+
*
|
|
5
|
+
* Internal to the geometry modules and not in the barrel — a caller building geometry of its own
|
|
6
|
+
* wants `MeshBuilder` or typed arrays, and this exists so the primitives share one place that
|
|
7
|
+
* decides how a vertex is laid out.
|
|
8
|
+
*/
|
|
9
|
+
import { GrowableF32, GrowableU32 } from './growable.js';
|
|
10
|
+
export class SolidWriter {
|
|
11
|
+
positions = new GrowableF32();
|
|
12
|
+
normals = new GrowableF32();
|
|
13
|
+
uvs = new GrowableF32();
|
|
14
|
+
indices = new GrowableU32();
|
|
15
|
+
count = 0;
|
|
16
|
+
/** Add one vertex and return its index. The normal is taken as given; pass it unit length. */
|
|
17
|
+
vertex(px, py, pz, nx, ny, nz, u, v) {
|
|
18
|
+
this.positions.push(px, py, pz);
|
|
19
|
+
this.normals.push(nx, ny, nz);
|
|
20
|
+
this.uvs.push(u, v);
|
|
21
|
+
return this.count++;
|
|
22
|
+
}
|
|
23
|
+
triangle(a, b, c) {
|
|
24
|
+
this.indices.push(a, b, c);
|
|
25
|
+
}
|
|
26
|
+
/** Two triangles over a counter-clockwise quad `a b c d`. */
|
|
27
|
+
quad(a, b, c, d) {
|
|
28
|
+
this.indices.push(a, b, c, a, c, d);
|
|
29
|
+
}
|
|
30
|
+
/** How many vertices so far, which is the index the next `vertex` returns. */
|
|
31
|
+
get vertexCount() {
|
|
32
|
+
return this.count;
|
|
33
|
+
}
|
|
34
|
+
finish() {
|
|
35
|
+
return {
|
|
36
|
+
positions: this.positions.toTyped(),
|
|
37
|
+
normals: this.normals.toTyped(),
|
|
38
|
+
uvs: this.uvs.toTyped(),
|
|
39
|
+
indices: this.indices.toTyped(),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -123,6 +123,13 @@ export { GLOBAL_FIELD_BLEND, composeGlobalField, createGlobalField, sampleGlobal
|
|
|
123
123
|
export type { FieldSource, GlobalField, GlobalFieldCascade, GlobalFieldInstance, } from './render/gi/globalField.ts';
|
|
124
124
|
export { FIELD_FADE_SEC, LightField } from './render/driftLight/lightField.ts';
|
|
125
125
|
export type { LightFieldOptions, LightFieldSource } from './render/driftLight/lightField.ts';
|
|
126
|
+
export { DENSE_MAX_AXIS, DENSE_MAX_HEIGHT, bakeDenseField, } from './render/driftLight/denseField.ts';
|
|
127
|
+
export type { DenseLightVolume } from './render/driftLight/denseField.ts';
|
|
128
|
+
export { WorldLightField } from './render/driftLight/worldLightField.ts';
|
|
129
|
+
export type { WorldLightFieldOptions } from './render/driftLight/worldLightField.ts';
|
|
130
|
+
export { SURFACE_EFFECT_TEXELS, packSurfaceEffects } from './render/surfaceEffects.ts';
|
|
131
|
+
export type { SurfaceLayerEffect } from './render/surfaceEffects.ts';
|
|
132
|
+
export type { FieldLight } from './render/driftLight/bake.ts';
|
|
126
133
|
export { GI_SCREEN_MARCH, SCREEN_TRACE_BIAS_M, screenRayOrigin, traceScreen, } from './render/gi/traceScreen.ts';
|
|
127
134
|
export { FIELD_MARCH, coneRadiusAt, newFieldHit, traceField } from './render/gi/traceField.ts';
|
|
128
135
|
export type { FieldHit, FieldMarch } from './render/gi/traceField.ts';
|
|
@@ -213,7 +220,9 @@ export { isWeakGpuFamily } from './render/gpuCapability.ts';
|
|
|
213
220
|
*/
|
|
214
221
|
export { describeGpu } from './render/gpuCapability.ts';
|
|
215
222
|
export type { GpuIdentity, DescribeGpuOptions } from './render/gpuCapability.ts';
|
|
216
|
-
export { DEFAULT_POINT_LIGHT_VIEW_RANGE, createPointLightBuffer, selectPointLights, } from './render/pointLightSelection.ts';
|
|
223
|
+
export { DEFAULT_POINT_LIGHT_VIEW_RANGE, createPointLightBuffer, selectGridLights, selectPointLights, } from './render/pointLightSelection.ts';
|
|
224
|
+
export { LIGHT_GRID_MAX_CELLS, createLightGrid, gatherLights } from './render/lightGrid.ts';
|
|
225
|
+
export type { LightGrid } from './render/lightGrid.ts';
|
|
217
226
|
export type { PointLightBuffer, PointLightSource } from './render/pointLightSelection.ts';
|
|
218
227
|
export { createFlame, flameFrequencyHz, updateFlame } from './render/flameLight.ts';
|
|
219
228
|
export type { Flame, FlameOptions } from './render/flameLight.ts';
|
|
@@ -238,6 +247,14 @@ export { WaterRenderer } from './render/waterRenderer.ts';
|
|
|
238
247
|
export type { WaterBody, WaterBounds, WaterSettings } from './render/waterRenderer.ts';
|
|
239
248
|
export { MeshBuilder } from './geometry/meshBuilder.ts';
|
|
240
249
|
export { concatMeshes, placeMesh } from './geometry/meshConcat.ts';
|
|
250
|
+
export { emptySolid, mergeSolids, solidToMesh, solidVolume, transformSolid, } from './geometry/solid.ts';
|
|
251
|
+
export type { Solid } from './geometry/solid.ts';
|
|
252
|
+
export { solidBox, solidExtrude, solidPrism, solidQuad, solidRoundedBox, solidSweep, } from './geometry/solidFlat.ts';
|
|
253
|
+
export { solidCapsule, solidCone, solidCylinder, solidFrustum, solidHemisphere, solidIcosphere, solidLathe, solidSphere, solidTorus, solidTube, } from './geometry/solidRound.ts';
|
|
254
|
+
export { solidIntersect, solidSubtract, solidUnion } from './geometry/csg.ts';
|
|
255
|
+
export { boxBooleanCells, solidBoxBoolean } from './geometry/boxCsg.ts';
|
|
256
|
+
export type { BoxOperation } from './geometry/boxCsg.ts';
|
|
257
|
+
export { smoothSolidNormals } from './geometry/solidNormals.ts';
|
|
241
258
|
export { generateTangents } from './geometry/tangents.ts';
|
|
242
259
|
export type { MeshBuildOptions } from './geometry/meshBuilder.ts';
|
|
243
260
|
export { Spline, createSplineSample } from './geometry/spline.ts';
|
|
@@ -358,7 +375,9 @@ export type { FlockParams } from './render/flockRenderer.ts';
|
|
|
358
375
|
export { buildTree } from './geometry/treeBuilder.ts';
|
|
359
376
|
export type { TreeParams, TreeGeometry } from './geometry/treeBuilder.ts';
|
|
360
377
|
export type { InstanceData } from './render/instancedMesh.ts';
|
|
361
|
-
export type { MeshInstances } from './render/instances.ts';
|
|
378
|
+
export type { InstancedOptions, MeshInstances } from './render/instances.ts';
|
|
379
|
+
export { batchBoxVisible, cullInstances, instancesBox } from './render/instanceCull.ts';
|
|
380
|
+
export type { BoxOccluder } from './render/instanceCull.ts';
|
|
362
381
|
export type { WindProfile, WindState } from './render/wind.ts';
|
|
363
382
|
export { cellBounds, cellCoord, cellCoordInRange, cellCoordsOf, cellIdFor, cellIdFrom, cellsInFrustum, cellsInRadius, createCellGrid, } from './world/cell.ts';
|
|
364
383
|
export type { CellGrid } from './world/cell.ts';
|
|
@@ -367,3 +386,5 @@ export { cameraPositionFrom, cellPredictor, createCellStream, pumpCellStream, se
|
|
|
367
386
|
export type { CellStore, CellStream, CellStreamOptions } from './world/cellStream.ts';
|
|
368
387
|
export { createFrozenCells, freezeCell, frozenEntities, frozenFingerprintContribution, isCellFrozen, thawCell, } from './world/freeze.ts';
|
|
369
388
|
export type { FreezableWorld, FrozenCell, FrozenCells } from './world/freeze.ts';
|
|
389
|
+
export { HLOD_NONE, HlodSet, createHlodDraws, projectionScaleOf } from './world/hlod.ts';
|
|
390
|
+
export type { HlodDraws, HlodOptions } from './world/hlod.ts';
|