grid toggle button and better grid look

This commit is contained in:
bflatastic committed 2025-04-10 23:31:05 +07:00
1 parent f22d267386
commit 06041a01e6
4 files changed
+175 -13

No files matched your search

@@ -0,0 +1,96 @@
import * as THREE from "three";
export class InfiniteGridHelper extends THREE.Mesh {
constructor(
size1?: number,
size2?: number,
color?: THREE.Color,
distance?: number,
axes: string = "xzy",
) {
color = color || new THREE.Color("white");
size1 = size1 || 10;
size2 = size2 || 100;
distance = distance || 8000;
const planeAxes = axes.substr(0, 2);
const geometry = new THREE.PlaneGeometry(2, 2, 1, 1);
const material = new THREE.ShaderMaterial({
side: THREE.DoubleSide,
uniforms: {
uSize1: {
value: size1,
},
uSize2: {
value: size2,
},
uColor: {
value: color,
},
uDistance: {
value: distance,
},
uFadeStrength: {
value: 1.5,
},
uFadeStart: {
value: 0.2,
},
},
transparent: true,
vertexShader: `
varying vec3 worldPosition;
uniform float uDistance;
void main() {
vec3 pos = position.${axes} * uDistance;
pos.${planeAxes} += cameraPosition.${planeAxes};
worldPosition = pos;
gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
}
`,
fragmentShader: `
varying vec3 worldPosition;
uniform float uSize1;
uniform float uSize2;
uniform vec3 uColor;
uniform float uDistance;
uniform float uFadeStrength;
uniform float uFadeStart;
float getGrid(float size) {
vec2 r = worldPosition.${planeAxes} / size;
vec2 grid = abs(fract(r - 0.5) - 0.5) / (fwidth(r) * 1.5);
float line = min(grid.x, grid.y);
return 1.0 - min(line, 1.0);
}
void main() {
float distFromCamera = length(cameraPosition.${planeAxes} - worldPosition.${planeAxes}) / uDistance;
float fadeOut = 1.0 - smoothstep(uFadeStart, 1.0, distFromCamera);
fadeOut = pow(fadeOut, uFadeStrength);
float g1 = getGrid(uSize1);
float g2 = getGrid(uSize2);
float grid = mix(g2 * 0.3, g1, g1);
gl_FragColor = vec4(uColor.rgb, grid * fadeOut);
gl_FragColor.a = mix(0.5 * gl_FragColor.a, gl_FragColor.a, g2);
if (gl_FragColor.a <= 0.0) discard;
}
`,
extensions: {
derivatives: true,
},
});
super(geometry, material);
this.frustumCulled = false;
}
}
@@ -15,10 +15,13 @@ import { ClipGroup, ClipType } from "~/enums";
import { ClipUI } from "../clips/clip_ui";
import { GetCdnOrigin } from "~/api/GetCdnOrigin";
import { GetFrontendEnvironment } from "~/Classes/GetFrontendEnvironment";
import { gridVisibility } from "../signals/engine";
import { InfiniteGridHelper } from "./InfiniteGridHelper";
class Scene {
name: string;
gridHelper: THREE.GridHelper | undefined;
gridHelper: InfiniteGridHelper | undefined;
groundPlane: THREE.Mesh | undefined;
scene: THREE.Scene;
hot_items: THREE.Object3D[] | undefined;
@@ -87,6 +90,19 @@ class Scene {
this.helper = new MMDAnimationHelper({ afterglow: 0.0 });
this.scene.userData["helper"] = this.helper;
// Subscribe to grid visibility changes
gridVisibility.subscribe((isVisible) => {
if (this.gridHelper) {
if (isVisible) {
if (!this.scene.children.includes(this.gridHelper)) {
this.scene.add(this.gridHelper);
}
} else {
this.scene.remove(this.gridHelper);
}
}
});
}
clear() {
@@ -357,9 +373,9 @@ class Scene {
const camera_position = new THREE.Vector3(0, 0.6, 1.5);
// TODO(bt,2025-02-10): Make the local dev assets more configurable or seedable.
const camera_id = GetFrontendEnvironment().getIsLocalDev() ?
"m_4djqzq89mbn6sdbbqm25ks6drhr55k" : // Local development
"m_cxh4asqhapdz10j880755dg4yevshb"; // Production
const camera_id = GetFrontendEnvironment().getIsLocalDev()
? "m_4djqzq89mbn6sdbbqm25ks6drhr55k" // Local development
: "m_cxh4asqhapdz10j880755dg4yevshb"; // Production
const camera_obj = await this.loadGlbWithPlaceholder(
camera_id,
@@ -848,16 +864,40 @@ class Scene {
}
_createGrid() {
const size = 25;
const divisions = 50;
this.gridHelper = new THREE.GridHelper(
size,
divisions,
new THREE.Color("rgb(199,195,195)"),
new THREE.Color("rgb(161,157,157)"),
);
// Create visual infinite grid
const size1 = 0.5; // Primary grid
const size2 = 2.5; // Secondary grid
const color = new THREE.Color(0x444444);
const distance = 80;
this.gridHelper = new InfiniteGridHelper(size1, size2, color, distance);
this.gridHelper.layers.set(1); // Enable default layer
this.scene.add(this.gridHelper);
// Create invisible ground plane for proper object placement
const planeGeometry = new THREE.PlaneGeometry(50, 50); // Same as original grid size
const planeMaterial = new THREE.MeshBasicMaterial({
visible: false,
side: THREE.DoubleSide,
});
this.groundPlane = new THREE.Mesh(planeGeometry, planeMaterial);
this.groundPlane.rotation.x = -Math.PI / 2;
this.groundPlane.name = "GroundPlane";
this.groundPlane.layers.set(2); // Put in layer 2 to avoid selection
this.groundPlane.userData["selectable"] = false; // Mark as non-selectable
this.scene.add(this.groundPlane);
}
// Update grid visibility based on the signal
updateGridVisibility() {
if (gridVisibility.value) {
if (!this.scene.children.includes(this.gridHelper)) {
this.scene.add(this.gridHelper);
}
} else {
this.scene.remove(this.gridHelper);
}
// Keep the ground plane regardless of grid visibility
}
}
@@ -20,6 +20,7 @@ import {
faRectangleVertical,
faSquare,
faRectangle,
faTableCellsLarge,
} from "@fortawesome/pro-regular-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
@@ -31,6 +32,8 @@ import {
disableHotkeyInput,
DomLevels,
enableHotkeyInput,
gridVisibility,
setGridVisibility,
} from "~/pages/PageEnigma/signals";
import { QueueNames } from "~/pages/PageEnigma/Queue/QueueNames";
import Queue from "~/pages/PageEnigma/Queue/Queue";
@@ -45,7 +48,6 @@ interface ReferenceImage {
mediaToken: string;
}
import { EngineApi } from "~/Classes/ApiManager/EngineApi";
import { scene } from "~/signals/scene";
import { UploaderStates } from "~/enums/UploaderStates";
import { CameraSettingsModal } from "../CameraSettingsModal";
@@ -111,6 +113,7 @@ export const PromptBox = () => {
[],
);
const [activeLibraryTab, setActiveLibraryTab] = useState("my-media");
const [isGridVisible, setIsGridVisible] = useState(true);
useEffect(() => {
if (textareaRef.current) {
@@ -138,6 +141,10 @@ export const PromptBox = () => {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [cameraAspectRatio.value]);
useEffect(() => {
setGridVisibility(isGridVisible);
}, [isGridVisible]);
const handleCameraSelect = (selectedItem: PopoverItem) => {
setCameraList((prev) =>
prev.map((item) => ({
@@ -600,6 +607,19 @@ export const PromptBox = () => {
onClick={() => setUseSystemPrompt(!useSystemPrompt)}
/>
</Tooltip>
<Tooltip
content={gridVisibility.value ? "Grid: ON" : "Grid: OFF"}
position="top"
className="z-50"
delay={200}
>
<ToggleButton
isActive={gridVisibility.value}
icon={faTableCellsLarge}
activeIcon={faTableCellsLarge}
onClick={() => setGridVisibility(!gridVisibility.value)}
/>
</Tooltip>
</div>
<div className="flex items-center gap-2">
<Tooltip
@@ -11,3 +11,9 @@ export const setCameraAspectRatio = (newAspectRatio: CameraAspectRatio) => {
cameraAspectRatio.value = newAspectRatio;
};
export const previewSrc = signal("");
export const gridVisibility = signal<boolean>(true);
export const setGridVisibility = (isVisible: boolean) => {
gridVisibility.value = isVisible;
};