check wall + clean attach/detach
This commit is contained in:
@@ -21,7 +21,7 @@ export const ItemTool: React.FC = () => {
|
|||||||
const draftItem = useRef<ItemNode | null>(null);
|
const draftItem = useRef<ItemNode | null>(null);
|
||||||
const gridPosition = useRef(new Vector3(0, 0, 0));
|
const gridPosition = useRef(new Vector3(0, 0, 0));
|
||||||
const selectedItem = useEditor((state) => state.selectedItem);
|
const selectedItem = useEditor((state) => state.selectedItem);
|
||||||
const { canPlace } = useSpatialQuery();
|
const { canPlaceOnFloor, canPlaceOnWall } = useSpatialQuery();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log("item-tool-reloaded");
|
console.log("item-tool-reloaded");
|
||||||
@@ -35,14 +35,30 @@ export const ItemTool: React.FC = () => {
|
|||||||
const checkCanPlace = () => {
|
const checkCanPlace = () => {
|
||||||
const currentLevelId = useViewer.getState().currentLevelId;
|
const currentLevelId = useViewer.getState().currentLevelId;
|
||||||
if (currentLevelId && draftItem.current) {
|
if (currentLevelId && draftItem.current) {
|
||||||
const placeable = canPlace(
|
let placeable = true;
|
||||||
currentLevelId,
|
if (draftItem.current.asset.attachTo) {
|
||||||
[gridPosition.current.x, 0, gridPosition.current.z],
|
if (!isOnWall) {
|
||||||
selectedItem.dimensions,
|
placeable = false;
|
||||||
[0, 0, 0],
|
} else {
|
||||||
[draftItem.current.id],
|
placeable = canPlaceOnWall(
|
||||||
);
|
currentLevelId,
|
||||||
if (placeable.valid) {
|
draftItem.current.parentId as WallNode["id"],
|
||||||
|
gridPosition.current.x,
|
||||||
|
gridPosition.current.y,
|
||||||
|
draftItem.current.asset.dimensions,
|
||||||
|
[draftItem.current.id],
|
||||||
|
).valid;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
placeable = canPlaceOnFloor(
|
||||||
|
currentLevelId,
|
||||||
|
[gridPosition.current.x, 0, gridPosition.current.z],
|
||||||
|
draftItem.current.asset.dimensions,
|
||||||
|
[0, 0, 0],
|
||||||
|
[draftItem.current.id],
|
||||||
|
).valid;
|
||||||
|
}
|
||||||
|
if (placeable) {
|
||||||
cursorRef.current.material.color.set("green");
|
cursorRef.current.material.color.set("green");
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
@@ -133,6 +149,7 @@ export const ItemTool: React.FC = () => {
|
|||||||
],
|
],
|
||||||
parentId: event.node.id,
|
parentId: event.node.id,
|
||||||
});
|
});
|
||||||
|
checkCanPlace();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -140,6 +157,18 @@ export const ItemTool: React.FC = () => {
|
|||||||
console.log("Wall leave", event);
|
console.log("Wall leave", event);
|
||||||
isOnWall = false;
|
isOnWall = false;
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
if (!draftItem.current) return;
|
||||||
|
const currentLevelId = useViewer.getState().currentLevelId;
|
||||||
|
draftItem.current.parentId = currentLevelId;
|
||||||
|
useScene.getState().updateNode(draftItem.current.id, {
|
||||||
|
position: [
|
||||||
|
gridPosition.current.x,
|
||||||
|
gridPosition.current.y,
|
||||||
|
gridPosition.current.z,
|
||||||
|
],
|
||||||
|
parentId: currentLevelId,
|
||||||
|
});
|
||||||
|
checkCanPlace();
|
||||||
};
|
};
|
||||||
|
|
||||||
const onWallClick = (event: WallEvent) => {
|
const onWallClick = (event: WallEvent) => {
|
||||||
@@ -147,7 +176,7 @@ export const ItemTool: React.FC = () => {
|
|||||||
if (wallLocked) {
|
if (wallLocked) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const { currentLevelId } = useViewer.getState();
|
const currentLevelId = useViewer.getState().currentLevelId;
|
||||||
if (!currentLevelId || !draftItem.current || !checkCanPlace()) return;
|
if (!currentLevelId || !draftItem.current || !checkCanPlace()) return;
|
||||||
|
|
||||||
wallLocked = true;
|
wallLocked = true;
|
||||||
@@ -166,6 +195,7 @@ export const ItemTool: React.FC = () => {
|
|||||||
|
|
||||||
useScene.temporal.getState().pause();
|
useScene.temporal.getState().pause();
|
||||||
createDraftItem();
|
createDraftItem();
|
||||||
|
checkCanPlace();
|
||||||
};
|
};
|
||||||
|
|
||||||
const onWallMove = (event: WallEvent) => {
|
const onWallMove = (event: WallEvent) => {
|
||||||
|
|||||||
@@ -138,25 +138,36 @@ export class SpatialGridManager {
|
|||||||
return grid.canPlace(position, dimensions, rotation, ignoreIds);
|
return grid.canPlace(position, dimensions, rotation, ignoreIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if an item can be placed on a wall
|
||||||
|
* @param levelId - the level containing the wall
|
||||||
|
* @param wallId - the wall to check
|
||||||
|
* @param localX - X position in wall-local space (distance from wall start)
|
||||||
|
* @param localY - Y position (height from floor)
|
||||||
|
* @param dimensions - item dimensions [width, height, depth]
|
||||||
|
* @param ignoreIds - item IDs to ignore in collision check
|
||||||
|
*/
|
||||||
canPlaceOnWall(
|
canPlaceOnWall(
|
||||||
levelId: string,
|
levelId: string,
|
||||||
wallId: string,
|
wallId: string,
|
||||||
tCenter: number,
|
localX: number,
|
||||||
itemWidth: number,
|
localY: number,
|
||||||
yCenter: number,
|
dimensions: [number, number, number],
|
||||||
itemHeight: number,
|
|
||||||
ignoreIds?: string[],
|
ignoreIds?: string[],
|
||||||
) {
|
) {
|
||||||
const wallLength = this.getWallLength(wallId);
|
const wallLength = this.getWallLength(wallId);
|
||||||
if (wallLength === 0) {
|
if (wallLength === 0) {
|
||||||
return { valid: false, conflictIds: [] };
|
return { valid: false, conflictIds: [] };
|
||||||
}
|
}
|
||||||
|
// Convert local X position to parametric t (0-1)
|
||||||
|
const tCenter = localX / wallLength;
|
||||||
|
const [itemWidth, itemHeight] = dimensions;
|
||||||
return this.getWallGrid(levelId).canPlaceOnWall(
|
return this.getWallGrid(levelId).canPlaceOnWall(
|
||||||
wallId,
|
wallId,
|
||||||
wallLength,
|
wallLength,
|
||||||
tCenter,
|
tCenter,
|
||||||
itemWidth,
|
itemWidth,
|
||||||
yCenter,
|
localY,
|
||||||
itemHeight,
|
itemHeight,
|
||||||
ignoreIds,
|
ignoreIds,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { useCallback } from "react";
|
import { useCallback } from "react";
|
||||||
import { spatialGridManager } from "./spatial-grid-manager";
|
import { spatialGridManager } from "./spatial-grid-manager";
|
||||||
import { LevelNode } from "../../schema";
|
import { LevelNode, WallNode } from "../../schema";
|
||||||
|
|
||||||
export function useSpatialQuery() {
|
export function useSpatialQuery() {
|
||||||
const canPlace = useCallback(
|
const canPlaceOnFloor = useCallback(
|
||||||
(
|
(
|
||||||
levelId: LevelNode["id"],
|
levelId: LevelNode["id"],
|
||||||
position: [number, number, number],
|
position: [number, number, number],
|
||||||
@@ -22,5 +22,26 @@ export function useSpatialQuery() {
|
|||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
|
|
||||||
return { canPlace };
|
const canPlaceOnWall = useCallback(
|
||||||
|
(
|
||||||
|
levelId: LevelNode["id"],
|
||||||
|
wallId: WallNode["id"],
|
||||||
|
localX: number,
|
||||||
|
localY: number,
|
||||||
|
dimensions: [number, number, number],
|
||||||
|
ignoreIds?: string[],
|
||||||
|
) => {
|
||||||
|
return spatialGridManager.canPlaceOnWall(
|
||||||
|
levelId,
|
||||||
|
wallId,
|
||||||
|
localX,
|
||||||
|
localY,
|
||||||
|
dimensions,
|
||||||
|
ignoreIds,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
|
return { canPlaceOnFloor, canPlaceOnWall };
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user