From 58ea7d827e05e235c19fab63b682c0c07af40de6 Mon Sep 17 00:00:00 2001 From: akastijn Date: Mon, 19 Jan 2026 23:07:12 +0100 Subject: [PATCH] Add grid to particle drawer --- .../render-container.component.html | 9 +++ .../render-container.component.ts | 17 +++++ .../services/intersection-plane.service.ts | 76 +++++++++++++++++++ 3 files changed, 102 insertions(+) diff --git a/frontend/src/app/pages/particles/components/render-container/render-container.component.html b/frontend/src/app/pages/particles/components/render-container/render-container.component.html index cc5392c..d90af58 100644 --- a/frontend/src/app/pages/particles/components/render-container/render-container.component.html +++ b/frontend/src/app/pages/particles/components/render-container/render-container.component.html @@ -5,6 +5,10 @@ Opacity + + Grid density + +
+ +
@if (isPlaneLocked) { diff --git a/frontend/src/app/pages/particles/components/render-container/render-container.component.ts b/frontend/src/app/pages/particles/components/render-container/render-container.component.ts index 99abd4b..defaa73 100644 --- a/frontend/src/app/pages/particles/components/render-container/render-container.component.ts +++ b/frontend/src/app/pages/particles/components/render-container/render-container.component.ts @@ -86,6 +86,23 @@ export class RenderContainerComponent implements AfterViewInit, OnDestroy { return this.intersectionPlaneService.currentOpacity; } + // Grid proxies + public get gridVisible(): boolean { + return this.intersectionPlaneService.getGridVisible(); + } + + public set gridVisible(v: boolean) { + this.intersectionPlaneService.setGridVisible(v); + } + + public get gridDensity(): number { + return this.intersectionPlaneService.getGridDensity(); + } + + public set gridDensity(d: number) { + this.intersectionPlaneService.setGridDensity(d); + } + /** * Toggle the plane locked state */ diff --git a/frontend/src/app/pages/particles/services/intersection-plane.service.ts b/frontend/src/app/pages/particles/services/intersection-plane.service.ts index 12e8b22..c287bbe 100644 --- a/frontend/src/app/pages/particles/services/intersection-plane.service.ts +++ b/frontend/src/app/pages/particles/services/intersection-plane.service.ts @@ -28,6 +28,11 @@ export class IntersectionPlaneService { private planeLocked: boolean = false; private opacity: number = 0.05; + // Grid overlay + private gridHelper?: THREE.GridHelper; + private gridVisible: boolean = true; + private gridDensity: number = 4; + // Emits whenever plane position, orientation, or lock-affecting orientation updates change visuals public readonly planeChanged$ = new Subject(); private lastPlaneSignature: string | null = null; @@ -35,6 +40,73 @@ export class IntersectionPlaneService { constructor(private rendererService: RendererService) { } + /** + * Creates or updates the grid helper attached to the intersection plane + * without affecting raycasting/placement. + */ + private createOrUpdateGrid(): void { + if (!this.intersectionPlane) return; + + if (this.gridHelper) { + this.intersectionPlane.remove(this.gridHelper); + (this.gridHelper.geometry as THREE.BufferGeometry).dispose(); + if (this.gridHelper.material.dispose) { + this.gridHelper.material.dispose(); + } + this.gridHelper = undefined; + } + + const size = 5; + const divisions = Math.max(1, Math.floor(size * this.gridDensity)); + + this.gridHelper = new THREE.GridHelper(size, divisions, 0x888888, 0xcccccc); + + this.gridHelper.rotation.x = Math.PI / 2; + this.gridHelper.position.z -= 0.005; + + this.gridHelper.renderOrder = 2; + const gridMat = this.gridHelper.material as THREE.Material | THREE.Material[]; + if (Array.isArray(gridMat)) { + gridMat.forEach(material => { + material.transparent = true; + material.depthWrite = false; + if (material.opacity !== undefined) { + material.opacity = 0.25; + } + }); + } else { + gridMat.transparent = true; + gridMat.depthWrite = false; + gridMat.opacity = 0.25; + } + + this.gridHelper.raycast = () => { + }; + + this.gridHelper.visible = this.gridVisible; + this.intersectionPlane.add(this.gridHelper); + } + + public setGridVisible(visible: boolean): void { + this.gridVisible = visible; + if (this.gridHelper) this.gridHelper.visible = visible; + } + + public getGridVisible(): boolean { + return this.gridVisible; + } + + public setGridDensity(density: number): void { + this.gridDensity = Math.max(1, Math.min(64, Math.floor(density))); + if (this.intersectionPlane) { + this.createOrUpdateGrid(); + } + } + + public getGridDensity(): number { + return this.gridDensity; + } + /** * Creates the intersection plane and adds it to the scene */ @@ -51,6 +123,10 @@ export class IntersectionPlaneService { this.intersectionPlane.position.z = 0; // Center the plane vertically with the player (player is about 2 blocks tall) this.intersectionPlane.position.y = 1; + + // Add grid overlay as a child so it follows rotation/position + this.createOrUpdateGrid(); + this.rendererService.scene.add(this.intersectionPlane); this.intersectionPlane.renderOrder = 1;