Generated
+165
-8
@@ -8,6 +8,8 @@
|
||||
"name": "lego-builder",
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"@liveblocks/client": "^1.0.1",
|
||||
"@liveblocks/zustand": "^1.0.1",
|
||||
"@react-three/drei": "^9.57.0",
|
||||
"@react-three/fiber": "^8.11.6",
|
||||
"@testing-library/jest-dom": "^5.16.5",
|
||||
@@ -18,7 +20,9 @@
|
||||
"react-dom": "^18.2.0",
|
||||
"react-scripts": "5.0.1",
|
||||
"three": "^0.150.1",
|
||||
"web-vitals": "^2.1.4"
|
||||
"use-keyboard-shortcut": "^1.1.6",
|
||||
"web-vitals": "^2.1.4",
|
||||
"zustand": "^4.3.6"
|
||||
}
|
||||
},
|
||||
"node_modules/@adobe/css-tools": {
|
||||
@@ -3015,6 +3019,31 @@
|
||||
"resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz",
|
||||
"integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A=="
|
||||
},
|
||||
"node_modules/@liveblocks/client": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@liveblocks/client/-/client-1.0.1.tgz",
|
||||
"integrity": "sha512-lU+A+v1bbvI6H/pOjo93G8LAf1HYM2yqZc4D/LCI6b/JiZT/3bMrJ2X8Or4Yc5DVmIK3T5Rumk2za9bSdcjBQQ==",
|
||||
"dependencies": {
|
||||
"@liveblocks/core": "1.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@liveblocks/core": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@liveblocks/core/-/core-1.0.1.tgz",
|
||||
"integrity": "sha512-IwtVtp1ogmVJLW7KSmkI7J+8+pPg0FMNdFV71oCPAugmlZRxUer3MvdIBOW4ULFBgoBJOzAmcjWRDFsl3K7ADA=="
|
||||
},
|
||||
"node_modules/@liveblocks/zustand": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@liveblocks/zustand/-/zustand-1.0.1.tgz",
|
||||
"integrity": "sha512-cAXMS5r9wRa+NFAnJ4IusV/azRxcVB9zWtc99WSCXNq2uhxwlrsG6z+kDNR8LemoDHweSVkpb/c3Hi8FDFnv6Q==",
|
||||
"dependencies": {
|
||||
"@liveblocks/client": "1.0.1",
|
||||
"@liveblocks/core": "1.0.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"zustand": "^4.1.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@nicolo-ribaudo/eslint-scope-5-internals": {
|
||||
"version": "5.1.1-v1",
|
||||
"resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz",
|
||||
@@ -3290,6 +3319,22 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@react-three/drei/node_modules/zustand": {
|
||||
"version": "3.7.2",
|
||||
"resolved": "https://registry.npmjs.org/zustand/-/zustand-3.7.2.tgz",
|
||||
"integrity": "sha512-PIJDIZKtokhof+9+60cpockVOq05sJzHCriyvaLBmEJixseQ1a5Kdov6fWZfWOu5SK9c+FhH1jU0tntLxRJYMA==",
|
||||
"engines": {
|
||||
"node": ">=12.7.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@react-three/fiber": {
|
||||
"version": "8.11.7",
|
||||
"resolved": "https://registry.npmjs.org/@react-three/fiber/-/fiber-8.11.7.tgz",
|
||||
@@ -3339,6 +3384,22 @@
|
||||
"loose-envify": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-three/fiber/node_modules/zustand": {
|
||||
"version": "3.7.2",
|
||||
"resolved": "https://registry.npmjs.org/zustand/-/zustand-3.7.2.tgz",
|
||||
"integrity": "sha512-PIJDIZKtokhof+9+60cpockVOq05sJzHCriyvaLBmEJixseQ1a5Kdov6fWZfWOu5SK9c+FhH1jU0tntLxRJYMA==",
|
||||
"engines": {
|
||||
"node": ">=12.7.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/plugin-babel": {
|
||||
"version": "5.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz",
|
||||
@@ -12520,6 +12581,22 @@
|
||||
"react": "^16.8 || ^17.0"
|
||||
}
|
||||
},
|
||||
"node_modules/leva/node_modules/zustand": {
|
||||
"version": "3.7.2",
|
||||
"resolved": "https://registry.npmjs.org/zustand/-/zustand-3.7.2.tgz",
|
||||
"integrity": "sha512-PIJDIZKtokhof+9+60cpockVOq05sJzHCriyvaLBmEJixseQ1a5Kdov6fWZfWOu5SK9c+FhH1jU0tntLxRJYMA==",
|
||||
"engines": {
|
||||
"node": ">=12.7.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/leven": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz",
|
||||
@@ -17292,6 +17369,23 @@
|
||||
"requires-port": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/use-keyboard-shortcut": {
|
||||
"version": "1.1.6",
|
||||
"resolved": "https://registry.npmjs.org/use-keyboard-shortcut/-/use-keyboard-shortcut-1.1.6.tgz",
|
||||
"integrity": "sha512-tOKjR7eKXQIfAgFMwhelpWSRTrwE1GaB8CE/47ohtcVCKxUdn7UbfNNhiigTkATpUVoPu+5tRPbHrwjIBgTYoQ==",
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8",
|
||||
"react-dom": ">=16.8"
|
||||
}
|
||||
},
|
||||
"node_modules/use-sync-external-store": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",
|
||||
"integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==",
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
@@ -18335,16 +18429,23 @@
|
||||
"integrity": "sha512-DCo0oxvcvOTGP/f5FA6tz2Z6wF+FIcEApSTu0zV5sQgn9hoT5lZ9YRAKUraxt9oP7l4e8TnNdi8IZTCX6WCkwA=="
|
||||
},
|
||||
"node_modules/zustand": {
|
||||
"version": "3.7.2",
|
||||
"resolved": "https://registry.npmjs.org/zustand/-/zustand-3.7.2.tgz",
|
||||
"integrity": "sha512-PIJDIZKtokhof+9+60cpockVOq05sJzHCriyvaLBmEJixseQ1a5Kdov6fWZfWOu5SK9c+FhH1jU0tntLxRJYMA==",
|
||||
"version": "4.3.6",
|
||||
"resolved": "https://registry.npmjs.org/zustand/-/zustand-4.3.6.tgz",
|
||||
"integrity": "sha512-6J5zDxjxLE+yukC2XZWf/IyWVKnXT9b9HUv09VJ/bwGCpKNcaTqp7Ws28Xr8jnbvnZcdRaidztAPsXFBIqufiw==",
|
||||
"dependencies": {
|
||||
"use-sync-external-store": "1.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.7.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"immer": ">=9.0",
|
||||
"react": ">=16.8"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"immer": {
|
||||
"optional": true
|
||||
},
|
||||
"react": {
|
||||
"optional": true
|
||||
}
|
||||
@@ -20379,6 +20480,28 @@
|
||||
"resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz",
|
||||
"integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A=="
|
||||
},
|
||||
"@liveblocks/client": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@liveblocks/client/-/client-1.0.1.tgz",
|
||||
"integrity": "sha512-lU+A+v1bbvI6H/pOjo93G8LAf1HYM2yqZc4D/LCI6b/JiZT/3bMrJ2X8Or4Yc5DVmIK3T5Rumk2za9bSdcjBQQ==",
|
||||
"requires": {
|
||||
"@liveblocks/core": "1.0.1"
|
||||
}
|
||||
},
|
||||
"@liveblocks/core": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@liveblocks/core/-/core-1.0.1.tgz",
|
||||
"integrity": "sha512-IwtVtp1ogmVJLW7KSmkI7J+8+pPg0FMNdFV71oCPAugmlZRxUer3MvdIBOW4ULFBgoBJOzAmcjWRDFsl3K7ADA=="
|
||||
},
|
||||
"@liveblocks/zustand": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@liveblocks/zustand/-/zustand-1.0.1.tgz",
|
||||
"integrity": "sha512-cAXMS5r9wRa+NFAnJ4IusV/azRxcVB9zWtc99WSCXNq2uhxwlrsG6z+kDNR8LemoDHweSVkpb/c3Hi8FDFnv6Q==",
|
||||
"requires": {
|
||||
"@liveblocks/client": "1.0.1",
|
||||
"@liveblocks/core": "1.0.1"
|
||||
}
|
||||
},
|
||||
"@nicolo-ribaudo/eslint-scope-5-internals": {
|
||||
"version": "5.1.1-v1",
|
||||
"resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz",
|
||||
@@ -20570,6 +20693,14 @@
|
||||
"troika-three-text": "^0.47.1",
|
||||
"utility-types": "^3.10.0",
|
||||
"zustand": "^3.5.13"
|
||||
},
|
||||
"dependencies": {
|
||||
"zustand": {
|
||||
"version": "3.7.2",
|
||||
"resolved": "https://registry.npmjs.org/zustand/-/zustand-3.7.2.tgz",
|
||||
"integrity": "sha512-PIJDIZKtokhof+9+60cpockVOq05sJzHCriyvaLBmEJixseQ1a5Kdov6fWZfWOu5SK9c+FhH1jU0tntLxRJYMA==",
|
||||
"requires": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@react-three/fiber": {
|
||||
@@ -20594,6 +20725,12 @@
|
||||
"requires": {
|
||||
"loose-envify": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"zustand": {
|
||||
"version": "3.7.2",
|
||||
"resolved": "https://registry.npmjs.org/zustand/-/zustand-3.7.2.tgz",
|
||||
"integrity": "sha512-PIJDIZKtokhof+9+60cpockVOq05sJzHCriyvaLBmEJixseQ1a5Kdov6fWZfWOu5SK9c+FhH1jU0tntLxRJYMA==",
|
||||
"requires": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -27356,6 +27493,12 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"zustand": {
|
||||
"version": "3.7.2",
|
||||
"resolved": "https://registry.npmjs.org/zustand/-/zustand-3.7.2.tgz",
|
||||
"integrity": "sha512-PIJDIZKtokhof+9+60cpockVOq05sJzHCriyvaLBmEJixseQ1a5Kdov6fWZfWOu5SK9c+FhH1jU0tntLxRJYMA==",
|
||||
"requires": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -30667,6 +30810,18 @@
|
||||
"requires-port": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"use-keyboard-shortcut": {
|
||||
"version": "1.1.6",
|
||||
"resolved": "https://registry.npmjs.org/use-keyboard-shortcut/-/use-keyboard-shortcut-1.1.6.tgz",
|
||||
"integrity": "sha512-tOKjR7eKXQIfAgFMwhelpWSRTrwE1GaB8CE/47ohtcVCKxUdn7UbfNNhiigTkATpUVoPu+5tRPbHrwjIBgTYoQ==",
|
||||
"requires": {}
|
||||
},
|
||||
"use-sync-external-store": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",
|
||||
"integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==",
|
||||
"requires": {}
|
||||
},
|
||||
"util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
@@ -31481,10 +31636,12 @@
|
||||
"integrity": "sha512-DCo0oxvcvOTGP/f5FA6tz2Z6wF+FIcEApSTu0zV5sQgn9hoT5lZ9YRAKUraxt9oP7l4e8TnNdi8IZTCX6WCkwA=="
|
||||
},
|
||||
"zustand": {
|
||||
"version": "3.7.2",
|
||||
"resolved": "https://registry.npmjs.org/zustand/-/zustand-3.7.2.tgz",
|
||||
"integrity": "sha512-PIJDIZKtokhof+9+60cpockVOq05sJzHCriyvaLBmEJixseQ1a5Kdov6fWZfWOu5SK9c+FhH1jU0tntLxRJYMA==",
|
||||
"requires": {}
|
||||
"version": "4.3.6",
|
||||
"resolved": "https://registry.npmjs.org/zustand/-/zustand-4.3.6.tgz",
|
||||
"integrity": "sha512-6J5zDxjxLE+yukC2XZWf/IyWVKnXT9b9HUv09VJ/bwGCpKNcaTqp7Ws28Xr8jnbvnZcdRaidztAPsXFBIqufiw==",
|
||||
"requires": {
|
||||
"use-sync-external-store": "1.2.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+8
-4
@@ -3,17 +3,21 @@
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@liveblocks/client": "^1.0.1",
|
||||
"@liveblocks/zustand": "^1.0.1",
|
||||
"@react-three/drei": "^9.57.0",
|
||||
"@react-three/fiber": "^8.11.6",
|
||||
"@testing-library/jest-dom": "^5.16.5",
|
||||
"@testing-library/react": "^13.4.0",
|
||||
"@testing-library/user-event": "^13.5.0",
|
||||
"leva": "^0.9.34",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-scripts": "5.0.1",
|
||||
"web-vitals": "^2.1.4",
|
||||
"@react-three/drei": "^9.57.0",
|
||||
"@react-three/fiber": "^8.11.6",
|
||||
"three": "^0.150.1",
|
||||
"leva": "^0.9.34"
|
||||
"use-keyboard-shortcut": "^1.1.6",
|
||||
"web-vitals": "^2.1.4",
|
||||
"zustand": "^4.3.6"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
|
||||
+12
-4
@@ -5,9 +5,9 @@ import { Suspense } from "react";
|
||||
import { LinearToneMapping } from "three";
|
||||
import { InfoPanel, Scene } from "./components";
|
||||
|
||||
function App() {
|
||||
const LegoRoom = () => {
|
||||
return (
|
||||
<div className="App">
|
||||
<>
|
||||
<Canvas
|
||||
gl={{
|
||||
alpha: false,
|
||||
@@ -20,8 +20,8 @@ function App() {
|
||||
near: 0.1,
|
||||
far: 20000,
|
||||
}}
|
||||
colorManagement={true}
|
||||
shadowMap={true} // highlight-line
|
||||
colorManagement
|
||||
shadows
|
||||
dpr={Math.min(2, window.devicePixelRatio)}
|
||||
>
|
||||
<Suspense fallback={null}>
|
||||
@@ -30,6 +30,14 @@ function App() {
|
||||
</Suspense>
|
||||
</Canvas>
|
||||
<InfoPanel />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<div className="App">
|
||||
<LegoRoom />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
+42
-17
@@ -12,8 +12,8 @@ export const Brick = ({
|
||||
color = "#ff0000",
|
||||
dimensions = { x: 1, z: 1 },
|
||||
rotation = 0,
|
||||
translation = 0,
|
||||
addBrick = () => {},
|
||||
translation = { x: 0, z: 0 },
|
||||
onClick = () => {},
|
||||
bricksBoundBox = { current: [] },
|
||||
uID = "",
|
||||
mouseMove = () => {},
|
||||
@@ -54,28 +54,53 @@ export const Brick = ({
|
||||
boundingBoxRef.push({ uID, brickBoundingBox });
|
||||
|
||||
return () => {
|
||||
boundingBoxRef.filter((brick) => brick.uID !== uID);
|
||||
boundingBoxRef.filter((brick) => {
|
||||
return brick.uID !== uID;
|
||||
});
|
||||
};
|
||||
}, [uID, bricksBoundBox]);
|
||||
}, []);
|
||||
|
||||
const compansateX =
|
||||
dimensions.x % 2 === 0 ? dimensions.x / 2 : (dimensions.x - 1) / 2;
|
||||
const compansateZ =
|
||||
dimensions.z % 2 === 0 ? dimensions.z / 2 : (dimensions.z - 1) / 2;
|
||||
|
||||
const offsetX =
|
||||
Math.sign(translation.x) < 0
|
||||
? Math.max(translation.x, -compansateX)
|
||||
: Math.min(translation.x, compansateX);
|
||||
|
||||
const offsetZ =
|
||||
Math.sign(translation.z) < 0
|
||||
? Math.max(translation.z, -compansateZ)
|
||||
: Math.min(translation.z, compansateZ);
|
||||
|
||||
return (
|
||||
<>
|
||||
<mesh
|
||||
<group
|
||||
ref={brickRef}
|
||||
onClick={addBrick}
|
||||
position={[position.x, Math.abs(position.y), position.z]}
|
||||
rotation={[0, rotation, 0]}
|
||||
castShadow={true}
|
||||
receiveShadow={true}
|
||||
geometry={brickGeometry}
|
||||
onPointerMove={mouseMove}
|
||||
position={[position.x, Math.abs(position.y), position.z]}
|
||||
>
|
||||
<meshStandardMaterial
|
||||
color={CSSToHex(color)}
|
||||
metalness={0.4}
|
||||
roughness={0.5}
|
||||
/>
|
||||
</mesh>
|
||||
<mesh
|
||||
castShadow
|
||||
receiveShadow
|
||||
position={[
|
||||
(offsetX * width) / dimensions.x,
|
||||
0.5,
|
||||
(offsetZ * depth) / dimensions.z,
|
||||
]}
|
||||
onClick={onClick}
|
||||
geometry={brickGeometry}
|
||||
onPointerMove={mouseMove}
|
||||
>
|
||||
<meshStandardMaterial
|
||||
color={CSSToHex(color)}
|
||||
metalness={0.4}
|
||||
roughness={0.5}
|
||||
/>
|
||||
</mesh>
|
||||
</group>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { useThree } from "@react-three/fiber";
|
||||
import React, { forwardRef, useMemo } from "react";
|
||||
import { Vector3 } from "three";
|
||||
import { getMeasurementsFromDimensions, base } from "../utils";
|
||||
@@ -12,7 +11,7 @@ export const BrickCursor = forwardRef(
|
||||
},
|
||||
dimensions = { x: 1, z: 1 },
|
||||
rotation = 0,
|
||||
translation = 0,
|
||||
translation = { x: 0, z: 0 },
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
@@ -37,18 +36,43 @@ export const BrickCursor = forwardRef(
|
||||
);
|
||||
}, [intersect, height, rotation, width, depth]);
|
||||
|
||||
const compansateX =
|
||||
dimensions.x % 2 === 0 ? dimensions.x / 2 : (dimensions.x - 1) / 2;
|
||||
const compansateZ =
|
||||
dimensions.z % 2 === 0 ? dimensions.z / 2 : (dimensions.z - 1) / 2;
|
||||
|
||||
const offsetX =
|
||||
Math.sign(translation.x) < 0
|
||||
? Math.max(translation.x, -compansateX)
|
||||
: Math.min(translation.x, compansateX);
|
||||
|
||||
const offsetZ =
|
||||
Math.sign(translation.z) < 0
|
||||
? Math.max(translation.z, -compansateZ)
|
||||
: Math.min(translation.z, compansateZ);
|
||||
|
||||
return (
|
||||
<>
|
||||
<mesh
|
||||
<group
|
||||
ref={ref}
|
||||
position={[position.x, Math.abs(position.y), position.z]}
|
||||
rotation={[0, rotation, 0]}
|
||||
castShadow={true}
|
||||
receiveShadow={true}
|
||||
>
|
||||
<boxGeometry args={[width, height, depth]} />
|
||||
<meshBasicMaterial color={"black"} transparent={true} opacity={0.3} />
|
||||
</mesh>
|
||||
<mesh
|
||||
position={[
|
||||
(offsetX * width) / dimensions.x,
|
||||
0,
|
||||
(offsetZ * width) / dimensions.z,
|
||||
]}
|
||||
>
|
||||
<boxGeometry args={[width, height, depth]} />
|
||||
<meshBasicMaterial
|
||||
color={"black"}
|
||||
transparent={true}
|
||||
opacity={0.3}
|
||||
/>
|
||||
</mesh>
|
||||
</group>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -13,7 +13,9 @@ export const InfoPanel = () => {
|
||||
width: "200px",
|
||||
}}
|
||||
>
|
||||
Use panel on right top corner to customize the Lego Bricks Properties
|
||||
Use panel on right top corner to customize the Lego Bricks Properties Use
|
||||
<br />
|
||||
"AWSD" to chnage the anchor points of bricks
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,17 +1,48 @@
|
||||
import React from "react";
|
||||
// import { useControls } from "leva";
|
||||
|
||||
export const Lights = () => {
|
||||
// const [{ left, bottom, far, near, zoom }, set] = useControls(() => ({
|
||||
// left: {
|
||||
// value: 1,
|
||||
// min: 1,
|
||||
// max: 500,
|
||||
// step: 1,
|
||||
// },
|
||||
// bottom: {
|
||||
// value: 1,
|
||||
// min: 1,
|
||||
// max: 500,
|
||||
// step: 1,
|
||||
// },
|
||||
// far: {
|
||||
// value: 1,
|
||||
// min: 1,
|
||||
// max: 50000,
|
||||
// step: 1,
|
||||
// },
|
||||
// near: {
|
||||
// value: 0.01,
|
||||
// min: 0.01,
|
||||
// max: 20,
|
||||
// step: 0.1,
|
||||
// },
|
||||
// zoom: {
|
||||
// value: 0,
|
||||
// min: 0,
|
||||
// max: 2,
|
||||
// step: 0.1,
|
||||
// },
|
||||
// }));
|
||||
|
||||
return (
|
||||
<>
|
||||
<spotLight
|
||||
position={[-1000, 1500, -500]}
|
||||
intensity={0.9}
|
||||
castShadow={true}
|
||||
shadowBias={-0.0000022}
|
||||
penumbra={0.5}
|
||||
decay={2}
|
||||
shadowMapHeight={4096}
|
||||
shadowMapWidth={4096}
|
||||
penumbra={1}
|
||||
shadow-mapSize={[512, 512]}
|
||||
castShadow
|
||||
/>
|
||||
<ambientLight intensity={0.4} />
|
||||
</>
|
||||
|
||||
+56
-25
@@ -1,7 +1,12 @@
|
||||
import React, { useEffect, useState, useRef } from "react";
|
||||
import { Shadow, Brick, BrickCursor, Lights, Workspace } from ".";
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { Brick, BrickCursor, Lights, Workspace } from ".";
|
||||
import { Vector3, Box3 } from "three";
|
||||
import { uID, getMeasurementsFromDimensions, base } from "../utils";
|
||||
import {
|
||||
uID,
|
||||
getMeasurementsFromDimensions,
|
||||
base,
|
||||
useAnchorShorcuts,
|
||||
} from "../utils";
|
||||
import { useControls } from "leva";
|
||||
|
||||
let t;
|
||||
@@ -13,22 +18,38 @@ export const Scene = () => {
|
||||
|
||||
const brickCursorRef = useRef();
|
||||
|
||||
const { width, depth, rotate, color } = useControls({
|
||||
width: {
|
||||
value: 1,
|
||||
min: 1,
|
||||
max: 5,
|
||||
step: 1,
|
||||
},
|
||||
depth: {
|
||||
value: 1,
|
||||
min: 1,
|
||||
max: 5,
|
||||
step: 1,
|
||||
},
|
||||
rotate: false,
|
||||
color: "#f00",
|
||||
});
|
||||
const [{ width, depth, rotate, color, anchorX, anchorZ }, set] = useControls(
|
||||
() => ({
|
||||
width: {
|
||||
value: 1,
|
||||
min: 1,
|
||||
max: 5,
|
||||
step: 1,
|
||||
},
|
||||
depth: {
|
||||
value: 1,
|
||||
min: 1,
|
||||
max: 5,
|
||||
step: 1,
|
||||
},
|
||||
rotate: false,
|
||||
color: "#ff0",
|
||||
anchorX: {
|
||||
value: 0,
|
||||
min: -2,
|
||||
max: 2,
|
||||
step: 1,
|
||||
},
|
||||
anchorZ: {
|
||||
value: 0,
|
||||
min: -2,
|
||||
max: 2,
|
||||
step: 1,
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
useAnchorShorcuts(anchorX, anchorZ, set);
|
||||
|
||||
const addBrick = (e) => {
|
||||
e.stopPropagation();
|
||||
@@ -53,7 +74,7 @@ export const Scene = () => {
|
||||
const collision =
|
||||
boundingBoxOfBrickToBeAdded.intersectsBox(brickBoundingBox);
|
||||
|
||||
console.log(collision);
|
||||
console.log(collision, brickBoundingBox, boundingBoxOfBrickToBeAdded);
|
||||
|
||||
if (collision) {
|
||||
const dx = Math.abs(
|
||||
@@ -82,16 +103,17 @@ export const Scene = () => {
|
||||
dimensions: { x: width, z: depth },
|
||||
rotation: rotate ? Math.PI / 2 : 0,
|
||||
color: color,
|
||||
translation: { x: anchorX, z: anchorZ },
|
||||
};
|
||||
|
||||
setBricks((prevState) => [...prevState, brickData]);
|
||||
setBricks((prevBricks) => [...prevBricks, brickData]);
|
||||
}
|
||||
} else {
|
||||
isDrag.current = false;
|
||||
}
|
||||
};
|
||||
|
||||
const mouseMove = (e) => {
|
||||
const setBrickCursorPosition = (e) => {
|
||||
e.stopPropagation();
|
||||
if (!brickCursorRef.current) return;
|
||||
|
||||
@@ -120,6 +142,14 @@ export const Scene = () => {
|
||||
);
|
||||
};
|
||||
|
||||
const onClick = (e) => {
|
||||
addBrick(e);
|
||||
};
|
||||
|
||||
const mouseMove = (e) => {
|
||||
setBrickCursorPosition(e);
|
||||
};
|
||||
|
||||
const isDrag = useRef(false);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -152,22 +182,23 @@ export const Scene = () => {
|
||||
key={b.uID}
|
||||
dimensions={b.dimensions}
|
||||
intersect={b.intersect}
|
||||
addBrick={addBrick}
|
||||
onClick={onClick}
|
||||
rotation={b.rotation}
|
||||
bricksBoundBox={bricksBoundBox}
|
||||
uID={b.uID}
|
||||
mouseMove={mouseMove}
|
||||
color={b.color}
|
||||
translation={b.translation}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
<Shadow />
|
||||
<Lights />
|
||||
<Workspace addBrick={addBrick} mouseMove={mouseMove} />
|
||||
<Workspace onClick={onClick} mouseMove={mouseMove} />
|
||||
<BrickCursor
|
||||
ref={brickCursorRef}
|
||||
rotation={rotate ? Math.PI / 2 : 0}
|
||||
dimensions={{ x: width, z: depth }}
|
||||
translation={{ x: anchorX, z: anchorZ }}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -1,17 +1,25 @@
|
||||
import React from "react";
|
||||
|
||||
export const Workspace = ({ addBrick = () => {}, mouseMove = () => {} }) => {
|
||||
export const Workspace = ({ onClick = () => {}, mouseMove = () => {} }) => {
|
||||
return (
|
||||
<>
|
||||
<gridHelper onClick={addBrick} args={[1500, 60]} />
|
||||
<gridHelper args={[1500, 60]} />
|
||||
<mesh
|
||||
rotation={[-Math.PI / 2, 0, 0]}
|
||||
onClick={addBrick}
|
||||
onClick={onClick}
|
||||
onPointerMove={mouseMove}
|
||||
>
|
||||
<planeGeometry args={[1500, 1500]} />
|
||||
<meshBasicMaterial visible={false} />
|
||||
</mesh>
|
||||
{/* <mesh
|
||||
rotation={[-0.5 * Math.PI, 0, 0]}
|
||||
position={[0, -1, 0]}
|
||||
receiveShadow
|
||||
>
|
||||
<planeGeometry args={[3000, 3000, 1, 1]} />
|
||||
<shadowMaterial color="red" />
|
||||
</mesh> */}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
import useKeyboardShortcut from "use-keyboard-shortcut";
|
||||
|
||||
export const useAnchorShorcuts = (anchorX, anchorZ, set) => {
|
||||
const anchorXPlus = () => {
|
||||
set({ anchorX: anchorX + 1 });
|
||||
};
|
||||
|
||||
const anchorXMinus = () => {
|
||||
set({ anchorX: anchorX - 1 });
|
||||
};
|
||||
|
||||
const anchorZPlus = () => {
|
||||
set({ anchorZ: anchorZ - 1 });
|
||||
};
|
||||
|
||||
const anchorZMinus = () => {
|
||||
set({ anchorZ: anchorZ + 1 });
|
||||
};
|
||||
|
||||
useKeyboardShortcut(["D"], anchorXPlus, {
|
||||
overrideSystem: true,
|
||||
ignoreInputFields: false,
|
||||
repeatOnHold: false,
|
||||
});
|
||||
|
||||
useKeyboardShortcut(["A"], anchorXMinus, {
|
||||
overrideSystem: true,
|
||||
ignoreInputFields: false,
|
||||
repeatOnHold: false,
|
||||
});
|
||||
|
||||
useKeyboardShortcut(["W"], anchorZPlus, {
|
||||
overrideSystem: true,
|
||||
ignoreInputFields: false,
|
||||
repeatOnHold: false,
|
||||
});
|
||||
|
||||
useKeyboardShortcut(["S"], anchorZMinus, {
|
||||
overrideSystem: true,
|
||||
ignoreInputFields: false,
|
||||
repeatOnHold: false,
|
||||
});
|
||||
|
||||
return null;
|
||||
};
|
||||
@@ -1,2 +1,3 @@
|
||||
export * from "./helpers";
|
||||
export * from "./constants";
|
||||
export * from "./hooks";
|
||||
|
||||
Reference in New Issue
Block a user