@mindexec/cli 0.2.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/README.md +275 -0
- package/codex-runtime.js +960 -0
- package/launch-bridge.cjs +162 -0
- package/package.json +61 -0
- package/port-guard.cjs +232 -0
- package/scripts/setup-tree-sitter-grammars.mjs +59 -0
- package/server.js +8422 -0
- package/start-bridge.bat +32 -0
- package/start-bridge.sh +81 -0
- package/tree-sitter-grammars/README.md +18 -0
- package/tree-sitter-grammars/tree-sitter-c_sharp.wasm +0 -0
- package/tree-sitter-grammars/tree-sitter-go.wasm +0 -0
- package/tree-sitter-grammars/tree-sitter-java.wasm +0 -0
- package/tree-sitter-grammars/tree-sitter-javascript.wasm +0 -0
- package/tree-sitter-grammars/tree-sitter-python.wasm +0 -0
- package/tree-sitter-grammars/tree-sitter-rust.wasm +0 -0
- package/tree-sitter-grammars/tree-sitter-tsx.wasm +0 -0
- package/tree-sitter-grammars/tree-sitter-typescript.wasm +0 -0
- package/wwwroot/MindExecution.Web.styles.css +3 -0
- package/wwwroot/_content/MindExecution.Plugins.Admin/css/admin-dashboard.css +546 -0
- package/wwwroot/_content/MindExecution.Plugins.Directory/MindExecution.Plugins.Directory.u7utcng611.bundle.scp.css +7 -0
- package/wwwroot/_content/MindExecution.Plugins.Directory/background.png +0 -0
- package/wwwroot/_content/MindExecution.Plugins.Directory/directory-manager.js +202 -0
- package/wwwroot/_content/MindExecution.Plugins.Directory/exampleJsInterop.js +6 -0
- package/wwwroot/_content/MindExecution.Plugins.YouTube/css/youtube-search.css +251 -0
- package/wwwroot/_content/MindExecution.Shared/MindExecution.Shared.wsano1j4wp.bundle.scp.css +4 -0
- package/wwwroot/_content/MindExecution.Shared/css/admin-dashboard.css +559 -0
- package/wwwroot/_content/MindExecution.Shared/css/app.css +1 -0
- package/wwwroot/_content/MindExecution.Shared/css/mind-map-overrides.css +2936 -0
- package/wwwroot/_content/MindExecution.Shared/fonts/NotoSansKR-Bold.ttf +0 -0
- package/wwwroot/_content/MindExecution.Shared/fonts/NotoSansKR-Regular.ttf +0 -0
- package/wwwroot/_content/MindExecution.Shared/js/agent-visualization.js +359 -0
- package/wwwroot/_content/MindExecution.Shared/js/background-themes.js +1721 -0
- package/wwwroot/_content/MindExecution.Shared/js/code-master.js +8316 -0
- package/wwwroot/_content/MindExecution.Shared/js/file-system-helper.js +639 -0
- package/wwwroot/_content/MindExecution.Shared/js/helpers/InfiniteGridHelper.js +109 -0
- package/wwwroot/_content/MindExecution.Shared/js/marked.min.js +69 -0
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-core.js +7982 -0
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-core.js.backup +1059 -0
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-css3d-manager.js +15803 -0
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-dev-guards.js +325 -0
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-dnd.js +1430 -0
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-dnd.js.bak +434 -0
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-glow-shader.js +260 -0
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-interactions.js +7640 -0
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-lod-plan-worker.js +160 -0
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-lod-renderer.js +9923 -0
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-logic-workers.js +977 -0
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-menu-manager.js +1431 -0
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-multi-select.js +1716 -0
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-node-search-worker.js +553 -0
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-nodes.js +4541 -0
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-object-manager.js +489 -0
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-object-manager.js.backup +372 -0
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-pipeline.js +2075 -0
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-text-lod-system.js +646 -0
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-text-overlay-v2.js +4323 -0
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-texture-factory.js +2260 -0
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-texture-factory.js.backup +1258 -0
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-visibility-worker.js +890 -0
- package/wwwroot/_content/MindExecution.Shared/js/mindmap-toolbar.js +594 -0
- package/wwwroot/_content/MindExecution.Shared/js/native-drop-handler.js +170 -0
- package/wwwroot/_content/MindExecution.Shared/js/plan-master.js +788 -0
- package/wwwroot/_content/MindExecution.Shared/js/renderers/CSS3DRenderer.js +50 -0
- package/wwwroot/_content/MindExecution.Shared/js/texture-worker-manager.js +188 -0
- package/wwwroot/_content/MindExecution.Shared/js/texture-worker.js +208 -0
- package/wwwroot/_content/MindExecution.Shared/js/three.min.js +6 -0
- package/wwwroot/_content/MindExecution.Shared/js/titlebar-handler.js +191 -0
- package/wwwroot/_content/MindExecution.Shared/js/token-manager.js +37 -0
- package/wwwroot/_content/MindExecution.Shared/js/token-worker.js +28 -0
- package/wwwroot/_content/MindExecution.Shared/js/troika-bundle.js +5626 -0
- package/wwwroot/_content/MindExecution.Shared/js/troika-bundle.js.map +7 -0
- package/wwwroot/_content/MindExecution.Shared/lib/font-awesome/css/all.min.css +9 -0
- package/wwwroot/_content/MindExecution.Shared/lib/font-awesome/webfonts/fa-brands-400.ttf +0 -0
- package/wwwroot/_content/MindExecution.Shared/lib/font-awesome/webfonts/fa-brands-400.woff2 +0 -0
- package/wwwroot/_content/MindExecution.Shared/lib/font-awesome/webfonts/fa-regular-400.ttf +0 -0
- package/wwwroot/_content/MindExecution.Shared/lib/font-awesome/webfonts/fa-regular-400.woff2 +0 -0
- package/wwwroot/_content/MindExecution.Shared/lib/font-awesome/webfonts/fa-solid-900.ttf +0 -0
- package/wwwroot/_content/MindExecution.Shared/lib/font-awesome/webfonts/fa-solid-900.woff2 +0 -0
- package/wwwroot/_content/MindExecution.Shared/models/all-MiniLM-L6-v2-quantized.onnx +0 -0
- package/wwwroot/_content/MindExecution.Shared/models/vocab.txt +30522 -0
- package/wwwroot/_framework/Google.Protobuf.9h59ukbel7.dll +0 -0
- package/wwwroot/_framework/Markdig.d1j7v41cl1.dll +0 -0
- package/wwwroot/_framework/MessagePack.Annotations.l6qv48kgpt.dll +0 -0
- package/wwwroot/_framework/MessagePack.eqoptzx9d5.dll +0 -0
- package/wwwroot/_framework/Microsoft.AspNetCore.Authorization.k7dsih5y5g.dll +0 -0
- package/wwwroot/_framework/Microsoft.AspNetCore.Components.6nyje9sa0g.dll +0 -0
- package/wwwroot/_framework/Microsoft.AspNetCore.Components.Authorization.iycd6unprw.dll +0 -0
- package/wwwroot/_framework/Microsoft.AspNetCore.Components.Web.487u3twia4.dll +0 -0
- package/wwwroot/_framework/Microsoft.AspNetCore.Components.WebAssembly.d0gcnmlxxz.dll +0 -0
- package/wwwroot/_framework/Microsoft.AspNetCore.Metadata.h4yevl9adi.dll +0 -0
- package/wwwroot/_framework/Microsoft.CSharp.qrvp77qmhs.dll +0 -0
- package/wwwroot/_framework/Microsoft.Data.Sqlite.jdlxgv2jtg.dll +0 -0
- package/wwwroot/_framework/Microsoft.EntityFrameworkCore.4gjazp7kjf.dll +0 -0
- package/wwwroot/_framework/Microsoft.EntityFrameworkCore.Abstractions.gocudnvz7b.dll +0 -0
- package/wwwroot/_framework/Microsoft.EntityFrameworkCore.Relational.lt4rsvinuo.dll +0 -0
- package/wwwroot/_framework/Microsoft.EntityFrameworkCore.Sqlite.69luj0fa9r.dll +0 -0
- package/wwwroot/_framework/Microsoft.Extensions.Caching.Abstractions.364t4jh3zz.dll +0 -0
- package/wwwroot/_framework/Microsoft.Extensions.Caching.Memory.izlxhpzosu.dll +0 -0
- package/wwwroot/_framework/Microsoft.Extensions.Configuration.8zq7hh41o7.dll +0 -0
- package/wwwroot/_framework/Microsoft.Extensions.Configuration.Abstractions.8if74zs6ea.dll +0 -0
- package/wwwroot/_framework/Microsoft.Extensions.Configuration.Json.duvlngw8i0.dll +0 -0
- package/wwwroot/_framework/Microsoft.Extensions.DependencyInjection.Abstractions.t2hh9kvx0o.dll +0 -0
- package/wwwroot/_framework/Microsoft.Extensions.DependencyInjection.n4tg99oy8l.dll +0 -0
- package/wwwroot/_framework/Microsoft.Extensions.DependencyModel.h0d06ixk3e.dll +0 -0
- package/wwwroot/_framework/Microsoft.Extensions.Logging.Abstractions.rl32bkx2sd.dll +0 -0
- package/wwwroot/_framework/Microsoft.Extensions.Logging.dlht1xei0t.dll +0 -0
- package/wwwroot/_framework/Microsoft.Extensions.Options.qeunebioml.dll +0 -0
- package/wwwroot/_framework/Microsoft.Extensions.Primitives.18cr6vnuuz.dll +0 -0
- package/wwwroot/_framework/Microsoft.IO.RecyclableMemoryStream.r915vovvw4.dll +0 -0
- package/wwwroot/_framework/Microsoft.IdentityModel.Abstractions.1ejljk3erv.dll +0 -0
- package/wwwroot/_framework/Microsoft.IdentityModel.JsonWebTokens.1596zr8gne.dll +0 -0
- package/wwwroot/_framework/Microsoft.IdentityModel.Logging.229uyvpgio.dll +0 -0
- package/wwwroot/_framework/Microsoft.IdentityModel.Tokens.9sibtajc9f.dll +0 -0
- package/wwwroot/_framework/Microsoft.JSInterop.17lq4j1j7g.dll +0 -0
- package/wwwroot/_framework/Microsoft.JSInterop.WebAssembly.ryia5gxiad.dll +0 -0
- package/wwwroot/_framework/Microsoft.ML.OnnxRuntime.w9deo1m5ss.dll +0 -0
- package/wwwroot/_framework/Microsoft.ML.Tokenizers.cm2vuv2z61.dll +0 -0
- package/wwwroot/_framework/Microsoft.NET.StringTools.3qbrf4v2ki.dll +0 -0
- package/wwwroot/_framework/MimeMapping.og9ys58ylm.dll +0 -0
- package/wwwroot/_framework/MindExecution.Core.1q1trifbuu.dll +0 -0
- package/wwwroot/_framework/MindExecution.Kernel.gwwc40sc45.dll +0 -0
- package/wwwroot/_framework/MindExecution.Plugins.Admin.0jgrn1sckv.dll +0 -0
- package/wwwroot/_framework/MindExecution.Plugins.Business.13mme2qcag.dll +0 -0
- package/wwwroot/_framework/MindExecution.Plugins.Concept.dfp2mdt45q.dll +0 -0
- package/wwwroot/_framework/MindExecution.Plugins.Directory.3w4t6n3se0.dll +0 -0
- package/wwwroot/_framework/MindExecution.Plugins.PlanMaster.s0qpntz420.dll +0 -0
- package/wwwroot/_framework/MindExecution.Plugins.YouTube.iu11fq8d16.dll +0 -0
- package/wwwroot/_framework/MindExecution.Shared.7j27dcqnrc.dll +0 -0
- package/wwwroot/_framework/MindExecution.Web.pq1ty8ov2v.dll +0 -0
- package/wwwroot/_framework/Newtonsoft.Json.a56zs13vug.dll +0 -0
- package/wwwroot/_framework/SQLitePCLRaw.batteries_v2.rrd1nzawpp.dll +0 -0
- package/wwwroot/_framework/SQLitePCLRaw.core.1dxloztpfz.dll +0 -0
- package/wwwroot/_framework/SQLitePCLRaw.provider.e_sqlite3.oekyzl53i1.dll +0 -0
- package/wwwroot/_framework/Supabase.Core.s1pkj4aj0l.dll +0 -0
- package/wwwroot/_framework/Supabase.Functions.qz4nu782sg.dll +0 -0
- package/wwwroot/_framework/Supabase.Gotrue.twah27pkik.dll +0 -0
- package/wwwroot/_framework/Supabase.Postgrest.gmuuv369ih.dll +0 -0
- package/wwwroot/_framework/Supabase.Realtime.ox3kchdy3w.dll +0 -0
- package/wwwroot/_framework/Supabase.Storage.fnjnepaowr.dll +0 -0
- package/wwwroot/_framework/Supabase.azmaw5pgcz.dll +0 -0
- package/wwwroot/_framework/System.Collections.Concurrent.y1zmvuyipi.dll +0 -0
- package/wwwroot/_framework/System.Collections.Immutable.ug3j698qms.dll +0 -0
- package/wwwroot/_framework/System.Collections.NonGeneric.h66hj3863h.dll +0 -0
- package/wwwroot/_framework/System.Collections.Specialized.umr3y27ntj.dll +0 -0
- package/wwwroot/_framework/System.Collections.x53e19vfsj.dll +0 -0
- package/wwwroot/_framework/System.ComponentModel.Annotations.tz6gnt4ebt.dll +0 -0
- package/wwwroot/_framework/System.ComponentModel.Primitives.j7tiphu4rg.dll +0 -0
- package/wwwroot/_framework/System.ComponentModel.TypeConverter.ujlztox1gx.dll +0 -0
- package/wwwroot/_framework/System.ComponentModel.x9xz0ojfb6.dll +0 -0
- package/wwwroot/_framework/System.Console.ijzpqmj7ne.dll +0 -0
- package/wwwroot/_framework/System.Data.Common.1r0sqffq1p.dll +0 -0
- package/wwwroot/_framework/System.Diagnostics.DiagnosticSource.9upoqwq09o.dll +0 -0
- package/wwwroot/_framework/System.Diagnostics.Process.m99azzntjm.dll +0 -0
- package/wwwroot/_framework/System.Diagnostics.TraceSource.pl7wv26myr.dll +0 -0
- package/wwwroot/_framework/System.Diagnostics.Tracing.crlhfx6tut.dll +0 -0
- package/wwwroot/_framework/System.Drawing.Primitives.22e4y9ikq9.dll +0 -0
- package/wwwroot/_framework/System.Drawing.mi7d8hwowb.dll +0 -0
- package/wwwroot/_framework/System.Formats.Asn1.jx23sjiqnn.dll +0 -0
- package/wwwroot/_framework/System.IO.Compression.6fyoii3uej.dll +0 -0
- package/wwwroot/_framework/System.IO.Pipelines.vg77t4cd4d.dll +0 -0
- package/wwwroot/_framework/System.IdentityModel.Tokens.Jwt.t67es60z5b.dll +0 -0
- package/wwwroot/_framework/System.Linq.1bkoxlqgmq.dll +0 -0
- package/wwwroot/_framework/System.Linq.Expressions.24xqiypwdt.dll +0 -0
- package/wwwroot/_framework/System.Linq.Queryable.hvd01d6rsa.dll +0 -0
- package/wwwroot/_framework/System.Memory.8dx3lwgym4.dll +0 -0
- package/wwwroot/_framework/System.Net.Http.Json.3mhdm9l1rf.dll +0 -0
- package/wwwroot/_framework/System.Net.Http.eitrz660my.dll +0 -0
- package/wwwroot/_framework/System.Net.NetworkInformation.3pkuofcv9r.dll +0 -0
- package/wwwroot/_framework/System.Net.Ping.8clj5pklrp.dll +0 -0
- package/wwwroot/_framework/System.Net.Primitives.qrp4wcjz1p.dll +0 -0
- package/wwwroot/_framework/System.Net.WebSockets.Client.2u6pv01g69.dll +0 -0
- package/wwwroot/_framework/System.Net.WebSockets.qp6u31zvm5.dll +0 -0
- package/wwwroot/_framework/System.Numerics.Tensors.0c7z4mt3on.dll +0 -0
- package/wwwroot/_framework/System.Numerics.Vectors.kc7ufp2j4l.dll +0 -0
- package/wwwroot/_framework/System.ObjectModel.qv82fot1ib.dll +0 -0
- package/wwwroot/_framework/System.Private.CoreLib.rkafq04oma.dll +0 -0
- package/wwwroot/_framework/System.Private.Uri.t9542hmr6j.dll +0 -0
- package/wwwroot/_framework/System.Private.Xml.Linq.n8n3ptrbwu.dll +0 -0
- package/wwwroot/_framework/System.Private.Xml.rxd3tytisn.dll +0 -0
- package/wwwroot/_framework/System.Reactive.t3fuon548l.dll +0 -0
- package/wwwroot/_framework/System.Reflection.Emit.9tjhp6y0j3.dll +0 -0
- package/wwwroot/_framework/System.Reflection.Emit.ILGeneration.stxyk8zoo1.dll +0 -0
- package/wwwroot/_framework/System.Reflection.Emit.Lightweight.6xrd5v8vg0.dll +0 -0
- package/wwwroot/_framework/System.Reflection.Primitives.wgn8fpwwvv.dll +0 -0
- package/wwwroot/_framework/System.Runtime.InteropServices.JavaScript.sliym526xh.dll +0 -0
- package/wwwroot/_framework/System.Runtime.InteropServices.RuntimeInformation.oji7zut14z.dll +0 -0
- package/wwwroot/_framework/System.Runtime.InteropServices.te07xr2we9.dll +0 -0
- package/wwwroot/_framework/System.Runtime.Intrinsics.507y4h8nzq.dll +0 -0
- package/wwwroot/_framework/System.Runtime.Loader.v7gk4bse0k.dll +0 -0
- package/wwwroot/_framework/System.Runtime.Numerics.eqy5xjv3nd.dll +0 -0
- package/wwwroot/_framework/System.Runtime.Serialization.Formatters.zpkrub8lab.dll +0 -0
- package/wwwroot/_framework/System.Runtime.Serialization.Primitives.vhkpnbxjip.dll +0 -0
- package/wwwroot/_framework/System.Runtime.jn319d5nyg.dll +0 -0
- package/wwwroot/_framework/System.Security.Claims.0ztig1q9vo.dll +0 -0
- package/wwwroot/_framework/System.Security.Cryptography.vttizqc9ho.dll +0 -0
- package/wwwroot/_framework/System.Text.Encoding.Extensions.utdd47ny8f.dll +0 -0
- package/wwwroot/_framework/System.Text.Encodings.Web.wah8r1zoe0.dll +0 -0
- package/wwwroot/_framework/System.Text.Json.kxlfxj0wrs.dll +0 -0
- package/wwwroot/_framework/System.Text.RegularExpressions.dbqn58klox.dll +0 -0
- package/wwwroot/_framework/System.Threading.42ao9vi047.dll +0 -0
- package/wwwroot/_framework/System.Threading.Channels.hfa7j0uv2w.dll +0 -0
- package/wwwroot/_framework/System.Threading.Thread.caul0pdqul.dll +0 -0
- package/wwwroot/_framework/System.Transactions.Local.fimi2hamzo.dll +0 -0
- package/wwwroot/_framework/System.Web.HttpUtility.gq8yz50p2e.dll +0 -0
- package/wwwroot/_framework/System.Xml.Linq.kitin4zjoj.dll +0 -0
- package/wwwroot/_framework/System.Xml.ReaderWriter.kzvw3qgxb0.dll +0 -0
- package/wwwroot/_framework/System.Xml.XDocument.c539ki6cuq.dll +0 -0
- package/wwwroot/_framework/System.m05i39uvk9.dll +0 -0
- package/wwwroot/_framework/Websocket.Client.vapounvmnl.dll +0 -0
- package/wwwroot/_framework/blazor.boot.json +305 -0
- package/wwwroot/_framework/blazor.webassembly.js +1 -0
- package/wwwroot/_framework/dotnet.js +4 -0
- package/wwwroot/_framework/dotnet.native.vz0adxojrz.wasm +0 -0
- package/wwwroot/_framework/dotnet.native.xsn1d6x2kd.js +16 -0
- package/wwwroot/_framework/dotnet.runtime.dstopyvqzi.js +4 -0
- package/wwwroot/_framework/icudt_CJK.tjcz0u77k5.dat +0 -0
- package/wwwroot/_framework/icudt_EFIGS.tptq2av103.dat +0 -0
- package/wwwroot/_framework/icudt_no_CJK.lfu7j35m59.dat +0 -0
- package/wwwroot/_framework/netstandard.0xet7jg7ky.dll +0 -0
- package/wwwroot/_headers +40 -0
- package/wwwroot/_redirects +1 -0
- package/wwwroot/appsettings.json +71 -0
- package/wwwroot/icon-192.png +0 -0
- package/wwwroot/icon-512.png +0 -0
- package/wwwroot/index.html +710 -0
- package/wwwroot/js/marketing-tool.js +180 -0
- package/wwwroot/manifest.webmanifest +22 -0
- package/wwwroot/robots.txt +4 -0
- package/wwwroot/service-worker-assets.js +857 -0
- package/wwwroot/service-worker.js +33 -0
- package/wwwroot/sitemap.xml +27 -0
|
@@ -0,0 +1,1721 @@
|
|
|
1
|
+
// File: X:\Dev@Work\MindExecution\wwwroot\js\background-themes.js
|
|
2
|
+
// 3D background theme library shared by MindMap, PlanMaster, etc.
|
|
3
|
+
(function () {
|
|
4
|
+
if (typeof globalThis.THREE === 'undefined') {
|
|
5
|
+
console.error('THREE not loaded, BackgroundThemes will not initialize.');
|
|
6
|
+
return;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const THREE = globalThis.THREE;
|
|
10
|
+
let plusTexture = null; // Reusable '+' texture for SpatialGrid
|
|
11
|
+
const THEME_ANIMATION_MODE = Object.freeze({
|
|
12
|
+
None: 'none',
|
|
13
|
+
Camera: 'camera',
|
|
14
|
+
Continuous: 'continuous'
|
|
15
|
+
});
|
|
16
|
+
const THEME_ANIMATION_MODE_BY_NAME = Object.freeze({
|
|
17
|
+
SpatialGrid2D: THEME_ANIMATION_MODE.Camera,
|
|
18
|
+
GridPlane: THEME_ANIMATION_MODE.Camera,
|
|
19
|
+
XYGrid: THEME_ANIMATION_MODE.Camera,
|
|
20
|
+
SpinningBoxes: THEME_ANIMATION_MODE.Continuous,
|
|
21
|
+
Space3D: THEME_ANIMATION_MODE.Continuous,
|
|
22
|
+
SpiritFlow: THEME_ANIMATION_MODE.Continuous,
|
|
23
|
+
None: THEME_ANIMATION_MODE.None
|
|
24
|
+
});
|
|
25
|
+
const THEME_CAMERA_EPSILON = 0.001;
|
|
26
|
+
|
|
27
|
+
// --- Helper 1: create '+' texture (for SpatialGrid) ---
|
|
28
|
+
function createPlusTexture() {
|
|
29
|
+
if (plusTexture) return plusTexture; // Reuse if already created
|
|
30
|
+
|
|
31
|
+
const canvas = document.createElement('canvas');
|
|
32
|
+
canvas.width = 64;
|
|
33
|
+
canvas.height = 64;
|
|
34
|
+
const ctx = canvas.getContext('2d');
|
|
35
|
+
// Mid gray so it remains visible on bright backgrounds
|
|
36
|
+
ctx.fillStyle = '#888888'; // '#333333' -> '#888888'
|
|
37
|
+
ctx.fillRect(28, 4, 8, 56);
|
|
38
|
+
ctx.fillRect(4, 28, 56, 8);
|
|
39
|
+
|
|
40
|
+
plusTexture = new THREE.CanvasTexture(canvas);
|
|
41
|
+
plusTexture.colorSpace = THREE.SRGBColorSpace;
|
|
42
|
+
plusTexture.needsUpdate = true;
|
|
43
|
+
return plusTexture;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// --- Helper 2: dispose theme resources ---
|
|
47
|
+
function disposeThemeObject(themeObject) {
|
|
48
|
+
if (!themeObject) return;
|
|
49
|
+
if (typeof themeObject.customDispose === 'function') {
|
|
50
|
+
themeObject.customDispose();
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
themeObject.traverse(child => {
|
|
54
|
+
if (child.geometry) child.geometry.dispose();
|
|
55
|
+
if (child.material) {
|
|
56
|
+
if (Array.isArray(child.material)) {
|
|
57
|
+
child.material.forEach(m => { if (m.map) m.map.dispose(); m.dispose(); });
|
|
58
|
+
} else {
|
|
59
|
+
if (child.material.map) child.material.map.dispose();
|
|
60
|
+
child.material.dispose();
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function setThemeAnimationMode(themeObject, mode) {
|
|
67
|
+
if (!themeObject) return themeObject;
|
|
68
|
+
if (!themeObject.userData) {
|
|
69
|
+
themeObject.userData = {};
|
|
70
|
+
}
|
|
71
|
+
themeObject.userData.backgroundThemeAnimationMode = mode;
|
|
72
|
+
return themeObject;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function getThemeAnimationMode(themeName, themeObject) {
|
|
76
|
+
const objectMode = themeObject?.userData?.backgroundThemeAnimationMode;
|
|
77
|
+
if (objectMode) {
|
|
78
|
+
return objectMode;
|
|
79
|
+
}
|
|
80
|
+
return THEME_ANIMATION_MODE_BY_NAME[themeName] || THEME_ANIMATION_MODE.None;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function readFiniteNumber(value, fallback = 0) {
|
|
84
|
+
const number = Number(value);
|
|
85
|
+
return Number.isFinite(number) ? number : fallback;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function captureThemeCameraSignature(camera) {
|
|
89
|
+
if (!camera) return null;
|
|
90
|
+
return {
|
|
91
|
+
x: readFiniteNumber(camera.position?.x),
|
|
92
|
+
y: readFiniteNumber(camera.position?.y),
|
|
93
|
+
z: readFiniteNumber(camera.position?.z),
|
|
94
|
+
rx: readFiniteNumber(camera.rotation?.x),
|
|
95
|
+
ry: readFiniteNumber(camera.rotation?.y),
|
|
96
|
+
rz: readFiniteNumber(camera.rotation?.z),
|
|
97
|
+
zoom: readFiniteNumber(camera.zoom, 1),
|
|
98
|
+
fov: readFiniteNumber(camera.fov),
|
|
99
|
+
aspect: readFiniteNumber(camera.aspect)
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function hasThemeCameraChanged(previous, next) {
|
|
104
|
+
if (!previous || !next) return true;
|
|
105
|
+
return Object.keys(next).some(key => Math.abs(next[key] - previous[key]) > THEME_CAMERA_EPSILON);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function getThemeAnimationState(themeObject) {
|
|
109
|
+
if (!themeObject) return null;
|
|
110
|
+
if (!themeObject.userData) {
|
|
111
|
+
themeObject.userData = {};
|
|
112
|
+
}
|
|
113
|
+
if (!themeObject.userData.backgroundThemeAnimationState) {
|
|
114
|
+
themeObject.userData.backgroundThemeAnimationState = {
|
|
115
|
+
lastCameraSignature: null
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
return themeObject.userData.backgroundThemeAnimationState;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function shouldAnimateTheme(themeName, themeObject, camera, options = {}) {
|
|
122
|
+
if (!themeObject || typeof themeObject.animate !== 'function') {
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const mode = getThemeAnimationMode(themeName, themeObject);
|
|
127
|
+
if (mode === THEME_ANIMATION_MODE.Continuous) {
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
130
|
+
if (mode === THEME_ANIMATION_MODE.None) {
|
|
131
|
+
return options.force === true;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const state = getThemeAnimationState(themeObject);
|
|
135
|
+
const nextSignature = captureThemeCameraSignature(camera);
|
|
136
|
+
return options.force === true || hasThemeCameraChanged(state?.lastCameraSignature, nextSignature);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function runThemeAnimationIfNeeded(themeName, themeObject, camera, options = {}) {
|
|
140
|
+
if (!shouldAnimateTheme(themeName, themeObject, camera, options)) {
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
themeObject.animate(camera);
|
|
145
|
+
|
|
146
|
+
const mode = getThemeAnimationMode(themeName, themeObject);
|
|
147
|
+
if (mode === THEME_ANIMATION_MODE.Camera || mode === THEME_ANIMATION_MODE.None) {
|
|
148
|
+
const state = getThemeAnimationState(themeObject);
|
|
149
|
+
if (state) {
|
|
150
|
+
state.lastCameraSignature = captureThemeCameraSignature(camera);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return true;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function resolveThemeGridColor(colorValue, fallback = 0xcccccc) {
|
|
158
|
+
if (colorValue && colorValue.isColor) {
|
|
159
|
+
return colorValue.clone();
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
try {
|
|
163
|
+
return new THREE.Color(colorValue ?? fallback);
|
|
164
|
+
} catch {
|
|
165
|
+
return new THREE.Color(fallback);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function setInfiniteGridHelperOpacity(gridHelper, opacityFactor, baseSmallOpacity, baseLargeOpacity) {
|
|
170
|
+
if (!gridHelper || !gridHelper.children) return;
|
|
171
|
+
|
|
172
|
+
const visible = opacityFactor > 0.01;
|
|
173
|
+
gridHelper.visible = visible;
|
|
174
|
+
if (!visible) {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (gridHelper.children[0]?.material) {
|
|
179
|
+
gridHelper.children[0].material.opacity = baseSmallOpacity * opacityFactor;
|
|
180
|
+
}
|
|
181
|
+
if (gridHelper.children[1]?.material) {
|
|
182
|
+
gridHelper.children[1].material.opacity = baseLargeOpacity * opacityFactor;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function getAdaptiveXYGridOpacityFactors(cameraZ) {
|
|
187
|
+
if (cameraZ < 1800) {
|
|
188
|
+
return { fine: 1.0, coarse: 0.0, far: 0.0 };
|
|
189
|
+
}
|
|
190
|
+
if (cameraZ < 3600) {
|
|
191
|
+
const t = (cameraZ - 1800) / 1800;
|
|
192
|
+
return { fine: 1.0 - t, coarse: t, far: 0.0 };
|
|
193
|
+
}
|
|
194
|
+
if (cameraZ < 12000) {
|
|
195
|
+
return { fine: 0.0, coarse: 1.0, far: 0.0 };
|
|
196
|
+
}
|
|
197
|
+
if (cameraZ < 22000) {
|
|
198
|
+
const t = (cameraZ - 12000) / 10000;
|
|
199
|
+
return { fine: 0.0, coarse: 1.0 - t, far: t };
|
|
200
|
+
}
|
|
201
|
+
if (cameraZ < 50000) {
|
|
202
|
+
return { fine: 0.0, coarse: 0.0, far: 0.9 };
|
|
203
|
+
}
|
|
204
|
+
if (cameraZ < 80000) {
|
|
205
|
+
const t = (cameraZ - 50000) / 30000;
|
|
206
|
+
return { fine: 0.0, coarse: 0.0, far: 0.9 - (t * 0.35) };
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return { fine: 0.0, coarse: 0.0, far: 0.55 };
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function createAdaptiveXYGridOverlay(colorValue, options = {}) {
|
|
213
|
+
const overlayGroup = new THREE.Group();
|
|
214
|
+
const color = resolveThemeGridColor(colorValue, 0xcccccc);
|
|
215
|
+
const tmp = new THREE.Vector3();
|
|
216
|
+
const settings = {
|
|
217
|
+
fineDistance: 45000,
|
|
218
|
+
coarseDistance: 120000,
|
|
219
|
+
farDistance: 320000,
|
|
220
|
+
fineSmallOpacity: 0.4,
|
|
221
|
+
fineLargeOpacity: 0.25,
|
|
222
|
+
coarseSmallOpacity: 0.34,
|
|
223
|
+
coarseLargeOpacity: 0.22,
|
|
224
|
+
farSmallOpacity: 0.26,
|
|
225
|
+
farLargeOpacity: 0.16,
|
|
226
|
+
...options
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
const grids = [
|
|
230
|
+
{
|
|
231
|
+
key: 'fine',
|
|
232
|
+
helper: new window.InfiniteGridHelper(10, 100, color.clone(), settings.fineDistance, 'xyz'),
|
|
233
|
+
snap: 100,
|
|
234
|
+
baseSmallOpacity: settings.fineSmallOpacity,
|
|
235
|
+
baseLargeOpacity: settings.fineLargeOpacity
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
key: 'coarse',
|
|
239
|
+
helper: new window.InfiniteGridHelper(100, 1000, color.clone(), settings.coarseDistance, 'xyz'),
|
|
240
|
+
snap: 1000,
|
|
241
|
+
baseSmallOpacity: settings.coarseSmallOpacity,
|
|
242
|
+
baseLargeOpacity: settings.coarseLargeOpacity
|
|
243
|
+
},
|
|
244
|
+
{
|
|
245
|
+
key: 'far',
|
|
246
|
+
helper: new window.InfiniteGridHelper(1000, 10000, color.clone(), settings.farDistance, 'xyz'),
|
|
247
|
+
snap: 10000,
|
|
248
|
+
baseSmallOpacity: settings.farSmallOpacity,
|
|
249
|
+
baseLargeOpacity: settings.farLargeOpacity
|
|
250
|
+
}
|
|
251
|
+
];
|
|
252
|
+
|
|
253
|
+
grids.forEach((grid, index) => {
|
|
254
|
+
grid.helper.visible = index === 0;
|
|
255
|
+
overlayGroup.add(grid.helper);
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
overlayGroup.animate = (camera) => {
|
|
259
|
+
if (!camera) return;
|
|
260
|
+
|
|
261
|
+
tmp.copy(camera.position);
|
|
262
|
+
tmp.z = 0;
|
|
263
|
+
|
|
264
|
+
grids.forEach(grid => {
|
|
265
|
+
const snappedX = Math.round(tmp.x / grid.snap) * grid.snap;
|
|
266
|
+
const snappedY = Math.round(tmp.y / grid.snap) * grid.snap;
|
|
267
|
+
grid.helper.position.set(snappedX, snappedY, 0);
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
const factors = getAdaptiveXYGridOpacityFactors(camera.position.z);
|
|
271
|
+
grids.forEach(grid => {
|
|
272
|
+
setInfiniteGridHelperOpacity(
|
|
273
|
+
grid.helper,
|
|
274
|
+
factors[grid.key] || 0,
|
|
275
|
+
grid.baseSmallOpacity,
|
|
276
|
+
grid.baseLargeOpacity);
|
|
277
|
+
});
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
return setThemeAnimationMode(overlayGroup, THEME_ANIMATION_MODE.Camera);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// --- Theme definitions ---
|
|
284
|
+
const themes = {
|
|
285
|
+
// Theme 1: SpatialGrid (3D dots + '+' markers)
|
|
286
|
+
'SpatialGrid3D': {
|
|
287
|
+
create: (scene) => {
|
|
288
|
+
scene.background = new THREE.Color(0xf0f2f5); // Bright background
|
|
289
|
+
const gridRange = 50, gridStep = 1, markerStep = 10;
|
|
290
|
+
const markerSize = 0.5, dotSize = 0.1;
|
|
291
|
+
const dotVertices = [], markerVertices = [];
|
|
292
|
+
for (let x = -gridRange; x <= gridRange; x += gridStep) {
|
|
293
|
+
for (let y = -gridRange; y <= gridRange; y += gridStep) {
|
|
294
|
+
for (let z = -gridRange; z <= gridRange; z += gridStep) {
|
|
295
|
+
if (x % markerStep === 0 && y % markerStep === 0 && z % markerStep === 0) { markerVertices.push(x, y, z); }
|
|
296
|
+
else { dotVertices.push(x, y, z); }
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
const themeGroup = new THREE.Group();
|
|
301
|
+
const dotGeom = new THREE.BufferGeometry();
|
|
302
|
+
dotGeom.setAttribute('position', new THREE.Float32BufferAttribute(dotVertices, 3));
|
|
303
|
+
const dotMaterial = new THREE.PointsMaterial({ color: 0x888888, size: dotSize, sizeAttenuation: true });
|
|
304
|
+
themeGroup.add(new THREE.Points(dotGeom, dotMaterial));
|
|
305
|
+
const markerGeom = new THREE.BufferGeometry();
|
|
306
|
+
markerGeom.setAttribute('position', new THREE.Float32BufferAttribute(markerVertices, 3));
|
|
307
|
+
const markerMaterial = new THREE.PointsMaterial({ map: createPlusTexture(), size: markerSize, sizeAttenuation: true, transparent: true, alphaTest: 0.1 });
|
|
308
|
+
themeGroup.add(new THREE.Points(markerGeom, markerMaterial));
|
|
309
|
+
return themeGroup;
|
|
310
|
+
},
|
|
311
|
+
dispose: (themeObject) => { disposeThemeObject(themeObject); }
|
|
312
|
+
},
|
|
313
|
+
|
|
314
|
+
// Theme 2: SpatialGrid2D (dots and '+' markers on the XY plane only)
|
|
315
|
+
'SpatialGrid2D': {
|
|
316
|
+
create: (scene) => {
|
|
317
|
+
scene.background = new THREE.Color(0xf0f2f5); // Bright background
|
|
318
|
+
const gridRange = 10000; // Larger XY plane range
|
|
319
|
+
const gridStep = 10; // Larger dot spacing
|
|
320
|
+
const markerStep = 100; // Larger '+' marker spacing
|
|
321
|
+
const markerSize = 10; // '+' marker size
|
|
322
|
+
const dotSize = 2; // Dot size
|
|
323
|
+
|
|
324
|
+
const dotVertices = [];
|
|
325
|
+
const markerVertices = [];
|
|
326
|
+
|
|
327
|
+
// Iterate X/Y only; keep Z fixed at 0
|
|
328
|
+
for (let x = -gridRange; x <= gridRange; x += gridStep) {
|
|
329
|
+
for (let y = -gridRange; y <= gridRange; y += gridStep) {
|
|
330
|
+
const z = 0; // Keep Z fixed at 0
|
|
331
|
+
|
|
332
|
+
// Check whether XY coordinates are multiples of markerStep
|
|
333
|
+
if (x % markerStep === 0 && y % markerStep === 0) {
|
|
334
|
+
markerVertices.push(x, y, z);
|
|
335
|
+
} else {
|
|
336
|
+
dotVertices.push(x, y, z);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
const themeGroup = new THREE.Group();
|
|
342
|
+
|
|
343
|
+
// 1. Dots
|
|
344
|
+
const dotGeom = new THREE.BufferGeometry();
|
|
345
|
+
dotGeom.setAttribute('position', new THREE.Float32BufferAttribute(dotVertices, 3));
|
|
346
|
+
const dotMaterial = new THREE.PointsMaterial({
|
|
347
|
+
color: 0xaaaaaa, // Dot color (slightly brighter)
|
|
348
|
+
size: dotSize,
|
|
349
|
+
sizeAttenuation: true,
|
|
350
|
+
transparent: true, // Needed for opacity control
|
|
351
|
+
opacity: 1.0,
|
|
352
|
+
depthWrite: false // ▼ [Fix] Prevent Z-fighting with nodes
|
|
353
|
+
});
|
|
354
|
+
const dotMesh = new THREE.Points(dotGeom, dotMaterial);
|
|
355
|
+
dotMesh.renderOrder = -1; // ▼ [Fix] Render before nodes to avoid Z-fighting
|
|
356
|
+
themeGroup.add(dotMesh);
|
|
357
|
+
|
|
358
|
+
// 2. '+' Markers
|
|
359
|
+
const markerGeom = new THREE.BufferGeometry();
|
|
360
|
+
markerGeom.setAttribute('position', new THREE.Float32BufferAttribute(markerVertices, 3));
|
|
361
|
+
const markerMaterial = new THREE.PointsMaterial({
|
|
362
|
+
map: createPlusTexture(),
|
|
363
|
+
size: markerSize,
|
|
364
|
+
sizeAttenuation: true,
|
|
365
|
+
transparent: true,
|
|
366
|
+
alphaTest: 0.1,
|
|
367
|
+
color: 0x888888, // Marker color (slightly darker than dots)
|
|
368
|
+
opacity: 1.0,
|
|
369
|
+
depthWrite: false // ▼ [Fix] Prevent Z-fighting with nodes
|
|
370
|
+
});
|
|
371
|
+
const markerMesh = new THREE.Points(markerGeom, markerMaterial);
|
|
372
|
+
markerMesh.renderOrder = -1; // ▼ [Fix] Render before nodes to avoid Z-fighting
|
|
373
|
+
themeGroup.add(markerMesh);
|
|
374
|
+
|
|
375
|
+
const tmp = new THREE.Vector3();
|
|
376
|
+
const snapSize = markerStep;
|
|
377
|
+
|
|
378
|
+
// ▼▼▼ [New] Distance-based opacity control (see XYGrid) ▼▼▼
|
|
379
|
+
themeGroup.animate = function (camera) {
|
|
380
|
+
if (!camera) return;
|
|
381
|
+
|
|
382
|
+
// Position snapping
|
|
383
|
+
tmp.copy(camera.position);
|
|
384
|
+
tmp.z = 0; // Same plane as nodes (no parallax)
|
|
385
|
+
tmp.x = Math.round(tmp.x / snapSize) * snapSize;
|
|
386
|
+
tmp.y = Math.round(tmp.y / snapSize) * snapSize;
|
|
387
|
+
themeGroup.position.copy(tmp);
|
|
388
|
+
|
|
389
|
+
// Distance-based opacity
|
|
390
|
+
const z = camera.position.z;
|
|
391
|
+
let opacity = 1.0;
|
|
392
|
+
|
|
393
|
+
if (z < 1600) {
|
|
394
|
+
// [~1600] Near: 100%
|
|
395
|
+
opacity = 1.0;
|
|
396
|
+
}
|
|
397
|
+
else if (z >= 1600 && z < 2400) {
|
|
398
|
+
// [1600~2400] Transition: start fading
|
|
399
|
+
const t = (z - 1600) / 800; // 0.0 ~ 1.0
|
|
400
|
+
opacity = 1.0 - (t * 0.5); // 1.0 -> 0.5
|
|
401
|
+
}
|
|
402
|
+
else if (z >= 2400 && z < 4000) {
|
|
403
|
+
// [2400~4000] Mid-range
|
|
404
|
+
opacity = 0.5;
|
|
405
|
+
}
|
|
406
|
+
else if (z >= 4000 && z < 6000) {
|
|
407
|
+
// [4000~6000] Far: fade out
|
|
408
|
+
const t = (z - 4000) / 2000; // 0.0 ~ 1.0
|
|
409
|
+
opacity = 0.5 - (t * 0.5); // 0.5 -> 0
|
|
410
|
+
}
|
|
411
|
+
else {
|
|
412
|
+
// [6000~] Very far: hidden
|
|
413
|
+
opacity = 0;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
// Apply opacity
|
|
417
|
+
dotMaterial.opacity = opacity;
|
|
418
|
+
markerMaterial.opacity = opacity;
|
|
419
|
+
};
|
|
420
|
+
// ▲▲▲ [New] ▲▲▲
|
|
421
|
+
|
|
422
|
+
return setThemeAnimationMode(themeGroup, THEME_ANIMATION_MODE.Camera);
|
|
423
|
+
},
|
|
424
|
+
dispose: (themeObject) => { disposeThemeObject(themeObject); }
|
|
425
|
+
},
|
|
426
|
+
|
|
427
|
+
// Theme 3: GridPlane (basic 2D grid - uses InfiniteGridHelper)
|
|
428
|
+
'GridPlane': {
|
|
429
|
+
create: (scene) => {
|
|
430
|
+
scene.background = new THREE.Color(0xffffff); // Very bright background
|
|
431
|
+
const gridHelper = new window.InfiniteGridHelper(
|
|
432
|
+
10, // size1 (small cells)
|
|
433
|
+
100, // size2 (large cells)
|
|
434
|
+
new THREE.Color(0xcccccc),
|
|
435
|
+
50000, // Distance
|
|
436
|
+
'xzy' // XZ plane
|
|
437
|
+
);
|
|
438
|
+
// ▼▼▼ [Change] Snap from 10 to 100 (align to large grid centers) ▼▼▼
|
|
439
|
+
const tmp = new THREE.Vector3();
|
|
440
|
+
const snap = 100; // size2 (large grid)
|
|
441
|
+
gridHelper.animate = (camera) => {
|
|
442
|
+
if (!camera) return;
|
|
443
|
+
tmp.copy(camera.position);
|
|
444
|
+
// axes = 'xzy' (XZ plane; Y is height)
|
|
445
|
+
tmp.y = -10; // Keep Y fixed below 0 (XZ plane)
|
|
446
|
+
tmp.x = Math.round(tmp.x / snap) * snap;
|
|
447
|
+
tmp.z = Math.round(tmp.z / snap) * snap;
|
|
448
|
+
gridHelper.position.copy(tmp);
|
|
449
|
+
};
|
|
450
|
+
// ▲▲▲ [Change] ▲▲▲
|
|
451
|
+
return setThemeAnimationMode(gridHelper, THEME_ANIMATION_MODE.Camera);
|
|
452
|
+
},
|
|
453
|
+
dispose: (themeObject) => { disposeThemeObject(themeObject); }
|
|
454
|
+
},
|
|
455
|
+
|
|
456
|
+
// Theme 4: SpinningBoxes (reuses 3d-model-viewer logic)
|
|
457
|
+
'SpinningBoxes': {
|
|
458
|
+
create: (scene) => {
|
|
459
|
+
scene.background = new THREE.Color(0xf0f2f5); // Bright background
|
|
460
|
+
|
|
461
|
+
const themeGroup = new THREE.Group();
|
|
462
|
+
|
|
463
|
+
// Lighting (adjusted for scale)
|
|
464
|
+
const ambientLight = new THREE.AmbientLight(0xE0E0E0, 0.8); themeGroup.add(ambientLight);
|
|
465
|
+
const directionalLight = new THREE.DirectionalLight(0xffffff, 1.0); directionalLight.position.set(10, 10, 5); directionalLight.castShadow = true; directionalLight.shadow.mapSize.width = 2048; directionalLight.shadow.mapSize.height = 2048; themeGroup.add(directionalLight);
|
|
466
|
+
const pointLight = new THREE.PointLight(0xd4a022, 1.0, 10000); pointLight.position.set(-500, 500, 500); themeGroup.add(pointLight);
|
|
467
|
+
|
|
468
|
+
// 5000 boxes (InstancedMesh)
|
|
469
|
+
const count = 5000;
|
|
470
|
+
// Box size scaled up (800 -> 16000)
|
|
471
|
+
const geometry = new THREE.BoxGeometry(200, 200, 200);
|
|
472
|
+
const material = new THREE.MeshPhongMaterial({ color: 0x808080, specular: 0x111111, shininess: 100 });
|
|
473
|
+
const instancedMesh = new THREE.InstancedMesh(geometry, material, count); instancedMesh.castShadow = true; instancedMesh.receiveShadow = true;
|
|
474
|
+
|
|
475
|
+
const dummy = new THREE.Object3D();
|
|
476
|
+
for (let i = 0; i < count; i++) {
|
|
477
|
+
// World distribution range expanded + Z offset -2000
|
|
478
|
+
const px = (Math.random() - 0.5) * 20000;
|
|
479
|
+
const py = (Math.random() - 0.5) * 10000;
|
|
480
|
+
const pz = (Math.random() - 0.5) * 20000;
|
|
481
|
+
dummy.position.set(px, py, pz);
|
|
482
|
+
dummy.rotation.set(Math.random() * 2 * Math.PI, Math.random() * 2 * Math.PI, Math.random() * 2 * Math.PI);
|
|
483
|
+
dummy.updateMatrix();
|
|
484
|
+
instancedMesh.setMatrixAt(i, dummy.matrix);
|
|
485
|
+
}
|
|
486
|
+
themeGroup.add(instancedMesh);
|
|
487
|
+
|
|
488
|
+
// Background-only: omit the ground
|
|
489
|
+
|
|
490
|
+
// Animation / dispose logic
|
|
491
|
+
themeGroup.userData.instancedMesh = instancedMesh; themeGroup.userData.pointLight = pointLight;
|
|
492
|
+
themeGroup.animate = (camera) => {
|
|
493
|
+
const mesh = themeGroup.userData.instancedMesh; const light = themeGroup.userData.pointLight;
|
|
494
|
+
if (mesh) { mesh.rotation.x += 0.0005; mesh.rotation.y += 0.001; }
|
|
495
|
+
if (light) {
|
|
496
|
+
const time = Date.now() * 0.0005;
|
|
497
|
+
light.position.x = Math.sin(time * 0.7) * 2000;
|
|
498
|
+
light.position.y = Math.cos(time * 0.5) * 2000;
|
|
499
|
+
light.position.z = Math.cos(time * 0.3) * 2000 - 1000;
|
|
500
|
+
}
|
|
501
|
+
};
|
|
502
|
+
themeGroup.customDispose = () => { disposeThemeObject(themeGroup); themeGroup.userData = {}; themeGroup.animate = undefined; themeGroup.customDispose = undefined; };
|
|
503
|
+
|
|
504
|
+
return setThemeAnimationMode(themeGroup, THEME_ANIMATION_MODE.Continuous);
|
|
505
|
+
},
|
|
506
|
+
dispose: (themeObject) => { if (typeof themeObject?.customDispose === 'function') { themeObject.customDispose(); } else { disposeThemeObject(themeObject); } }
|
|
507
|
+
},
|
|
508
|
+
|
|
509
|
+
// Theme 5: FloorBoxes (boxes placed on the XZ plane)
|
|
510
|
+
'FloorBoxes': {
|
|
511
|
+
create: (scene) => {
|
|
512
|
+
scene.background = new THREE.Color(0xf0f2f5); // Bright background
|
|
513
|
+
|
|
514
|
+
const themeGroup = new THREE.Group();
|
|
515
|
+
|
|
516
|
+
// Lighting
|
|
517
|
+
const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
|
|
518
|
+
themeGroup.add(ambientLight);
|
|
519
|
+
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
|
|
520
|
+
directionalLight.position.set(50, 100, 75);
|
|
521
|
+
directionalLight.castShadow = true;
|
|
522
|
+
directionalLight.shadow.mapSize.width = 1024;
|
|
523
|
+
directionalLight.shadow.mapSize.height = 1024;
|
|
524
|
+
// Shadow camera bounds
|
|
525
|
+
if (directionalLight.shadow && directionalLight.shadow.camera) {
|
|
526
|
+
const cam = directionalLight.shadow.camera;
|
|
527
|
+
cam.left = -200; cam.right = 200; cam.top = 200; cam.bottom = -200;
|
|
528
|
+
cam.near = 0.5; cam.far = 500;
|
|
529
|
+
}
|
|
530
|
+
themeGroup.add(directionalLight);
|
|
531
|
+
|
|
532
|
+
// Boxes
|
|
533
|
+
const count = 10000;
|
|
534
|
+
const boxSize = 500;
|
|
535
|
+
const spread = 500000; // Range on the XZ plane
|
|
536
|
+
const geometry = new THREE.BoxGeometry(boxSize, boxSize, boxSize);
|
|
537
|
+
const material = new THREE.MeshLambertMaterial({ color: 0xaaaaaa });
|
|
538
|
+
const instancedMesh = new THREE.InstancedMesh(geometry, material, count);
|
|
539
|
+
instancedMesh.castShadow = true;
|
|
540
|
+
instancedMesh.receiveShadow = true;
|
|
541
|
+
|
|
542
|
+
const dummy = new THREE.Object3D();
|
|
543
|
+
for (let i = 0; i < count; i++) {
|
|
544
|
+
const px = (Math.random() - 0.5) * spread;
|
|
545
|
+
const pz = (Math.random() - 0.5) * spread;
|
|
546
|
+
const py = (Math.random() * boxSize * 0.5) + (boxSize / 2) - 10000;
|
|
547
|
+
dummy.position.set(px, py, pz);
|
|
548
|
+
//dummy.rotation.set(0, Math.random() * Math.PI *2,0);
|
|
549
|
+
dummy.rotation.set(0, 40, 0);
|
|
550
|
+
const scale = Math.random() * 20; // Random scale
|
|
551
|
+
dummy.scale.set(scale, scale, scale);
|
|
552
|
+
dummy.updateMatrix();
|
|
553
|
+
instancedMesh.setMatrixAt(i, dummy.matrix);
|
|
554
|
+
}
|
|
555
|
+
themeGroup.add(instancedMesh);
|
|
556
|
+
|
|
557
|
+
|
|
558
|
+
// Dispose logic
|
|
559
|
+
themeGroup.customDispose = () => {
|
|
560
|
+
disposeThemeObject(themeGroup);
|
|
561
|
+
themeGroup.userData = {};
|
|
562
|
+
themeGroup.customDispose = undefined;
|
|
563
|
+
};
|
|
564
|
+
|
|
565
|
+
return themeGroup;
|
|
566
|
+
},
|
|
567
|
+
dispose: (themeObject) => {
|
|
568
|
+
if (typeof themeObject?.customDispose === 'function') {
|
|
569
|
+
themeObject.customDispose();
|
|
570
|
+
} else {
|
|
571
|
+
disposeThemeObject(themeObject);
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
},
|
|
575
|
+
|
|
576
|
+
// Theme 6: XYGrid (distance-based auto LOD grid)
|
|
577
|
+
'XYGrid': {
|
|
578
|
+
create: (scene) => {
|
|
579
|
+
scene.background = new THREE.Color(0xfefefe);
|
|
580
|
+
return createAdaptiveXYGridOverlay(new THREE.Color(0xcccccc), {
|
|
581
|
+
fineDistance: 45000,
|
|
582
|
+
coarseDistance: 120000,
|
|
583
|
+
farDistance: 320000,
|
|
584
|
+
fineSmallOpacity: 0.4,
|
|
585
|
+
fineLargeOpacity: 0.25,
|
|
586
|
+
coarseSmallOpacity: 0.34,
|
|
587
|
+
coarseLargeOpacity: 0.22,
|
|
588
|
+
farSmallOpacity: 0.26,
|
|
589
|
+
farLargeOpacity: 0.16
|
|
590
|
+
});
|
|
591
|
+
},
|
|
592
|
+
dispose: (themeObject) => { disposeThemeObject(themeObject); }
|
|
593
|
+
},
|
|
594
|
+
|
|
595
|
+
// Theme 7: Core Particles (inverted galaxy shader particles + faint XYGrid overlay)
|
|
596
|
+
'Space3D': {
|
|
597
|
+
create: (scene, renderer) => {
|
|
598
|
+
scene.background = new THREE.Color(0xfefefe);
|
|
599
|
+
|
|
600
|
+
const themeGroup = new THREE.Group();
|
|
601
|
+
const simSize = 64;
|
|
602
|
+
const pointCount = simSize * simSize;
|
|
603
|
+
const resolution = new THREE.Vector2(simSize, simSize);
|
|
604
|
+
const galaxyCount = 2;
|
|
605
|
+
const galaxyMass = 0.000075;
|
|
606
|
+
const galaxyHaloSize = 6;
|
|
607
|
+
const renderTargetOptions = {
|
|
608
|
+
wrapS: THREE.ClampToEdgeWrapping,
|
|
609
|
+
wrapT: THREE.ClampToEdgeWrapping,
|
|
610
|
+
minFilter: THREE.NearestFilter,
|
|
611
|
+
magFilter: THREE.NearestFilter,
|
|
612
|
+
format: THREE.RGBAFormat,
|
|
613
|
+
type: THREE.FloatType,
|
|
614
|
+
depthBuffer: false,
|
|
615
|
+
stencilBuffer: false
|
|
616
|
+
};
|
|
617
|
+
|
|
618
|
+
const createRenderTarget = () => {
|
|
619
|
+
const target = new THREE.WebGLRenderTarget(simSize, simSize, renderTargetOptions);
|
|
620
|
+
target.texture.generateMipmaps = false;
|
|
621
|
+
return target;
|
|
622
|
+
};
|
|
623
|
+
|
|
624
|
+
const createInitialTextures = () => {
|
|
625
|
+
const positionData = new Float32Array(pointCount * 4);
|
|
626
|
+
const velocityData = new Float32Array(pointCount * 4);
|
|
627
|
+
const randomSigned = () => Math.random() * 2 - 1;
|
|
628
|
+
const particleCountWithoutCenters = pointCount - galaxyCount;
|
|
629
|
+
const firstGalaxyParticleCount = Math.round(particleCountWithoutCenters / 2);
|
|
630
|
+
const galaxyParticleCounts = [
|
|
631
|
+
firstGalaxyParticleCount,
|
|
632
|
+
particleCountWithoutCenters - firstGalaxyParticleCount
|
|
633
|
+
];
|
|
634
|
+
const galaxySizes = [0.78 * galaxyHaloSize, 0.84 * galaxyHaloSize];
|
|
635
|
+
const galaxyInclinations = [0.35 * Math.PI, -0.28 * Math.PI];
|
|
636
|
+
const galaxyPositions = [
|
|
637
|
+
new THREE.Vector3(-1.2 * galaxyHaloSize, -0.1 * galaxyHaloSize, 0),
|
|
638
|
+
new THREE.Vector3(1.2 * galaxyHaloSize, 0.1 * galaxyHaloSize, 0)
|
|
639
|
+
];
|
|
640
|
+
const galaxyVelocities = [
|
|
641
|
+
new THREE.Vector3(0.00145, 0.00014, 0.0018),
|
|
642
|
+
new THREE.Vector3(-0.00145, -0.00014, -0.0018)
|
|
643
|
+
];
|
|
644
|
+
|
|
645
|
+
const writeParticle = (particleIndex, position, velocity) => {
|
|
646
|
+
const offset = particleIndex * 4;
|
|
647
|
+
positionData[offset] = position.x;
|
|
648
|
+
positionData[offset + 1] = position.y;
|
|
649
|
+
positionData[offset + 2] = position.z;
|
|
650
|
+
positionData[offset + 3] = 1;
|
|
651
|
+
velocityData[offset] = velocity.x;
|
|
652
|
+
velocityData[offset + 1] = velocity.y;
|
|
653
|
+
velocityData[offset + 2] = velocity.z;
|
|
654
|
+
velocityData[offset + 3] = 1;
|
|
655
|
+
};
|
|
656
|
+
|
|
657
|
+
for (let i = 0; i < galaxyCount; i++) {
|
|
658
|
+
writeParticle(i, galaxyPositions[i], galaxyVelocities[i]);
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
let startIndex = galaxyCount;
|
|
662
|
+
for (let i = 0; i < galaxyCount; i++) {
|
|
663
|
+
const center = galaxyPositions[i];
|
|
664
|
+
const centerVelocity = galaxyVelocities[i];
|
|
665
|
+
const sinInclination = Math.sin(galaxyInclinations[i]);
|
|
666
|
+
const cosInclination = Math.cos(galaxyInclinations[i]);
|
|
667
|
+
|
|
668
|
+
for (let j = startIndex; j < startIndex + galaxyParticleCounts[i]; j++) {
|
|
669
|
+
const diffuseSeed = Math.random();
|
|
670
|
+
const position = new THREE.Vector3();
|
|
671
|
+
const velocity = new THREE.Vector3();
|
|
672
|
+
|
|
673
|
+
if (diffuseSeed < 0.16) {
|
|
674
|
+
const radius = galaxySizes[i] * (0.72 + Math.pow(Math.random(), 0.55) * 0.62);
|
|
675
|
+
const angle = 2 * Math.PI * Math.random();
|
|
676
|
+
|
|
677
|
+
position.set(
|
|
678
|
+
center.x + Math.cos(angle) * radius + randomSigned() * 0.75,
|
|
679
|
+
center.y + cosInclination * Math.sin(angle) * radius * 0.64 + randomSigned() * 0.72,
|
|
680
|
+
center.z - sinInclination * Math.sin(angle) * radius * 0.64 + randomSigned() * 0.95
|
|
681
|
+
);
|
|
682
|
+
velocity.set(
|
|
683
|
+
-Math.sin(angle) * 0.0034 + centerVelocity.x * 0.66,
|
|
684
|
+
cosInclination * Math.cos(angle) * 0.0021 + centerVelocity.y * 0.66,
|
|
685
|
+
-sinInclination * Math.cos(angle) * 0.0021 + randomSigned() * 0.0009 + centerVelocity.z * 0.66
|
|
686
|
+
);
|
|
687
|
+
} else {
|
|
688
|
+
const distance = galaxySizes[i] * (0.18 + Math.pow(Math.random(), 0.7) * 0.82);
|
|
689
|
+
const safeDistance = Math.max(distance, 0.08);
|
|
690
|
+
const angle = 2 * Math.PI * Math.random();
|
|
691
|
+
const massAtPosition = galaxyMass * Math.min(safeDistance, galaxyHaloSize) / galaxyHaloSize;
|
|
692
|
+
const speed = Math.sqrt(massAtPosition / safeDistance);
|
|
693
|
+
const x = distance * Math.cos(angle);
|
|
694
|
+
const y = distance * Math.sin(angle);
|
|
695
|
+
const vx = -speed * Math.sin(angle) * 1.75;
|
|
696
|
+
const vy = speed * Math.cos(angle) * 1.75;
|
|
697
|
+
|
|
698
|
+
position.set(
|
|
699
|
+
center.x + x,
|
|
700
|
+
center.y + cosInclination * y,
|
|
701
|
+
center.z - sinInclination * y
|
|
702
|
+
);
|
|
703
|
+
velocity.set(
|
|
704
|
+
centerVelocity.x + vx,
|
|
705
|
+
centerVelocity.y + cosInclination * vy,
|
|
706
|
+
centerVelocity.z - sinInclination * vy
|
|
707
|
+
);
|
|
708
|
+
|
|
709
|
+
const jitter = 0.14 + Math.pow(Math.random(), 2.0) * 0.62;
|
|
710
|
+
position.x += randomSigned() * jitter;
|
|
711
|
+
position.y += randomSigned() * jitter * 0.42;
|
|
712
|
+
position.z += randomSigned() * jitter * 0.46;
|
|
713
|
+
|
|
714
|
+
if (Math.random() < 0.16) {
|
|
715
|
+
const direction = i === 0 ? -1 : 1;
|
|
716
|
+
position.x += direction * (0.8 + Math.random() * 2.5);
|
|
717
|
+
position.y += randomSigned() * 0.72;
|
|
718
|
+
position.z += randomSigned() * 0.82;
|
|
719
|
+
velocity.x += direction * (0.00022 + Math.random() * 0.00035);
|
|
720
|
+
velocity.y += randomSigned() * 0.00018;
|
|
721
|
+
velocity.z += randomSigned() * 0.00022;
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
writeParticle(j, position, velocity);
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
startIndex += galaxyParticleCounts[i];
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
const positionTexture = new THREE.DataTexture(positionData, simSize, simSize, THREE.RGBAFormat, THREE.FloatType);
|
|
732
|
+
const velocityTexture = new THREE.DataTexture(velocityData, simSize, simSize, THREE.RGBAFormat, THREE.FloatType);
|
|
733
|
+
positionTexture.needsUpdate = true;
|
|
734
|
+
velocityTexture.needsUpdate = true;
|
|
735
|
+
|
|
736
|
+
return { positionTexture, velocityTexture };
|
|
737
|
+
};
|
|
738
|
+
|
|
739
|
+
const computeVertexShader = `
|
|
740
|
+
void main() {
|
|
741
|
+
gl_Position = vec4(position.xy, 0.0, 1.0);
|
|
742
|
+
}
|
|
743
|
+
`;
|
|
744
|
+
|
|
745
|
+
const copyMaterial = new THREE.ShaderMaterial({
|
|
746
|
+
uniforms: {
|
|
747
|
+
u_texture: { value: null },
|
|
748
|
+
u_resolution: { value: resolution }
|
|
749
|
+
},
|
|
750
|
+
vertexShader: computeVertexShader,
|
|
751
|
+
fragmentShader: `
|
|
752
|
+
precision highp float;
|
|
753
|
+
uniform sampler2D u_texture;
|
|
754
|
+
uniform vec2 u_resolution;
|
|
755
|
+
|
|
756
|
+
void main() {
|
|
757
|
+
vec2 uv = gl_FragCoord.xy / u_resolution;
|
|
758
|
+
gl_FragColor = texture2D(u_texture, uv);
|
|
759
|
+
}
|
|
760
|
+
`,
|
|
761
|
+
depthTest: false,
|
|
762
|
+
depthWrite: false
|
|
763
|
+
});
|
|
764
|
+
|
|
765
|
+
const positionMaterial = new THREE.ShaderMaterial({
|
|
766
|
+
uniforms: {
|
|
767
|
+
u_positionTexture: { value: null },
|
|
768
|
+
u_velocityTexture: { value: null },
|
|
769
|
+
u_resolution: { value: resolution },
|
|
770
|
+
u_dt: { value: 0.18 }
|
|
771
|
+
},
|
|
772
|
+
vertexShader: computeVertexShader,
|
|
773
|
+
fragmentShader: `
|
|
774
|
+
precision highp float;
|
|
775
|
+
uniform sampler2D u_positionTexture;
|
|
776
|
+
uniform sampler2D u_velocityTexture;
|
|
777
|
+
uniform vec2 u_resolution;
|
|
778
|
+
uniform float u_dt;
|
|
779
|
+
|
|
780
|
+
void main() {
|
|
781
|
+
vec2 uv = gl_FragCoord.xy / u_resolution;
|
|
782
|
+
vec3 position = texture2D(u_positionTexture, uv).xyz;
|
|
783
|
+
vec3 velocity = texture2D(u_velocityTexture, uv).xyz;
|
|
784
|
+
float speedSeed = fract(sin(dot(uv, vec2(127.1, 311.7))) * 43758.5453);
|
|
785
|
+
float particleDt = u_dt * mix(0.82, 1.22, speedSeed);
|
|
786
|
+
gl_FragColor = vec4(position + particleDt * velocity, 1.0);
|
|
787
|
+
}
|
|
788
|
+
`,
|
|
789
|
+
depthTest: false,
|
|
790
|
+
depthWrite: false
|
|
791
|
+
});
|
|
792
|
+
|
|
793
|
+
const velocityMaterial = new THREE.ShaderMaterial({
|
|
794
|
+
uniforms: {
|
|
795
|
+
u_positionTexture: { value: null },
|
|
796
|
+
u_velocityTexture: { value: null },
|
|
797
|
+
u_resolution: { value: resolution },
|
|
798
|
+
u_dt: { value: 0.18 },
|
|
799
|
+
u_mass: { value: galaxyMass },
|
|
800
|
+
u_haloSize: { value: galaxyHaloSize },
|
|
801
|
+
u_softening: { value: 1.7 },
|
|
802
|
+
u_damping: { value: 0.991 },
|
|
803
|
+
u_maxSpeed: { value: 0.038 },
|
|
804
|
+
u_velocityDeadzone: { value: 0.0026 },
|
|
805
|
+
u_settleDamping: { value: 0.78 }
|
|
806
|
+
},
|
|
807
|
+
vertexShader: computeVertexShader,
|
|
808
|
+
fragmentShader: `
|
|
809
|
+
precision highp float;
|
|
810
|
+
#define GALAXY_COUNT 2
|
|
811
|
+
|
|
812
|
+
uniform sampler2D u_positionTexture;
|
|
813
|
+
uniform sampler2D u_velocityTexture;
|
|
814
|
+
uniform vec2 u_resolution;
|
|
815
|
+
uniform float u_dt;
|
|
816
|
+
uniform float u_mass;
|
|
817
|
+
uniform float u_haloSize;
|
|
818
|
+
uniform float u_softening;
|
|
819
|
+
uniform float u_damping;
|
|
820
|
+
uniform float u_maxSpeed;
|
|
821
|
+
uniform float u_velocityDeadzone;
|
|
822
|
+
uniform float u_settleDamping;
|
|
823
|
+
|
|
824
|
+
void main() {
|
|
825
|
+
vec2 uv = gl_FragCoord.xy / u_resolution;
|
|
826
|
+
vec3 position = texture2D(u_positionTexture, uv).xyz;
|
|
827
|
+
vec3 velocity = texture2D(u_velocityTexture, uv).xyz;
|
|
828
|
+
vec3 totalForce = vec3(0.0);
|
|
829
|
+
vec3 centerSum = vec3(0.0);
|
|
830
|
+
vec3 nearestCenter = vec3(0.0);
|
|
831
|
+
float nearestDistance = 99999.0;
|
|
832
|
+
|
|
833
|
+
for (int i = 0; i < GALAXY_COUNT; i++) {
|
|
834
|
+
float particleIndex = float(i);
|
|
835
|
+
vec2 particleUv = vec2(
|
|
836
|
+
mod(particleIndex, u_resolution.x) + 0.5,
|
|
837
|
+
floor(particleIndex / u_resolution.x) + 0.5
|
|
838
|
+
) / u_resolution;
|
|
839
|
+
vec3 particlePosition = texture2D(u_positionTexture, particleUv).xyz;
|
|
840
|
+
vec3 forceDirection = particlePosition - position;
|
|
841
|
+
float distance = length(forceDirection);
|
|
842
|
+
centerSum += particlePosition;
|
|
843
|
+
|
|
844
|
+
if (distance > 0.0001) {
|
|
845
|
+
vec3 directionToCenter = forceDirection / distance;
|
|
846
|
+
float nearFade = smoothstep(u_haloSize * 0.45, u_haloSize * 1.05, distance);
|
|
847
|
+
float massAtPosition = u_mass * nearFade * min(distance, u_haloSize) / u_haloSize;
|
|
848
|
+
float coreRepulsion = 1.0 - smoothstep(u_haloSize * 0.42, u_haloSize * 0.88, distance);
|
|
849
|
+
totalForce += massAtPosition * directionToCenter / pow(distance + u_softening, 2.0);
|
|
850
|
+
totalForce -= directionToCenter * coreRepulsion * u_mass * 0.36 / (distance + u_softening);
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
if (distance < nearestDistance) {
|
|
854
|
+
nearestDistance = distance;
|
|
855
|
+
nearestCenter = particlePosition;
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
vec3 barycenter = centerSum / float(GALAXY_COUNT);
|
|
860
|
+
vec3 fromBarycenter = position - barycenter;
|
|
861
|
+
float baryDistance = length(fromBarycenter);
|
|
862
|
+
if (baryDistance > 0.0001) {
|
|
863
|
+
vec3 outwardDirection = fromBarycenter / baryDistance;
|
|
864
|
+
float compression = 1.0 - smoothstep(u_haloSize * 0.65, u_haloSize * 1.75, baryDistance);
|
|
865
|
+
vec3 orbitDirection = vec3(-outwardDirection.y, outwardDirection.x, 0.0);
|
|
866
|
+
float orbitLength = length(orbitDirection);
|
|
867
|
+
totalForce += outwardDirection * compression * u_mass * 0.42 / (baryDistance + 0.75);
|
|
868
|
+
if (orbitLength > 0.0001) {
|
|
869
|
+
totalForce += (orbitDirection / orbitLength) * compression * u_mass * 0.28;
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
vec3 nextVelocity = (velocity + u_dt * totalForce) * u_damping;
|
|
874
|
+
if (nearestDistance > 0.0001 && nearestDistance < u_haloSize * 0.72) {
|
|
875
|
+
vec3 awayFromCore = (position - nearestCenter) / nearestDistance;
|
|
876
|
+
float corePressure = (u_haloSize * 0.72 - nearestDistance) / (u_haloSize * 0.72);
|
|
877
|
+
nextVelocity += awayFromCore * corePressure * 0.0018;
|
|
878
|
+
}
|
|
879
|
+
float settleFactor = 1.0 - smoothstep(u_velocityDeadzone, u_velocityDeadzone * 4.0, length(nextVelocity));
|
|
880
|
+
nextVelocity *= mix(1.0, u_settleDamping, settleFactor);
|
|
881
|
+
float speed = length(nextVelocity);
|
|
882
|
+
if (speed > u_maxSpeed) {
|
|
883
|
+
nextVelocity = normalize(nextVelocity) * u_maxSpeed;
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
gl_FragColor = vec4(nextVelocity, 1.0);
|
|
887
|
+
}
|
|
888
|
+
`,
|
|
889
|
+
depthTest: false,
|
|
890
|
+
depthWrite: false
|
|
891
|
+
});
|
|
892
|
+
|
|
893
|
+
const computeScene = new THREE.Scene();
|
|
894
|
+
const computeCamera = new THREE.Camera();
|
|
895
|
+
const computeQuad = new THREE.Mesh(new THREE.PlaneGeometry(2, 2), copyMaterial);
|
|
896
|
+
computeQuad.frustumCulled = false;
|
|
897
|
+
computeScene.add(computeQuad);
|
|
898
|
+
|
|
899
|
+
const initialTextures = createInitialTextures();
|
|
900
|
+
let positionTargetA = createRenderTarget();
|
|
901
|
+
let positionTargetB = createRenderTarget();
|
|
902
|
+
let velocityTargetA = createRenderTarget();
|
|
903
|
+
let velocityTargetB = createRenderTarget();
|
|
904
|
+
let currentPositionTarget = positionTargetA;
|
|
905
|
+
let nextPositionTarget = positionTargetB;
|
|
906
|
+
let currentVelocityTarget = velocityTargetA;
|
|
907
|
+
let nextVelocityTarget = velocityTargetB;
|
|
908
|
+
let simulationReady = renderer && typeof renderer.setRenderTarget === 'function';
|
|
909
|
+
let simulationWarningShown = false;
|
|
910
|
+
const simulationStepsPerTick = 16;
|
|
911
|
+
|
|
912
|
+
const renderComputePass = (material, target) => {
|
|
913
|
+
computeQuad.material = material;
|
|
914
|
+
renderer.setRenderTarget(target);
|
|
915
|
+
renderer.render(computeScene, computeCamera);
|
|
916
|
+
};
|
|
917
|
+
|
|
918
|
+
const copyTextureToTarget = (texture, target) => {
|
|
919
|
+
copyMaterial.uniforms.u_texture.value = texture;
|
|
920
|
+
renderComputePass(copyMaterial, target);
|
|
921
|
+
};
|
|
922
|
+
|
|
923
|
+
if (simulationReady) {
|
|
924
|
+
const previousTarget = renderer.getRenderTarget ? renderer.getRenderTarget() : null;
|
|
925
|
+
try {
|
|
926
|
+
copyTextureToTarget(initialTextures.positionTexture, currentPositionTarget);
|
|
927
|
+
copyTextureToTarget(initialTextures.velocityTexture, currentVelocityTarget);
|
|
928
|
+
renderer.setRenderTarget(previousTarget);
|
|
929
|
+
} catch (error) {
|
|
930
|
+
simulationReady = false;
|
|
931
|
+
console.warn('[BackgroundThemes] Galaxy particle simulation disabled.', error);
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
const particleGeometry = new THREE.BufferGeometry();
|
|
936
|
+
const indices = new Float32Array(pointCount);
|
|
937
|
+
const dummyPositions = new Float32Array(pointCount * 3);
|
|
938
|
+
for (let i = 0; i < pointCount; i++) {
|
|
939
|
+
indices[i] = i;
|
|
940
|
+
}
|
|
941
|
+
particleGeometry.setAttribute('a_index', new THREE.BufferAttribute(indices, 1));
|
|
942
|
+
particleGeometry.setAttribute('position', new THREE.BufferAttribute(dummyPositions, 3));
|
|
943
|
+
|
|
944
|
+
const particleMaterial = new THREE.ShaderMaterial({
|
|
945
|
+
uniforms: {
|
|
946
|
+
u_width: { value: simSize },
|
|
947
|
+
u_height: { value: simSize },
|
|
948
|
+
u_particleSize: { value: 64800 },
|
|
949
|
+
u_worldScale: { value: 720 },
|
|
950
|
+
u_depthScale: { value: 620 },
|
|
951
|
+
u_backgroundZ: { value: -14000 },
|
|
952
|
+
u_time: { value: 0 },
|
|
953
|
+
u_positionTexture: { value: currentPositionTarget.texture }
|
|
954
|
+
},
|
|
955
|
+
vertexShader: `
|
|
956
|
+
attribute float a_index;
|
|
957
|
+
uniform float u_width;
|
|
958
|
+
uniform float u_height;
|
|
959
|
+
uniform float u_particleSize;
|
|
960
|
+
uniform float u_worldScale;
|
|
961
|
+
uniform float u_depthScale;
|
|
962
|
+
uniform float u_backgroundZ;
|
|
963
|
+
uniform float u_time;
|
|
964
|
+
uniform sampler2D u_positionTexture;
|
|
965
|
+
varying float vDepth;
|
|
966
|
+
varying float vNearness;
|
|
967
|
+
|
|
968
|
+
void main() {
|
|
969
|
+
const float TWO_PI = 6.28318530718;
|
|
970
|
+
vec2 uv = vec2(
|
|
971
|
+
(mod(a_index, u_width) + 0.5) / u_width,
|
|
972
|
+
(floor(a_index / u_width) + 0.5) / u_height
|
|
973
|
+
);
|
|
974
|
+
vec3 gravityPosition = texture2D(u_positionTexture, uv).xyz;
|
|
975
|
+
float seed = fract(sin(a_index * 12.9898) * 43758.5453);
|
|
976
|
+
float sizeSeed = fract(sin((a_index + 17.31) * 78.233) * 43758.5453);
|
|
977
|
+
float depthSeed = fract(sin((a_index + 91.73) * 39.425) * 24634.6345);
|
|
978
|
+
float speedSeed = fract(sin((a_index + 211.13) * 17.317) * 19542.349);
|
|
979
|
+
float phaseSeed = fract(sin((a_index + 47.77) * 63.721) * 31571.231);
|
|
980
|
+
float localTime = u_time * mix(0.72, 1.38, speedSeed) + phaseSeed * TWO_PI;
|
|
981
|
+
float radius = length(gravityPosition.xy);
|
|
982
|
+
float fieldFlow = mod(u_time * mix(0.046, 0.062, speedSeed), TWO_PI);
|
|
983
|
+
float fc = cos(fieldFlow);
|
|
984
|
+
float fs = sin(fieldFlow);
|
|
985
|
+
gravityPosition.xy = mat2(fc, -fs, fs, fc) * gravityPosition.xy;
|
|
986
|
+
|
|
987
|
+
float flow = mod(localTime * mix(0.018, 0.054, seed) * (1.0 - smoothstep(9.0, 18.0, radius)), TWO_PI);
|
|
988
|
+
float c = cos(flow);
|
|
989
|
+
float s = sin(flow);
|
|
990
|
+
gravityPosition.xy = mat2(c, -s, s, c) * gravityPosition.xy;
|
|
991
|
+
gravityPosition.xy += vec2(
|
|
992
|
+
sin(mod(localTime * mix(0.08, 0.22, seed) + seed * TWO_PI, TWO_PI)),
|
|
993
|
+
cos(mod(localTime * mix(0.07, 0.2, seed) + seed * 8.713, TWO_PI))
|
|
994
|
+
) * mix(0.055, 0.18, seed);
|
|
995
|
+
float depthLayer = (depthSeed - 0.5) * mix(4.0, 8.4, sizeSeed);
|
|
996
|
+
float depthDrift = sin(mod(localTime * mix(0.035, 0.1, seed) + seed * 12.421, TWO_PI)) * mix(0.1, 0.34, depthSeed);
|
|
997
|
+
float layeredZ = clamp(gravityPosition.z + depthLayer + depthDrift, -6.8, 4.2);
|
|
998
|
+
vec3 simPosition = vec3(
|
|
999
|
+
gravityPosition.xy * u_worldScale,
|
|
1000
|
+
u_backgroundZ + layeredZ * u_depthScale
|
|
1001
|
+
);
|
|
1002
|
+
vec4 mvPosition = modelViewMatrix * vec4(simPosition, 1.0);
|
|
1003
|
+
float viewDepth = max(-mvPosition.z, 1.0);
|
|
1004
|
+
float seededSize = mix(0.55, 2.05, pow(sizeSeed, 1.42));
|
|
1005
|
+
gl_PointSize = clamp((u_particleSize / viewDepth) * seededSize * 2.0, 1.0, 17.0);
|
|
1006
|
+
gl_Position = projectionMatrix * mvPosition;
|
|
1007
|
+
vDepth = clamp((viewDepth - 9000.0) / 52000.0, 0.0, 1.0);
|
|
1008
|
+
vNearness = 1.0 - clamp((viewDepth - 9000.0) / 28000.0, 0.0, 1.0);
|
|
1009
|
+
}
|
|
1010
|
+
`,
|
|
1011
|
+
fragmentShader: `
|
|
1012
|
+
precision mediump float;
|
|
1013
|
+
varying float vDepth;
|
|
1014
|
+
varying float vNearness;
|
|
1015
|
+
|
|
1016
|
+
void main() {
|
|
1017
|
+
vec2 uv = gl_PointCoord - vec2(0.5);
|
|
1018
|
+
float dist = length(uv);
|
|
1019
|
+
float radius = mix(0.38, 0.49, vNearness);
|
|
1020
|
+
float core = 1.0 - smoothstep(0.02, radius, dist);
|
|
1021
|
+
float halo = (1.0 - smoothstep(0.28, 0.5, dist)) * vNearness * 0.24;
|
|
1022
|
+
float alpha = (pow(max(core, 0.0), mix(1.6, 1.05, vNearness)) + halo) * mix(0.92, 0.46, vDepth);
|
|
1023
|
+
if (alpha < 0.015) discard;
|
|
1024
|
+
gl_FragColor = vec4(vec3(0.0, 0.0, 0.0), min(alpha, 0.94));
|
|
1025
|
+
}
|
|
1026
|
+
`,
|
|
1027
|
+
transparent: true,
|
|
1028
|
+
depthTest: true,
|
|
1029
|
+
depthWrite: false,
|
|
1030
|
+
blending: THREE.NormalBlending
|
|
1031
|
+
});
|
|
1032
|
+
|
|
1033
|
+
const gravityPoints = new THREE.Points(particleGeometry, particleMaterial);
|
|
1034
|
+
gravityPoints.frustumCulled = false;
|
|
1035
|
+
gravityPoints.renderOrder = -40;
|
|
1036
|
+
themeGroup.add(gravityPoints);
|
|
1037
|
+
|
|
1038
|
+
const themeStartTime = (typeof performance !== 'undefined' ? performance.now() : Date.now()) * 0.001;
|
|
1039
|
+
|
|
1040
|
+
const syncParticleView = (camera) => {
|
|
1041
|
+
if (!camera) return;
|
|
1042
|
+
|
|
1043
|
+
const cameraZ = Math.max(600, Math.abs(Number(camera.position?.z || 1200)));
|
|
1044
|
+
const cameraFar = Math.max(120000, Number(camera.far || 500000));
|
|
1045
|
+
const clipSafeDepth = Math.max(14000, cameraFar - cameraZ - 5000);
|
|
1046
|
+
const backgroundDepth = Math.min(Math.max(26000, cameraZ * 6.0), clipSafeDepth * 0.82);
|
|
1047
|
+
const viewDistance = cameraZ + backgroundDepth;
|
|
1048
|
+
const elapsedTime = (typeof performance !== 'undefined' ? performance.now() : Date.now()) * 0.001 - themeStartTime;
|
|
1049
|
+
const animationTime = elapsedTime * 8.0;
|
|
1050
|
+
particleMaterial.uniforms.u_worldScale.value = Math.max(820, backgroundDepth * 0.052);
|
|
1051
|
+
particleMaterial.uniforms.u_depthScale.value = Math.max(1050, backgroundDepth * 0.075);
|
|
1052
|
+
particleMaterial.uniforms.u_backgroundZ.value = -backgroundDepth;
|
|
1053
|
+
particleMaterial.uniforms.u_particleSize.value = Math.max(68000, Math.min(cameraZ * 34, viewDistance * 2.75));
|
|
1054
|
+
particleMaterial.uniforms.u_time.value = animationTime;
|
|
1055
|
+
gravityPoints.rotation.z = (animationTime * 0.085) % (Math.PI * 2);
|
|
1056
|
+
gravityPoints.rotation.x = 0;
|
|
1057
|
+
gravityPoints.position.x = 0;
|
|
1058
|
+
gravityPoints.position.y = 0;
|
|
1059
|
+
gravityPoints.updateMatrixWorld(true);
|
|
1060
|
+
};
|
|
1061
|
+
|
|
1062
|
+
gravityPoints.onBeforeRender = (_renderer, _scene, camera) => {
|
|
1063
|
+
syncParticleView(camera);
|
|
1064
|
+
};
|
|
1065
|
+
|
|
1066
|
+
// --- 2) XYGrid overlay (same distance-based LOD as XYGrid theme) ---
|
|
1067
|
+
const adaptiveGridOverlay = createAdaptiveXYGridOverlay(new THREE.Color(0xc8cdd2), {
|
|
1068
|
+
fineDistance: 45000,
|
|
1069
|
+
coarseDistance: 120000,
|
|
1070
|
+
farDistance: 320000,
|
|
1071
|
+
fineSmallOpacity: 0.17,
|
|
1072
|
+
fineLargeOpacity: 0.1,
|
|
1073
|
+
coarseSmallOpacity: 0.15,
|
|
1074
|
+
coarseLargeOpacity: 0.085,
|
|
1075
|
+
farSmallOpacity: 0.12,
|
|
1076
|
+
farLargeOpacity: 0.07
|
|
1077
|
+
});
|
|
1078
|
+
themeGroup.add(adaptiveGridOverlay);
|
|
1079
|
+
|
|
1080
|
+
// Animation: galaxy texture simulation and camera tracking for grid
|
|
1081
|
+
themeGroup.animate = (camera) => {
|
|
1082
|
+
if (!camera) return;
|
|
1083
|
+
|
|
1084
|
+
if (simulationReady) {
|
|
1085
|
+
const previousTarget = renderer.getRenderTarget ? renderer.getRenderTarget() : null;
|
|
1086
|
+
try {
|
|
1087
|
+
for (let i = 0; i < simulationStepsPerTick; i++) {
|
|
1088
|
+
velocityMaterial.uniforms.u_positionTexture.value = currentPositionTarget.texture;
|
|
1089
|
+
velocityMaterial.uniforms.u_velocityTexture.value = currentVelocityTarget.texture;
|
|
1090
|
+
renderComputePass(velocityMaterial, nextVelocityTarget);
|
|
1091
|
+
|
|
1092
|
+
positionMaterial.uniforms.u_positionTexture.value = currentPositionTarget.texture;
|
|
1093
|
+
positionMaterial.uniforms.u_velocityTexture.value = nextVelocityTarget.texture;
|
|
1094
|
+
renderComputePass(positionMaterial, nextPositionTarget);
|
|
1095
|
+
|
|
1096
|
+
const oldPositionTarget = currentPositionTarget;
|
|
1097
|
+
currentPositionTarget = nextPositionTarget;
|
|
1098
|
+
nextPositionTarget = oldPositionTarget;
|
|
1099
|
+
|
|
1100
|
+
const oldVelocityTarget = currentVelocityTarget;
|
|
1101
|
+
currentVelocityTarget = nextVelocityTarget;
|
|
1102
|
+
nextVelocityTarget = oldVelocityTarget;
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
renderer.setRenderTarget(previousTarget);
|
|
1106
|
+
|
|
1107
|
+
particleMaterial.uniforms.u_positionTexture.value = currentPositionTarget.texture;
|
|
1108
|
+
} catch (error) {
|
|
1109
|
+
simulationReady = false;
|
|
1110
|
+
if (!simulationWarningShown) {
|
|
1111
|
+
simulationWarningShown = true;
|
|
1112
|
+
console.warn('[BackgroundThemes] Galaxy particle simulation stopped.', error);
|
|
1113
|
+
}
|
|
1114
|
+
if (renderer.setRenderTarget) {
|
|
1115
|
+
renderer.setRenderTarget(previousTarget);
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
syncParticleView(camera);
|
|
1121
|
+
|
|
1122
|
+
adaptiveGridOverlay.animate(camera);
|
|
1123
|
+
};
|
|
1124
|
+
|
|
1125
|
+
themeGroup.customDispose = () => {
|
|
1126
|
+
themeGroup.traverse(child => {
|
|
1127
|
+
if (child.geometry) child.geometry.dispose();
|
|
1128
|
+
if (child.material) {
|
|
1129
|
+
if (Array.isArray(child.material)) {
|
|
1130
|
+
child.material.forEach(material => material.dispose());
|
|
1131
|
+
} else {
|
|
1132
|
+
child.material.dispose();
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
});
|
|
1136
|
+
initialTextures.positionTexture.dispose();
|
|
1137
|
+
initialTextures.velocityTexture.dispose();
|
|
1138
|
+
positionTargetA.dispose();
|
|
1139
|
+
positionTargetB.dispose();
|
|
1140
|
+
velocityTargetA.dispose();
|
|
1141
|
+
velocityTargetB.dispose();
|
|
1142
|
+
copyMaterial.dispose();
|
|
1143
|
+
positionMaterial.dispose();
|
|
1144
|
+
velocityMaterial.dispose();
|
|
1145
|
+
particleMaterial.dispose();
|
|
1146
|
+
};
|
|
1147
|
+
|
|
1148
|
+
return setThemeAnimationMode(themeGroup, THEME_ANIMATION_MODE.Continuous);
|
|
1149
|
+
},
|
|
1150
|
+
dispose: (themeObject) => { disposeThemeObject(themeObject); }
|
|
1151
|
+
},
|
|
1152
|
+
|
|
1153
|
+
// Theme 8: SpiritFlow (curl-noise particle flow inspired by ref/~spirit)
|
|
1154
|
+
'SpiritFlow': {
|
|
1155
|
+
create: (scene, renderer) => {
|
|
1156
|
+
scene.background = new THREE.Color(0xf0f2f5);
|
|
1157
|
+
|
|
1158
|
+
const themeGroup = new THREE.Group();
|
|
1159
|
+
const textureWidth = 256;
|
|
1160
|
+
const textureHeight = 256;
|
|
1161
|
+
const pointCount = textureWidth * textureHeight;
|
|
1162
|
+
const resolution = new THREE.Vector2(textureWidth, textureHeight);
|
|
1163
|
+
const renderTargetOptions = {
|
|
1164
|
+
wrapS: THREE.ClampToEdgeWrapping,
|
|
1165
|
+
wrapT: THREE.ClampToEdgeWrapping,
|
|
1166
|
+
minFilter: THREE.NearestFilter,
|
|
1167
|
+
magFilter: THREE.NearestFilter,
|
|
1168
|
+
format: THREE.RGBAFormat,
|
|
1169
|
+
type: THREE.FloatType,
|
|
1170
|
+
depthBuffer: false,
|
|
1171
|
+
stencilBuffer: false
|
|
1172
|
+
};
|
|
1173
|
+
|
|
1174
|
+
const createRenderTarget = () => {
|
|
1175
|
+
const target = new THREE.WebGLRenderTarget(textureWidth, textureHeight, renderTargetOptions);
|
|
1176
|
+
target.texture.generateMipmaps = false;
|
|
1177
|
+
return target;
|
|
1178
|
+
};
|
|
1179
|
+
|
|
1180
|
+
const createDefaultPositionTexture = () => {
|
|
1181
|
+
const data = new Float32Array(pointCount * 4);
|
|
1182
|
+
for (let i = 0; i < pointCount; i++) {
|
|
1183
|
+
const offset = i * 4;
|
|
1184
|
+
const radius = (0.5 + Math.random() * 0.5) * 50;
|
|
1185
|
+
const phi = (Math.random() - 0.5) * Math.PI;
|
|
1186
|
+
const theta = Math.random() * Math.PI * 2;
|
|
1187
|
+
data[offset] = radius * Math.cos(theta) * Math.cos(phi);
|
|
1188
|
+
data[offset + 1] = radius * Math.sin(phi);
|
|
1189
|
+
data[offset + 2] = radius * Math.sin(theta) * Math.cos(phi);
|
|
1190
|
+
data[offset + 3] = Math.random();
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
const texture = new THREE.DataTexture(data, textureWidth, textureHeight, THREE.RGBAFormat, THREE.FloatType);
|
|
1194
|
+
texture.minFilter = THREE.NearestFilter;
|
|
1195
|
+
texture.magFilter = THREE.NearestFilter;
|
|
1196
|
+
texture.generateMipmaps = false;
|
|
1197
|
+
texture.flipY = false;
|
|
1198
|
+
texture.needsUpdate = true;
|
|
1199
|
+
return texture;
|
|
1200
|
+
};
|
|
1201
|
+
|
|
1202
|
+
const computeVertexShader = `
|
|
1203
|
+
void main() {
|
|
1204
|
+
gl_Position = vec4(position.xy, 0.0, 1.0);
|
|
1205
|
+
}
|
|
1206
|
+
`;
|
|
1207
|
+
|
|
1208
|
+
const copyMaterial = new THREE.ShaderMaterial({
|
|
1209
|
+
uniforms: {
|
|
1210
|
+
u_texture: { value: null },
|
|
1211
|
+
u_resolution: { value: resolution }
|
|
1212
|
+
},
|
|
1213
|
+
vertexShader: computeVertexShader,
|
|
1214
|
+
fragmentShader: `
|
|
1215
|
+
precision highp float;
|
|
1216
|
+
uniform sampler2D u_texture;
|
|
1217
|
+
uniform vec2 u_resolution;
|
|
1218
|
+
|
|
1219
|
+
void main() {
|
|
1220
|
+
vec2 uv = gl_FragCoord.xy / u_resolution;
|
|
1221
|
+
gl_FragColor = texture2D(u_texture, uv);
|
|
1222
|
+
}
|
|
1223
|
+
`,
|
|
1224
|
+
depthTest: false,
|
|
1225
|
+
depthWrite: false
|
|
1226
|
+
});
|
|
1227
|
+
|
|
1228
|
+
const positionMaterial = new THREE.ShaderMaterial({
|
|
1229
|
+
uniforms: {
|
|
1230
|
+
u_positionTexture: { value: null },
|
|
1231
|
+
u_defaultTexture: { value: null },
|
|
1232
|
+
u_resolution: { value: resolution },
|
|
1233
|
+
u_mouse3d: { value: new THREE.Vector3() },
|
|
1234
|
+
u_speed: { value: 0.81 },
|
|
1235
|
+
u_dieSpeed: { value: 0 },
|
|
1236
|
+
u_radius: { value: 0.6 },
|
|
1237
|
+
u_curlSize: { value: 0.018 },
|
|
1238
|
+
u_attraction: { value: 0.87 },
|
|
1239
|
+
u_time: { value: 0 },
|
|
1240
|
+
u_initAnimation: { value: 0 }
|
|
1241
|
+
},
|
|
1242
|
+
vertexShader: computeVertexShader,
|
|
1243
|
+
fragmentShader: `
|
|
1244
|
+
precision highp float;
|
|
1245
|
+
|
|
1246
|
+
uniform sampler2D u_positionTexture;
|
|
1247
|
+
uniform sampler2D u_defaultTexture;
|
|
1248
|
+
uniform vec2 u_resolution;
|
|
1249
|
+
uniform vec3 u_mouse3d;
|
|
1250
|
+
uniform float u_speed;
|
|
1251
|
+
uniform float u_dieSpeed;
|
|
1252
|
+
uniform float u_radius;
|
|
1253
|
+
uniform float u_curlSize;
|
|
1254
|
+
uniform float u_attraction;
|
|
1255
|
+
uniform float u_time;
|
|
1256
|
+
uniform float u_initAnimation;
|
|
1257
|
+
|
|
1258
|
+
vec4 mod289(vec4 x) {
|
|
1259
|
+
return x - floor(x * (1.0 / 289.0)) * 289.0;
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
float mod289(float x) {
|
|
1263
|
+
return x - floor(x * (1.0 / 289.0)) * 289.0;
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
vec4 permute(vec4 x) {
|
|
1267
|
+
return mod289(((x * 34.0) + 1.0) * x);
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
float permute(float x) {
|
|
1271
|
+
return mod289(((x * 34.0) + 1.0) * x);
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
vec4 taylorInvSqrt(vec4 r) {
|
|
1275
|
+
return 1.79284291400159 - 0.85373472095314 * r;
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
float taylorInvSqrt(float r) {
|
|
1279
|
+
return 1.79284291400159 - 0.85373472095314 * r;
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1282
|
+
vec4 grad4(float j, vec4 ip) {
|
|
1283
|
+
const vec4 ones = vec4(1.0, 1.0, 1.0, -1.0);
|
|
1284
|
+
vec4 p;
|
|
1285
|
+
vec4 s;
|
|
1286
|
+
|
|
1287
|
+
p.xyz = floor(fract(vec3(j) * ip.xyz) * 7.0) * ip.z - 1.0;
|
|
1288
|
+
p.w = 1.5 - dot(abs(p.xyz), ones.xyz);
|
|
1289
|
+
s = vec4(
|
|
1290
|
+
p.x < 0.0 ? 1.0 : 0.0,
|
|
1291
|
+
p.y < 0.0 ? 1.0 : 0.0,
|
|
1292
|
+
p.z < 0.0 ? 1.0 : 0.0,
|
|
1293
|
+
p.w < 0.0 ? 1.0 : 0.0
|
|
1294
|
+
);
|
|
1295
|
+
p.xyz = p.xyz + (s.xyz * 2.0 - 1.0) * s.www;
|
|
1296
|
+
|
|
1297
|
+
return p;
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
#define F4 0.309016994374947451
|
|
1301
|
+
|
|
1302
|
+
vec4 simplexNoiseDerivatives(vec4 v) {
|
|
1303
|
+
const vec4 C = vec4(0.138196601125011, 0.276393202250021, 0.414589803375032, -0.447213595499958);
|
|
1304
|
+
|
|
1305
|
+
vec4 i = floor(v + dot(v, vec4(F4)));
|
|
1306
|
+
vec4 x0 = v - i + dot(i, C.xxxx);
|
|
1307
|
+
|
|
1308
|
+
vec4 i0;
|
|
1309
|
+
vec3 isX = step(x0.yzw, x0.xxx);
|
|
1310
|
+
vec3 isYZ = step(x0.zww, x0.yyz);
|
|
1311
|
+
i0.x = isX.x + isX.y + isX.z;
|
|
1312
|
+
i0.yzw = 1.0 - isX;
|
|
1313
|
+
i0.y += isYZ.x + isYZ.y;
|
|
1314
|
+
i0.zw += 1.0 - isYZ.xy;
|
|
1315
|
+
i0.z += isYZ.z;
|
|
1316
|
+
i0.w += 1.0 - isYZ.z;
|
|
1317
|
+
|
|
1318
|
+
vec4 i3 = clamp(i0, 0.0, 1.0);
|
|
1319
|
+
vec4 i2 = clamp(i0 - 1.0, 0.0, 1.0);
|
|
1320
|
+
vec4 i1 = clamp(i0 - 2.0, 0.0, 1.0);
|
|
1321
|
+
|
|
1322
|
+
vec4 x1 = x0 - i1 + C.xxxx;
|
|
1323
|
+
vec4 x2 = x0 - i2 + C.yyyy;
|
|
1324
|
+
vec4 x3 = x0 - i3 + C.zzzz;
|
|
1325
|
+
vec4 x4 = x0 + C.wwww;
|
|
1326
|
+
|
|
1327
|
+
i = mod289(i);
|
|
1328
|
+
float j0 = permute(permute(permute(permute(i.w) + i.z) + i.y) + i.x);
|
|
1329
|
+
vec4 j1 = permute(permute(permute(permute(
|
|
1330
|
+
i.w + vec4(i1.w, i2.w, i3.w, 1.0))
|
|
1331
|
+
+ i.z + vec4(i1.z, i2.z, i3.z, 1.0))
|
|
1332
|
+
+ i.y + vec4(i1.y, i2.y, i3.y, 1.0))
|
|
1333
|
+
+ i.x + vec4(i1.x, i2.x, i3.x, 1.0));
|
|
1334
|
+
|
|
1335
|
+
vec4 ip = vec4(1.0 / 294.0, 1.0 / 49.0, 1.0 / 7.0, 0.0);
|
|
1336
|
+
|
|
1337
|
+
vec4 p0 = grad4(j0, ip);
|
|
1338
|
+
vec4 p1 = grad4(j1.x, ip);
|
|
1339
|
+
vec4 p2 = grad4(j1.y, ip);
|
|
1340
|
+
vec4 p3 = grad4(j1.z, ip);
|
|
1341
|
+
vec4 p4 = grad4(j1.w, ip);
|
|
1342
|
+
|
|
1343
|
+
vec4 norm = taylorInvSqrt(vec4(dot(p0, p0), dot(p1, p1), dot(p2, p2), dot(p3, p3)));
|
|
1344
|
+
p0 *= norm.x;
|
|
1345
|
+
p1 *= norm.y;
|
|
1346
|
+
p2 *= norm.z;
|
|
1347
|
+
p3 *= norm.w;
|
|
1348
|
+
p4 *= taylorInvSqrt(dot(p4, p4));
|
|
1349
|
+
|
|
1350
|
+
vec3 values0 = vec3(dot(p0, x0), dot(p1, x1), dot(p2, x2));
|
|
1351
|
+
vec2 values1 = vec2(dot(p3, x3), dot(p4, x4));
|
|
1352
|
+
|
|
1353
|
+
vec3 m0 = max(0.5 - vec3(dot(x0, x0), dot(x1, x1), dot(x2, x2)), 0.0);
|
|
1354
|
+
vec2 m1 = max(0.5 - vec2(dot(x3, x3), dot(x4, x4)), 0.0);
|
|
1355
|
+
|
|
1356
|
+
vec3 temp0 = -6.0 * m0 * m0 * values0;
|
|
1357
|
+
vec2 temp1 = -6.0 * m1 * m1 * values1;
|
|
1358
|
+
|
|
1359
|
+
vec3 mmm0 = m0 * m0 * m0;
|
|
1360
|
+
vec2 mmm1 = m1 * m1 * m1;
|
|
1361
|
+
|
|
1362
|
+
float dx = temp0[0] * x0.x + temp0[1] * x1.x + temp0[2] * x2.x + temp1[0] * x3.x + temp1[1] * x4.x + mmm0[0] * p0.x + mmm0[1] * p1.x + mmm0[2] * p2.x + mmm1[0] * p3.x + mmm1[1] * p4.x;
|
|
1363
|
+
float dy = temp0[0] * x0.y + temp0[1] * x1.y + temp0[2] * x2.y + temp1[0] * x3.y + temp1[1] * x4.y + mmm0[0] * p0.y + mmm0[1] * p1.y + mmm0[2] * p2.y + mmm1[0] * p3.y + mmm1[1] * p4.y;
|
|
1364
|
+
float dz = temp0[0] * x0.z + temp0[1] * x1.z + temp0[2] * x2.z + temp1[0] * x3.z + temp1[1] * x4.z + mmm0[0] * p0.z + mmm0[1] * p1.z + mmm0[2] * p2.z + mmm1[0] * p3.z + mmm1[1] * p4.z;
|
|
1365
|
+
float dw = temp0[0] * x0.w + temp0[1] * x1.w + temp0[2] * x2.w + temp1[0] * x3.w + temp1[1] * x4.w + mmm0[0] * p0.w + mmm0[1] * p1.w + mmm0[2] * p2.w + mmm1[0] * p3.w + mmm1[1] * p4.w;
|
|
1366
|
+
|
|
1367
|
+
return vec4(dx, dy, dz, dw) * 49.0;
|
|
1368
|
+
}
|
|
1369
|
+
|
|
1370
|
+
vec3 curl(vec3 p, float noiseTime, float persistence) {
|
|
1371
|
+
vec4 xNoisePotentialDerivatives = vec4(0.0);
|
|
1372
|
+
vec4 yNoisePotentialDerivatives = vec4(0.0);
|
|
1373
|
+
vec4 zNoisePotentialDerivatives = vec4(0.0);
|
|
1374
|
+
|
|
1375
|
+
for (int i = 0; i < 3; ++i) {
|
|
1376
|
+
float twoPowI = pow(2.0, float(i));
|
|
1377
|
+
float scale = 0.5 * twoPowI * pow(persistence, float(i));
|
|
1378
|
+
|
|
1379
|
+
xNoisePotentialDerivatives += simplexNoiseDerivatives(vec4(p * twoPowI, noiseTime)) * scale;
|
|
1380
|
+
yNoisePotentialDerivatives += simplexNoiseDerivatives(vec4((p + vec3(123.4, 129845.6, -1239.1)) * twoPowI, noiseTime)) * scale;
|
|
1381
|
+
zNoisePotentialDerivatives += simplexNoiseDerivatives(vec4((p + vec3(-9519.0, 9051.0, -123.0)) * twoPowI, noiseTime)) * scale;
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1384
|
+
return vec3(
|
|
1385
|
+
zNoisePotentialDerivatives[1] - yNoisePotentialDerivatives[2],
|
|
1386
|
+
xNoisePotentialDerivatives[2] - zNoisePotentialDerivatives[0],
|
|
1387
|
+
yNoisePotentialDerivatives[0] - xNoisePotentialDerivatives[1]
|
|
1388
|
+
);
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1391
|
+
void main() {
|
|
1392
|
+
vec2 uv = gl_FragCoord.xy / u_resolution;
|
|
1393
|
+
vec4 positionInfo = texture2D(u_positionTexture, uv);
|
|
1394
|
+
vec4 defaultInfo = texture2D(u_defaultTexture, uv);
|
|
1395
|
+
vec3 position = mix(vec3(0.0, -200.0, 0.0), positionInfo.xyz, smoothstep(0.0, 0.3, u_initAnimation));
|
|
1396
|
+
float life = positionInfo.a - u_dieSpeed;
|
|
1397
|
+
|
|
1398
|
+
vec3 followPosition = mix(
|
|
1399
|
+
vec3(0.0, -(1.0 - u_initAnimation) * 200.0, 0.0),
|
|
1400
|
+
u_mouse3d,
|
|
1401
|
+
smoothstep(0.2, 0.7, u_initAnimation)
|
|
1402
|
+
);
|
|
1403
|
+
|
|
1404
|
+
if (life < 0.0) {
|
|
1405
|
+
position = defaultInfo.xyz * (1.0 + sin(u_time * 15.0) * 0.2 + (1.0 - u_initAnimation)) * 0.4 * u_radius;
|
|
1406
|
+
position += followPosition;
|
|
1407
|
+
life = 0.5 + fract(defaultInfo.w * 21.4131 + u_time);
|
|
1408
|
+
} else {
|
|
1409
|
+
vec3 delta = followPosition - position;
|
|
1410
|
+
float distanceToFollow = length(delta);
|
|
1411
|
+
position += delta * (0.005 + life * 0.01) * u_attraction * (1.0 - smoothstep(50.0, 350.0, distanceToFollow)) * u_speed;
|
|
1412
|
+
position += curl(position * u_curlSize, u_time, 0.1 + (1.0 - life) * 0.1) * u_speed;
|
|
1413
|
+
}
|
|
1414
|
+
|
|
1415
|
+
gl_FragColor = vec4(position, life);
|
|
1416
|
+
}
|
|
1417
|
+
`,
|
|
1418
|
+
depthTest: false,
|
|
1419
|
+
depthWrite: false,
|
|
1420
|
+
blending: THREE.NoBlending
|
|
1421
|
+
});
|
|
1422
|
+
|
|
1423
|
+
const computeScene = new THREE.Scene();
|
|
1424
|
+
const computeCamera = new THREE.Camera();
|
|
1425
|
+
const computeQuad = new THREE.Mesh(new THREE.PlaneGeometry(2, 2), copyMaterial);
|
|
1426
|
+
computeQuad.frustumCulled = false;
|
|
1427
|
+
computeScene.add(computeQuad);
|
|
1428
|
+
|
|
1429
|
+
const defaultTexture = createDefaultPositionTexture();
|
|
1430
|
+
let positionTargetA = createRenderTarget();
|
|
1431
|
+
let positionTargetB = createRenderTarget();
|
|
1432
|
+
let currentPositionTarget = positionTargetA;
|
|
1433
|
+
let nextPositionTarget = positionTargetB;
|
|
1434
|
+
let simulationReady = renderer && typeof renderer.setRenderTarget === 'function';
|
|
1435
|
+
let simulationWarningShown = false;
|
|
1436
|
+
|
|
1437
|
+
const renderComputePass = (material, target) => {
|
|
1438
|
+
computeQuad.material = material;
|
|
1439
|
+
renderer.setRenderTarget(target);
|
|
1440
|
+
renderer.render(computeScene, computeCamera);
|
|
1441
|
+
};
|
|
1442
|
+
|
|
1443
|
+
const copyTextureToTarget = (texture, target) => {
|
|
1444
|
+
copyMaterial.uniforms.u_texture.value = texture;
|
|
1445
|
+
renderComputePass(copyMaterial, target);
|
|
1446
|
+
};
|
|
1447
|
+
|
|
1448
|
+
if (simulationReady) {
|
|
1449
|
+
const previousTarget = renderer.getRenderTarget ? renderer.getRenderTarget() : null;
|
|
1450
|
+
try {
|
|
1451
|
+
copyTextureToTarget(defaultTexture, currentPositionTarget);
|
|
1452
|
+
copyTextureToTarget(defaultTexture, nextPositionTarget);
|
|
1453
|
+
renderer.setRenderTarget(previousTarget);
|
|
1454
|
+
} catch (error) {
|
|
1455
|
+
simulationReady = false;
|
|
1456
|
+
console.warn('[BackgroundThemes] Spirit particle simulation disabled.', error);
|
|
1457
|
+
if (renderer.setRenderTarget) {
|
|
1458
|
+
renderer.setRenderTarget(previousTarget);
|
|
1459
|
+
}
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1462
|
+
|
|
1463
|
+
const particleGeometry = new THREE.BufferGeometry();
|
|
1464
|
+
const particleUvs = new Float32Array(pointCount * 3);
|
|
1465
|
+
for (let i = 0; i < pointCount; i++) {
|
|
1466
|
+
const offset = i * 3;
|
|
1467
|
+
particleUvs[offset] = (i % textureWidth + 0.5) / textureWidth;
|
|
1468
|
+
particleUvs[offset + 1] = (Math.floor(i / textureWidth) + 0.5) / textureHeight;
|
|
1469
|
+
particleUvs[offset + 2] = 0;
|
|
1470
|
+
}
|
|
1471
|
+
particleGeometry.setAttribute('position', new THREE.BufferAttribute(particleUvs, 3));
|
|
1472
|
+
|
|
1473
|
+
const backdropMaterial = new THREE.ShaderMaterial({
|
|
1474
|
+
vertexShader: `
|
|
1475
|
+
varying vec2 vUv;
|
|
1476
|
+
|
|
1477
|
+
void main() {
|
|
1478
|
+
vUv = uv;
|
|
1479
|
+
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
|
1480
|
+
}
|
|
1481
|
+
`,
|
|
1482
|
+
fragmentShader: `
|
|
1483
|
+
precision mediump float;
|
|
1484
|
+
varying vec2 vUv;
|
|
1485
|
+
|
|
1486
|
+
void main() {
|
|
1487
|
+
vec3 topColor = vec3(0.84, 0.85, 0.84);
|
|
1488
|
+
vec3 horizonColor = vec3(0.97, 0.97, 0.94);
|
|
1489
|
+
vec3 bottomColor = vec3(0.72, 0.73, 0.72);
|
|
1490
|
+
float horizon = 1.0 - smoothstep(0.20, 0.64, abs(vUv.y - 0.53));
|
|
1491
|
+
vec3 vertical = mix(bottomColor, topColor, smoothstep(0.0, 1.0, vUv.y));
|
|
1492
|
+
vec3 color = mix(vertical, horizonColor, horizon * 0.78);
|
|
1493
|
+
gl_FragColor = vec4(color, 1.0);
|
|
1494
|
+
}
|
|
1495
|
+
`,
|
|
1496
|
+
depthTest: false,
|
|
1497
|
+
depthWrite: false
|
|
1498
|
+
});
|
|
1499
|
+
const backdrop = new THREE.Mesh(new THREE.PlaneGeometry(1, 1), backdropMaterial);
|
|
1500
|
+
backdrop.frustumCulled = false;
|
|
1501
|
+
backdrop.renderOrder = -90;
|
|
1502
|
+
themeGroup.add(backdrop);
|
|
1503
|
+
|
|
1504
|
+
const particleMaterial = new THREE.ShaderMaterial({
|
|
1505
|
+
uniforms: {
|
|
1506
|
+
u_positionTexture: { value: simulationReady ? currentPositionTarget.texture : defaultTexture },
|
|
1507
|
+
u_particleSize: { value: 42000 },
|
|
1508
|
+
u_worldScale: { value: 120 },
|
|
1509
|
+
u_depthScale: { value: 85 },
|
|
1510
|
+
u_backgroundZ: { value: -26000 },
|
|
1511
|
+
u_color: { value: new THREE.Color(0xffffff) }
|
|
1512
|
+
},
|
|
1513
|
+
vertexShader: `
|
|
1514
|
+
uniform sampler2D u_positionTexture;
|
|
1515
|
+
uniform float u_particleSize;
|
|
1516
|
+
uniform float u_worldScale;
|
|
1517
|
+
uniform float u_depthScale;
|
|
1518
|
+
uniform float u_backgroundZ;
|
|
1519
|
+
varying float vLife;
|
|
1520
|
+
varying float vAlpha;
|
|
1521
|
+
varying float vNearness;
|
|
1522
|
+
varying float vShade;
|
|
1523
|
+
|
|
1524
|
+
void main() {
|
|
1525
|
+
vec4 positionInfo = texture2D(u_positionTexture, position.xy);
|
|
1526
|
+
float life = clamp(positionInfo.w, 0.0, 1.0);
|
|
1527
|
+
vec3 flowPosition = positionInfo.xyz;
|
|
1528
|
+
float sizeSeed = fract(sin(dot(position.xy, vec2(127.1, 311.7))) * 43758.5453);
|
|
1529
|
+
|
|
1530
|
+
vec3 simPosition = vec3(
|
|
1531
|
+
flowPosition.x * u_worldScale,
|
|
1532
|
+
flowPosition.y * u_worldScale,
|
|
1533
|
+
u_backgroundZ + flowPosition.z * u_depthScale
|
|
1534
|
+
);
|
|
1535
|
+
|
|
1536
|
+
vec4 mvPosition = modelViewMatrix * vec4(simPosition, 1.0);
|
|
1537
|
+
float viewDepth = max(-mvPosition.z, 1.0);
|
|
1538
|
+
float fadeOut = smoothstep(0.0, 0.28, life);
|
|
1539
|
+
float seededSize = mix(0.62, 2.15, pow(sizeSeed, 1.35));
|
|
1540
|
+
float nearness = 1.0 - clamp((viewDepth - 9000.0) / 28000.0, 0.0, 1.0);
|
|
1541
|
+
float depthBoost = mix(0.78, 1.62, nearness);
|
|
1542
|
+
gl_PointSize = clamp((u_particleSize / viewDepth) * seededSize * depthBoost * fadeOut, 0.12, 9.6);
|
|
1543
|
+
gl_Position = projectionMatrix * mvPosition;
|
|
1544
|
+
vLife = life;
|
|
1545
|
+
vAlpha = fadeOut;
|
|
1546
|
+
vNearness = nearness;
|
|
1547
|
+
float upperLight = smoothstep(-20.0, 115.0, flowPosition.y);
|
|
1548
|
+
float frontLight = smoothstep(-90.0, 85.0, flowPosition.z);
|
|
1549
|
+
vShade = mix(0.46, 1.18, clamp(upperLight * 0.72 + frontLight * 0.35, 0.0, 1.0));
|
|
1550
|
+
}
|
|
1551
|
+
`,
|
|
1552
|
+
fragmentShader: `
|
|
1553
|
+
precision mediump float;
|
|
1554
|
+
uniform vec3 u_color;
|
|
1555
|
+
varying float vLife;
|
|
1556
|
+
varying float vAlpha;
|
|
1557
|
+
varying float vNearness;
|
|
1558
|
+
varying float vShade;
|
|
1559
|
+
|
|
1560
|
+
void main() {
|
|
1561
|
+
vec2 uv = gl_PointCoord - vec2(0.5);
|
|
1562
|
+
float dist = length(uv);
|
|
1563
|
+
float core = 1.0 - smoothstep(0.16, mix(0.44, 0.5, vNearness), dist);
|
|
1564
|
+
float halo = (1.0 - smoothstep(0.3, 0.5, dist)) * vNearness * 0.22;
|
|
1565
|
+
float particleAlpha = (core + halo) * vAlpha;
|
|
1566
|
+
if (particleAlpha < 0.01) discard;
|
|
1567
|
+
vec3 outgoingLight = mix(u_color * 0.58, u_color, smoothstep(0.0, 0.7, vLife));
|
|
1568
|
+
gl_FragColor = vec4(outgoingLight * vShade, min(particleAlpha, 0.96));
|
|
1569
|
+
}
|
|
1570
|
+
`,
|
|
1571
|
+
transparent: true,
|
|
1572
|
+
depthTest: true,
|
|
1573
|
+
depthWrite: false,
|
|
1574
|
+
blending: THREE.NormalBlending
|
|
1575
|
+
});
|
|
1576
|
+
|
|
1577
|
+
const particles = new THREE.Points(particleGeometry, particleMaterial);
|
|
1578
|
+
particles.frustumCulled = false;
|
|
1579
|
+
particles.renderOrder = -40;
|
|
1580
|
+
themeGroup.add(particles);
|
|
1581
|
+
|
|
1582
|
+
const themeStartTime = (typeof performance !== 'undefined' ? performance.now() : Date.now()) * 0.001;
|
|
1583
|
+
let lastElapsedTime = 0;
|
|
1584
|
+
let followPointTime = 0;
|
|
1585
|
+
const followPoint = new THREE.Vector3(0, 0, 0);
|
|
1586
|
+
const autoFollowPoint = new THREE.Vector3(0, 0, 0);
|
|
1587
|
+
const spiritMotion = Object.freeze({
|
|
1588
|
+
followTimeScale: 0.79,
|
|
1589
|
+
followBlend: 0.16,
|
|
1590
|
+
followX: 182,
|
|
1591
|
+
followY: 49,
|
|
1592
|
+
followZ: 132,
|
|
1593
|
+
depthTravelScale: 0.67,
|
|
1594
|
+
particleSpeed: 0.81,
|
|
1595
|
+
lifeDecay: 0.013
|
|
1596
|
+
});
|
|
1597
|
+
|
|
1598
|
+
const syncParticleView = (camera, elapsedTime) => {
|
|
1599
|
+
if (!camera) return;
|
|
1600
|
+
|
|
1601
|
+
const cameraZ = Math.max(600, Math.abs(Number(camera.position?.z || 1200)));
|
|
1602
|
+
const cameraFar = Math.max(120000, Number(camera.far || 500000));
|
|
1603
|
+
const clipSafeDepth = Math.max(14000, cameraFar - cameraZ - 5000);
|
|
1604
|
+
const backgroundDepth = Math.min(Math.max(26000, cameraZ * 6.0), clipSafeDepth * 0.82);
|
|
1605
|
+
const viewDistance = cameraZ + backgroundDepth;
|
|
1606
|
+
const fovRadians = THREE.MathUtils?.degToRad
|
|
1607
|
+
? THREE.MathUtils.degToRad(Number(camera.fov || 45))
|
|
1608
|
+
: Number(camera.fov || 45) * Math.PI / 180;
|
|
1609
|
+
const viewportHeight = Math.max(1, Math.tan(fovRadians * 0.5) * viewDistance * 2);
|
|
1610
|
+
const viewportWidth = viewportHeight * Math.max(0.1, Number(camera.aspect || 1));
|
|
1611
|
+
particleMaterial.uniforms.u_worldScale.value = Math.max(105, backgroundDepth * 0.0043);
|
|
1612
|
+
particleMaterial.uniforms.u_depthScale.value = Math.max(56, backgroundDepth * 0.0037 * spiritMotion.depthTravelScale);
|
|
1613
|
+
particleMaterial.uniforms.u_backgroundZ.value = -backgroundDepth;
|
|
1614
|
+
particleMaterial.uniforms.u_particleSize.value = Math.max(44000, viewDistance * 1.92);
|
|
1615
|
+
particles.rotation.z = Math.sin(elapsedTime * 0.08) * 0.08;
|
|
1616
|
+
particles.rotation.x = Math.sin(elapsedTime * 0.11) * 0.05;
|
|
1617
|
+
particles.position.x = Number(camera.position?.x || 0);
|
|
1618
|
+
particles.position.y = Number(camera.position?.y || 0);
|
|
1619
|
+
particles.updateMatrixWorld(true);
|
|
1620
|
+
backdrop.position.x = Number(camera.position?.x || 0);
|
|
1621
|
+
backdrop.position.y = Number(camera.position?.y || 0);
|
|
1622
|
+
backdrop.position.z = -backgroundDepth - 1200;
|
|
1623
|
+
backdrop.scale.set(viewportWidth * 1.12, viewportHeight * 1.12, 1);
|
|
1624
|
+
backdrop.updateMatrixWorld(true);
|
|
1625
|
+
};
|
|
1626
|
+
|
|
1627
|
+
particles.onBeforeRender = (_renderer, _scene, camera) => {
|
|
1628
|
+
const elapsedTime = (typeof performance !== 'undefined' ? performance.now() : Date.now()) * 0.001 - themeStartTime;
|
|
1629
|
+
syncParticleView(camera, elapsedTime);
|
|
1630
|
+
};
|
|
1631
|
+
|
|
1632
|
+
themeGroup.animate = (camera) => {
|
|
1633
|
+
if (!camera) return;
|
|
1634
|
+
|
|
1635
|
+
const elapsedTime = (typeof performance !== 'undefined' ? performance.now() : Date.now()) * 0.001 - themeStartTime;
|
|
1636
|
+
const deltaMs = Math.min(48, Math.max(0, (elapsedTime - lastElapsedTime) * 1000 || 16.6667));
|
|
1637
|
+
const deltaRatio = deltaMs / 16.6667;
|
|
1638
|
+
lastElapsedTime = elapsedTime;
|
|
1639
|
+
|
|
1640
|
+
followPointTime += deltaMs * 0.001 * spiritMotion.followTimeScale;
|
|
1641
|
+
autoFollowPoint.set(
|
|
1642
|
+
Math.cos(followPointTime * 0.9) * spiritMotion.followX,
|
|
1643
|
+
Math.cos(followPointTime * 2.8) * spiritMotion.followY,
|
|
1644
|
+
Math.sin(followPointTime * 1.55) * spiritMotion.followZ
|
|
1645
|
+
);
|
|
1646
|
+
followPoint.lerp(autoFollowPoint, Math.min(1.0, spiritMotion.followBlend * deltaRatio));
|
|
1647
|
+
|
|
1648
|
+
if (simulationReady) {
|
|
1649
|
+
const previousTarget = renderer.getRenderTarget ? renderer.getRenderTarget() : null;
|
|
1650
|
+
try {
|
|
1651
|
+
positionMaterial.uniforms.u_positionTexture.value = currentPositionTarget.texture;
|
|
1652
|
+
positionMaterial.uniforms.u_defaultTexture.value = defaultTexture;
|
|
1653
|
+
positionMaterial.uniforms.u_mouse3d.value.copy(followPoint);
|
|
1654
|
+
positionMaterial.uniforms.u_speed.value = spiritMotion.particleSpeed * deltaRatio;
|
|
1655
|
+
positionMaterial.uniforms.u_dieSpeed.value = spiritMotion.lifeDecay * deltaRatio;
|
|
1656
|
+
positionMaterial.uniforms.u_time.value = elapsedTime;
|
|
1657
|
+
positionMaterial.uniforms.u_initAnimation.value = Math.min(1, elapsedTime * 0.25);
|
|
1658
|
+
renderComputePass(positionMaterial, nextPositionTarget);
|
|
1659
|
+
|
|
1660
|
+
const oldPositionTarget = currentPositionTarget;
|
|
1661
|
+
currentPositionTarget = nextPositionTarget;
|
|
1662
|
+
nextPositionTarget = oldPositionTarget;
|
|
1663
|
+
renderer.setRenderTarget(previousTarget);
|
|
1664
|
+
|
|
1665
|
+
particleMaterial.uniforms.u_positionTexture.value = currentPositionTarget.texture;
|
|
1666
|
+
} catch (error) {
|
|
1667
|
+
simulationReady = false;
|
|
1668
|
+
if (!simulationWarningShown) {
|
|
1669
|
+
simulationWarningShown = true;
|
|
1670
|
+
console.warn('[BackgroundThemes] Spirit particle simulation stopped.', error);
|
|
1671
|
+
}
|
|
1672
|
+
if (renderer.setRenderTarget) {
|
|
1673
|
+
renderer.setRenderTarget(previousTarget);
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1676
|
+
}
|
|
1677
|
+
|
|
1678
|
+
syncParticleView(camera, elapsedTime);
|
|
1679
|
+
};
|
|
1680
|
+
|
|
1681
|
+
themeGroup.customDispose = () => {
|
|
1682
|
+
themeGroup.traverse(child => {
|
|
1683
|
+
if (child.geometry) child.geometry.dispose();
|
|
1684
|
+
if (child.material) {
|
|
1685
|
+
if (Array.isArray(child.material)) {
|
|
1686
|
+
child.material.forEach(material => material.dispose());
|
|
1687
|
+
} else {
|
|
1688
|
+
child.material.dispose();
|
|
1689
|
+
}
|
|
1690
|
+
}
|
|
1691
|
+
});
|
|
1692
|
+
defaultTexture.dispose();
|
|
1693
|
+
positionTargetA.dispose();
|
|
1694
|
+
positionTargetB.dispose();
|
|
1695
|
+
copyMaterial.dispose();
|
|
1696
|
+
positionMaterial.dispose();
|
|
1697
|
+
particleMaterial.dispose();
|
|
1698
|
+
backdropMaterial.dispose();
|
|
1699
|
+
};
|
|
1700
|
+
|
|
1701
|
+
return setThemeAnimationMode(themeGroup, THEME_ANIMATION_MODE.Continuous);
|
|
1702
|
+
},
|
|
1703
|
+
dispose: (themeObject) => { disposeThemeObject(themeObject); }
|
|
1704
|
+
},
|
|
1705
|
+
|
|
1706
|
+
// Theme 9: None (no background)
|
|
1707
|
+
'None': { create: (scene) => { scene.background = new THREE.Color(0xf0f2f5); return null; }, dispose: (themeObject) => { } }
|
|
1708
|
+
};
|
|
1709
|
+
|
|
1710
|
+
// --- Expose theme manager on window ---
|
|
1711
|
+
window.MindMapThemes = {
|
|
1712
|
+
getTheme: (themeName) => themes[themeName],
|
|
1713
|
+
getAnimationMode: getThemeAnimationMode,
|
|
1714
|
+
shouldAnimate: shouldAnimateTheme,
|
|
1715
|
+
runAnimationIfNeeded: runThemeAnimationIfNeeded,
|
|
1716
|
+
disposeShared: () => { if (plusTexture) { plusTexture.dispose(); plusTexture = null; } console.log('[BackgroundThemes] Shared resources disposed.'); }
|
|
1717
|
+
};
|
|
1718
|
+
|
|
1719
|
+
console.log('✅ background-themes.js loaded (InfiniteGridHelper integrated)');
|
|
1720
|
+
})();
|
|
1721
|
+
|