mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-05 17:58:55 +00:00
grid toggle button and better grid look
This commit is contained in:
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;
|
||||
};
|
||||
Reference in new issue
Block a user