summaryrefslogtreecommitdiff
path: root/internal/generator/theme_ocean.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/generator/theme_ocean.go')
-rw-r--r--internal/generator/theme_ocean.go164
1 files changed, 131 insertions, 33 deletions
diff --git a/internal/generator/theme_ocean.go b/internal/generator/theme_ocean.go
index 12b2151..af4c80b 100644
--- a/internal/generator/theme_ocean.go
+++ b/internal/generator/theme_ocean.go
@@ -1,7 +1,8 @@
package generator
-// oceanTemplate is a deep-ocean theme — dark navy/midnight blue background,
-// WebGL animated wave surface with per-vertex sine displacement, teal/aqua accents.
+// oceanTemplate is a deep-ocean theme — dramatic animated wave surface with
+// sea rock formations, bioluminescent jellyfish, rising bubble particles,
+// and a whale silhouette cruising through the depths.
const oceanTemplate = `<!DOCTYPE html>
<html lang="en">
<head>
@@ -51,11 +52,10 @@ const oceanTemplate = `<!DOCTYPE html>
.post-text a:hover { text-shadow:0 0 8px var(--teal); }
.post-audio { width:100%; margin-top:10px; }
.post-modal { display:none; position:fixed; inset:0; z-index:100;
- background:rgba(3,4,94,0.96); backdrop-filter:blur(20px);
overflow-y:auto; padding:40px 20px; }
.post-modal.active { display:block; }
- .modal-inner { max-width:760px; margin:0 auto; background:rgba(2,30,80,0.98);
- border:1px solid var(--teal); border-radius:12px;
+ .modal-inner { max-width:760px; margin:0 auto; background:rgba(2,30,80,0.92);
+ border:1px solid var(--teal); border-radius:12px; backdrop-filter:blur(16px);
box-shadow:0 0 60px rgba(0,180,216,0.3); padding:40px; }
.modal-close { float:right; background:none; border:none; color:var(--teal);
font-size:0.9rem; cursor:pointer; letter-spacing:1px; }
@@ -94,19 +94,96 @@ const oceanTemplate = `<!DOCTYPE html>
</div>
{{template "navmodal" .}}
<script>
- // Ocean WebGL: a large PlaneGeometry wave surface whose vertices are displaced
- // each frame by two overlapping sine functions, lit by a moving teal point light.
+ // Ocean WebGL: dramatic wave surface + sea rock spires + bioluminescent
+ // jellyfish + rising bubbles + a slow whale cruising the deep.
(function() {
var scene, camera, renderer, clock;
- var waveMesh, waveGeo, pointLight;
+ var waveGeo, waveMesh, sunLight;
+ var whale, jellyfish = [];
+ var BUBBLE_COUNT = 600;
+ var bubblePos, bubbleVY;
+
+ function buildWaves() {
+ // High-density plane for smooth vertex displacement
+ waveGeo = new THREE.PlaneGeometry(300, 300, 100, 100);
+ waveMesh = new THREE.Mesh(waveGeo, new THREE.MeshPhongMaterial({
+ color: 0x0077b6, emissive: 0x023e8a, emissiveIntensity: 0.25,
+ transparent: true, opacity: 0.88, side: THREE.DoubleSide, shininess: 80
+ }));
+ waveMesh.rotation.x = -Math.PI / 2;
+ waveMesh.position.y = 0;
+ scene.add(waveMesh);
+ }
+
+ function buildRocks() {
+ // 5 jagged sea rock spires poking above the wave baseline
+ var rockPositions = [[-30,0,-30],[20,-2,-20],[-15,2,-45],[35,-1,-35],[-45,1,-25]];
+ rockPositions.forEach(function(p) {
+ var h = 8 + Math.random() * 10;
+ var rock = new THREE.Mesh(
+ new THREE.ConeGeometry(2 + Math.random(), h, 6),
+ new THREE.MeshPhongMaterial({ color: 0x023e8a, emissive: 0x00b4d8, emissiveIntensity: 0.15 })
+ );
+ rock.position.set(p[0], p[1] + h / 2 - 3, p[2]);
+ scene.add(rock);
+ });
+ }
+
+ function buildJellyfish() {
+ // Bioluminescent jellyfish: torus body + cone cap, additive blending
+ var jPos = [[-12, 6,-15],[18,10,-22],[-25,4,-18],[8,8,-30]];
+ jPos.forEach(function(p) {
+ var body = new THREE.Mesh(
+ new THREE.TorusGeometry(2.2, 0.5, 12, 24),
+ new THREE.MeshBasicMaterial({ color: 0x48cae4, transparent: true, opacity: 0.5, blending: THREE.AdditiveBlending, depthWrite: false })
+ );
+ var cap = new THREE.Mesh(
+ new THREE.SphereGeometry(2.2, 12, 8, 0, Math.PI * 2, 0, Math.PI / 2),
+ new THREE.MeshBasicMaterial({ color: 0x00b4d8, transparent: true, opacity: 0.35, blending: THREE.AdditiveBlending, depthWrite: false, side: THREE.DoubleSide })
+ );
+ cap.position.y = 0.5;
+ body.add(cap);
+ body.position.set(p[0], p[1], p[2]);
+ jellyfish.push({ mesh: body, baseY: p[1], phase: Math.random() * Math.PI * 2 });
+ scene.add(body);
+ });
+ }
+
+ function buildWhale() {
+ // Dark elongated flattened sphere — whale silhouette in the deep
+ var geo = new THREE.SphereGeometry(1, 16, 8);
+ whale = new THREE.Mesh(geo, new THREE.MeshBasicMaterial({ color: 0x011f40, transparent: true, opacity: 0.7 }));
+ whale.scale.set(12, 3, 5);
+ whale.position.set(-60, -8, -20);
+ scene.add(whale);
+ }
+
+ function buildBubbles() {
+ bubblePos = new Float32Array(BUBBLE_COUNT * 3);
+ bubbleVY = new Float32Array(BUBBLE_COUNT);
+ for (var i = 0; i < BUBBLE_COUNT; i++) {
+ bubblePos[i*3] = (Math.random() - 0.5) * 100;
+ bubblePos[i*3+1] = -15 - Math.random() * 15;
+ bubblePos[i*3+2] = (Math.random() - 0.5) * 60 - 10;
+ bubbleVY[i] = 0.04 + Math.random() * 0.06;
+ }
+ var geo = new THREE.BufferGeometry();
+ geo.setAttribute('position', new THREE.BufferAttribute(bubblePos, 3));
+ scene.add(new THREE.Points(geo, new THREE.PointsMaterial({
+ color: 0xcaf0f8, size: 0.18, transparent: true, opacity: 0.6
+ })));
+ return geo;
+ }
+
+ var bubbleGeo;
function initThree() {
scene = new THREE.Scene();
scene.background = new THREE.Color(0x03045e);
- scene.fog = new THREE.Fog(0x03045e, 30, 120);
+ scene.fog = new THREE.Fog(0x03045e, 40, 130);
- camera = new THREE.PerspectiveCamera(60, window.innerWidth/window.innerHeight, 0.1, 200);
- camera.position.set(0, 25, 50);
+ camera = new THREE.PerspectiveCamera(60, window.innerWidth/window.innerHeight, 0.1, 220);
+ camera.position.set(0, 20, 55);
camera.lookAt(0, 0, 0);
renderer = new THREE.WebGLRenderer({ canvas: document.getElementById('three-canvas'), antialias: true });
@@ -114,21 +191,19 @@ const oceanTemplate = `<!DOCTYPE html>
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
clock = new THREE.Clock();
- // Wave surface — high segment count so vertex displacement looks smooth
- waveGeo = new THREE.PlaneGeometry(200, 200, 80, 80);
- waveMesh = new THREE.Mesh(waveGeo, new THREE.MeshPhongMaterial({
- color: 0x0077b6, emissive: 0x023e8a, emissiveIntensity: 0.3,
- transparent: true, opacity: 0.85, side: THREE.DoubleSide
- }));
- waveMesh.rotation.x = -Math.PI / 2;
- waveMesh.position.y = -5;
- scene.add(waveMesh);
+ scene.add(new THREE.AmbientLight(0x023e8a, 0.5));
+ sunLight = new THREE.PointLight(0x48cae4, 2.5, 100);
+ sunLight.position.set(0, 30, 10);
+ scene.add(sunLight);
+ var deepLight = new THREE.PointLight(0x0077b6, 1.5, 60);
+ deepLight.position.set(0, -10, 0);
+ scene.add(deepLight);
- // Moving teal light circling above the wave
- pointLight = new THREE.PointLight(0x48cae4, 2, 80);
- pointLight.position.set(0, 20, 10);
- scene.add(pointLight);
- scene.add(new THREE.AmbientLight(0x023e8a, 0.6));
+ buildWaves();
+ buildRocks();
+ buildJellyfish();
+ buildWhale();
+ bubbleGeo = buildBubbles();
window.addEventListener('resize', onResize);
animate();
@@ -145,21 +220,44 @@ const oceanTemplate = `<!DOCTYPE html>
var t = clock.getElapsedTime();
var pos = waveGeo.attributes.position;
- // Two overlapping sine waves produce realistic ocean surface chop
+ // Dramatic overlapping waves — larger amplitude than before
for (var i = 0; i < pos.count; i++) {
- var x = pos.getX(i);
- var z = pos.getZ(i);
+ var x = pos.getX(i), z = pos.getZ(i);
pos.setY(i,
- Math.sin(x * 0.05 + t * 1.2) * 1.8 +
- Math.cos(z * 0.07 + t * 0.9) * 1.4
+ Math.sin(x * 0.04 + t * 1.1) * 3.2 +
+ Math.cos(z * 0.06 + t * 0.85) * 2.4 +
+ Math.sin((x + z) * 0.025 + t * 0.6) * 1.5
);
}
pos.needsUpdate = true;
waveGeo.computeVertexNormals();
- // Light orbits lazily
- pointLight.position.x = Math.cos(t * 0.3) * 30;
- pointLight.position.z = Math.sin(t * 0.3) * 30;
+ // Jellyfish bob and slowly drift horizontally
+ jellyfish.forEach(function(j) {
+ j.mesh.position.y = j.baseY + Math.sin(t * 0.8 + j.phase) * 1.2;
+ j.mesh.position.x += 0.005 * Math.sin(t * 0.3 + j.phase);
+ j.mesh.rotation.y += 0.006;
+ });
+
+ // Whale cruises across at depth, wraps around
+ whale.position.x += 0.04;
+ if (whale.position.x > 80) whale.position.x = -80;
+ whale.position.y = -8 + Math.sin(t * 0.15) * 2;
+
+ // Rising bubbles — reset when they reach the surface
+ var bp = bubbleGeo.attributes.position;
+ for (var bi = 0; bi < BUBBLE_COUNT; bi++) {
+ bubblePos[bi*3+1] += bubbleVY[bi];
+ if (bubblePos[bi*3+1] > 8) {
+ bubblePos[bi*3] = (Math.random() - 0.5) * 100;
+ bubblePos[bi*3+1] = -15 - Math.random() * 10;
+ }
+ }
+ bp.needsUpdate = true;
+
+ // Sunlight orbits above
+ sunLight.position.x = Math.cos(t * 0.2) * 35;
+ sunLight.position.z = Math.sin(t * 0.2) * 35;
renderer.render(scene, camera);
}