From 529b464b9b8bcfd9d1d8b7f6ec4e238a7ee7942b Mon Sep 17 00:00:00 2001 From: Scott Motte Date: Fri, 10 Apr 2026 08:54:39 -0700 Subject: [PATCH 01/17] fortress --- .../v1/components/three-fortress-rect.html | 387 ++++++++++++++++++ assets/css/main.css | 2 +- ops/index.md | 24 +- 3 files changed, 407 insertions(+), 6 deletions(-) create mode 100644 _includes/v1/components/three-fortress-rect.html diff --git a/_includes/v1/components/three-fortress-rect.html b/_includes/v1/components/three-fortress-rect.html new file mode 100644 index 0000000..a8387dc --- /dev/null +++ b/_includes/v1/components/three-fortress-rect.html @@ -0,0 +1,387 @@ +
+ +
+ + + + diff --git a/assets/css/main.css b/assets/css/main.css index 77010ba..a07c308 100644 --- a/assets/css/main.css +++ b/assets/css/main.css @@ -735,7 +735,7 @@ width: 100%; position: static; } - .feature-card-head { +.feature-card-head { flex-direction: column; align-items: flex-start; } diff --git a/ops/index.md b/ops/index.md index 10dabb6..6d05ca8 100644 --- a/ops/index.md +++ b/ops/index.md @@ -2,9 +2,23 @@ title: "Dotenvx Ops" --- -
-

- dotenvx-ops. ⛨ ARMORED KEYS: Harden your private keys. -

-

coming soon

+
+
+
+

Dotenvx Ops

+

+ Secrets, under guard. +

+

+ Inspired by the ghost key on the home page, but forged into a fortress for Ops. +

+

+ Coming soon +

+
+ +
+ {% include v1/components/three-fortress-rect.html height="170px" class="w-full max-w-[24rem] md:max-w-[27rem] md:!h-[460px]" %} +
+
From dc5c4bb5b0017696e6d34c99ff95932842397d68 Mon Sep 17 00:00:00 2001 From: Scott Motte Date: Fri, 10 Apr 2026 15:10:02 -0700 Subject: [PATCH 02/17] citadel --- .../v1/components/three-citadel-rect.html | 401 ++++++++++++++++++ .../components/three-fortress-gated-rect.html | 355 ++++++++++++++++ .../v1/components/three-fortress-rect.html | 2 + ops/index.md | 4 +- 4 files changed, 761 insertions(+), 1 deletion(-) create mode 100644 _includes/v1/components/three-citadel-rect.html create mode 100644 _includes/v1/components/three-fortress-gated-rect.html diff --git a/_includes/v1/components/three-citadel-rect.html b/_includes/v1/components/three-citadel-rect.html new file mode 100644 index 0000000..e77a36e --- /dev/null +++ b/_includes/v1/components/three-citadel-rect.html @@ -0,0 +1,401 @@ +
+ +
+ + + + diff --git a/_includes/v1/components/three-fortress-gated-rect.html b/_includes/v1/components/three-fortress-gated-rect.html new file mode 100644 index 0000000..2988124 --- /dev/null +++ b/_includes/v1/components/three-fortress-gated-rect.html @@ -0,0 +1,355 @@ +
+ +
+ + + + diff --git a/_includes/v1/components/three-fortress-rect.html b/_includes/v1/components/three-fortress-rect.html index a8387dc..ef05840 100644 --- a/_includes/v1/components/three-fortress-rect.html +++ b/_includes/v1/components/three-fortress-rect.html @@ -217,6 +217,7 @@ } }; + for (let x = -9; x <= 9; x += 1) { for (let z = -9; z <= 9; z += 1) { const edge = Math.max(Math.abs(x), Math.abs(z)) === 9; @@ -258,6 +259,7 @@ addStack(-2, 9, 4); addStack(2, 9, 4); + fortressGroup.rotation.x = 0.215; fortressGroup.rotation.y = -0.17; fortressGroup.rotation.z = -0.03; diff --git a/ops/index.md b/ops/index.md index 6d05ca8..942cd30 100644 --- a/ops/index.md +++ b/ops/index.md @@ -18,7 +18,9 @@ title: "Dotenvx Ops"
- {% include v1/components/three-fortress-rect.html height="170px" class="w-full max-w-[24rem] md:max-w-[27rem] md:!h-[460px]" %} + {% include v1/components/three-fortress-gated-rect.html height="170px" class="w-full max-w-[24rem] md:max-w-[27rem] md:!h-[460px]" %} + {% comment %}{% include v1/components/three-fortress-rect.html height="170px" class="w-full max-w-[24rem] md:max-w-[27rem] md:!h-[460px]" %}{% endcomment %} + {% comment %}{% include v1/components/three-citadel-rect.html height="170px" class="w-full max-w-[24rem] md:max-w-[27rem] md:!h-[460px]" %}{% endcomment %}
From 73078caa7222e96b764d80b21496652a80ce4824 Mon Sep 17 00:00:00 2001 From: Scott Motte Date: Fri, 10 Apr 2026 15:42:57 -0700 Subject: [PATCH 03/17] steel --- .../components/three-fortress-gated-rect.html | 16 ++++---- assets/css/main.css | 8 ++++ ops/index.md | 37 ++++++++----------- 3 files changed, 32 insertions(+), 29 deletions(-) diff --git a/_includes/v1/components/three-fortress-gated-rect.html b/_includes/v1/components/three-fortress-gated-rect.html index 2988124..8addeb9 100644 --- a/_includes/v1/components/three-fortress-gated-rect.html +++ b/_includes/v1/components/three-fortress-gated-rect.html @@ -68,13 +68,13 @@ const boxGeometry = new THREE.BoxGeometry(0.12, 0.1662, 0.12); const edgeGeometry = new THREE.EdgesGeometry(boxGeometry); const edgeHighlightMaterial = new THREE.LineBasicMaterial({ - color: 0xf1dfab, + color: 0xdfe6ef, transparent: true, opacity: 0.28, toneMapped: false }); const glowShellMaterial = new THREE.MeshBasicMaterial({ - color: 0xffe9a8, + color: 0xc7d8ea, transparent: true, opacity: 0.2, blending: THREE.AdditiveBlending, @@ -103,7 +103,7 @@ ctx.clearRect(0, 0, 128, 128); ctx.fillStyle = 'rgba(99, 104, 113, 0.24)'; ctx.fillRect(0, 0, 128, 128); - ctx.fillStyle = 'rgba(217, 191, 117, 0.82)'; + ctx.fillStyle = 'rgba(210, 220, 233, 0.84)'; ctx.font = '500 16px sans-serif'; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; @@ -124,7 +124,7 @@ const getFaceMaterial = (label) => { if (!materialCache.has(label)) { materialCache.set(label, new THREE.MeshPhysicalMaterial({ - color: 0xfff6d1, + color: 0xf2f6fb, transparent: true, opacity: 0.72, map: getLabelTexture(label), @@ -135,9 +135,9 @@ ior: 1.5, clearcoat: 1.0, clearcoatRoughness: 0.035, - attenuationColor: 0xc7bb8c, + attenuationColor: 0x90a4ba, attenuationDistance: 2.2, - envMapIntensity: 1.9 + envMapIntensity: 2.05 })); } return materialCache.get(label); @@ -258,13 +258,13 @@ addStack(1, 0, 9); const checkpointMaterial = new THREE.MeshBasicMaterial({ - color: 0xf1dc91, + color: 0xd8e5f3, transparent: true, opacity: 0.24, toneMapped: false }); const checkpointGeometry = new THREE.BoxGeometry(0.06, 0.06, 0.06); - const checkpointPath = [8.2, 5.2, 1.3]; + const checkpointPath = [1.3]; for (let i = 0; i < checkpointPath.length; i += 1) { const marker = new THREE.Mesh(checkpointGeometry, checkpointMaterial.clone()); marker.position.set(0, baseY + 0.34, checkpointPath[i] * pitch); diff --git a/assets/css/main.css b/assets/css/main.css index a07c308..975f8d0 100644 --- a/assets/css/main.css +++ b/assets/css/main.css @@ -280,6 +280,14 @@ html.dark .contact-muted-link:hover, text-shadow: 0 1px 0 rgba(255, 255, 255, 0.08), 0 12px 24px rgba(11, 7, 2, 0.34); } +.hero-title-steel { + background: linear-gradient(180deg, #ffffff 0%, #f6f8fb 22%, #e9edf2 48%, #cfd7e0 74%, #b6c0cc 100%); + -webkit-background-clip: text; + background-clip: text; + color: transparent; + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.2), 0 14px 28px rgba(5, 8, 12, 0.38); +} + blockquote { @apply text-zinc-800 dark:text-zinc-300; } p { @apply leading-relaxed; } diff --git a/ops/index.md b/ops/index.md index 942cd30..11a2489 100644 --- a/ops/index.md +++ b/ops/index.md @@ -2,25 +2,20 @@ title: "Dotenvx Ops" --- -
-
-
-

Dotenvx Ops

-

- Secrets, under guard. -

-

- Inspired by the ghost key on the home page, but forged into a fortress for Ops. -

-

- Coming soon -

-
- -
- {% include v1/components/three-fortress-gated-rect.html height="170px" class="w-full max-w-[24rem] md:max-w-[27rem] md:!h-[460px]" %} - {% comment %}{% include v1/components/three-fortress-rect.html height="170px" class="w-full max-w-[24rem] md:max-w-[27rem] md:!h-[460px]" %}{% endcomment %} - {% comment %}{% include v1/components/three-citadel-rect.html height="170px" class="w-full max-w-[24rem] md:max-w-[27rem] md:!h-[460px]" %}{% endcomment %} -
+
+
+
+
Dotenvx Ops
+

Armor for Dotenvx

+

Layered walls and controls around your secrets. Defense in depth for environments, keys, and runtime access.

+
+

Coming soon

+
+
+
+ {% include v1/components/three-fortress-gated-rect.html height="170px" class="h-[170px] md:!h-[560px]" %} + {% comment %}{% include v1/components/three-fortress-rect.html height="170px" class="h-[170px] md:!h-[560px]" %}{% endcomment %} + {% comment %}{% include v1/components/three-citadel-rect.html height="170px" class="h-[170px] md:!h-[560px]" %}{% endcomment %} +
-
+ From 8257cda858855266ca28921edbf6bce2b952d6fe Mon Sep 17 00:00:00 2001 From: Scott Motte Date: Fri, 10 Apr 2026 18:32:04 -0700 Subject: [PATCH 04/17] mace --- .../v1/components/three-gauntlet-rect.html | 320 +++++++++++++++++ _includes/v1/components/three-mace-rect.html | 325 ++++++++++++++++++ ops/index.md | 4 +- 3 files changed, 648 insertions(+), 1 deletion(-) create mode 100644 _includes/v1/components/three-gauntlet-rect.html create mode 100644 _includes/v1/components/three-mace-rect.html diff --git a/_includes/v1/components/three-gauntlet-rect.html b/_includes/v1/components/three-gauntlet-rect.html new file mode 100644 index 0000000..69c516b --- /dev/null +++ b/_includes/v1/components/three-gauntlet-rect.html @@ -0,0 +1,320 @@ +
+ +
+ + + + diff --git a/_includes/v1/components/three-mace-rect.html b/_includes/v1/components/three-mace-rect.html new file mode 100644 index 0000000..f55e6d8 --- /dev/null +++ b/_includes/v1/components/three-mace-rect.html @@ -0,0 +1,325 @@ +
+ +
+ + + + diff --git a/ops/index.md b/ops/index.md index 11a2489..0baa4ab 100644 --- a/ops/index.md +++ b/ops/index.md @@ -13,7 +13,9 @@ title: "Dotenvx Ops"
- {% include v1/components/three-fortress-gated-rect.html height="170px" class="h-[170px] md:!h-[560px]" %} + {% include v1/components/three-mace-rect.html height="170px" class="h-[170px] md:!h-[560px]" %} + {% comment %}{% include v1/components/three-gauntlet-rect.html height="170px" class="h-[170px] md:!h-[560px]" %}{% endcomment %} + {% comment %}{% include v1/components/three-fortress-gated-rect.html height="170px" class="h-[170px] md:!h-[560px]" %}{% endcomment %} {% comment %}{% include v1/components/three-fortress-rect.html height="170px" class="h-[170px] md:!h-[560px]" %}{% endcomment %} {% comment %}{% include v1/components/three-citadel-rect.html height="170px" class="h-[170px] md:!h-[560px]" %}{% endcomment %}
From f74f8d91a235cc98c1501e9c665cb82a2a8fddaf Mon Sep 17 00:00:00 2001 From: Scott Motte Date: Fri, 10 Apr 2026 18:33:58 -0700 Subject: [PATCH 05/17] fix --- _includes/v1/components/three-mace-rect.html | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/_includes/v1/components/three-mace-rect.html b/_includes/v1/components/three-mace-rect.html index f55e6d8..f9c3633 100644 --- a/_includes/v1/components/three-mace-rect.html +++ b/_includes/v1/components/three-mace-rect.html @@ -231,9 +231,8 @@ '00100', '00100', '00100', - '00100', '01110', - '00100', + '01110', '00100', '00100', '00100', From 57c842f547ba365299e9a13f9964916bf42b102b Mon Sep 17 00:00:00 2001 From: Scott Motte Date: Fri, 10 Apr 2026 18:35:08 -0700 Subject: [PATCH 06/17] mace --- _includes/v1/components/three-mace-rect.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_includes/v1/components/three-mace-rect.html b/_includes/v1/components/three-mace-rect.html index f9c3633..24e8389 100644 --- a/_includes/v1/components/three-mace-rect.html +++ b/_includes/v1/components/three-mace-rect.html @@ -232,7 +232,7 @@ '00100', '00100', '01110', - '01110', + '00100', '00100', '00100', '00100', From 9103a7775c7c715e74fad05bdd90830e5c4158d3 Mon Sep 17 00:00:00 2001 From: Scott Motte Date: Fri, 10 Apr 2026 18:41:58 -0700 Subject: [PATCH 07/17] add --- _includes/v1/components/three-mace-rect.html | 53 ++++++++++++++++---- 1 file changed, 42 insertions(+), 11 deletions(-) diff --git a/_includes/v1/components/three-mace-rect.html b/_includes/v1/components/three-mace-rect.html index 24e8389..cf5e657 100644 --- a/_includes/v1/components/three-mace-rect.html +++ b/_includes/v1/components/three-mace-rect.html @@ -165,16 +165,19 @@ return mat; }; - const createBox = ({ position = [0, 0.74, 0] } = {}) => { + const createBox = ({ + position = [0, 0.74, 0], + labels = { px: 's1', nx: 's2', py: 't', ny: 'b', pz: 's3', nz: 's4' } + } = {}) => { const roughnessOffset = (Math.random() - 0.5) * 0.02; const thicknessOffset = (Math.random() - 0.5) * 0.18; const mesh = new THREE.Mesh(boxGeometry, [ - createFaceMaterialVariant('s1', roughnessOffset, thicknessOffset, 'side'), - createFaceMaterialVariant('s2', roughnessOffset, thicknessOffset, 'side'), - createFaceMaterialVariant('t', roughnessOffset, thicknessOffset, 'top'), - createFaceMaterialVariant('b', roughnessOffset, thicknessOffset, 'bottom'), - createFaceMaterialVariant('s3', roughnessOffset, thicknessOffset, 'side'), - createFaceMaterialVariant('s4', roughnessOffset, thicknessOffset, 'side') + createFaceMaterialVariant(labels.px, roughnessOffset, thicknessOffset, 'side'), + createFaceMaterialVariant(labels.nx, roughnessOffset, thicknessOffset, 'side'), + createFaceMaterialVariant(labels.py, roughnessOffset, thicknessOffset, 'top'), + createFaceMaterialVariant(labels.ny, roughnessOffset, thicknessOffset, 'bottom'), + createFaceMaterialVariant(labels.pz, roughnessOffset, thicknessOffset, 'side'), + createFaceMaterialVariant(labels.nz, roughnessOffset, thicknessOffset, 'side') ]); mesh.position.set(position[0], position[1], position[2]); const edgeLines = new THREE.LineSegments(edgeGeometry, edgeHighlightMaterial); @@ -191,17 +194,38 @@ const pitch = 0.125; const yLift = 0.152; const baseY = 0.28; + let blockCounter = 0; + + const toBlockId = (index) => { + let n = index; + let out = ''; + do { + out = String.fromCharCode(65 + (n % 26)) + out; + n = Math.floor(n / 26) - 1; + } while (n >= 0); + return out; + }; const addStack = (x, z, height) => { for (let level = 0; level < height; level += 1) { - const box = createBox({ position: [x * pitch, baseY + level * yLift, z * pitch] }); + const id = toBlockId(blockCounter); + blockCounter += 1; + const box = createBox({ + position: [x * pitch, baseY + level * yLift, z * pitch], + labels: { px: id, nx: id, py: id, ny: id, pz: id, nz: id } + }); maceGroup.add(box); } }; const addStackFromLevel = (x, z, startLevel, height) => { for (let level = 0; level < height; level += 1) { - const box = createBox({ position: [x * pitch, baseY + (startLevel + level) * yLift, z * pitch] }); + const id = toBlockId(blockCounter); + blockCounter += 1; + const box = createBox({ + position: [x * pitch, baseY + (startLevel + level) * yLift, z * pitch], + labels: { px: id, nx: id, py: id, ny: id, pz: id, nz: id } + }); maceGroup.add(box); } }; @@ -216,7 +240,7 @@ // Top-down mace based on the sketched block map (23 rows tall). const maceRows = [ - '00100', + '01110', '01110', '01110', '11111', @@ -251,9 +275,16 @@ } } + // In the 90-degree shaft-roll view, these add the requested side pairs + // at two shaft positions (4 total blocks). + addStackFromLevel(0, 15, -1, 1); + addStackFromLevel(0, 15, 1, 1); + addStackFromLevel(0, 21, -1, 1); + addStackFromLevel(0, 21, 1, 1); + maceGroup.rotation.x = 0; maceGroup.rotation.y = 0; - maceGroup.rotation.z = 0; + maceGroup.rotation.z = Math.PI / 2; const maceBaseY = maceGroup.position.y; scene.add(maceGroup); From bace7da84cc0422846106f88420b79832474c6d7 Mon Sep 17 00:00:00 2001 From: Scott Motte Date: Fri, 10 Apr 2026 18:50:40 -0700 Subject: [PATCH 08/17] mace --- _includes/v1/components/three-mace-rect.html | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/_includes/v1/components/three-mace-rect.html b/_includes/v1/components/three-mace-rect.html index cf5e657..a842fbd 100644 --- a/_includes/v1/components/three-mace-rect.html +++ b/_includes/v1/components/three-mace-rect.html @@ -276,7 +276,9 @@ } // In the 90-degree shaft-roll view, these add the requested side pairs - // at two shaft positions (4 total blocks). + // at three shaft positions. + addStackFromLevel(0, 4, -1, 1); + addStackFromLevel(0, 4, 1, 1); addStackFromLevel(0, 15, -1, 1); addStackFromLevel(0, 15, 1, 1); addStackFromLevel(0, 21, -1, 1); From 06be0ff0cfe7009428eab611569932a9e1905063 Mon Sep 17 00:00:00 2001 From: Scott Motte Date: Fri, 10 Apr 2026 18:56:05 -0700 Subject: [PATCH 09/17] mace --- _includes/v1/components/three-mace-rect.html | 15 +++++++++++++-- _includes/v1/components/three-standing-rect.html | 6 +----- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/_includes/v1/components/three-mace-rect.html b/_includes/v1/components/three-mace-rect.html index a842fbd..55c5304 100644 --- a/_includes/v1/components/three-mace-rect.html +++ b/_includes/v1/components/three-mace-rect.html @@ -275,10 +275,21 @@ } } - // In the 90-degree shaft-roll view, these add the requested side pairs - // at three shaft positions. + // In the 90-degree shaft-roll view, add side pairs at requested shaft positions. + addStackFromLevel(0, 1, -1, 1); + addStackFromLevel(0, 1, 1, 1); + addStackFromLevel(0, 2, -1, 1); + addStackFromLevel(0, 2, 1, 1); + addStackFromLevel(0, 3, -1, 1); + addStackFromLevel(0, 3, 1, 1); addStackFromLevel(0, 4, -1, 1); addStackFromLevel(0, 4, 1, 1); + addStackFromLevel(0, 4, -2, 1); + addStackFromLevel(0, 4, 2, 1); + addStackFromLevel(0, 5, -1, 1); + addStackFromLevel(0, 5, 1, 1); + addStackFromLevel(0, 6, -1, 1); + addStackFromLevel(0, 6, 1, 1); addStackFromLevel(0, 15, -1, 1); addStackFromLevel(0, 15, 1, 1); addStackFromLevel(0, 21, -1, 1); diff --git a/_includes/v1/components/three-standing-rect.html b/_includes/v1/components/three-standing-rect.html index 0d9e0f4..6ed6715 100644 --- a/_includes/v1/components/three-standing-rect.html +++ b/_includes/v1/components/three-standing-rect.html @@ -106,11 +106,7 @@ ctx.clearRect(0, 0, 128, 128); ctx.fillStyle = 'rgba(99, 104, 113, 0.24)'; ctx.fillRect(0, 0, 128, 128); - ctx.fillStyle = 'rgba(217, 191, 117, 0.82)'; - ctx.font = '500 16px sans-serif'; - ctx.textAlign = 'center'; - ctx.textBaseline = 'middle'; - ctx.fillText(label, 64, 64); + // Intentionally no face text labels on homepage key blocks. const texture = new THREE.CanvasTexture(c); texture.colorSpace = THREE.SRGBColorSpace; texture.needsUpdate = true; From 2804b9babe6a9168e6791a36c697807fc010a7da Mon Sep 17 00:00:00 2001 From: Scott Motte Date: Fri, 10 Apr 2026 19:02:42 -0700 Subject: [PATCH 10/17] mace --- ...standing-rect.html => three-key-rect.html} | 0 _includes/v1/components/three-mace-rect.html | 78 ++++++++++--------- banner.md | 2 +- index.md | 2 +- 4 files changed, 42 insertions(+), 40 deletions(-) rename _includes/v1/components/{three-standing-rect.html => three-key-rect.html} (100%) diff --git a/_includes/v1/components/three-standing-rect.html b/_includes/v1/components/three-key-rect.html similarity index 100% rename from _includes/v1/components/three-standing-rect.html rename to _includes/v1/components/three-key-rect.html diff --git a/_includes/v1/components/three-mace-rect.html b/_includes/v1/components/three-mace-rect.html index 55c5304..586ddb7 100644 --- a/_includes/v1/components/three-mace-rect.html +++ b/_includes/v1/components/three-mace-rect.html @@ -66,15 +66,15 @@ const boxGeometry = new THREE.BoxGeometry(0.12, 0.1662, 0.12); const edgeGeometry = new THREE.EdgesGeometry(boxGeometry); const edgeHighlightMaterial = new THREE.LineBasicMaterial({ - color: 0xdfe6ef, + color: 0xe6edf7, transparent: true, - opacity: 0.28, + opacity: 0.42, toneMapped: false }); const glowShellMaterial = new THREE.MeshBasicMaterial({ - color: 0xc7d8ea, + color: 0xd6e2f0, transparent: true, - opacity: 0.2, + opacity: 0.12, blending: THREE.AdditiveBlending, side: THREE.BackSide, depthWrite: false, @@ -96,13 +96,9 @@ c.height = 128; const ctx = c.getContext('2d'); ctx.clearRect(0, 0, 128, 128); - ctx.fillStyle = 'rgba(99, 104, 113, 0.24)'; + ctx.fillStyle = 'rgba(112, 122, 136, 0.16)'; ctx.fillRect(0, 0, 128, 128); - ctx.fillStyle = 'rgba(210, 220, 233, 0.84)'; - ctx.font = '500 16px sans-serif'; - ctx.textAlign = 'center'; - ctx.textBaseline = 'middle'; - ctx.fillText(label, 64, 64); + // Intentionally no face text labels on mace blocks. const texture = new THREE.CanvasTexture(c); texture.colorSpace = THREE.SRGBColorSpace; texture.needsUpdate = true; @@ -117,20 +113,20 @@ const getFaceMaterial = (label) => { if (!materialCache.has(label)) { materialCache.set(label, new THREE.MeshPhysicalMaterial({ - color: 0xf2f6fb, + color: 0xb7c2cf, transparent: true, - opacity: 0.72, + opacity: 0.94, map: getLabelTexture(label), - metalness: 0.0, - roughness: baseRoughness, - transmission: 0.88, - thickness: baseThickness, + metalness: 0.86, + roughness: 0.2, + transmission: 0.08, + thickness: 0.22, ior: 1.5, - clearcoat: 1.0, - clearcoatRoughness: 0.035, - attenuationColor: 0x90a4ba, + clearcoat: 0.65, + clearcoatRoughness: 0.08, + attenuationColor: 0x8f9aab, attenuationDistance: 2.2, - envMapIntensity: 2.05 + envMapIntensity: 2.35 })); } return materialCache.get(label); @@ -141,21 +137,23 @@ mat.roughness = THREE.MathUtils.clamp(baseRoughness + roughnessOffset, 0.006, 0.06); mat.thickness = THREE.MathUtils.clamp(baseThickness + thicknessOffset, 1.35, 1.9); if (faceRole === 'top') { - mat.opacity = 0.36; - mat.transmission = 0.92; - mat.roughness = Math.min(0.06, mat.roughness + 0.004); - mat.thickness = 0.24; - mat.clearcoatRoughness = 0.03; + mat.opacity = 0.96; + mat.transmission = 0.06; + mat.roughness = 0.16; + mat.thickness = 0.2; + mat.clearcoatRoughness = 0.06; } else if (faceRole === 'bottom') { - mat.opacity = 0.44; - mat.transmission = 0.8; - mat.thickness = 0.58; - mat.clearcoatRoughness = 0.05; + mat.opacity = 0.92; + mat.transmission = 0.05; + mat.roughness = 0.24; + mat.thickness = 0.2; + mat.clearcoatRoughness = 0.1; } else { - mat.opacity = 0.46; - mat.transmission = 0.9; - mat.thickness = 0.5; - mat.clearcoatRoughness = 0.032; + mat.opacity = 0.94; + mat.transmission = 0.07; + mat.roughness = 0.2; + mat.thickness = 0.22; + mat.clearcoatRoughness = 0.08; } mat.userData.baseOpacity = mat.opacity; mat.userData.baseEnvMapIntensity = mat.envMapIntensity; @@ -240,7 +238,7 @@ // Top-down mace based on the sketched block map (23 rows tall). const maceRows = [ - '01110', + '00100', '01110', '01110', '11111', @@ -284,8 +282,8 @@ addStackFromLevel(0, 3, 1, 1); addStackFromLevel(0, 4, -1, 1); addStackFromLevel(0, 4, 1, 1); - addStackFromLevel(0, 4, -2, 1); - addStackFromLevel(0, 4, 2, 1); + addStackFromLevel(0, 3, -2, 1); + addStackFromLevel(0, 3, 2, 1); addStackFromLevel(0, 5, -1, 1); addStackFromLevel(0, 5, 1, 1); addStackFromLevel(0, 6, -1, 1); @@ -316,10 +314,12 @@ controls.enableZoom = false; controls.enablePan = false; controls.enableRotate = false; - controls.autoRotate = false; - controls.autoRotateSpeed = 0; + controls.autoRotate = true; + controls.autoRotateSpeed = 0.22; controls.target.set(center.x, center.y, center.z); controls.update(); + const clock = new THREE.Clock(); + const shaftSpinSpeed = 0.14; // radians/sec const resize = () => { const width = root.clientWidth || 1; @@ -348,6 +348,7 @@ const fortifyMix = 1; const animate = () => { + const delta = clock.getDelta(); for (let i = 0; i < faceMaterials.length; i += 1) { const mat = faceMaterials[i]; mat.opacity = Math.min(0.74, mat.userData.baseOpacity * (1 + fortifyMix * 0.95)); @@ -359,6 +360,7 @@ glowShellMaterial.opacity = Math.min(0.42, baseGlowOpacity * (1 + fortifyMix * 1.4)); maceGroup.scale.setScalar(1); maceGroup.position.y = maceBaseY; + maceGroup.rotateZ(shaftSpinSpeed * delta); controls.update(); renderer.render(scene, camera); if (canvas.style.opacity !== '1') canvas.style.opacity = '1'; diff --git a/banner.md b/banner.md index 2fe13cc..7dfdf42 100644 --- a/banner.md +++ b/banner.md @@ -15,7 +15,7 @@ hide_nav: true
Get Started - {% include v1/components/three-standing-rect.html height="140px" class="!h-[140px]" zoom="2.2" %} + {% include v1/components/three-key-rect.html height="140px" class="!h-[140px]" zoom="2.2" %}
diff --git a/index.md b/index.md index b8a69fd..857fb1e 100644 --- a/index.md +++ b/index.md @@ -14,7 +14,7 @@ title: ""
- {% include v1/components/three-standing-rect.html height="170px" class="h-[170px] md:!h-[560px]" %} + {% include v1/components/three-key-rect.html height="170px" class="h-[170px] md:!h-[560px]" %}
From 905e7ed0e8ce5241cd37416fe80f236aef18af77 Mon Sep 17 00:00:00 2001 From: Scott Motte Date: Fri, 10 Apr 2026 20:52:08 -0700 Subject: [PATCH 11/17] buckler --- .../v1/components/three-buckler-rect.html | 324 ++++++++++++++++++ _includes/v1/components/three-mace-rect.html | 52 +-- ops/index.md | 2 +- 3 files changed, 335 insertions(+), 43 deletions(-) create mode 100644 _includes/v1/components/three-buckler-rect.html diff --git a/_includes/v1/components/three-buckler-rect.html b/_includes/v1/components/three-buckler-rect.html new file mode 100644 index 0000000..7d1e54f --- /dev/null +++ b/_includes/v1/components/three-buckler-rect.html @@ -0,0 +1,324 @@ +
+ +
+ + + + diff --git a/_includes/v1/components/three-mace-rect.html b/_includes/v1/components/three-mace-rect.html index 586ddb7..80ea6bc 100644 --- a/_includes/v1/components/three-mace-rect.html +++ b/_includes/v1/components/three-mace-rect.html @@ -163,19 +163,16 @@ return mat; }; - const createBox = ({ - position = [0, 0.74, 0], - labels = { px: 's1', nx: 's2', py: 't', ny: 'b', pz: 's3', nz: 's4' } - } = {}) => { + const createBox = ({ position = [0, 0.74, 0] } = {}) => { const roughnessOffset = (Math.random() - 0.5) * 0.02; const thicknessOffset = (Math.random() - 0.5) * 0.18; const mesh = new THREE.Mesh(boxGeometry, [ - createFaceMaterialVariant(labels.px, roughnessOffset, thicknessOffset, 'side'), - createFaceMaterialVariant(labels.nx, roughnessOffset, thicknessOffset, 'side'), - createFaceMaterialVariant(labels.py, roughnessOffset, thicknessOffset, 'top'), - createFaceMaterialVariant(labels.ny, roughnessOffset, thicknessOffset, 'bottom'), - createFaceMaterialVariant(labels.pz, roughnessOffset, thicknessOffset, 'side'), - createFaceMaterialVariant(labels.nz, roughnessOffset, thicknessOffset, 'side') + createFaceMaterialVariant('s1', roughnessOffset, thicknessOffset, 'side'), + createFaceMaterialVariant('s2', roughnessOffset, thicknessOffset, 'side'), + createFaceMaterialVariant('t', roughnessOffset, thicknessOffset, 'top'), + createFaceMaterialVariant('b', roughnessOffset, thicknessOffset, 'bottom'), + createFaceMaterialVariant('s3', roughnessOffset, thicknessOffset, 'side'), + createFaceMaterialVariant('s4', roughnessOffset, thicknessOffset, 'side') ]); mesh.position.set(position[0], position[1], position[2]); const edgeLines = new THREE.LineSegments(edgeGeometry, edgeHighlightMaterial); @@ -192,51 +189,22 @@ const pitch = 0.125; const yLift = 0.152; const baseY = 0.28; - let blockCounter = 0; - - const toBlockId = (index) => { - let n = index; - let out = ''; - do { - out = String.fromCharCode(65 + (n % 26)) + out; - n = Math.floor(n / 26) - 1; - } while (n >= 0); - return out; - }; const addStack = (x, z, height) => { for (let level = 0; level < height; level += 1) { - const id = toBlockId(blockCounter); - blockCounter += 1; - const box = createBox({ - position: [x * pitch, baseY + level * yLift, z * pitch], - labels: { px: id, nx: id, py: id, ny: id, pz: id, nz: id } - }); + const box = createBox({ position: [x * pitch, baseY + level * yLift, z * pitch] }); maceGroup.add(box); } }; const addStackFromLevel = (x, z, startLevel, height) => { for (let level = 0; level < height; level += 1) { - const id = toBlockId(blockCounter); - blockCounter += 1; - const box = createBox({ - position: [x * pitch, baseY + (startLevel + level) * yLift, z * pitch], - labels: { px: id, nx: id, py: id, ny: id, pz: id, nz: id } - }); + const box = createBox({ position: [x * pitch, baseY + (startLevel + level) * yLift, z * pitch] }); maceGroup.add(box); } }; - const addPrism = (xMin, xMax, zMin, zMax, height) => { - for (let x = xMin; x <= xMax; x += 1) { - for (let z = zMin; z <= zMax; z += 1) { - addStack(x, z, height); - } - } - }; - - // Top-down mace based on the sketched block map (23 rows tall). + // Mace block map (top-down footprint). const maceRows = [ '00100', '01110', diff --git a/ops/index.md b/ops/index.md index 0baa4ab..6d75b8e 100644 --- a/ops/index.md +++ b/ops/index.md @@ -13,7 +13,7 @@ title: "Dotenvx Ops"
- {% include v1/components/three-mace-rect.html height="170px" class="h-[170px] md:!h-[560px]" %} + {% include v1/components/three-buckler-rect.html height="170px" class="h-[170px] md:!h-[560px]" %} {% comment %}{% include v1/components/three-gauntlet-rect.html height="170px" class="h-[170px] md:!h-[560px]" %}{% endcomment %} {% comment %}{% include v1/components/three-fortress-gated-rect.html height="170px" class="h-[170px] md:!h-[560px]" %}{% endcomment %} {% comment %}{% include v1/components/three-fortress-rect.html height="170px" class="h-[170px] md:!h-[560px]" %}{% endcomment %} From d1f7db8efbbdc7fae71c4fb3b12ce4f4c222785b Mon Sep 17 00:00:00 2001 From: Scott Motte Date: Fri, 10 Apr 2026 21:12:59 -0700 Subject: [PATCH 12/17] combined --- .../components/three-buckler-mace-rect.html | 368 ++++++++++++++++++ .../v1/components/three-buckler-rect.html | 15 +- ops/index.md | 2 +- 3 files changed, 377 insertions(+), 8 deletions(-) create mode 100644 _includes/v1/components/three-buckler-mace-rect.html diff --git a/_includes/v1/components/three-buckler-mace-rect.html b/_includes/v1/components/three-buckler-mace-rect.html new file mode 100644 index 0000000..58c9d34 --- /dev/null +++ b/_includes/v1/components/three-buckler-mace-rect.html @@ -0,0 +1,368 @@ +
+ +
+ + + + diff --git a/_includes/v1/components/three-buckler-rect.html b/_includes/v1/components/three-buckler-rect.html index 7d1e54f..759836c 100644 --- a/_includes/v1/components/three-buckler-rect.html +++ b/_includes/v1/components/three-buckler-rect.html @@ -211,15 +211,13 @@ '11111111111', '11111111111', '11111111111', - '11111011111', - '11111011111', - '11100000111', - '11111011111', - '11111011111', - '11111011111', + '11111111111', + '11111111111', + '11111111111', + '11111111111', + '11111111111', '01111111110', '00111111100', - '00111111100', '00011111000', '00001110000', '00000100000' @@ -238,6 +236,9 @@ return out; }; const bucklerRows = upscaleRows(bucklerBaseRows, 2); + // Trim four center rows to compress overall height. + const cutStart = Math.floor(bucklerRows.length / 2) - 2; + bucklerRows.splice(cutStart, 2); for (let z = 0; z < bucklerRows.length; z += 1) { const row = bucklerRows[z]; diff --git a/ops/index.md b/ops/index.md index 6d75b8e..98da781 100644 --- a/ops/index.md +++ b/ops/index.md @@ -13,7 +13,7 @@ title: "Dotenvx Ops"
- {% include v1/components/three-buckler-rect.html height="170px" class="h-[170px] md:!h-[560px]" %} + {% include v1/components/three-buckler-mace-rect.html height="170px" class="h-[170px] md:!h-[560px]" %} {% comment %}{% include v1/components/three-gauntlet-rect.html height="170px" class="h-[170px] md:!h-[560px]" %}{% endcomment %} {% comment %}{% include v1/components/three-fortress-gated-rect.html height="170px" class="h-[170px] md:!h-[560px]" %}{% endcomment %} {% comment %}{% include v1/components/three-fortress-rect.html height="170px" class="h-[170px] md:!h-[560px]" %}{% endcomment %} From 6eeed4b9a3982dc248868bf8fb1268e588f75cf3 Mon Sep 17 00:00:00 2001 From: Scott Motte Date: Fri, 10 Apr 2026 21:52:54 -0700 Subject: [PATCH 13/17] buckler --- .../components/three-buckler-mace-rect.html | 65 ++++++++++++------- 1 file changed, 41 insertions(+), 24 deletions(-) diff --git a/_includes/v1/components/three-buckler-mace-rect.html b/_includes/v1/components/three-buckler-mace-rect.html index 58c9d34..4c89e46 100644 --- a/_includes/v1/components/three-buckler-mace-rect.html +++ b/_includes/v1/components/three-buckler-mace-rect.html @@ -36,7 +36,7 @@ const mobileMinFrustumSize = 0.95; const includeZoom = Number({{ include.zoom | default: 1 }}); const zoomFactor = Number.isFinite(includeZoom) && includeZoom > 0 ? includeZoom : 1; - const camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0.1, 100); + const camera = new THREE.PerspectiveCamera(36, 1, 0.1, 100); scene.background = null; renderer.setClearColor(0x000000, 0); @@ -242,6 +242,30 @@ } } + // Raised center cross (solid cubes on top of shield surface, no negative space). + // Vertical bar + for (let z = 2; z <= 9; z += 1) { + addStackFromLevel(0, z, 1, 1); + } + // Horizontal bar + for (let x = -3; x <= 3; x += 1) { + addStackFromLevel(x, 5, 1, 1); + } + + // Back-side bolt anchors (first two). + addStackFromLevel(-3, 3, -2, 2); + addStackFromLevel(3, 3, -2, 2); + addStackFromLevel(-3, 7, -2, 2); + addStackFromLevel(3, 7, -2, 2); + // Left-side handle bridge between top and bottom protrusions. + addStackFromLevel(-3, 4, -2, 1); + addStackFromLevel(-3, 5, -2, 1); + addStackFromLevel(-3, 6, -2, 1); + // Right-side handle bridge between top and bottom protrusions. + addStackFromLevel(3, 4, -2, 1); + addStackFromLevel(3, 5, -2, 1); + addStackFromLevel(3, 6, -2, 1); + // Mace overlay. const maceGroup = new THREE.Group(); const maceRows = [ @@ -286,36 +310,35 @@ addStackToGroup(maceGroup, 0, 3, 1, -2); addStackToGroup(maceGroup, 0, 3, 1, 2); - // Raise and angle mace so it's clearly in front of the buckler. - maceGroup.position.set(0, yLift * 1.35, -0.02); - maceGroup.rotation.set(0, 0, 0.16); + // Composition pass: slight offset + stronger diagonal for a staged overlap. + maceGroup.position.set(0.14, yLift * 1.28, 0.08); + maceGroup.rotation.set(0, 0, 0.3); - // Locked top-down pose for precise editing. - bucklerGroup.rotation.x = 0; - bucklerGroup.rotation.y = 0; - bucklerGroup.rotation.z = 0; + // Flip to show the back side of the shield, with stronger tilt for depth. + bucklerGroup.rotation.x = Math.PI - THREE.MathUtils.degToRad(52); + bucklerGroup.rotation.y = Math.PI; + bucklerGroup.rotation.z = -0.04; const bucklerScale = 1; bucklerGroup.scale.setScalar(bucklerScale); const bucklerBaseY = bucklerGroup.position.y; scene.add(bucklerGroup); + maceGroup.visible = false; scene.add(maceGroup); const bounds = new THREE.Box3().setFromObject(bucklerGroup); - bounds.expandByObject(maceGroup); const center = new THREE.Vector3(); const size = new THREE.Vector3(); bounds.getCenter(center); bounds.getSize(size); - const topDownFrustumSize = Math.max(size.x, size.z) * 1.18; - - camera.position.set(center.x, center.y + 6, center.z + 0.0001); - camera.lookAt(center.x, center.y, center.z); + camera.position.set(center.x + 0.05, center.y + 1.65, center.z + 3.1); + camera.lookAt(center.x, center.y + 0.1, center.z + 0.05); const controls = new OrbitControls(camera, canvas); - controls.enableDamping = false; + controls.enableDamping = true; + controls.dampingFactor = 0.08; controls.enableZoom = false; controls.enablePan = false; - controls.enableRotate = false; + controls.enableRotate = true; controls.autoRotate = false; controls.autoRotateSpeed = 0; controls.target.set(center.x, center.y, center.z); @@ -325,15 +348,9 @@ const width = root.clientWidth || 1; const height = root.clientHeight || 1; const aspect = width / height; - const minFrustumSize = isMobileViewport() ? mobileMinFrustumSize : desktopMinFrustumSize; - const fitSize = topDownFrustumSize * (isMobileViewport() ? 0.9 : 1); - const baseFrustumSize = Math.max(minFrustumSize, fitSize); - const frustumSize = baseFrustumSize / zoomFactor; - - camera.left = -0.5 * frustumSize * aspect; - camera.right = 0.5 * frustumSize * aspect; - camera.top = 0.5 * frustumSize; - camera.bottom = -0.5 * frustumSize; + const baseFov = isMobileViewport() ? 41 : 36; + camera.fov = baseFov / zoomFactor; + camera.aspect = aspect; camera.updateProjectionMatrix(); renderer.setSize(width, height, false); }; From 2be12ce3ee97eceb992e39564bd5c9fb6f116976 Mon Sep 17 00:00:00 2001 From: Scott Motte Date: Fri, 10 Apr 2026 21:57:43 -0700 Subject: [PATCH 14/17] buckler --- .../v1/components/three-buckler-rect.html | 144 +++++++++++++----- 1 file changed, 102 insertions(+), 42 deletions(-) diff --git a/_includes/v1/components/three-buckler-rect.html b/_includes/v1/components/three-buckler-rect.html index 759836c..dbd8b2a 100644 --- a/_includes/v1/components/three-buckler-rect.html +++ b/_includes/v1/components/three-buckler-rect.html @@ -36,7 +36,7 @@ const mobileMinFrustumSize = 0.95; const includeZoom = Number({{ include.zoom | default: 1 }}); const zoomFactor = Number.isFinite(includeZoom) && includeZoom > 0 ? includeZoom : 1; - const camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0.1, 100); + const camera = new THREE.PerspectiveCamera(36, 1, 0.1, 100); scene.background = null; renderer.setClearColor(0x000000, 0); @@ -63,7 +63,7 @@ rimLight.position.set(-4, 2.5, -3); scene.add(rimLight); - const boxGeometry = new THREE.BoxGeometry(0.06, 0.0831, 0.06); + const boxGeometry = new THREE.BoxGeometry(0.12, 0.1662, 0.12); const edgeGeometry = new THREE.EdgesGeometry(boxGeometry); const edgeHighlightMaterial = new THREE.LineBasicMaterial({ color: 0xe6edf7, @@ -186,8 +186,8 @@ }; const bucklerGroup = new THREE.Group(); - const pitch = 0.0625; - const yLift = 0.076; + const pitch = 0.125; + const yLift = 0.152; const baseY = 0.28; const addStack = (x, z, height) => { @@ -204,8 +204,14 @@ } }; - // Buckler base map shaped like ⛨ (shield silhouette with cross cutout), - // then upscaled for higher detail density. + const addStackToGroup = (group, x, z, height, startLevel = 0) => { + for (let level = 0; level < height; level += 1) { + const box = createBox({ position: [x * pitch, baseY + (startLevel + level) * yLift, z * pitch] }); + group.add(box); + } + }; + + // Buckler base map. const bucklerBaseRows = [ '01111111110', '11111111111', @@ -223,22 +229,7 @@ '00000100000' ]; - const upscaleRows = (rows, scale = 2) => { - const out = []; - for (let r = 0; r < rows.length; r += 1) { - const src = rows[r]; - let expanded = ''; - for (let i = 0; i < src.length; i += 1) { - expanded += src[i].repeat(scale); - } - for (let k = 0; k < scale; k += 1) out.push(expanded); - } - return out; - }; - const bucklerRows = upscaleRows(bucklerBaseRows, 2); - // Trim four center rows to compress overall height. - const cutStart = Math.floor(bucklerRows.length / 2) - 2; - bucklerRows.splice(cutStart, 2); + const bucklerRows = [...bucklerBaseRows]; for (let z = 0; z < bucklerRows.length; z += 1) { const row = bucklerRows[z]; @@ -251,28 +242,103 @@ } } - // Locked top-down pose for precise editing. - bucklerGroup.rotation.x = 0; - bucklerGroup.rotation.y = 0; - bucklerGroup.rotation.z = 0; + // Raised center cross (solid cubes on top of shield surface, no negative space). + // Vertical bar + for (let z = 2; z <= 9; z += 1) { + addStackFromLevel(0, z, 1, 1); + } + // Horizontal bar + for (let x = -3; x <= 3; x += 1) { + addStackFromLevel(x, 5, 1, 1); + } + + // Back-side bolt anchors (first two). + addStackFromLevel(-3, 3, -2, 2); + addStackFromLevel(3, 3, -2, 2); + addStackFromLevel(-3, 7, -2, 2); + addStackFromLevel(3, 7, -2, 2); + // Left-side handle bridge between top and bottom protrusions. + addStackFromLevel(-3, 4, -2, 1); + addStackFromLevel(-3, 5, -2, 1); + addStackFromLevel(-3, 6, -2, 1); + // Right-side handle bridge between top and bottom protrusions. + addStackFromLevel(3, 4, -2, 1); + addStackFromLevel(3, 5, -2, 1); + addStackFromLevel(3, 6, -2, 1); + + // Mace overlay. + const maceGroup = new THREE.Group(); + const maceRows = [ + '00100', + '01110', + '01110', + '11111', + '01110', + '01110', + '01110', + '00100', + '00100', + '00100', + '00100', + '00100', + '00100', + '00100', + '00100', + '01110', + '00100', + '00100', + '00100', + '00100', + '00100', + '01110' + ]; + + for (let z = 0; z < maceRows.length; z += 1) { + const row = maceRows[z]; + for (let i = 0; i < row.length; i += 1) { + if (row[i] === '0') continue; + addStackToGroup(maceGroup, i - 2, z, 1); + } + } + + // Side pairs that define the mace silhouette in this top view. + [1, 2, 3, 4, 5, 6, 15, 21].forEach((z) => { + addStackToGroup(maceGroup, 0, z, 1, -1); + addStackToGroup(maceGroup, 0, z, 1, 1); + }); + // Wider pair at z3. + addStackToGroup(maceGroup, 0, 3, 1, -2); + addStackToGroup(maceGroup, 0, 3, 1, 2); + + // Composition pass: slight offset + stronger diagonal for a staged overlap. + maceGroup.position.set(0.14, yLift * 1.28, 0.08); + maceGroup.rotation.set(0, 0, 0.3); + + // Flip to show the back side of the shield, with stronger tilt for depth. + bucklerGroup.rotation.x = Math.PI - THREE.MathUtils.degToRad(52); + bucklerGroup.rotation.y = Math.PI; + bucklerGroup.rotation.z = -0.04; + const bucklerScale = 1; + bucklerGroup.scale.setScalar(bucklerScale); const bucklerBaseY = bucklerGroup.position.y; scene.add(bucklerGroup); + maceGroup.visible = false; + scene.add(maceGroup); const bounds = new THREE.Box3().setFromObject(bucklerGroup); const center = new THREE.Vector3(); const size = new THREE.Vector3(); bounds.getCenter(center); bounds.getSize(size); - const topDownFrustumSize = Math.max(size.x, size.z) * 1.18; - - camera.position.set(center.x, center.y + 6, center.z + 0.0001); - camera.lookAt(center.x, center.y, center.z); + camera.position.set(center.x + 0.05, center.y + 1.65, center.z + 3.1); + camera.lookAt(center.x, center.y + 0.1, center.z + 0.05); const controls = new OrbitControls(camera, canvas); - controls.enableDamping = false; + controls.enableDamping = true; + controls.dampingFactor = 0.08; controls.enableZoom = false; controls.enablePan = false; - controls.enableRotate = false; + controls.enableRotate = true; controls.autoRotate = false; controls.autoRotateSpeed = 0; controls.target.set(center.x, center.y, center.z); @@ -282,15 +348,9 @@ const width = root.clientWidth || 1; const height = root.clientHeight || 1; const aspect = width / height; - const minFrustumSize = isMobileViewport() ? mobileMinFrustumSize : desktopMinFrustumSize; - const fitSize = topDownFrustumSize * (isMobileViewport() ? 0.9 : 1); - const baseFrustumSize = Math.max(minFrustumSize, fitSize); - const frustumSize = baseFrustumSize / zoomFactor; - - camera.left = -0.5 * frustumSize * aspect; - camera.right = 0.5 * frustumSize * aspect; - camera.top = 0.5 * frustumSize; - camera.bottom = -0.5 * frustumSize; + const baseFov = isMobileViewport() ? 41 : 36; + camera.fov = baseFov / zoomFactor; + camera.aspect = aspect; camera.updateProjectionMatrix(); renderer.setSize(width, height, false); }; @@ -314,7 +374,7 @@ } edgeHighlightMaterial.opacity = Math.min(0.62, baseEdgeOpacity * (1 + fortifyMix * 2.1)); glowShellMaterial.opacity = Math.min(0.42, baseGlowOpacity * (1 + fortifyMix * 1.4)); - bucklerGroup.scale.setScalar(1); + bucklerGroup.scale.setScalar(bucklerScale); bucklerGroup.position.y = bucklerBaseY; controls.update(); renderer.render(scene, camera); From 51fe56f0924640d241e22f5a8124be238eee3e76 Mon Sep 17 00:00:00 2001 From: Scott Motte Date: Sat, 11 Apr 2026 00:11:48 -0700 Subject: [PATCH 15/17] buckler and mace --- .../components/three-buckler-mace-rect.html | 479 +++++++++++++++++- 1 file changed, 463 insertions(+), 16 deletions(-) diff --git a/_includes/v1/components/three-buckler-mace-rect.html b/_includes/v1/components/three-buckler-mace-rect.html index 4c89e46..31e5730 100644 --- a/_includes/v1/components/three-buckler-mace-rect.html +++ b/_includes/v1/components/three-buckler-mace-rect.html @@ -7,6 +7,60 @@ class="block h-full w-full" style="opacity: 0; -webkit-mask-image: radial-gradient(120% 112% at 50% 50%, rgba(0,0,0,1) 62%, rgba(0,0,0,0.92) 74%, rgba(0,0,0,0.48) 85%, rgba(0,0,0,0.1) 93%, rgba(0,0,0,0) 100%); mask-image: radial-gradient(120% 112% at 50% 50%, rgba(0,0,0,1) 62%, rgba(0,0,0,0.92) 74%, rgba(0,0,0,0.48) 85%, rgba(0,0,0,0.1) 93%, rgba(0,0,0,0) 100%);" > +