draft item tool
This commit is contained in:
@@ -51,10 +51,10 @@ export default function Editor() {
|
||||
}
|
||||
|
||||
const TestUndo = () => {
|
||||
const {undo, redo, futureStates, pastStates} = useScene.temporal.getState();
|
||||
const { undo, redo, futureStates, pastStates } = useScene.temporal.getState();
|
||||
|
||||
|
||||
return ( <div className="absolute top-4 right-4 z-10 flex gap-2">
|
||||
return (
|
||||
<div className="absolute top-4 right-4 z-10 flex gap-2">
|
||||
<button
|
||||
className="px-4 py-2 rounded bg-white"
|
||||
onClick={() => {
|
||||
@@ -73,7 +73,7 @@ const TestUndo = () => {
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export const Passes = ({}) => {
|
||||
const { gl: renderer, scene, camera } = useThree();
|
||||
@@ -169,13 +169,13 @@ const Grid = ({
|
||||
grid.x
|
||||
.div(fw.x)
|
||||
.add(1 - thickness)
|
||||
.min(1)
|
||||
.min(1),
|
||||
);
|
||||
const lineY = float(1).sub(
|
||||
grid.y
|
||||
.div(fw.y)
|
||||
.add(1 - thickness)
|
||||
.min(1)
|
||||
.min(1),
|
||||
);
|
||||
// Combine both axes - max gives us lines in both directions
|
||||
return lineX.max(lineY);
|
||||
@@ -192,7 +192,7 @@ const Grid = ({
|
||||
const gridColor = mix(
|
||||
color(cellColor),
|
||||
color(sectionColor),
|
||||
float(sectionThickness).mul(g2).min(1)
|
||||
float(sectionThickness).mul(g2).min(1),
|
||||
);
|
||||
|
||||
// Combined alpha
|
||||
@@ -229,37 +229,36 @@ const DraftSelector = () => {
|
||||
const selectedItemId = useRef<ItemNode["id"] | WallNode["id"]>(null);
|
||||
const itemSelectedAt = useRef<number>(0);
|
||||
useEffect(() => {
|
||||
emitter.on('building:enter', (event) => {
|
||||
console.log('Entered building:', event.node.id);
|
||||
emitter.on("building:enter", (event) => {
|
||||
console.log("Entered building:", event.node.id);
|
||||
const itemMesh = sceneRegistry.nodes.get(event.node.id);
|
||||
selectedObjects.length = 0;
|
||||
selectedObjects.push(itemMesh);
|
||||
});
|
||||
emitter.on('building:leave', (event) => {
|
||||
console.log('Leaving building:', event.node.id);
|
||||
emitter.on("building:leave", (event) => {
|
||||
console.log("Leaving building:", event.node.id);
|
||||
selectedObjects.length = 0;
|
||||
});
|
||||
|
||||
// emitter.on("item:click", (event) => {
|
||||
// event.stopPropagation();
|
||||
// if (Date.now() - itemSelectedAt.current < 50) {
|
||||
// return;
|
||||
// }
|
||||
// itemSelectedAt.current = Date.now();
|
||||
// if (selectedItemId.current === event.node.id) {
|
||||
// selectedItemId.current = null;
|
||||
// console.log("Deselected item:", event.node.id);
|
||||
// selectedObjects.length = 0;
|
||||
// return;
|
||||
// }
|
||||
// selectedItemId.current = event.node.id;
|
||||
// const itemMesh = sceneRegistry.nodes.get(event.node.id);
|
||||
// if (!itemMesh) return;
|
||||
// selectedObjects.push(itemMesh);
|
||||
|
||||
emitter.on("item:click", (event) => {
|
||||
event.stopPropagation();
|
||||
if (Date.now() - itemSelectedAt.current < 50) {
|
||||
return;
|
||||
}
|
||||
itemSelectedAt.current = Date.now();
|
||||
if (selectedItemId.current === event.node.id) {
|
||||
selectedItemId.current = null;
|
||||
console.log("Deselected item:", event.node.id);
|
||||
selectedObjects.length = 0;
|
||||
return;
|
||||
}
|
||||
selectedItemId.current = event.node.id;
|
||||
const itemMesh = sceneRegistry.nodes.get(event.node.id);
|
||||
if (!itemMesh) return;
|
||||
selectedObjects.push(itemMesh);
|
||||
|
||||
console.log("Selected item:", event.node.id);
|
||||
});
|
||||
// console.log("Selected item:", event.node.id);
|
||||
// });
|
||||
|
||||
emitter.on("wall:click", (event) => {
|
||||
if (Date.now() - itemSelectedAt.current < 50) {
|
||||
@@ -294,14 +293,14 @@ const DraftSelector = () => {
|
||||
itemMesh.position.set(
|
||||
event.position[0],
|
||||
event.position[1],
|
||||
event.position[2]
|
||||
event.position[2],
|
||||
);
|
||||
useScene.getState().dirtyNodes.add(wallNode.id);
|
||||
console.log(
|
||||
"Wall move event:",
|
||||
wallNode.id,
|
||||
"Point position:",
|
||||
event.position
|
||||
event.position,
|
||||
);
|
||||
});
|
||||
}, []);
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
import {
|
||||
emitter,
|
||||
GridEvent,
|
||||
ItemNode,
|
||||
sceneRegistry,
|
||||
useRegistry,
|
||||
useScene,
|
||||
WallNode,
|
||||
} from "@pascal-app/core";
|
||||
import { useViewer } from "@pascal-app/viewer";
|
||||
import { useFrame } from "@react-three/fiber";
|
||||
import { use, useEffect, useRef } from "react";
|
||||
import { Line, Mesh, Vector3 } from "three";
|
||||
import { randInt } from "three/src/math/MathUtils.js";
|
||||
|
||||
export const ItemTool: React.FC = () => {
|
||||
const cursorRef = useRef<Mesh>(null);
|
||||
const draftItem = useRef<ItemNode | null>(null);
|
||||
const gridPosition = useRef(new Vector3(0, 0, 0));
|
||||
|
||||
useEffect(() => {
|
||||
const createDraftItem = () => {
|
||||
const { currentLevelId } = useViewer.getState();
|
||||
if (!currentLevelId) {
|
||||
return null;
|
||||
}
|
||||
useScene.temporal.getState().pause();
|
||||
draftItem.current = ItemNode.parse({
|
||||
position: [randInt(-10, 10), 0, randInt(-10, 10)],
|
||||
name: "Draft Item",
|
||||
asset: {
|
||||
category: "furniture",
|
||||
src: "/items/couch-small/model.glb",
|
||||
dimensions: [2, 1, 3],
|
||||
},
|
||||
});
|
||||
useScene.getState().createNode(draftItem.current, currentLevelId);
|
||||
};
|
||||
createDraftItem();
|
||||
|
||||
const onGridMove = (event: GridEvent) => {
|
||||
if (!cursorRef.current) return;
|
||||
|
||||
gridPosition.current.set(
|
||||
Math.round(event.position[0] * 2) / 2,
|
||||
0,
|
||||
Math.round(event.position[1] * 2) / 2,
|
||||
);
|
||||
cursorRef.current.position.set(
|
||||
gridPosition.current.x,
|
||||
0.1,
|
||||
gridPosition.current.z,
|
||||
);
|
||||
if (draftItem.current) {
|
||||
draftItem.current.position = [
|
||||
gridPosition.current.x,
|
||||
0,
|
||||
gridPosition.current.z,
|
||||
];
|
||||
}
|
||||
};
|
||||
const onGridClick = (event: GridEvent) => {
|
||||
const { currentLevelId } = useViewer.getState();
|
||||
|
||||
console.log("oh", currentLevelId, draftItem.current);
|
||||
if (!currentLevelId || !draftItem.current) return;
|
||||
|
||||
console.log("oh");
|
||||
useScene.temporal.getState().resume();
|
||||
useScene.getState().updateNode(draftItem.current.id, {
|
||||
position: [gridPosition.current.x, 0, gridPosition.current.z],
|
||||
});
|
||||
draftItem.current = null;
|
||||
|
||||
useScene.temporal.getState().pause();
|
||||
createDraftItem();
|
||||
};
|
||||
|
||||
emitter.on("grid:move", onGridMove);
|
||||
emitter.on("grid:click", onGridClick);
|
||||
|
||||
return () => {
|
||||
if (draftItem.current) {
|
||||
useScene.getState().deleteNode(draftItem.current.id);
|
||||
}
|
||||
emitter.off("grid:move", onGridMove);
|
||||
emitter.off("grid:click", onGridClick);
|
||||
};
|
||||
}, []);
|
||||
|
||||
useFrame((_, delta) => {
|
||||
if (draftItem.current) {
|
||||
const draftItemMesh = sceneRegistry.nodes.get(draftItem.current.id);
|
||||
if (draftItemMesh) {
|
||||
draftItemMesh.position.lerp(gridPosition.current, delta * 10);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<group>
|
||||
<mesh ref={cursorRef}>
|
||||
<boxGeometry args={[0.2, 0.2, 0.2]} />
|
||||
<meshStandardMaterial color="red" />
|
||||
</mesh>
|
||||
</group>
|
||||
);
|
||||
};
|
||||
@@ -1,13 +1,16 @@
|
||||
import useEditor, { Phase, Tool } from "@/store/use-editor";
|
||||
import { WallTool } from "./wall/wall-tool";
|
||||
import { ItemTool } from "./item/item-tool";
|
||||
|
||||
const tools: Record<Phase, Partial<Record<Tool, React.FC>>> = {
|
||||
site: {
|
||||
},
|
||||
structure: {
|
||||
wall: WallTool,
|
||||
item: ItemTool,
|
||||
},
|
||||
furnish: {
|
||||
item: ItemTool
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -3,6 +3,18 @@ import { useViewer } from "@pascal-app/viewer";
|
||||
import { useEffect, useRef } from "react"
|
||||
import { Line, Mesh, Vector3 } from "three";
|
||||
|
||||
|
||||
const commitWallDrawing = (start: [number, number], end: [number, number]) => {
|
||||
const { currentLevelId } = useViewer.getState();
|
||||
const { createNode } = useScene.getState();
|
||||
|
||||
if (!currentLevelId) return;
|
||||
|
||||
const wall = WallNode.parse({ start, end });
|
||||
|
||||
createNode(wall, currentLevelId);
|
||||
};
|
||||
|
||||
export const WallTool: React.FC = () => {
|
||||
|
||||
const cursorRef = useRef<Mesh>(null);
|
||||
@@ -42,15 +54,7 @@ export const WallTool: React.FC = () => {
|
||||
drawingLineRef.current.visible = true;
|
||||
|
||||
} else if (buildingState === 1) {
|
||||
const currentLevelId = useViewer.getState().currentLevelId;
|
||||
console.log('currentLevelId:', currentLevelId);
|
||||
if (currentLevelId) {
|
||||
const wallNode = WallNode.parse({
|
||||
start: [startingPoint.x, startingPoint.z],
|
||||
end: [gridPosition[0], gridPosition[1]],
|
||||
})
|
||||
useScene.getState().createNode(wallNode, currentLevelId);
|
||||
}
|
||||
commitWallDrawing([startingPoint.x, startingPoint.z], [gridPosition[0], gridPosition[1]]);
|
||||
drawingLineRef.current.visible = false;
|
||||
buildingState = 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user