Skip to content

Commit 4882e6f

Browse files
add out-of-bounds visual to 3D add plant pointer
1 parent 10981c0 commit 4882e6f

File tree

2 files changed

+92
-60
lines changed

2 files changed

+92
-60
lines changed

frontend/three_d_garden/bed/objects/pointer_objects.tsx

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,12 @@ import { getMode, round, xyDistance } from "../../../farm_designer/map/util";
77
import { isMobile } from "../../../screen_size";
88
import { HOVER_OBJECT_MODES, DRAW_POINT_MODES, RenderOrder } from "../../constants";
99
import {
10-
DrawnPoint, POINT_CYLINDER_SCALE_FACTOR, WEED_IMG_SIZE_FRACTION,
10+
DrawnPoint,
11+
getBoundsCenter,
12+
getHalfSize,
13+
outOfBoundsShaderModification,
14+
POINT_CYLINDER_SCALE_FACTOR,
15+
WEED_IMG_SIZE_FRACTION,
1116
} from "../../garden";
1217
import {
1318
zero as zeroFunc,
@@ -21,7 +26,7 @@ import { SpecialStatus, TaggedGenericPointer } from "farmbot";
2126
import { AddPlantProps } from "../bed";
2227
import { DEFAULT_PLANT_RADIUS } from "../../../farm_designer/plant";
2328
import { isUndefined, round as mathRound } from "lodash";
24-
import { Mesh as MeshType, Group as GroupType } from "three";
29+
import { Mesh as MeshType, Group as GroupType, Color } from "three";
2530
import { Path } from "../../../internal_urls";
2631
import { ThreeEvent } from "@react-three/fiber";
2732
import { dropPlant } from "../../../farm_designer/map/layers/plants/plant_actions";
@@ -73,7 +78,10 @@ export const PointerObjects = (props: PointerObjectsProps) => {
7378
const gridPreview = mapPoints
7479
.filter(p => p.specialStatus == SpecialStatus.DIRTY && p.body.meta.gridId)
7580
.length > 0;
76-
81+
// eslint-disable-next-line react-hooks/exhaustive-deps
82+
const boundsCenter = React.useMemo(getBoundsCenter(config), []);
83+
// eslint-disable-next-line react-hooks/exhaustive-deps
84+
const halfSize = React.useMemo(getHalfSize(config), []);
7785
return HOVER_OBJECT_MODES.includes(getMode()) &&
7886
!isMobile() &&
7987
<Group name={"hover-elements"}>
@@ -131,6 +139,13 @@ export const PointerObjects = (props: PointerObjectsProps) => {
131139
color={"white"}
132140
transparent={true}
133141
opacity={0.4}
142+
onBeforeCompile={(shader) => {
143+
shader.uniforms.uBoundsCenter = { value: boundsCenter };
144+
shader.uniforms.uHalfSize = { value: halfSize };
145+
shader.uniforms.uInside = { value: new Color("white") };
146+
shader.uniforms.uOutside = { value: new Color("red") };
147+
outOfBoundsShaderModification(shader);
148+
}}
134149
depthWrite={false} />
135150
</Sphere>
136151
</Group>}

frontend/three_d_garden/garden/plants.tsx

Lines changed: 74 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
1-
import React, { useMemo } from "react";
1+
import React from "react";
22
import { Config } from "../config";
33
import { HOVER_OBJECT_MODES, RenderOrder } from "../constants";
44
import { Billboard, Plane, Sphere, useTexture } from "@react-three/drei";
5-
import { Vector3, Mesh, Group as GroupType, Color } from "three";
5+
import {
6+
Vector3,
7+
Mesh,
8+
Group as GroupType,
9+
Color,
10+
WebGLProgramParametersWithUniforms,
11+
} from "three";
612
import {
713
getGardenPositionFunc,
814
threeSpace,
@@ -119,40 +125,33 @@ interface PlantPartProps extends CustomImageProps {
119125

120126
const PlantPart = (props: PlantPartProps) => {
121127
const { config } = props;
122-
const boundsCenter = useMemo(() =>
123-
new Vector3(
124-
0,
125-
0,
126-
-10000 + zZero(config),
127-
// eslint-disable-next-line react-hooks/exhaustive-deps
128-
), []);
129-
const halfSize = useMemo(() => new Vector3(
130-
config.bedLengthOuter / 2 - 300,
131-
config.bedWidthOuter / 2 - config.bedWallThickness,
132-
10000,
133-
// eslint-disable-next-line react-hooks/exhaustive-deps
134-
), []);
128+
// eslint-disable-next-line react-hooks/exhaustive-deps
129+
const boundsCenter = React.useMemo(getBoundsCenter(config), []);
130+
// eslint-disable-next-line react-hooks/exhaustive-deps
131+
const halfSize = React.useMemo(getHalfSize(config), []);
135132
const spreadRadii = getSpreadRadii({
136133
activeDragSpread: findCrop(Path.getCropSlug()).spread,
137134
inactiveSpread: props.plant.spread,
138135
radius: props.plant.size / 2,
139136
});
140-
const worldPos = props.activePositionRef.current || { x: -10000, y: -10000 };
141-
const active = getGardenPositionFunc(config)(worldPos);
142-
const overlap = getSpreadOverlap({
143-
spreadRadii,
144-
activeDragXY: {
145-
x: round(active.x + config.bedXOffset),
146-
y: round(active.y + config.bedYOffset),
147-
z: 0,
148-
},
149-
plantXY: { x: round(props.plant.x), y: round(props.plant.y), z: 0 },
137+
138+
const rgb = React.useMemo(() => ({ value: [0, 1, 0] }), []);
139+
useFrame(() => {
140+
const worldPos = props.activePositionRef.current || { x: -10000, y: -10000 };
141+
const active = getGardenPositionFunc(config)(worldPos);
142+
const overlap = getSpreadOverlap({
143+
spreadRadii,
144+
activeDragXY: {
145+
x: round(active.x + config.bedXOffset),
146+
y: round(active.y + config.bedYOffset),
147+
z: 0,
148+
},
149+
plantXY: { x: round(props.plant.x), y: round(props.plant.y), z: 0 },
150+
});
151+
const color = props.plant.id ? overlap.color.rgb : [1, 1, 1];
152+
rgb.value = getMode() == Mode.clickToAdd ? color : [0, 1, 0];
150153
});
151-
const rgb = useMemo(() => ({ value: [0, 1, 0] }), []);
152-
const mode = getMode();
153-
React.useEffect(() => {
154-
rgb.value = mode == Mode.clickToAdd ? overlap.color.rgb : [0, 1, 0];
155-
}, [rgb, overlap.color.rgb, mode]);
154+
156155
return <Group>
157156
<Image {...props} />
158157
{props.spreadVisible &&
@@ -166,39 +165,57 @@ const PlantPart = (props: PlantPartProps) => {
166165
shader.uniforms.uHalfSize = { value: halfSize };
167166
shader.uniforms.uInside = rgb;
168167
shader.uniforms.uOutside = { value: new Color("red") };
169-
shader.vertexShader = shader.vertexShader.replace(
170-
"#include <common>",
171-
`#include <common>
172-
varying vec3 vWorldPosition;`,
173-
).replace(
174-
"#include <worldpos_vertex>",
175-
`#include <worldpos_vertex>
176-
vWorldPosition = worldPosition.xyz;`);
177-
shader.fragmentShader = shader.fragmentShader.replace(
178-
"#include <common>",
179-
`#include <common>
180-
varying vec3 vWorldPosition;
181-
uniform vec3 uBoundsCenter;
182-
uniform vec3 uHalfSize;
183-
uniform vec3 uInside;
184-
uniform vec3 uOutside;`,
185-
).replace(
186-
"#include <color_fragment>",
187-
`#include <color_fragment>
188-
vec3 p = vWorldPosition - uBoundsCenter;
189-
bool inside =
190-
p.x > -uHalfSize.x &&
191-
abs(p.y) <= uHalfSize.y &&
192-
abs(p.z) <= uHalfSize.z;
193-
diffuseColor.rgb = mix(uOutside, uInside, float(inside));
194-
`,
195-
);
168+
outOfBoundsShaderModification(shader);
196169
}}
197170
depthWrite={false} />
198171
</Sphere>}
199172
</Group>;
200173
};
201174

175+
export const getBoundsCenter = (config: Config) => () =>
176+
new Vector3(
177+
0,
178+
0,
179+
-10000 + zZero(config),
180+
);
181+
182+
export const getHalfSize = (config: Config) => () => new Vector3(
183+
config.bedLengthOuter / 2 - 300,
184+
config.bedWidthOuter / 2 - config.bedWallThickness,
185+
10000,
186+
);
187+
188+
export const outOfBoundsShaderModification =
189+
(shader: WebGLProgramParametersWithUniforms) => {
190+
shader.vertexShader = shader.vertexShader.replace(
191+
"#include <common>",
192+
`#include <common>
193+
varying vec3 vWorldPosition;`,
194+
).replace(
195+
"#include <worldpos_vertex>",
196+
`#include <worldpos_vertex>
197+
vWorldPosition = worldPosition.xyz;`);
198+
shader.fragmentShader = shader.fragmentShader.replace(
199+
"#include <common>",
200+
`#include <common>
201+
varying vec3 vWorldPosition;
202+
uniform vec3 uBoundsCenter;
203+
uniform vec3 uHalfSize;
204+
uniform vec3 uInside;
205+
uniform vec3 uOutside;`,
206+
).replace(
207+
"#include <color_fragment>",
208+
`#include <color_fragment>
209+
vec3 p = vWorldPosition - uBoundsCenter;
210+
bool inside =
211+
p.x > -uHalfSize.x &&
212+
abs(p.y) <= uHalfSize.y &&
213+
abs(p.z) <= uHalfSize.z;
214+
diffuseColor.rgb = mix(uOutside, uInside, float(inside));
215+
`,
216+
);
217+
};
218+
202219
type MeshProps = ThreeElements["mesh"];
203220
interface CustomImageProps extends MeshProps {
204221
url: string;

0 commit comments

Comments
 (0)