From d52886d3b63b0a01b394ba871beb74221a8c224d Mon Sep 17 00:00:00 2001 From: Thomas Williams Date: Mon, 30 Nov 2020 18:06:15 +0800 Subject: [PATCH 1/3] Upgrade to React 17 due to React reconciler incompatibility Add drei helper library for three --- frontend/package.json | 3 +- frontend/yarn.lock | 154 ++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 149 insertions(+), 8 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index 17e260e..d1254bc 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -17,8 +17,9 @@ "@use-it/event-listener": "^0.1.6", "babel-preset-env": "^1.7.0", "color": "^3.1.3", + "drei": "^1.5.7", "next": "latest", - "react": "^16.12.0", + "react": "17.0.1", "react-dom": "^16.12.0", "react-three-fiber": "^5.3.1", "three": "^0.122.0", diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 79bf9a2..197ec91 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -2,6 +2,11 @@ # yarn lockfile v1 +"@alloc/types@^1.2.1": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@alloc/types/-/types-1.3.0.tgz#904245b8d3260a4b7d8a801c12501968f64fac08" + integrity sha512-mH7LiFiq9g6rX2tvt1LtwsclfG5hnsmtIfkZiauAGrm1AwXhoRS0sF2WrN9JGN7eV5vFXqNaB0eXZ3IvMsVi9g== + "@ampproject/toolbox-core@^2.6.0": version "2.6.1" resolved "https://registry.yarnpkg.com/@ampproject/toolbox-core/-/toolbox-core-2.6.1.tgz#af97ec253bf39e5fe5121b8ec28f1f35d1878446" @@ -351,6 +356,46 @@ resolved "https://registry.yarnpkg.com/@next/react-refresh-utils/-/react-refresh-utils-10.0.2.tgz#a9a3daa55385749cd62fb57d4423ca27ae5f4e81" integrity sha512-ILc5/BipD7+GBLmtfnQDMG71KLx0fEpVI5kJw8jN9SoeygMrb4auZhCl7gA6QP3oynocYouJ8piLj1Ol80ZvVw== +"@react-spring/animated@9.0.0-rc.3": + version "9.0.0-rc.3" + resolved "https://registry.yarnpkg.com/@react-spring/animated/-/animated-9.0.0-rc.3.tgz#e792cb76aacecfc78db2be6020ac11ce96503eb5" + integrity sha512-dAvgtKhkYpzzr+EkmZ4ZuJ5CujxCW0LaT109DvO/2MQNk3EWIxcgl+ik4tSulSbgau1GN8RlkRKyDp0wISdQ3Q== + dependencies: + "@babel/runtime" "^7.3.1" + "@react-spring/shared" "9.0.0-rc.3" + react-layout-effect "^1.0.1" + +"@react-spring/core@9.0.0-rc.3": + version "9.0.0-rc.3" + resolved "https://registry.yarnpkg.com/@react-spring/core/-/core-9.0.0-rc.3.tgz#c8e697573936c525bd0f6ca0c0869f75c86e8a83" + integrity sha512-3OzsVFxpfMJNkkQj8TwAH3NhUAX76AXu6WkslQF4EgBeEoG5eY3m+VvM9RsAsGWDuBKpscZ/wBpFt5Ih6KdGHA== + dependencies: + "@babel/runtime" "^7.3.1" + "@react-spring/animated" "9.0.0-rc.3" + "@react-spring/shared" "9.0.0-rc.3" + react-layout-effect "^1.0.1" + use-memo-one "^1.1.0" + +"@react-spring/shared@9.0.0-rc.3": + version "9.0.0-rc.3" + resolved "https://registry.yarnpkg.com/@react-spring/shared/-/shared-9.0.0-rc.3.tgz#3f4c9d90accc20fef51a283a7806d78390b84161" + integrity sha512-dd50TxwwMWd+dSB0InjndUN9w17cbnMCPy+0sag6zRxxKIo7eOyWSliOtLKxvufgmdC8Prm4M3GT5dmB1yxKEQ== + dependencies: + "@alloc/types" "^1.2.1" + "@babel/runtime" "^7.3.1" + fluids "^0.1.6" + tslib "^1.11.1" + +"@react-spring/web@^9.0.0-rc.3": + version "9.0.0-rc.3" + resolved "https://registry.yarnpkg.com/@react-spring/web/-/web-9.0.0-rc.3.tgz#da977382f91d9af4c400e4aa7dc37d3db07b87e0" + integrity sha512-rEvipblmihiz8+Eo01zDp5dqWn6XfYk8q2rlN9c18YIOL4o6nuY/VplDoocUMHYfH4liurpO4o1QudKOO1nAiQ== + dependencies: + "@babel/runtime" "^7.3.1" + "@react-spring/animated" "9.0.0-rc.3" + "@react-spring/core" "9.0.0-rc.3" + "@react-spring/shared" "9.0.0-rc.3" + "@types/color-convert@*": version "1.9.0" resolved "https://registry.yarnpkg.com/@types/color-convert/-/color-convert-1.9.0.tgz#bfa8203e41e7c65471e9841d7e306a7cd8b5172d" @@ -2048,11 +2093,23 @@ des.js@^1.0.0: inherits "^2.0.1" minimalistic-assert "^1.0.0" +detect-gpu@^1.3.0: + version "1.5.1" + resolved "https://registry.yarnpkg.com/detect-gpu/-/detect-gpu-1.5.1.tgz#487c734334a0d41f64302cf77c7986b62aee19bc" + integrity sha512-Ws//qvt0Kv9uXli1xJbC4jvrJdm+KonOMBjkTkICy4Ko+6qIggOXaF567o4kpapsX+pqW9FXmxh4IIAmqB+q7Q== + dependencies: + detect-ua "^1.0.2" + detect-libc@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= +detect-ua@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/detect-ua/-/detect-ua-1.0.2.tgz#4958d12a6af62adf522a26f0d9dec764ab3938c0" + integrity sha512-EsqFycxcggrkzRm3IaHr1nP1pSCDVLHW3haUvaSqws70B277a1SKk5hFtuJo3BKuPwTTRGTGsYYiJwS/97ptEg== + diffie-hellman@^5.0.0: version "5.0.3" resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" @@ -2138,6 +2195,24 @@ domutils@^2.0.0: domelementtype "^2.0.1" domhandler "^3.3.0" +drei@^1.5.7: + version "1.5.7" + resolved "https://registry.yarnpkg.com/drei/-/drei-1.5.7.tgz#ec6bf88f01d297eec6e09fb75f326b0cc78b9fa7" + integrity sha512-x7PQfvoSI1OqOwghZuZBhrNtaxMHh0bpVBLFYu39qwDghnBYgNIpNhoRtqK5FzRbcS1Rjdq0KWP0leSGKG/rsA== + dependencies: + "@babel/runtime" "^7.11.2" + "@react-spring/web" "^9.0.0-rc.3" + detect-gpu "^1.3.0" + glsl-noise "^0.0.0" + lodash.omit "^4.5.0" + lodash.pick "^4.4.0" + r3f-troika "^0.31.1" + react-merge-refs "^1.0.0" + stats.js "^0.17.0" + troika-three-text "^0.31.0" + utility-types "^3.10.0" + zustand "^3.0.3" + duplexer2@^0.1.2: version "0.1.4" resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1" @@ -2441,6 +2516,11 @@ find-up@^4.0.0: locate-path "^5.0.0" path-exists "^4.0.0" +fluids@^0.1.6: + version "0.1.10" + resolved "https://registry.yarnpkg.com/fluids/-/fluids-0.1.10.tgz#0517e7a53dbce1db011dddec301b75178518ba0e" + integrity sha512-66FLmUJOrkvEHIsRVeM+88MG0bjd2TOBuR0BkM0hzyCb68W9drzqeX/AHDNp3ouZALQN7JvBvmKdVhHI+PZsdg== + flush-write-stream@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz#8dd7d873a1babc207d94ead0c2e0e44276ebf2e8" @@ -2568,6 +2648,11 @@ globals@^9.18.0: resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a" integrity sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ== +glsl-noise@^0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/glsl-noise/-/glsl-noise-0.0.0.tgz#367745f3a33382c0eeec4cb54b7e99cfc1d7670b" + integrity sha1-NndF86MzgsDu7Ey1S36Zz8HXZws= + graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2: version "4.2.4" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb" @@ -3203,6 +3288,16 @@ locate-path@^5.0.0: dependencies: p-locate "^4.1.0" +lodash.omit@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.omit/-/lodash.omit-4.5.0.tgz#6eb19ae5a1ee1dd9df0b969e66ce0b7fa30b5e60" + integrity sha1-brGa5aHuHdnfC5aeZs4Lf6MLXmA= + +lodash.pick@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.pick/-/lodash.pick-4.4.0.tgz#52f05610fff9ded422611441ed1fc123a03001b3" + integrity sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM= + lodash.sortby@^4.7.0: version "4.7.0" resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" @@ -4041,6 +4136,14 @@ querystring@0.2.0, querystring@^0.2.0: resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= +r3f-troika@^0.31.1: + version "0.31.1" + resolved "https://registry.yarnpkg.com/r3f-troika/-/r3f-troika-0.31.1.tgz#456228c63b524a20489a1f4be90015e9b345d3d3" + integrity sha512-d0AI2ZCpU5u9axqJ3I+KI786ltQNOJKimeuq+96Sir/sg0Nl2m5JNA9fBg+YEZ6fOR7+kcbyaycQcTbfTpYDDQ== + dependencies: + troika-three-utils "^0.31.0" + troika-worker-utils "^0.31.0" + randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" @@ -4091,7 +4194,12 @@ react-is@16.13.1, react-is@^16.7.0, react-is@^16.8.0, react-is@^16.8.1: resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== -react-merge-refs@^1.1.0: +react-layout-effect@^1.0.1: + version "1.0.5" + resolved "https://registry.yarnpkg.com/react-layout-effect/-/react-layout-effect-1.0.5.tgz#0dc4e24452aee5de66c93c166f0ec512dfb1be80" + integrity sha512-zdRXHuch+OBHU6bvjTelOGUCM+UDr/iCY+c0wXLEAc+G4/FlcJruD/hUOzlKH5XgO90Y/BUJPNhI/g9kl+VAsA== + +react-merge-refs@^1.0.0, react-merge-refs@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/react-merge-refs/-/react-merge-refs-1.1.0.tgz#73d88b892c6c68cbb7a66e0800faa374f4c38b06" integrity sha512-alTKsjEL0dKH/ru1Iyn7vliS2QRcBp9zZPGoWxUOvRGWPUYgjo+V01is7p04It6KhgrzhJGnIj9GgX8W4bZoCQ== @@ -4142,14 +4250,13 @@ react-use-measure@^2.0.3: dependencies: debounce "^1.2.0" -react@^16.12.0: - version "16.14.0" - resolved "https://registry.yarnpkg.com/react/-/react-16.14.0.tgz#94d776ddd0aaa37da3eda8fc5b6b18a4c9a3114d" - integrity sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g== +react@17.0.1: + version "17.0.1" + resolved "https://registry.yarnpkg.com/react/-/react-17.0.1.tgz#6e0600416bd57574e3f86d92edba3d9008726127" + integrity sha512-lG9c9UuMHdcAexXtigOZLX8exLWkW0Ku29qPRU8uhF2R9BN96dLCt0psvzPLlHc5OWkgymP3qwTRgbnw5BKx3w== dependencies: loose-envify "^1.1.0" object-assign "^4.1.1" - prop-types "^15.6.2" "readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.6: version "2.3.7" @@ -4647,6 +4754,11 @@ static-extend@^0.1.1: define-property "^0.2.5" object-copy "^0.1.0" +stats.js@^0.17.0: + version "0.17.0" + resolved "https://registry.yarnpkg.com/stats.js/-/stats.js-0.17.0.tgz#b1c3dc46d94498b578b7fd3985b81ace7131cc7d" + integrity sha1-scPcRtlEmLV4t/05hbgaznExzH0= + "statuses@>= 1.5.0 < 2": version "1.5.0" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" @@ -5009,12 +5121,30 @@ traverse@0.6.6: resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.6.6.tgz#cbdf560fd7b9af632502fed40f918c157ea97137" integrity sha1-y99WD9e5r2MlAv7UD5GMFX6pcTc= +troika-three-text@^0.31.0: + version "0.31.0" + resolved "https://registry.yarnpkg.com/troika-three-text/-/troika-three-text-0.31.0.tgz#0c4dbd55c2d6d4516d60fc5247031464b056cf3f" + integrity sha512-JpRO+AC9mLZ49IymBwwwFLcasN0xjjQmSkGO9tlzlpeHpexsK4JB80qs6d6vhtt+vmfdJi27/m+WYhaR6+OzaQ== + dependencies: + troika-three-utils "^0.31.0" + troika-worker-utils "^0.31.0" + +troika-three-utils@^0.31.0: + version "0.31.0" + resolved "https://registry.yarnpkg.com/troika-three-utils/-/troika-three-utils-0.31.0.tgz#825d79313d04112fdfc63389ff5531ab8d7ba1a7" + integrity sha512-IUy61i+Kv0EjievWhOmIkqjDa1acIO2J98oO2tZ+PpEfA+Q0syRknH4SN+iiYpa/MNJH1gQz0WfZF4Vk1/pumQ== + +troika-worker-utils@^0.31.0: + version "0.31.0" + resolved "https://registry.yarnpkg.com/troika-worker-utils/-/troika-worker-utils-0.31.0.tgz#3d6783265f74cda1860e9d8369de433d29c70b92" + integrity sha512-WxHhfFmy6dWBMACUnzInBlqlR601Dy3c9hiRp4aAN+ACzSldwsv1SHOqix4X2+6HTz/pBtHtfJVDbwJiO+lbCw== + ts-pnp@^1.1.6: version "1.2.0" resolved "https://registry.yarnpkg.com/ts-pnp/-/ts-pnp-1.2.0.tgz#a500ad084b0798f1c3071af391e65912c86bca92" integrity sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw== -tslib@^1.9.0: +tslib@^1.11.1, tslib@^1.9.0: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== @@ -5130,6 +5260,11 @@ use-asset@^0.1.5: dependencies: fast-deep-equal "^3.1.3" +use-memo-one@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/use-memo-one/-/use-memo-one-1.1.1.tgz#39e6f08fe27e422a7d7b234b5f9056af313bd22c" + integrity sha512-oFfsyun+bP7RX8X2AskHNTxu+R3QdE/RC5IefMbqptmACAA/gfol1KDD5KRzPsGMa62sWxGZw+Ui43u6x4ddoQ== + use-subscription@1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/use-subscription/-/use-subscription-1.5.1.tgz#73501107f02fad84c6dd57965beb0b75c68c42d1" @@ -5303,3 +5438,8 @@ yaml@^1.7.2: version "1.10.0" resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.0.tgz#3b593add944876077d4d683fee01081bd9fff31e" integrity sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg== + +zustand@^3.0.3: + version "3.2.0" + resolved "https://registry.yarnpkg.com/zustand/-/zustand-3.2.0.tgz#a718a964440796d44edda3073b69e78b95f4c8f5" + integrity sha512-MBYFrnUdgFVi38tdQNSzVN9cPpRDf7w2HhdHGDSgBRHN7vIbUGUR3aBdVQykelXzSFR7iVj3YNBuq7B9ceMI5w== From f78447a22b1e19988be62d6441166a72815ddd73 Mon Sep 17 00:00:00 2001 From: Thomas Williams Date: Mon, 30 Nov 2020 22:27:31 +0800 Subject: [PATCH 2/3] Add FPS counter, toggleable with F Use instanced mesh and bulk line segments to speed up block rendering --- frontend/components/Stats.tsx | 3 + frontend/components/World.tsx | 238 ++++++++++++++++++++++++---------- 2 files changed, 175 insertions(+), 66 deletions(-) create mode 100644 frontend/components/Stats.tsx diff --git a/frontend/components/Stats.tsx b/frontend/components/Stats.tsx new file mode 100644 index 0000000..f9b8808 --- /dev/null +++ b/frontend/components/Stats.tsx @@ -0,0 +1,3 @@ +import { Stats } from 'drei'; + +export default Stats; \ No newline at end of file diff --git a/frontend/components/World.tsx b/frontend/components/World.tsx index 888c3bb..9e2ec43 100644 --- a/frontend/components/World.tsx +++ b/frontend/components/World.tsx @@ -1,12 +1,30 @@ import { Canvas, MeshProps, extend, useFrame, useThree, ReactThreeFiber, useLoader } from 'react-three-fiber'; -import { useRef, useState, useMemo, useEffect, Suspense, HTMLProps, RefObject, SetStateAction, Dispatch, useContext } from 'react'; -import { Mesh, BoxBufferGeometry, Vector3, Quaternion, Euler, Raycaster, Vector2, Object3D } from 'three'; +import { lazy, useRef, useState, useMemo, useEffect, Suspense, HTMLProps, RefObject, SetStateAction, Dispatch, useContext } from 'react'; +import { + Mesh, + MeshBasicMaterial, + InstancedMesh, + EdgesGeometry, + BoxBufferGeometry, + Vector3, + Quaternion, + Euler, + Raycaster, + Vector2, + Object3D, + LineBasicMaterial, + BufferGeometry, + Float32BufferAttribute, + LineSegments +} from 'three'; +import { Color as ThreeColor} from 'three'; import { OrbitControls } from 'three-orbitcontrols-ts'; // import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'; import { Turtle, TurtleContext, World } from '../pages'; import useEventListener from '@use-it/event-listener'; import Color from 'color'; import Tooltip from '@material-ui/core/Tooltip'; +import {use} from "ast-types"; extend({ OrbitControls }); declare global { @@ -17,6 +35,20 @@ declare global { } } +interface BlockInstance { + position: [number, number, number]; + color: string; + name: string; +} + +interface BlockInstanceGroup { + blockInstances: BlockInstance[]; + transparent: boolean; + renderUuid: string | null; +} + +const Stats = lazy(() => import('./Stats')); + export const hashCode = function (s: string): number { return s.split("").reduce(function (a, b) { a = ((a << 5) - a) + b.charCodeAt(0); return a & a }, 0); } @@ -102,9 +134,19 @@ function OtherTurtle({ obj, turtle, switchTurtle }: { obj: any, turtle: Turtle, ; } -function TooltipRaycaster({ mouse, setHovered }: { mouse: RefObject<{ x: number, y: number }>, setHovered: Dispatch> }) { +function findBlockGroupByRenderId(blockGroups: Map, renderUuid: string): BlockInstanceGroup | null { + for (const g of blockGroups.values()) { + if (g.renderUuid === renderUuid) { + return g; + } + } + return null; +} + +function TooltipRaycaster({ mouse, setHovered, blockGroups }: { mouse: RefObject<{ x: number, y: number }>, setHovered: Dispatch>, blockGroups: Map }) { const { camera, scene, size } = useThree(); const ray = useRef(null); + // const rayLine = useRef(null); useFrame(() => { if (!ray.current || !mouse.current) return; @@ -114,9 +156,15 @@ function TooltipRaycaster({ mouse, setHovered }: { mouse: RefObject<{ x: number, ray.current.setFromCamera(pos, camera); var intersects = ray.current.intersectObjects(scene.children); - let object: Object3D | null = null; + let object: Object3D | BlockInstance | null = null; for (let i = 0; i < intersects.length; i++) { object = intersects[i].object; + if (intersects[i].instanceId && intersects[i].instanceId >= 0) { + const group = findBlockGroupByRenderId(blockGroups, object.uuid); + if (group && intersects[i].instanceId < group.blockInstances.length) { + object = group.blockInstances[intersects[i].instanceId]; + } + } if (object.name) break; } if (object) { @@ -125,8 +173,56 @@ function TooltipRaycaster({ mouse, setHovered }: { mouse: RefObject<{ x: number, setHovered(''); } }); + // const geometry = new BufferGeometry(); + // const material = new LineBasicMaterial({color: 0x0000ff}); + // useFrame(() => { + // const points = []; + // points.push(ray.current.ray.origin); + // points.push(new Vector3().copy(ray.current.ray.origin).add(ray.current.ray.direction.multiplyScalar(ray.current.ray.far))); + // geometry.setFromPoints(points); + // }); + return ( + <> + + {/**/} + + ); +} - return ; +function groupBlockInstances(world: World, dontShowStone: boolean, showWholeWorld: boolean, turtle: Turtle | undefined, turtles: Turtle[]): Map { + const blockGroups = new Map(); + Object.keys(world).map((k, i) => { + const positions = k.split(',').map(p => parseInt(p)) as [number, number, number]; + const {name, metadata} = world[k]; + if (dontShowStone && name === 'minecraft:stone') { + return null; + } + if (!showWholeWorld && turtle && (Math.pow(positions[0] - turtle.x, 2) + Math.pow(positions[1] - turtle.y, 2) + Math.pow(positions[2] - turtle.z, 2)) > 1000) { + return null; + } + const transparent = name.includes('water') || name.includes('lava') || !!turtles.find(t => { + let checkEqual = (t: Turtle, positions: number[], x: number, y: number, z: number) => t.x === positions[0] + x && t.y === positions[1] + y && t.z === positions[2] + z; + for (let x = -1; x <= 1; x++) { + for (let y = -1; y <= 1; y++) { + for (let z = -1; z <= 1; z++) { + if (checkEqual(t, positions, x, y, z)) return true; + } + } + } + return false; + }); + const block = { + position: positions, + color: new ThreeColor(Color({h: hashCode(name + ':' + metadata) % 360, s: 60, l: 40}).toString()), + name: name + ':' + metadata + }; + let groupKey = transparent.toString(); + if (!blockGroups.has(groupKey)) { + blockGroups.set(groupKey, {blockInstances: [], transparent, renderUuid: null}); + } + blockGroups.get(groupKey).blockInstances.push(block); + }); + return blockGroups; } export default function WorldRenderer({ turtle, world, disableEvents, ...props }: { turtle?: Turtle, world: World, disableEvents: boolean } & HTMLProps) { @@ -137,6 +233,7 @@ export default function WorldRenderer({ turtle, world, disableEvents, ...props } const [hovered, setHovered] = useState(''); const [showWholeWorld, setShowWholeWorld] = useState(false); const [dontShowStone, setDontShowStone] = useState(false); + const [showFPS, setShowFPS] = useState(false); const disableEventsRef = useRef(disableEvents); useEffect(() => { @@ -148,8 +245,18 @@ export default function WorldRenderer({ turtle, world, disableEvents, ...props } }, [turtle]); useEventListener('keyup', (ev: KeyboardEvent) => { - if (disableEventsRef.current || !currentTurtleRef.current) return; let moved = false; + if (ev.code === 'KeyV') { + moved = true; + setShowWholeWorld(w => !w); + } else if (ev.code === 'KeyB') { + moved = true; + setDontShowStone(w => !w); + } else if (ev.code === 'KeyF') { + moved = true; + setShowFPS(w => !w); + } + if (disableEventsRef.current || !currentTurtleRef.current) return; if (ev.code === 'KeyW') { moved = true; currentTurtleRef.current.forward(); @@ -168,18 +275,14 @@ export default function WorldRenderer({ turtle, world, disableEvents, ...props } } else if (ev.code === 'ShiftLeft') { moved = true; currentTurtleRef.current.down(); - } else if (ev.code === 'KeyV') { - moved = true; - setShowWholeWorld(w => !w); - } else if (ev.code === 'KeyB') { - moved = true; - setDontShowStone(w => !w); } if (moved) { ev.stopPropagation(); ev.preventDefault(); } }); + + const blockGroups: Map = groupBlockInstances(world, dontShowStone, showWholeWorld, turtle, turtles); return (
+ { + showFPS && ( + + + + ) + } - + { turtle && @@ -226,33 +336,15 @@ export default function WorldRenderer({ turtle, world, disableEvents, ...props } } - {Object.keys(world).map(k => { - let positions = k.split(',').map(p => parseInt(p)) as [number, number, number]; - let { name, metadata } = world[k]; - if (dontShowStone && name ==='minecraft:stone') { - return null; - } - if (!showWholeWorld && turtle && (Math.pow(positions[0] - turtle.x, 2) + Math.pow(positions[1] - turtle.y, 2) + Math.pow(positions[2] - turtle.z, 2)) > 1000) { - return null; + + { + [...blockGroups.keys()].map(k => ( + + )) } - return { - let checkEqual = (t: Turtle, positions: number[], x: number, y: number, z: number) => t.x === positions[0] + x && t.y === positions[1] + y && t.z === positions[2] + z; - for (let x = -1; x <= 1; x++) { - for (let y = -1; y <= 1; y++) { - for (let z = -1; z <= 1; z++) { - if (checkEqual(t, positions, x, y, z)) return true; - } - } - } - return false; - })} - key={k} position={positions} name={name + ':' + metadata} color={Color({ - h: hashCode(name + ':' + metadata) % 360, - s: 60, - l: 40, - }).toString()} /> - }).filter(b => b)} + + + { setTurtleIndex(turtle.id); @@ -263,36 +355,50 @@ export default function WorldRenderer({ turtle, world, disableEvents, ...props } ) } +const boxGeom = new BoxBufferGeometry(1, 1, 1); +const boxEdgeGeom = new EdgesGeometry(boxGeom).toNonIndexed(); -function Box(props: MeshProps & { color: string, name: string, transparent: boolean }) { - if (props.name.includes('computercraft:turtle_expanded')) return null; - // This reference will give us direct access to the mesh - const mesh = useRef() +function Blocks(props: { group: BlockInstanceGroup }) { + const instancedMesh = useRef(); + const mat = new MeshBasicMaterial({color: Color({h: 0, s: 60, l: 40, }), transparent: props.group.transparent, opacity: props.group.transparent ? 0.5 : 1}); + const blockTransform = new Object3D(); + let set = false; + useFrame(() => { + if (set) { + return; + } + props.group.blockInstances.map((block, i) => { + blockTransform.position.set(...(block.position)); + blockTransform.rotation.set(0, 0, 0); + blockTransform.updateMatrix(); + instancedMesh.current.setMatrixAt(i, blockTransform.matrix); + block.color && instancedMesh.current.setColorAt(i, block.color); + }); + instancedMesh.current.instanceMatrix.needsUpdate = true; + props.group.renderUuid = instancedMesh.current.uuid; + set = true; + }); + return ( + + ); +} - // Set up state for the hovered and active state +function BlockLines(props: {groups: Iterable}) { + const geometry = new BufferGeometry(); + const material = new LineBasicMaterial({ color: '#000000' }); - // Rotate mesh every frame, this is outside of React without overhead - // useFrame(() => { - // if (mesh.current) mesh.current.rotation.x = mesh.current.rotation.y += 0.01 - // if (lines.current) lines.current.rotation.x = lines.current.rotation.y += 0.01 - // }) + const positions = []; + for (const group of props.groups) { + for (const block of group.blockInstances) { + const edgePositions = boxEdgeGeom.getAttribute('position'); + edgePositions.array.forEach((v, i) => { + positions.push(v + block.position[i % 3]); + }); + } + } - const geom = useMemo(() => new BoxBufferGeometry(1, 1, 1), []); + geometry.setAttribute('position', new Float32BufferAttribute(positions, 3)); + geometry.computeBoundingSphere(); - return ( - <> - - - - - - - - - - ) + return () } \ No newline at end of file From 10726339d72b6d850d135f86683aaefc4c1cb4ef Mon Sep 17 00:00:00 2001 From: Thomas Williams Date: Tue, 1 Dec 2020 12:27:18 +0800 Subject: [PATCH 3/3] Fix block color and attempt to avoid some line z fighting Fix some offset issues in calculating mouse coords Slightly optimise tooltip ray-casting --- frontend/components/World.tsx | 39 +++++++++++++---------------------- 1 file changed, 14 insertions(+), 25 deletions(-) diff --git a/frontend/components/World.tsx b/frontend/components/World.tsx index 9e2ec43..9244a16 100644 --- a/frontend/components/World.tsx +++ b/frontend/components/World.tsx @@ -143,10 +143,9 @@ function findBlockGroupByRenderId(blockGroups: Map, return null; } -function TooltipRaycaster({ mouse, setHovered, blockGroups }: { mouse: RefObject<{ x: number, y: number }>, setHovered: Dispatch>, blockGroups: Map }) { - const { camera, scene, size } = useThree(); +function TooltipRaycaster({ mouse, hovered, setHovered, blockGroups }: { mouse: RefObject<{ x: number, y: number }>, setHovered: Dispatch>, blockGroups: Map }) { + const {camera, scene, size} = useThree(); const ray = useRef(null); - // const rayLine = useRef(null); useFrame(() => { if (!ray.current || !mouse.current) return; @@ -167,26 +166,15 @@ function TooltipRaycaster({ mouse, setHovered, blockGroups }: { mouse: RefObject } if (object.name) break; } - if (object) { - setHovered(object.name); - } else { + if (object && object.name) { + if (hovered !== object.name) { + setHovered(object.name); // This call is actually quite expensive + } + } else if (hovered !== '') { setHovered(''); } }); - // const geometry = new BufferGeometry(); - // const material = new LineBasicMaterial({color: 0x0000ff}); - // useFrame(() => { - // const points = []; - // points.push(ray.current.ray.origin); - // points.push(new Vector3().copy(ray.current.ray.origin).add(ray.current.ray.direction.multiplyScalar(ray.current.ray.far))); - // geometry.setFromPoints(points); - // }); - return ( - <> - - {/**/} - - ); + return (); } function groupBlockInstances(world: World, dontShowStone: boolean, showWholeWorld: boolean, turtle: Turtle | undefined, turtles: Turtle[]): Map { @@ -313,7 +301,8 @@ export default function WorldRenderer({ turtle, world, disableEvents, ...props } // console.log("Nothing"); // } // console.log(ev.clientY); - position.current = { x: ev.clientX, y: ev.clientY - 100 }; + const canvasYOffset = ev.target.getBoundingClientRect().top; + position.current = { x: ev.clientX, y: ev.clientY - canvasYOffset }; if (popperRef.current) popperRef.current.update(); }} @@ -328,7 +317,7 @@ export default function WorldRenderer({ turtle, world, disableEvents, ...props } ) } - + { turtle && @@ -356,11 +345,11 @@ export default function WorldRenderer({ turtle, world, disableEvents, ...props } ) } const boxGeom = new BoxBufferGeometry(1, 1, 1); -const boxEdgeGeom = new EdgesGeometry(boxGeom).toNonIndexed(); +const boxEdgeGeom = new EdgesGeometry(boxGeom.scale(1.0000001, 1.0000001, 1.0000001)); function Blocks(props: { group: BlockInstanceGroup }) { const instancedMesh = useRef(); - const mat = new MeshBasicMaterial({color: Color({h: 0, s: 60, l: 40, }), transparent: props.group.transparent, opacity: props.group.transparent ? 0.5 : 1}); + const mat = new MeshBasicMaterial({color: '#ffffff', transparent: props.group.transparent, opacity: props.group.transparent ? 0.5 : 1}); const blockTransform = new Object3D(); let set = false; useFrame(() => { @@ -400,5 +389,5 @@ function BlockLines(props: {groups: Iterable}) { geometry.setAttribute('position', new Float32BufferAttribute(positions, 3)); geometry.computeBoundingSphere(); - return () + return (); } \ No newline at end of file