synabun 1.3.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.md +665 -0
- package/LICENSE +191 -0
- package/README.md +853 -0
- package/hooks/claude-code/post-plan.mjs +391 -0
- package/hooks/claude-code/post-remember.mjs +239 -0
- package/hooks/claude-code/pre-compact.mjs +241 -0
- package/hooks/claude-code/pre-websearch.mjs +90 -0
- package/hooks/claude-code/prompt-submit.mjs +861 -0
- package/hooks/claude-code/session-start.mjs +494 -0
- package/hooks/claude-code/shared.mjs +368 -0
- package/hooks/claude-code/stop.mjs +646 -0
- package/hooks/claude-code/test-user-learning.mjs +531 -0
- package/mcp-server/package.json +38 -0
- package/mcp-server/run.mjs +10 -0
- package/mcp-server/src/config.ts +70 -0
- package/mcp-server/src/http.ts +93 -0
- package/mcp-server/src/index.ts +203 -0
- package/mcp-server/src/preload-http.ts +25 -0
- package/mcp-server/src/preload.ts +29 -0
- package/mcp-server/src/scripts/migrate-qdrant-to-sqlite.ts +506 -0
- package/mcp-server/src/services/categories.ts +429 -0
- package/mcp-server/src/services/discord.ts +702 -0
- package/mcp-server/src/services/file-checksums.ts +33 -0
- package/mcp-server/src/services/local-embeddings.ts +77 -0
- package/mcp-server/src/services/neural-interface.ts +344 -0
- package/mcp-server/src/services/sqlite.ts +823 -0
- package/mcp-server/src/tools/browser-advanced.ts +123 -0
- package/mcp-server/src/tools/browser-interact.ts +255 -0
- package/mcp-server/src/tools/browser-navigate.ts +77 -0
- package/mcp-server/src/tools/browser-observe.ts +1330 -0
- package/mcp-server/src/tools/browser.ts +107 -0
- package/mcp-server/src/tools/card-tools.ts +193 -0
- package/mcp-server/src/tools/card.ts +22 -0
- package/mcp-server/src/tools/category.ts +379 -0
- package/mcp-server/src/tools/discord-channel.ts +274 -0
- package/mcp-server/src/tools/discord-guild.ts +188 -0
- package/mcp-server/src/tools/discord-member.ts +158 -0
- package/mcp-server/src/tools/discord-message.ts +231 -0
- package/mcp-server/src/tools/discord-onboarding.ts +228 -0
- package/mcp-server/src/tools/discord-role.ts +209 -0
- package/mcp-server/src/tools/discord-thread.ts +110 -0
- package/mcp-server/src/tools/discord-webhook.ts +176 -0
- package/mcp-server/src/tools/discord.ts +40 -0
- package/mcp-server/src/tools/forget.ts +38 -0
- package/mcp-server/src/tools/git-tools.ts +141 -0
- package/mcp-server/src/tools/git.ts +13 -0
- package/mcp-server/src/tools/image-tools.ts +98 -0
- package/mcp-server/src/tools/image.ts +17 -0
- package/mcp-server/src/tools/leonardo-browser-tools.ts +281 -0
- package/mcp-server/src/tools/leonardo.ts +28 -0
- package/mcp-server/src/tools/loop.ts +348 -0
- package/mcp-server/src/tools/memories.ts +118 -0
- package/mcp-server/src/tools/recall.ts +323 -0
- package/mcp-server/src/tools/reflect.ts +145 -0
- package/mcp-server/src/tools/remember.ts +115 -0
- package/mcp-server/src/tools/response.ts +26 -0
- package/mcp-server/src/tools/restore.ts +34 -0
- package/mcp-server/src/tools/sync.ts +114 -0
- package/mcp-server/src/tools/tictactoe-tools.ts +98 -0
- package/mcp-server/src/tools/tictactoe.ts +13 -0
- package/mcp-server/src/tools/utils.ts +25 -0
- package/mcp-server/src/tools/whiteboard-tools.ts +260 -0
- package/mcp-server/src/tools/whiteboard.ts +22 -0
- package/mcp-server/src/tui.ts +940 -0
- package/mcp-server/src/types.ts +85 -0
- package/mcp-server/tsconfig.json +19 -0
- package/neural-interface/package.json +27 -0
- package/neural-interface/public/apple-touch-icon.png +0 -0
- package/neural-interface/public/category-logos/claude.svg +1 -0
- package/neural-interface/public/category-logos/gemini-color.svg +1 -0
- package/neural-interface/public/category-logos/openai.svg +1 -0
- package/neural-interface/public/category-logos/synabun.png +0 -0
- package/neural-interface/public/claude-chat.css +878 -0
- package/neural-interface/public/claude-chat.html +92 -0
- package/neural-interface/public/claude-chat.js +1168 -0
- package/neural-interface/public/favicon-16x16.png +0 -0
- package/neural-interface/public/favicon-32x32.png +0 -0
- package/neural-interface/public/gallery.svg +6 -0
- package/neural-interface/public/icon-192.png +0 -0
- package/neural-interface/public/icon-512.png +0 -0
- package/neural-interface/public/index-legacy.html +14677 -0
- package/neural-interface/public/index.html +212 -0
- package/neural-interface/public/index2d-legacy.html +8193 -0
- package/neural-interface/public/index2d.html +127 -0
- package/neural-interface/public/invite.html +351 -0
- package/neural-interface/public/leonardoai.svg +1 -0
- package/neural-interface/public/logo-synabun.png +0 -0
- package/neural-interface/public/logoHD.png +0 -0
- package/neural-interface/public/manifest.json +14 -0
- package/neural-interface/public/offline.html +299 -0
- package/neural-interface/public/onboarding.html +2335 -0
- package/neural-interface/public/openclaw-logo-text.png +0 -0
- package/neural-interface/public/pin-svgrepo-com.svg +25 -0
- package/neural-interface/public/sessionicon.svg +8 -0
- package/neural-interface/public/shared/ansi-parser.js +305 -0
- package/neural-interface/public/shared/api.js +1043 -0
- package/neural-interface/public/shared/colors.js +153 -0
- package/neural-interface/public/shared/constants.js +141 -0
- package/neural-interface/public/shared/html-shell.js +777 -0
- package/neural-interface/public/shared/html-term-renderer.js +1022 -0
- package/neural-interface/public/shared/i18n.js +147 -0
- package/neural-interface/public/shared/registry.js +82 -0
- package/neural-interface/public/shared/state.js +86 -0
- package/neural-interface/public/shared/storage.js +150 -0
- package/neural-interface/public/shared/styles.css +21212 -0
- package/neural-interface/public/shared/ui-automation-studio.js +3573 -0
- package/neural-interface/public/shared/ui-bookmarks.js +231 -0
- package/neural-interface/public/shared/ui-claude-panel.js +6333 -0
- package/neural-interface/public/shared/ui-command-runner.js +534 -0
- package/neural-interface/public/shared/ui-cost-widget.js +602 -0
- package/neural-interface/public/shared/ui-detail.js +1454 -0
- package/neural-interface/public/shared/ui-explorer.js +1356 -0
- package/neural-interface/public/shared/ui-file-explorer.js +2750 -0
- package/neural-interface/public/shared/ui-games.js +411 -0
- package/neural-interface/public/shared/ui-help.js +107 -0
- package/neural-interface/public/shared/ui-image-gallery.js +461 -0
- package/neural-interface/public/shared/ui-invite.js +519 -0
- package/neural-interface/public/shared/ui-keybinds.js +452 -0
- package/neural-interface/public/shared/ui-layouts.js +297 -0
- package/neural-interface/public/shared/ui-link.js +731 -0
- package/neural-interface/public/shared/ui-loading.js +255 -0
- package/neural-interface/public/shared/ui-menubar.js +499 -0
- package/neural-interface/public/shared/ui-multiselect.js +93 -0
- package/neural-interface/public/shared/ui-navbar.js +222 -0
- package/neural-interface/public/shared/ui-notifications.js +287 -0
- package/neural-interface/public/shared/ui-panels.js +309 -0
- package/neural-interface/public/shared/ui-resume.js +653 -0
- package/neural-interface/public/shared/ui-search.js +145 -0
- package/neural-interface/public/shared/ui-sessions.js +531 -0
- package/neural-interface/public/shared/ui-settings.js +5987 -0
- package/neural-interface/public/shared/ui-sidebar.js +1905 -0
- package/neural-interface/public/shared/ui-skills.js +2805 -0
- package/neural-interface/public/shared/ui-stats.js +38 -0
- package/neural-interface/public/shared/ui-sync.js +201 -0
- package/neural-interface/public/shared/ui-terminal.js +6385 -0
- package/neural-interface/public/shared/ui-tooltip.js +123 -0
- package/neural-interface/public/shared/ui-trash.js +439 -0
- package/neural-interface/public/shared/ui-tutorial-draw.js +875 -0
- package/neural-interface/public/shared/ui-tutorial-steps.js +2276 -0
- package/neural-interface/public/shared/ui-tutorial.js +1117 -0
- package/neural-interface/public/shared/ui-update.js +244 -0
- package/neural-interface/public/shared/ui-whiteboard.js +3262 -0
- package/neural-interface/public/shared/ui-workspaces.js +722 -0
- package/neural-interface/public/shared/utils.js +360 -0
- package/neural-interface/public/shared/vterm-buffer.js +1102 -0
- package/neural-interface/public/sw.js +52 -0
- package/neural-interface/public/synabun.png +0 -0
- package/neural-interface/public/toolbar-album.svg +8 -0
- package/neural-interface/public/toolbar-question.svg +6 -0
- package/neural-interface/public/toolbar-sessions.svg +6 -0
- package/neural-interface/public/variant/2d/background.js +113 -0
- package/neural-interface/public/variant/2d/context-menu.js +133 -0
- package/neural-interface/public/variant/2d/gfx.js +45 -0
- package/neural-interface/public/variant/2d/graph.js +1635 -0
- package/neural-interface/public/variant/2d/hulls.js +207 -0
- package/neural-interface/public/variant/2d/lasso.js +135 -0
- package/neural-interface/public/variant/2d/layout.js +594 -0
- package/neural-interface/public/variant/2d/main.js +521 -0
- package/neural-interface/public/variant/2d/minimap.js +312 -0
- package/neural-interface/public/variant/2d/settings-gfx.js +98 -0
- package/neural-interface/public/variant/3d/background.js +884 -0
- package/neural-interface/public/variant/3d/camera.js +661 -0
- package/neural-interface/public/variant/3d/gfx.js +116 -0
- package/neural-interface/public/variant/3d/graph.js +2399 -0
- package/neural-interface/public/variant/3d/main.js +500 -0
- package/neural-interface/public/variant/3d/settings-gfx.js +228 -0
- package/neural-interface/scripts/rebuild-pty.js +100 -0
- package/neural-interface/server.js +15000 -0
- package/neural-interface/templates/CLAUDE-template.md +211 -0
- package/package.json +53 -0
- package/postinstall.js +135 -0
- package/setup.js +260 -0
- package/skills/leonardo/SKILL.md +159 -0
- package/skills/leonardo/icon.svg +1 -0
- package/skills/leonardo/modules/browser-guide.md +378 -0
- package/skills/leonardo/modules/image-prompter.md +277 -0
- package/skills/leonardo/modules/model-advisor.md +100 -0
- package/skills/leonardo/modules/prompt-library.md +110 -0
- package/skills/leonardo/modules/style-guide.md +186 -0
- package/skills/leonardo/modules/video-prompter.md +311 -0
- package/skills/synabun/SKILL.md +185 -0
- package/skills/synabun/icon.png +0 -0
- package/skills/synabun/modules/audit.md +374 -0
- package/skills/synabun/modules/changelog.md +194 -0
- package/skills/synabun/modules/idea.md +144 -0
- package/skills/synabun/modules/memorize.md +188 -0
- package/skills/synabun/synabunicon.png +0 -0
|
@@ -0,0 +1,884 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════
|
|
2
|
+
// SynaBun Neural Interface — 3D Background
|
|
3
|
+
// Floor effects, deep-grid theme, sky dome,
|
|
4
|
+
// star fields, pillars, horizon glow
|
|
5
|
+
// ═══════════════════════════════════════════
|
|
6
|
+
|
|
7
|
+
import { gfx } from './gfx.js';
|
|
8
|
+
|
|
9
|
+
// ── Module state ──
|
|
10
|
+
let bgGrid = null;
|
|
11
|
+
let bgGridPillars = null;
|
|
12
|
+
let bgWorldParticles = null;
|
|
13
|
+
let bgGridFloor2 = null;
|
|
14
|
+
let bgSkyDome = null;
|
|
15
|
+
let _cloudPlanes = [];
|
|
16
|
+
let bgSkyParticles = null;
|
|
17
|
+
let _origControlsUpdate = null;
|
|
18
|
+
|
|
19
|
+
let floorMesh = null;
|
|
20
|
+
let floorParticles = null;
|
|
21
|
+
|
|
22
|
+
export const FLOOR_Y = -500;
|
|
23
|
+
|
|
24
|
+
// ═══════════════════════════════════════════
|
|
25
|
+
// FLOOR EFFECTS — Selectable floor styles
|
|
26
|
+
// ═══════════════════════════════════════════
|
|
27
|
+
|
|
28
|
+
function removeFloor(scene) {
|
|
29
|
+
if (floorMesh) {
|
|
30
|
+
scene.remove(floorMesh);
|
|
31
|
+
floorMesh.geometry.dispose();
|
|
32
|
+
floorMesh.material.dispose();
|
|
33
|
+
floorMesh = null;
|
|
34
|
+
}
|
|
35
|
+
if (floorParticles) {
|
|
36
|
+
scene.remove(floorParticles);
|
|
37
|
+
floorParticles.geometry.dispose();
|
|
38
|
+
floorParticles.material.dispose();
|
|
39
|
+
floorParticles = null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// ── Floor: Grid ──
|
|
44
|
+
function addFloorGrid(scene) {
|
|
45
|
+
const gridSize = 8000;
|
|
46
|
+
const gridGeo = new THREE.PlaneGeometry(gridSize, gridSize, 1, 1);
|
|
47
|
+
gridGeo.rotateX(-Math.PI / 2);
|
|
48
|
+
|
|
49
|
+
const gridMat = new THREE.ShaderMaterial({
|
|
50
|
+
transparent: true,
|
|
51
|
+
depthWrite: false,
|
|
52
|
+
side: THREE.DoubleSide,
|
|
53
|
+
uniforms: {
|
|
54
|
+
uTime: { value: 0 },
|
|
55
|
+
uCamY: { value: 300 },
|
|
56
|
+
},
|
|
57
|
+
vertexShader: `
|
|
58
|
+
varying vec3 vWorldPos;
|
|
59
|
+
void main() {
|
|
60
|
+
vWorldPos = (modelMatrix * vec4(position, 1.0)).xyz;
|
|
61
|
+
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
|
62
|
+
}
|
|
63
|
+
`,
|
|
64
|
+
fragmentShader: `
|
|
65
|
+
varying vec3 vWorldPos;
|
|
66
|
+
uniform float uTime;
|
|
67
|
+
uniform float uCamY;
|
|
68
|
+
|
|
69
|
+
float softLine(float coord, float spacing, float sharpness) {
|
|
70
|
+
float d = abs(mod(coord + spacing * 0.5, spacing) - spacing * 0.5);
|
|
71
|
+
// Combine gaussian glow with screen-space AA to kill edge aliasing
|
|
72
|
+
float fw = fwidth(coord / spacing) * spacing * 1.5;
|
|
73
|
+
float aa = 1.0 - smoothstep(0.0, max(fw, 0.5), d);
|
|
74
|
+
float glow = exp(-d * d * sharpness);
|
|
75
|
+
return max(aa * 0.6, glow);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
void main() {
|
|
79
|
+
float distFromCenter = length(vWorldPos.xz);
|
|
80
|
+
float fade = 1.0 - smoothstep(1200.0, 3800.0, distFromCenter);
|
|
81
|
+
|
|
82
|
+
// Sharpness decreases with distance — lines get softer/wider further out
|
|
83
|
+
float sharpness = mix(1.2, 0.15, smoothstep(0.0, 2500.0, distFromCenter));
|
|
84
|
+
|
|
85
|
+
float lx = softLine(vWorldPos.x, 200.0, sharpness);
|
|
86
|
+
float lz = softLine(vWorldPos.z, 200.0, sharpness);
|
|
87
|
+
float grid = max(lx, lz);
|
|
88
|
+
|
|
89
|
+
float dot = lx * lz;
|
|
90
|
+
float pulse = sin(uTime * 2.0 + vWorldPos.x * 0.04 + vWorldPos.z * 0.03) * 0.5 + 0.5;
|
|
91
|
+
float dotGlow = dot * (0.08 + pulse * 0.08);
|
|
92
|
+
|
|
93
|
+
vec3 col = vec3(1.0);
|
|
94
|
+
float alpha = (grid * 0.045 + dotGlow) * fade;
|
|
95
|
+
|
|
96
|
+
float camFade = smoothstep(0.0, 10.0, abs(uCamY - ${FLOOR_Y}.0));
|
|
97
|
+
alpha *= 0.4 + camFade * 0.6;
|
|
98
|
+
|
|
99
|
+
if (alpha < 0.002) discard;
|
|
100
|
+
gl_FragColor = vec4(col, alpha);
|
|
101
|
+
}
|
|
102
|
+
`,
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
floorMesh = new THREE.Mesh(gridGeo, gridMat);
|
|
106
|
+
floorMesh.position.y = FLOOR_Y;
|
|
107
|
+
floorMesh.renderOrder = -10;
|
|
108
|
+
scene.add(floorMesh);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// ── Floor: Dot Field with proximity glow ──
|
|
112
|
+
function addFloorDots(scene) {
|
|
113
|
+
const spacing = 40;
|
|
114
|
+
const extent = 3200;
|
|
115
|
+
const count = Math.pow(Math.floor(extent * 2 / spacing) + 1, 2);
|
|
116
|
+
const positions = new Float32Array(count * 3);
|
|
117
|
+
const phases = new Float32Array(count);
|
|
118
|
+
let idx = 0;
|
|
119
|
+
|
|
120
|
+
for (let x = -extent; x <= extent; x += spacing) {
|
|
121
|
+
for (let z = -extent; z <= extent; z += spacing) {
|
|
122
|
+
positions[idx * 3] = x + (Math.random() - 0.5) * 6;
|
|
123
|
+
positions[idx * 3 + 1] = FLOOR_Y;
|
|
124
|
+
positions[idx * 3 + 2] = z + (Math.random() - 0.5) * 6;
|
|
125
|
+
phases[idx] = Math.random() * Math.PI * 2;
|
|
126
|
+
idx++;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const geo = new THREE.BufferGeometry();
|
|
131
|
+
geo.setAttribute('position', new THREE.Float32BufferAttribute(positions.slice(0, idx * 3), 3));
|
|
132
|
+
geo.setAttribute('aPhase', new THREE.Float32BufferAttribute(phases.slice(0, idx), 1));
|
|
133
|
+
|
|
134
|
+
const mat = new THREE.ShaderMaterial({
|
|
135
|
+
transparent: true,
|
|
136
|
+
depthWrite: false,
|
|
137
|
+
blending: THREE.AdditiveBlending,
|
|
138
|
+
uniforms: {
|
|
139
|
+
uTime: { value: 0 },
|
|
140
|
+
uCamPos: { value: new THREE.Vector3(0, 300, 0) },
|
|
141
|
+
},
|
|
142
|
+
vertexShader: `
|
|
143
|
+
attribute float aPhase;
|
|
144
|
+
varying float vAlpha;
|
|
145
|
+
varying float vProximity;
|
|
146
|
+
varying float vWave;
|
|
147
|
+
uniform float uTime;
|
|
148
|
+
uniform vec3 uCamPos;
|
|
149
|
+
void main() {
|
|
150
|
+
vec4 worldPos = modelMatrix * vec4(position, 1.0);
|
|
151
|
+
float distFromCenter = length(worldPos.xz);
|
|
152
|
+
float fade = 1.0 - smoothstep(1500.0, 3200.0, distFromCenter);
|
|
153
|
+
|
|
154
|
+
// Proximity glow — wide, strong falloff around camera xz
|
|
155
|
+
float camDist = length(worldPos.xz - uCamPos.xz);
|
|
156
|
+
vProximity = exp(-camDist * camDist * 0.0000008);
|
|
157
|
+
|
|
158
|
+
// Ripple wave expanding outward from camera
|
|
159
|
+
float wave = sin(camDist * 0.015 - uTime * 2.5) * 0.5 + 0.5;
|
|
160
|
+
wave *= exp(-camDist * 0.0008);
|
|
161
|
+
vWave = wave;
|
|
162
|
+
|
|
163
|
+
float twinkle = 0.5 + 0.5 * sin(uTime * 2.0 + aPhase * 6.28);
|
|
164
|
+
vAlpha = fade * (0.15 + twinkle * 0.1 + vProximity * 0.55 + wave * 0.2);
|
|
165
|
+
|
|
166
|
+
vec4 mvPos = modelViewMatrix * vec4(position, 1.0);
|
|
167
|
+
float size = (3.0 + vProximity * 8.0 + wave * 3.0) * (300.0 / max(-mvPos.z, 1.0));
|
|
168
|
+
gl_PointSize = size;
|
|
169
|
+
gl_Position = projectionMatrix * mvPos;
|
|
170
|
+
}
|
|
171
|
+
`,
|
|
172
|
+
fragmentShader: `
|
|
173
|
+
varying float vAlpha;
|
|
174
|
+
varying float vProximity;
|
|
175
|
+
varying float vWave;
|
|
176
|
+
void main() {
|
|
177
|
+
float dist = length(gl_PointCoord - vec2(0.5));
|
|
178
|
+
if (dist > 0.5) discard;
|
|
179
|
+
float core = exp(-dist * dist * 16.0);
|
|
180
|
+
float glow = exp(-dist * dist * 4.0) * 0.5;
|
|
181
|
+
float alpha = (core + glow) * vAlpha;
|
|
182
|
+
vec3 baseCol = vec3(0.55, 0.62, 0.95);
|
|
183
|
+
vec3 hotCol = vec3(0.85, 0.9, 1.0);
|
|
184
|
+
vec3 col = mix(baseCol, hotCol, vProximity * 0.8 + vWave * 0.3);
|
|
185
|
+
gl_FragColor = vec4(col, alpha);
|
|
186
|
+
}
|
|
187
|
+
`,
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
floorParticles = new THREE.Points(geo, mat);
|
|
191
|
+
floorParticles.renderOrder = -10;
|
|
192
|
+
scene.add(floorParticles);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// ── Floor: Hex Grid ──
|
|
196
|
+
function addFloorHex(scene) {
|
|
197
|
+
const gridSize = 8000;
|
|
198
|
+
const gridGeo = new THREE.PlaneGeometry(gridSize, gridSize, 1, 1);
|
|
199
|
+
gridGeo.rotateX(-Math.PI / 2);
|
|
200
|
+
|
|
201
|
+
const hexMat = new THREE.ShaderMaterial({
|
|
202
|
+
transparent: true,
|
|
203
|
+
depthWrite: false,
|
|
204
|
+
side: THREE.DoubleSide,
|
|
205
|
+
uniforms: {
|
|
206
|
+
uTime: { value: 0 },
|
|
207
|
+
uCamY: { value: 300 },
|
|
208
|
+
},
|
|
209
|
+
vertexShader: `
|
|
210
|
+
varying vec3 vWorldPos;
|
|
211
|
+
void main() {
|
|
212
|
+
vWorldPos = (modelMatrix * vec4(position, 1.0)).xyz;
|
|
213
|
+
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
|
214
|
+
}
|
|
215
|
+
`,
|
|
216
|
+
fragmentShader: `
|
|
217
|
+
varying vec3 vWorldPos;
|
|
218
|
+
uniform float uTime;
|
|
219
|
+
uniform float uCamY;
|
|
220
|
+
|
|
221
|
+
// Hex distance field — returns distance to nearest hex edge
|
|
222
|
+
float hexDist(vec2 p) {
|
|
223
|
+
p = abs(p);
|
|
224
|
+
return max(dot(p, normalize(vec2(1.0, 1.732))), p.x);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
vec4 hexCoords(vec2 uv) {
|
|
228
|
+
vec2 r = vec2(1.0, 1.732);
|
|
229
|
+
vec2 h = r * 0.5;
|
|
230
|
+
vec2 a = mod(uv, r) - h;
|
|
231
|
+
vec2 b = mod(uv - h, r) - h;
|
|
232
|
+
vec2 gv = length(a) < length(b) ? a : b;
|
|
233
|
+
vec2 id = uv - gv;
|
|
234
|
+
return vec4(gv, id);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
void main() {
|
|
238
|
+
float distFromCenter = length(vWorldPos.xz);
|
|
239
|
+
float fade = 1.0 - smoothstep(1500.0, 3800.0, distFromCenter);
|
|
240
|
+
|
|
241
|
+
float scale = 220.0;
|
|
242
|
+
vec2 uv = vWorldPos.xz / scale;
|
|
243
|
+
vec4 hc = hexCoords(uv);
|
|
244
|
+
|
|
245
|
+
// Thin crisp edge — slightly thicker than 1px for clean reads at distance
|
|
246
|
+
float d = 0.5 - hexDist(hc.xy);
|
|
247
|
+
float fw = fwidth(d);
|
|
248
|
+
float edge = 1.0 - smoothstep(fw * 0.3, fw * 1.8, d);
|
|
249
|
+
|
|
250
|
+
vec3 col = vec3(0.7, 0.75, 0.95);
|
|
251
|
+
float alpha = edge * 0.07 * fade;
|
|
252
|
+
|
|
253
|
+
float camFade = smoothstep(0.0, 10.0, abs(uCamY - ${FLOOR_Y}.0));
|
|
254
|
+
alpha *= 0.4 + camFade * 0.6;
|
|
255
|
+
|
|
256
|
+
if (alpha < 0.002) discard;
|
|
257
|
+
gl_FragColor = vec4(col, alpha);
|
|
258
|
+
}
|
|
259
|
+
`,
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
floorMesh = new THREE.Mesh(gridGeo, hexMat);
|
|
263
|
+
floorMesh.position.y = FLOOR_Y;
|
|
264
|
+
floorMesh.renderOrder = -10;
|
|
265
|
+
scene.add(floorMesh);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// ── Floor: Concentric Ripples ──
|
|
269
|
+
function addFloorRipples(scene) {
|
|
270
|
+
const gridSize = 8000;
|
|
271
|
+
const gridGeo = new THREE.PlaneGeometry(gridSize, gridSize, 1, 1);
|
|
272
|
+
gridGeo.rotateX(-Math.PI / 2);
|
|
273
|
+
|
|
274
|
+
const rippleMat = new THREE.ShaderMaterial({
|
|
275
|
+
transparent: true,
|
|
276
|
+
depthWrite: false,
|
|
277
|
+
side: THREE.DoubleSide,
|
|
278
|
+
uniforms: {
|
|
279
|
+
uTime: { value: 0 },
|
|
280
|
+
uCamY: { value: 300 },
|
|
281
|
+
},
|
|
282
|
+
vertexShader: `
|
|
283
|
+
varying vec3 vWorldPos;
|
|
284
|
+
void main() {
|
|
285
|
+
vWorldPos = (modelMatrix * vec4(position, 1.0)).xyz;
|
|
286
|
+
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
|
287
|
+
}
|
|
288
|
+
`,
|
|
289
|
+
fragmentShader: `
|
|
290
|
+
varying vec3 vWorldPos;
|
|
291
|
+
uniform float uTime;
|
|
292
|
+
uniform float uCamY;
|
|
293
|
+
|
|
294
|
+
void main() {
|
|
295
|
+
float distFromCenter = length(vWorldPos.xz);
|
|
296
|
+
float fade = 1.0 - smoothstep(1200.0, 3800.0, distFromCenter);
|
|
297
|
+
|
|
298
|
+
// Concentric rings expanding outward
|
|
299
|
+
float ringSpacing = 80.0;
|
|
300
|
+
float scrollDist = distFromCenter - uTime * 15.0;
|
|
301
|
+
float ring = abs(mod(scrollDist, ringSpacing) - ringSpacing * 0.5);
|
|
302
|
+
float ringLine = exp(-ring * ring * 0.08);
|
|
303
|
+
|
|
304
|
+
// Static rings for structure
|
|
305
|
+
float staticRing = abs(mod(distFromCenter, ringSpacing * 2.5) - ringSpacing * 1.25);
|
|
306
|
+
float staticLine = exp(-staticRing * staticRing * 0.04);
|
|
307
|
+
|
|
308
|
+
// Radial spokes — very subtle
|
|
309
|
+
float angle = atan(vWorldPos.z, vWorldPos.x);
|
|
310
|
+
float spoke = exp(-pow(mod(angle + 3.1416, 0.3927) - 0.19635, 2.0) * 600.0);
|
|
311
|
+
float spokeFade = smoothstep(0.0, 400.0, distFromCenter) * 0.3;
|
|
312
|
+
|
|
313
|
+
vec3 col = vec3(1.0);
|
|
314
|
+
float alpha = (ringLine * 0.06 + staticLine * 0.03 + spoke * spokeFade * 0.04) * fade;
|
|
315
|
+
|
|
316
|
+
float camFade = smoothstep(0.0, 10.0, abs(uCamY - ${FLOOR_Y}.0));
|
|
317
|
+
alpha *= 0.4 + camFade * 0.6;
|
|
318
|
+
|
|
319
|
+
if (alpha < 0.002) discard;
|
|
320
|
+
gl_FragColor = vec4(col, alpha);
|
|
321
|
+
}
|
|
322
|
+
`,
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
floorMesh = new THREE.Mesh(gridGeo, rippleMat);
|
|
326
|
+
floorMesh.position.y = FLOOR_Y;
|
|
327
|
+
floorMesh.renderOrder = -10;
|
|
328
|
+
scene.add(floorMesh);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// ── Floor: Ground Fog Particles ──
|
|
332
|
+
function addFloorFog(scene) {
|
|
333
|
+
const count = 3000;
|
|
334
|
+
const positions = new Float32Array(count * 3);
|
|
335
|
+
const sizes = new Float32Array(count);
|
|
336
|
+
const alphas = new Float32Array(count);
|
|
337
|
+
const phases = new Float32Array(count);
|
|
338
|
+
|
|
339
|
+
for (let i = 0; i < count; i++) {
|
|
340
|
+
const angle = Math.random() * Math.PI * 2;
|
|
341
|
+
const radius = Math.random() * 3000;
|
|
342
|
+
positions[i * 3] = Math.cos(angle) * radius;
|
|
343
|
+
positions[i * 3 + 1] = FLOOR_Y + Math.random() * 60;
|
|
344
|
+
positions[i * 3 + 2] = Math.sin(angle) * radius;
|
|
345
|
+
sizes[i] = 8 + Math.random() * 25;
|
|
346
|
+
alphas[i] = 0.01 + Math.random() * 0.04;
|
|
347
|
+
phases[i] = Math.random() * Math.PI * 2;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
const geo = new THREE.BufferGeometry();
|
|
351
|
+
geo.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3));
|
|
352
|
+
geo.setAttribute('aSize', new THREE.Float32BufferAttribute(sizes, 1));
|
|
353
|
+
geo.setAttribute('aAlpha', new THREE.Float32BufferAttribute(alphas, 1));
|
|
354
|
+
geo.setAttribute('aPhase', new THREE.Float32BufferAttribute(phases, 1));
|
|
355
|
+
|
|
356
|
+
const mat = new THREE.ShaderMaterial({
|
|
357
|
+
transparent: true,
|
|
358
|
+
depthWrite: false,
|
|
359
|
+
blending: THREE.AdditiveBlending,
|
|
360
|
+
uniforms: { uTime: { value: 0 } },
|
|
361
|
+
vertexShader: `
|
|
362
|
+
attribute float aSize;
|
|
363
|
+
attribute float aAlpha;
|
|
364
|
+
attribute float aPhase;
|
|
365
|
+
varying float vAlpha;
|
|
366
|
+
uniform float uTime;
|
|
367
|
+
void main() {
|
|
368
|
+
vec3 pos = position;
|
|
369
|
+
float t = uTime * 0.15 + aPhase;
|
|
370
|
+
pos.x += sin(t * 0.7 + aPhase * 3.0) * 8.0;
|
|
371
|
+
pos.z += cos(t * 0.5 + aPhase * 2.0) * 8.0;
|
|
372
|
+
pos.y += sin(t) * 3.0;
|
|
373
|
+
|
|
374
|
+
vec4 mvPos = modelViewMatrix * vec4(pos, 1.0);
|
|
375
|
+
float camDist = length(mvPos.xyz);
|
|
376
|
+
float distFade = 1.0 - smoothstep(500.0, 3000.0, camDist);
|
|
377
|
+
float breathe = 0.6 + 0.4 * sin(uTime * 0.4 + aPhase * 6.28);
|
|
378
|
+
|
|
379
|
+
vAlpha = aAlpha * distFade * breathe;
|
|
380
|
+
gl_PointSize = aSize * (400.0 / max(-mvPos.z, 1.0));
|
|
381
|
+
gl_Position = projectionMatrix * mvPos;
|
|
382
|
+
}
|
|
383
|
+
`,
|
|
384
|
+
fragmentShader: `
|
|
385
|
+
varying float vAlpha;
|
|
386
|
+
void main() {
|
|
387
|
+
float dist = length(gl_PointCoord - vec2(0.5));
|
|
388
|
+
if (dist > 0.5) discard;
|
|
389
|
+
float soft = exp(-dist * dist * 8.0);
|
|
390
|
+
gl_FragColor = vec4(0.75, 0.78, 0.88, soft * vAlpha);
|
|
391
|
+
}
|
|
392
|
+
`,
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
floorParticles = new THREE.Points(geo, mat);
|
|
396
|
+
floorParticles.renderOrder = -10;
|
|
397
|
+
scene.add(floorParticles);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// ── Floor dispatcher ──
|
|
401
|
+
export function applyFloorStyle(style, graph) {
|
|
402
|
+
if (!graph) return;
|
|
403
|
+
const scene = graph.scene();
|
|
404
|
+
removeFloor(scene);
|
|
405
|
+
switch (style) {
|
|
406
|
+
case 'grid': addFloorGrid(scene); break;
|
|
407
|
+
case 'dots': addFloorDots(scene); break;
|
|
408
|
+
case 'hex': addFloorHex(scene); break;
|
|
409
|
+
case 'ripples': addFloorRipples(scene); break;
|
|
410
|
+
case 'fog': addFloorFog(scene); break;
|
|
411
|
+
case 'none': break;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// ═══════════════════════════════════════════
|
|
416
|
+
// BACKGROUND — Deep Grid theme
|
|
417
|
+
// Perspective grid floor + world-space particles + vertical pillars
|
|
418
|
+
// Gives true spatial depth and grounding
|
|
419
|
+
// ═══════════════════════════════════════════
|
|
420
|
+
function addBackgroundDeepGrid(graph) {
|
|
421
|
+
const scene = graph.scene();
|
|
422
|
+
|
|
423
|
+
// Floor is now handled by applyFloorStyle() — separate system
|
|
424
|
+
|
|
425
|
+
// ── Layer B: Vertical Accent Pillars ──
|
|
426
|
+
// Muted grey-blue, very subtle
|
|
427
|
+
const pillarCount = 90;
|
|
428
|
+
const pillarPositions = new Float32Array(pillarCount * 6);
|
|
429
|
+
const pillarAlphas = new Float32Array(pillarCount * 2);
|
|
430
|
+
const pillarPhases = new Float32Array(pillarCount * 2);
|
|
431
|
+
const pillarSpacing = 70;
|
|
432
|
+
const pillarSpread = 2000;
|
|
433
|
+
|
|
434
|
+
for (let i = 0; i < pillarCount; i++) {
|
|
435
|
+
const gx = (Math.floor(Math.random() * (pillarSpread / pillarSpacing) * 2) - pillarSpread / pillarSpacing) * pillarSpacing;
|
|
436
|
+
const gz = (Math.floor(Math.random() * (pillarSpread / pillarSpacing) * 2) - pillarSpread / pillarSpacing) * pillarSpacing;
|
|
437
|
+
const height = 30 + Math.random() * 120;
|
|
438
|
+
|
|
439
|
+
pillarPositions[i * 6] = gx;
|
|
440
|
+
pillarPositions[i * 6 + 1] = FLOOR_Y;
|
|
441
|
+
pillarPositions[i * 6 + 2] = gz;
|
|
442
|
+
pillarPositions[i * 6 + 3] = gx;
|
|
443
|
+
pillarPositions[i * 6 + 4] = FLOOR_Y + height;
|
|
444
|
+
pillarPositions[i * 6 + 5] = gz;
|
|
445
|
+
|
|
446
|
+
const a = 0.04 + Math.random() * 0.08;
|
|
447
|
+
const ph = Math.random() * Math.PI * 2;
|
|
448
|
+
pillarAlphas[i * 2] = a * 0.2;
|
|
449
|
+
pillarAlphas[i * 2 + 1] = a;
|
|
450
|
+
pillarPhases[i * 2] = ph;
|
|
451
|
+
pillarPhases[i * 2 + 1] = ph;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
const pillarGeo = new THREE.BufferGeometry();
|
|
455
|
+
pillarGeo.setAttribute('position', new THREE.Float32BufferAttribute(pillarPositions, 3));
|
|
456
|
+
pillarGeo.setAttribute('aAlpha', new THREE.Float32BufferAttribute(pillarAlphas, 1));
|
|
457
|
+
pillarGeo.setAttribute('aPhase', new THREE.Float32BufferAttribute(pillarPhases, 1));
|
|
458
|
+
|
|
459
|
+
const pillarMat = new THREE.ShaderMaterial({
|
|
460
|
+
transparent: true,
|
|
461
|
+
depthWrite: false,
|
|
462
|
+
blending: THREE.AdditiveBlending,
|
|
463
|
+
uniforms: { uTime: { value: 0 } },
|
|
464
|
+
vertexShader: `
|
|
465
|
+
attribute float aAlpha;
|
|
466
|
+
attribute float aPhase;
|
|
467
|
+
varying float vAlpha;
|
|
468
|
+
uniform float uTime;
|
|
469
|
+
void main() {
|
|
470
|
+
vec4 mvPos = modelViewMatrix * vec4(position, 1.0);
|
|
471
|
+
float camDist = length(mvPos.xyz);
|
|
472
|
+
float distFade = 1.0 - smoothstep(300.0, 1500.0, camDist);
|
|
473
|
+
vAlpha = aAlpha * (0.6 + 0.4 * sin(uTime * 0.3 + aPhase * 6.28)) * distFade;
|
|
474
|
+
gl_Position = projectionMatrix * mvPos;
|
|
475
|
+
}
|
|
476
|
+
`,
|
|
477
|
+
fragmentShader: `
|
|
478
|
+
varying float vAlpha;
|
|
479
|
+
void main() {
|
|
480
|
+
if (vAlpha < 0.003) discard;
|
|
481
|
+
gl_FragColor = vec4(0.50, 0.56, 0.75, vAlpha);
|
|
482
|
+
}
|
|
483
|
+
`,
|
|
484
|
+
});
|
|
485
|
+
|
|
486
|
+
bgGridPillars = new THREE.LineSegments(pillarGeo, pillarMat);
|
|
487
|
+
bgGridPillars.renderOrder = -9;
|
|
488
|
+
scene.add(bgGridPillars);
|
|
489
|
+
|
|
490
|
+
// ── Layer C: World-Space Floating Particles ──
|
|
491
|
+
// Muted blue-grey dots — NOT camera-attached (real parallax)
|
|
492
|
+
const pCount = Math.max(200, gfx.bgParticleCount || 2000);
|
|
493
|
+
const pPositions = new Float32Array(pCount * 3);
|
|
494
|
+
const pSizes = new Float32Array(pCount);
|
|
495
|
+
const pAlphas = new Float32Array(pCount);
|
|
496
|
+
const pColors = new Float32Array(pCount * 3);
|
|
497
|
+
const pPhases = new Float32Array(pCount);
|
|
498
|
+
|
|
499
|
+
for (let i = 0; i < pCount; i++) {
|
|
500
|
+
const angle = Math.random() * Math.PI * 2;
|
|
501
|
+
const radius = 50 + Math.random() * 2800;
|
|
502
|
+
const height = FLOOR_Y + 10 + Math.random() * 600;
|
|
503
|
+
|
|
504
|
+
pPositions[i * 3] = Math.cos(angle) * radius;
|
|
505
|
+
pPositions[i * 3 + 1] = height;
|
|
506
|
+
pPositions[i * 3 + 2] = Math.sin(angle) * radius;
|
|
507
|
+
|
|
508
|
+
const distNorm = radius / 2800;
|
|
509
|
+
pSizes[i] = (1.0 - distNorm * 0.5) * (1.2 + Math.random() * 2.0);
|
|
510
|
+
pAlphas[i] = (1.0 - distNorm * 0.6) * (0.03 + Math.random() * 0.09);
|
|
511
|
+
|
|
512
|
+
// Muted blue-grey palette (matching website rgba(130,145,195))
|
|
513
|
+
const v = Math.random();
|
|
514
|
+
// Base: desaturated blue-grey with slight variation
|
|
515
|
+
pColors[i * 3] = 0.45 + (Math.random() - 0.5) * 0.12;
|
|
516
|
+
pColors[i * 3 + 1] = 0.50 + (Math.random() - 0.5) * 0.12;
|
|
517
|
+
pColors[i * 3 + 2] = 0.68 + (Math.random() - 0.5) * 0.12;
|
|
518
|
+
// 15% chance of slightly warmer accent
|
|
519
|
+
if (v > 0.85) {
|
|
520
|
+
pColors[i * 3] = 0.58 + Math.random() * 0.08;
|
|
521
|
+
pColors[i * 3 + 1] = 0.55 + Math.random() * 0.06;
|
|
522
|
+
pColors[i * 3 + 2] = 0.72 + Math.random() * 0.08;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
pPhases[i] = Math.random() * Math.PI * 2;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
const pGeo = new THREE.BufferGeometry();
|
|
529
|
+
pGeo.setAttribute('position', new THREE.Float32BufferAttribute(pPositions, 3));
|
|
530
|
+
pGeo.setAttribute('aSize', new THREE.Float32BufferAttribute(pSizes, 1));
|
|
531
|
+
pGeo.setAttribute('aAlpha', new THREE.Float32BufferAttribute(pAlphas, 1));
|
|
532
|
+
pGeo.setAttribute('aColor', new THREE.Float32BufferAttribute(pColors, 3));
|
|
533
|
+
pGeo.setAttribute('aPhase', new THREE.Float32BufferAttribute(pPhases, 1));
|
|
534
|
+
|
|
535
|
+
const pMat = new THREE.ShaderMaterial({
|
|
536
|
+
transparent: true,
|
|
537
|
+
depthWrite: false,
|
|
538
|
+
blending: THREE.AdditiveBlending,
|
|
539
|
+
uniforms: { uTime: { value: 0 }, uIntensity: { value: gfx.bgIntensity } },
|
|
540
|
+
vertexShader: `
|
|
541
|
+
attribute float aSize;
|
|
542
|
+
attribute float aAlpha;
|
|
543
|
+
attribute vec3 aColor;
|
|
544
|
+
attribute float aPhase;
|
|
545
|
+
varying float vAlpha;
|
|
546
|
+
varying vec3 vColor;
|
|
547
|
+
uniform float uTime;
|
|
548
|
+
uniform float uIntensity;
|
|
549
|
+
void main() {
|
|
550
|
+
vColor = aColor;
|
|
551
|
+
vec3 pos = position;
|
|
552
|
+
float t = uTime * 0.08 + aPhase;
|
|
553
|
+
pos.y += sin(t) * 6.0;
|
|
554
|
+
pos.x += cos(t * 0.7 + aPhase * 3.0) * 3.0;
|
|
555
|
+
pos.z += sin(t * 0.5 + aPhase * 2.0) * 3.0;
|
|
556
|
+
|
|
557
|
+
vec4 mvPos = modelViewMatrix * vec4(pos, 1.0);
|
|
558
|
+
float camDist = length(mvPos.xyz);
|
|
559
|
+
float distFade = 1.0 - smoothstep(200.0, 2800.0, camDist);
|
|
560
|
+
|
|
561
|
+
vAlpha = aAlpha * (0.5 + 0.5 * sin(uTime * 0.4 + aPhase * 6.28)) * distFade * uIntensity;
|
|
562
|
+
gl_PointSize = aSize * (400.0 / max(-mvPos.z, 1.0));
|
|
563
|
+
gl_Position = projectionMatrix * mvPos;
|
|
564
|
+
}
|
|
565
|
+
`,
|
|
566
|
+
fragmentShader: `
|
|
567
|
+
varying float vAlpha;
|
|
568
|
+
varying vec3 vColor;
|
|
569
|
+
void main() {
|
|
570
|
+
float dist = length(gl_PointCoord - vec2(0.5));
|
|
571
|
+
if (dist > 0.5) discard;
|
|
572
|
+
float core = smoothstep(0.5, 0.0, dist);
|
|
573
|
+
float glow = smoothstep(0.5, 0.15, dist);
|
|
574
|
+
float alpha = (core * 0.6 + glow * 0.4) * vAlpha;
|
|
575
|
+
gl_FragColor = vec4(vColor * (0.7 + core * 0.3), alpha);
|
|
576
|
+
}
|
|
577
|
+
`,
|
|
578
|
+
});
|
|
579
|
+
|
|
580
|
+
bgWorldParticles = new THREE.Points(pGeo, pMat);
|
|
581
|
+
bgWorldParticles.renderOrder = -8;
|
|
582
|
+
scene.add(bgWorldParticles);
|
|
583
|
+
|
|
584
|
+
// ── Layer D: Horizon Glow ──
|
|
585
|
+
// Cylindrical band at the grid edge creating a soft horizon line
|
|
586
|
+
const horizGeo = new THREE.CylinderGeometry(3800, 3800, 800, 64, 1, true);
|
|
587
|
+
const horizMat = new THREE.ShaderMaterial({
|
|
588
|
+
transparent: true,
|
|
589
|
+
depthWrite: false,
|
|
590
|
+
side: THREE.DoubleSide,
|
|
591
|
+
blending: THREE.AdditiveBlending,
|
|
592
|
+
uniforms: { uTime: { value: 0 } },
|
|
593
|
+
vertexShader: `
|
|
594
|
+
varying vec2 vUv;
|
|
595
|
+
varying vec3 vWorldPos;
|
|
596
|
+
void main() {
|
|
597
|
+
vUv = uv;
|
|
598
|
+
vWorldPos = (modelMatrix * vec4(position, 1.0)).xyz;
|
|
599
|
+
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
|
600
|
+
}
|
|
601
|
+
`,
|
|
602
|
+
fragmentShader: `
|
|
603
|
+
varying vec2 vUv;
|
|
604
|
+
varying vec3 vWorldPos;
|
|
605
|
+
uniform float uTime;
|
|
606
|
+
void main() {
|
|
607
|
+
// Wide, soft gaussian-like glow centered at lower-mid height
|
|
608
|
+
float center = 0.3;
|
|
609
|
+
float spread = 0.4; // very wide spread
|
|
610
|
+
float yFade = exp(-pow((vUv.y - center) / spread, 2.0));
|
|
611
|
+
|
|
612
|
+
// Muted blue-grey glow
|
|
613
|
+
vec3 col = vec3(0.22, 0.28, 0.42);
|
|
614
|
+
|
|
615
|
+
// Very subtle shimmer
|
|
616
|
+
float shimmer = 0.9 + 0.1 * sin(vUv.x * 20.0 + uTime * 0.15);
|
|
617
|
+
|
|
618
|
+
float alpha = yFade * 0.025 * shimmer;
|
|
619
|
+
gl_FragColor = vec4(col, alpha);
|
|
620
|
+
}
|
|
621
|
+
`,
|
|
622
|
+
});
|
|
623
|
+
|
|
624
|
+
bgGridFloor2 = new THREE.Mesh(horizGeo, horizMat);
|
|
625
|
+
bgGridFloor2.position.y = FLOOR_Y + 20;
|
|
626
|
+
bgGridFloor2.renderOrder = -7;
|
|
627
|
+
scene.add(bgGridFloor2);
|
|
628
|
+
|
|
629
|
+
// ── Layer E: Sky gradient sphere — atmospheric perspective ──
|
|
630
|
+
// Inverted sphere behind everything: zenith=black, horizon=faint warm-purple
|
|
631
|
+
const skyGradGeo = new THREE.SphereGeometry(30000, 24, 16);
|
|
632
|
+
const skyGradMat = new THREE.ShaderMaterial({
|
|
633
|
+
side: THREE.BackSide,
|
|
634
|
+
transparent: true,
|
|
635
|
+
depthWrite: false,
|
|
636
|
+
vertexShader: `
|
|
637
|
+
varying vec3 vWorldPos;
|
|
638
|
+
void main() {
|
|
639
|
+
vWorldPos = (modelMatrix * vec4(position, 1.0)).xyz;
|
|
640
|
+
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
|
641
|
+
}
|
|
642
|
+
`,
|
|
643
|
+
fragmentShader: `
|
|
644
|
+
varying vec3 vWorldPos;
|
|
645
|
+
void main() {
|
|
646
|
+
// Normalized height: 0 at bottom, 1 at top
|
|
647
|
+
float h = normalize(vWorldPos).y;
|
|
648
|
+
// Zenith (h=1): pure black
|
|
649
|
+
vec3 zenith = vec3(0.003, 0.003, 0.004);
|
|
650
|
+
// Mid-sky (h~0.4): barely-there dark grey
|
|
651
|
+
vec3 midSky = vec3(0.01, 0.01, 0.012);
|
|
652
|
+
// Horizon (h=0): subtle dark grey
|
|
653
|
+
vec3 horizon = vec3(0.022, 0.02, 0.022);
|
|
654
|
+
// Below horizon: black
|
|
655
|
+
vec3 below = vec3(0.002, 0.002, 0.002);
|
|
656
|
+
|
|
657
|
+
vec3 col;
|
|
658
|
+
if (h > 0.4) {
|
|
659
|
+
col = mix(midSky, zenith, smoothstep(0.4, 1.0, h));
|
|
660
|
+
} else if (h > 0.0) {
|
|
661
|
+
col = mix(horizon, midSky, smoothstep(0.0, 0.4, h));
|
|
662
|
+
} else {
|
|
663
|
+
col = mix(below, horizon, smoothstep(-0.3, 0.0, h));
|
|
664
|
+
}
|
|
665
|
+
gl_FragColor = vec4(col, 0.95);
|
|
666
|
+
}
|
|
667
|
+
`,
|
|
668
|
+
});
|
|
669
|
+
bgSkyDome = new THREE.Mesh(skyGradGeo, skyGradMat);
|
|
670
|
+
bgSkyDome.renderOrder = -10;
|
|
671
|
+
scene.add(bgSkyDome);
|
|
672
|
+
_cloudPlanes = [];
|
|
673
|
+
|
|
674
|
+
// ── Layer F: World-space parallax star field ──
|
|
675
|
+
// 3 shells at increasing radii with color temperature variation
|
|
676
|
+
// NOT camera-attached — real parallax as camera orbits
|
|
677
|
+
const shells = [
|
|
678
|
+
{ start: 0, count: 600, rMin: 3500, rMax: 5000, sMin: 3.0, sMax: 6.0, aMin: 0.20, aMax: 0.50, col: [0.78, 0.74, 0.68] }, // near: warm white-gold
|
|
679
|
+
{ start: 600, count: 1000, rMin: 6000, rMax: 10000, sMin: 4.0, sMax: 8.0, aMin: 0.15, aMax: 0.35, col: [0.62, 0.66, 0.80] }, // mid: neutral blue-white
|
|
680
|
+
{ start: 1600, count: 1400, rMin: 14000, rMax: 25000, sMin: 6.0, sMax: 14.0, aMin: 0.10, aMax: 0.25, col: [0.48, 0.50, 0.75] }, // far: cool blue-violet
|
|
681
|
+
];
|
|
682
|
+
const spCount = 3000;
|
|
683
|
+
const spPositions = new Float32Array(spCount * 3);
|
|
684
|
+
const spSizes = new Float32Array(spCount);
|
|
685
|
+
const spAlphas = new Float32Array(spCount);
|
|
686
|
+
const spColors = new Float32Array(spCount * 3);
|
|
687
|
+
const spPhases = new Float32Array(spCount);
|
|
688
|
+
|
|
689
|
+
for (const shell of shells) {
|
|
690
|
+
for (let i = 0; i < shell.count; i++) {
|
|
691
|
+
const idx = shell.start + i;
|
|
692
|
+
// Fibonacci sphere, upper hemisphere bias
|
|
693
|
+
const y = 1 - (i / shell.count) * 1.5; // range 1 to -0.5
|
|
694
|
+
const xyRadius = Math.sqrt(Math.max(0, 1 - y * y));
|
|
695
|
+
const theta = Math.PI * (1 + Math.sqrt(5)) * i;
|
|
696
|
+
const r = shell.rMin + Math.random() * (shell.rMax - shell.rMin);
|
|
697
|
+
|
|
698
|
+
spPositions[idx * 3] = Math.cos(theta) * xyRadius * r;
|
|
699
|
+
spPositions[idx * 3 + 1] = y * r;
|
|
700
|
+
spPositions[idx * 3 + 2] = Math.sin(theta) * xyRadius * r;
|
|
701
|
+
|
|
702
|
+
spSizes[idx] = shell.sMin + Math.random() * (shell.sMax - shell.sMin);
|
|
703
|
+
spAlphas[idx] = shell.aMin + Math.random() * (shell.aMax - shell.aMin);
|
|
704
|
+
// Color with slight per-star variation
|
|
705
|
+
spColors[idx * 3] = shell.col[0] + (Math.random() - 0.5) * 0.08;
|
|
706
|
+
spColors[idx * 3 + 1] = shell.col[1] + (Math.random() - 0.5) * 0.08;
|
|
707
|
+
spColors[idx * 3 + 2] = shell.col[2] + (Math.random() - 0.5) * 0.08;
|
|
708
|
+
spPhases[idx] = Math.random() * Math.PI * 2;
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
const spGeo = new THREE.BufferGeometry();
|
|
713
|
+
spGeo.setAttribute('position', new THREE.Float32BufferAttribute(spPositions, 3));
|
|
714
|
+
spGeo.setAttribute('aSize', new THREE.Float32BufferAttribute(spSizes, 1));
|
|
715
|
+
spGeo.setAttribute('aAlpha', new THREE.Float32BufferAttribute(spAlphas, 1));
|
|
716
|
+
spGeo.setAttribute('aColor', new THREE.Float32BufferAttribute(spColors, 3));
|
|
717
|
+
spGeo.setAttribute('aPhase', new THREE.Float32BufferAttribute(spPhases, 1));
|
|
718
|
+
|
|
719
|
+
const spMat = new THREE.ShaderMaterial({
|
|
720
|
+
transparent: true,
|
|
721
|
+
depthWrite: false,
|
|
722
|
+
blending: THREE.AdditiveBlending,
|
|
723
|
+
uniforms: { uTime: { value: 0 } },
|
|
724
|
+
vertexShader: `
|
|
725
|
+
attribute float aSize;
|
|
726
|
+
attribute float aAlpha;
|
|
727
|
+
attribute vec3 aColor;
|
|
728
|
+
attribute float aPhase;
|
|
729
|
+
varying float vAlpha;
|
|
730
|
+
varying vec3 vColor;
|
|
731
|
+
uniform float uTime;
|
|
732
|
+
void main() {
|
|
733
|
+
vColor = aColor;
|
|
734
|
+
// Differential Y-axis rotation: near stars drift faster than far stars
|
|
735
|
+
float r = length(position);
|
|
736
|
+
float rotSpeed = mix(0.003, 0.0008, smoothstep(3500.0, 25000.0, r));
|
|
737
|
+
float angle = uTime * rotSpeed;
|
|
738
|
+
float cs = cos(angle);
|
|
739
|
+
float sn = sin(angle);
|
|
740
|
+
vec3 rotPos = vec3(
|
|
741
|
+
position.x * cs - position.z * sn,
|
|
742
|
+
position.y,
|
|
743
|
+
position.x * sn + position.z * cs
|
|
744
|
+
);
|
|
745
|
+
vec4 mvPos = modelViewMatrix * vec4(rotPos, 1.0);
|
|
746
|
+
vAlpha = aAlpha * (0.3 + 0.7 * sin(uTime * 0.6 + aPhase * 6.28));
|
|
747
|
+
// Perspective size with floor so distant stars stay visible
|
|
748
|
+
gl_PointSize = max(1.0, aSize * (3000.0 / max(-mvPos.z, 1.0)));
|
|
749
|
+
gl_Position = projectionMatrix * mvPos;
|
|
750
|
+
}
|
|
751
|
+
`,
|
|
752
|
+
fragmentShader: `
|
|
753
|
+
varying float vAlpha;
|
|
754
|
+
varying vec3 vColor;
|
|
755
|
+
void main() {
|
|
756
|
+
float dist = length(gl_PointCoord - vec2(0.5));
|
|
757
|
+
if (dist > 0.5) discard;
|
|
758
|
+
float core = exp(-dist * dist * 80.0);
|
|
759
|
+
float glow = exp(-dist * dist * 12.0) * 0.2;
|
|
760
|
+
gl_FragColor = vec4(vColor, (core + glow) * vAlpha);
|
|
761
|
+
}
|
|
762
|
+
`,
|
|
763
|
+
});
|
|
764
|
+
|
|
765
|
+
bgSkyParticles = new THREE.Points(spGeo, spMat);
|
|
766
|
+
bgSkyParticles.renderOrder = -6;
|
|
767
|
+
scene.add(bgSkyParticles);
|
|
768
|
+
|
|
769
|
+
// ── Scene fog for atmospheric depth ──
|
|
770
|
+
scene.fog = new THREE.FogExp2(0x000000, 0.00012);
|
|
771
|
+
|
|
772
|
+
// ── Camera constraints & orbit polish ──
|
|
773
|
+
const controls = graph.controls();
|
|
774
|
+
if (controls) {
|
|
775
|
+
// Zoom limit: keep camera inside the sky dome
|
|
776
|
+
controls.maxDistance = 2800;
|
|
777
|
+
controls.minDistance = 30;
|
|
778
|
+
|
|
779
|
+
// Enable damping for smooth, polished orbit feel
|
|
780
|
+
controls.enableDamping = true;
|
|
781
|
+
controls.dampingFactor = 0.08;
|
|
782
|
+
|
|
783
|
+
// Smoother zoom
|
|
784
|
+
controls.zoomSpeed = 0.8;
|
|
785
|
+
|
|
786
|
+
// Smoother rotation
|
|
787
|
+
controls.rotateSpeed = 0.6;
|
|
788
|
+
|
|
789
|
+
// Smoother pan
|
|
790
|
+
controls.panSpeed = 0.6;
|
|
791
|
+
|
|
792
|
+
// Don't auto-rotate
|
|
793
|
+
controls.autoRotate = false;
|
|
794
|
+
|
|
795
|
+
// Floor + ceiling constraint via update override
|
|
796
|
+
_origControlsUpdate = controls.update.bind(controls);
|
|
797
|
+
controls.update = function() {
|
|
798
|
+
_origControlsUpdate();
|
|
799
|
+
const cam = graph.camera();
|
|
800
|
+
if (cam.position.y < FLOOR_Y + 15) cam.position.y = FLOOR_Y + 15;
|
|
801
|
+
if (cam.position.y > 2500) cam.position.y = 2500;
|
|
802
|
+
};
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
// ═══════════════════════════════════════════
|
|
807
|
+
// BACKGROUND — Cleanup
|
|
808
|
+
// ═══════════════════════════════════════════
|
|
809
|
+
function removeBackground(graph) {
|
|
810
|
+
const scene = graph.scene();
|
|
811
|
+
// Remove deep-grid objects (floor is managed separately by removeFloor)
|
|
812
|
+
if (bgGridPillars) { scene.remove(bgGridPillars); bgGridPillars.geometry.dispose(); bgGridPillars.material.dispose(); bgGridPillars = null; }
|
|
813
|
+
if (bgWorldParticles) { scene.remove(bgWorldParticles); bgWorldParticles.geometry.dispose(); bgWorldParticles.material.dispose(); bgWorldParticles = null; }
|
|
814
|
+
if (bgGridFloor2) { scene.remove(bgGridFloor2); bgGridFloor2.geometry.dispose(); bgGridFloor2.material.dispose(); bgGridFloor2 = null; }
|
|
815
|
+
if (bgSkyDome) {
|
|
816
|
+
scene.remove(bgSkyDome);
|
|
817
|
+
if (bgSkyDome.isGroup) {
|
|
818
|
+
bgSkyDome.children.forEach(c => { c.geometry.dispose(); c.material.dispose(); });
|
|
819
|
+
} else {
|
|
820
|
+
bgSkyDome.geometry.dispose(); bgSkyDome.material.dispose();
|
|
821
|
+
}
|
|
822
|
+
bgSkyDome = null; _cloudPlanes = [];
|
|
823
|
+
}
|
|
824
|
+
if (bgSkyParticles) { scene.remove(bgSkyParticles); bgSkyParticles.geometry.dispose(); bgSkyParticles.material.dispose(); bgSkyParticles = null; }
|
|
825
|
+
// Restore original controls if overridden
|
|
826
|
+
const controls = graph.controls();
|
|
827
|
+
if (controls && _origControlsUpdate) {
|
|
828
|
+
controls.update = _origControlsUpdate;
|
|
829
|
+
_origControlsUpdate = null;
|
|
830
|
+
controls.maxDistance = Infinity;
|
|
831
|
+
controls.minDistance = 0;
|
|
832
|
+
}
|
|
833
|
+
// Clear scene fog
|
|
834
|
+
scene.fog = null;
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
// ═══════════════════════════════════════════
|
|
838
|
+
// BACKGROUND — Dispatcher
|
|
839
|
+
// Calls the right background builder based on theme
|
|
840
|
+
// ═══════════════════════════════════════════
|
|
841
|
+
export function applyBgTheme(graph, bloomPass) {
|
|
842
|
+
if (!graph) return;
|
|
843
|
+
removeBackground(graph);
|
|
844
|
+
addBackgroundDeepGrid(graph);
|
|
845
|
+
graph.backgroundColor('#000000');
|
|
846
|
+
if (bloomPass) {
|
|
847
|
+
bloomPass.strength = 0.35;
|
|
848
|
+
bloomPass.threshold = 1.0;
|
|
849
|
+
bloomPass.radius = 0.3;
|
|
850
|
+
}
|
|
851
|
+
applyFloorStyle(gfx.floorStyle || 'grid', graph);
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
// ═══════════════════════════════════════════
|
|
855
|
+
// ANIMATION — Per-frame background updates
|
|
856
|
+
// Called from the main animate() loop
|
|
857
|
+
// ═══════════════════════════════════════════
|
|
858
|
+
export function animateBackground(time, graph) {
|
|
859
|
+
// ── Floor effect (independent of bg theme) ──
|
|
860
|
+
if (floorMesh && floorMesh.material.uniforms) {
|
|
861
|
+
if (floorMesh.material.uniforms.uTime) floorMesh.material.uniforms.uTime.value = time;
|
|
862
|
+
if (floorMesh.material.uniforms.uCamY && graph) floorMesh.material.uniforms.uCamY.value = graph.camera().position.y;
|
|
863
|
+
}
|
|
864
|
+
if (floorParticles && floorParticles.material.uniforms) {
|
|
865
|
+
if (floorParticles.material.uniforms.uTime) floorParticles.material.uniforms.uTime.value = time;
|
|
866
|
+
if (floorParticles.material.uniforms.uCamPos && graph) floorParticles.material.uniforms.uCamPos.value.copy(graph.camera().position);
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
// ── Background — Deep Grid theme (world-space, real depth) ──
|
|
870
|
+
if (bgGridPillars) {
|
|
871
|
+
bgGridPillars.material.uniforms.uTime.value = time;
|
|
872
|
+
}
|
|
873
|
+
if (bgWorldParticles) {
|
|
874
|
+
bgWorldParticles.material.uniforms.uTime.value = time;
|
|
875
|
+
bgWorldParticles.material.uniforms.uIntensity.value = gfx.bgIntensity;
|
|
876
|
+
}
|
|
877
|
+
if (bgGridFloor2) {
|
|
878
|
+
bgGridFloor2.material.uniforms.uTime.value = time;
|
|
879
|
+
}
|
|
880
|
+
// Layer F: world-space stars (no camera-attach — real parallax)
|
|
881
|
+
if (bgSkyParticles) {
|
|
882
|
+
bgSkyParticles.material.uniforms.uTime.value = time;
|
|
883
|
+
}
|
|
884
|
+
}
|