├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── assets ├── by-the-ocean │ └── by-the-ocean.glb ├── cell │ └── scene.glb ├── eiffel │ ├── license.txt │ ├── scene.bin │ └── scene.gltf ├── icons │ ├── in-out-arrow.svg │ ├── left-arrow.svg │ ├── left-mouse.svg │ ├── lock.svg │ ├── middle-mouse.svg │ ├── mouse-wheel.svg │ ├── move-arrows.svg │ ├── pitch-yaw-arrow.svg │ ├── roll.svg │ └── yaw-arrow.svg ├── milk-delivery │ └── milk-delivery.glb ├── starry-night │ ├── CoarseBristles.png │ └── starry-night.glb └── t-rex │ ├── license.txt │ ├── scene.bin │ ├── scene.gltf │ └── textures │ ├── body_baseColor.jpeg │ ├── eyes_0_metallicRoughness.png │ └── eyes_baseColor.jpeg ├── component-usage ├── adaptive-frame-rate.html ├── bricks │ ├── colors.js │ ├── falling.html │ ├── flexible.html │ ├── single-alt.html │ ├── single.html │ ├── snapping.html │ ├── snapping.js │ └── wall.html ├── connecting-line.html ├── cursor-tracker.html ├── dat-gui │ └── torus.html ├── desktop-vr-controller.html ├── desktop-xr-hands │ ├── aframe-example.html │ └── message.html ├── dynamic-snap.html ├── entity-screen-position.html ├── frame-rate.html ├── graph.html ├── labels.html ├── low-cost-raycasting.html ├── mouse-manipulation.html ├── mouse-object-control.html ├── object-manipulation.html ├── polygon-wireframe.html ├── raycaster-thresholds.html ├── screen-position.html ├── stats-collector.html ├── stats-panel.html └── window-3d │ ├── by-the-ocean.html │ ├── configure-example.js │ ├── draggable-div.js │ ├── hello-aframe.html │ ├── index.html │ ├── milk-delivery.html │ ├── starry-night.html │ └── styles.css ├── components ├── anchored │ ├── README.md │ ├── dist │ │ ├── anchored.js │ │ ├── anchored.min.js │ │ └── anchored.min.js.LICENSE.txt │ ├── index.js │ ├── package-lock.json │ ├── package.json │ ├── test │ │ ├── index.html │ │ └── reset.js │ ├── webpack.config.js │ └── webpack.prod.config.js ├── ball-blaster │ ├── README.md │ ├── image-20230701171702787.png │ ├── index.js │ ├── package.json │ └── test │ │ ├── ammo-blaster.html │ │ ├── cannon-blaster.html │ │ └── physx-blaster.html ├── bricks │ ├── README.md │ ├── image-20230318103822805.png │ ├── image-20230318113905914.png │ ├── image-20230418165028833.png │ ├── index.js │ ├── package.json │ └── test │ │ ├── basic-bricks.html │ │ ├── brick-geometry.html │ │ └── single-brick.html ├── connecting-line │ ├── README.md │ ├── index.js │ ├── package.json │ └── test │ │ ├── event-updates.html │ │ ├── length-factor.html │ │ └── width.html ├── cursor-tracker │ ├── README.md │ ├── index.js │ └── package.json ├── dat-gui │ ├── README.md │ ├── dist │ │ ├── dat-gui.js │ │ └── dat-gui.min.js │ ├── image-20240304145551473.png │ ├── image-20240304150412011.png │ ├── index.js │ ├── package-lock.json │ ├── package.json │ ├── test │ │ ├── bricks.html │ │ └── multiple-unnamed.html │ ├── webpack.config.js │ └── webpack.prod.config.js ├── desktop-vr-controller │ ├── .gitignore │ ├── .npmignore │ ├── README.md │ ├── dist │ │ ├── desktop-vr-controller.js │ │ └── desktop-vr-controller.min.js │ ├── image-20220802185450073.png │ ├── image-20220802185543518.png │ ├── image-20220802185741939.png │ ├── image-20220802190100076.png │ ├── index.js │ ├── package-lock.json │ ├── package.json │ ├── webpack.config.js │ └── webpack.prod.config.js ├── desktop-xr-hands │ ├── README.md │ ├── dist │ │ ├── desktop-xr-hands.js │ │ ├── desktop-xr-hands.js.map │ │ ├── desktop-xr-hands.min.js │ │ └── desktop-xr-hands.min.js.map │ ├── image-20230829170643885.png │ ├── image-20230829170851279.png │ ├── image-20230829175405101.png │ ├── index.js │ ├── package-lock.json │ ├── package.json │ ├── src │ │ ├── desktop-xr-hands.js │ │ └── hand-landmarker.js │ ├── test │ │ ├── dots.html │ │ ├── mesh.html │ │ └── wrist-twist.html │ └── webpack.config.js ├── desktop-xr-plane │ ├── README.md │ ├── index.js │ └── package.json ├── dynamic-snap │ ├── README.md │ ├── dist │ │ ├── dynamic-snap.js │ │ └── dynamic-snap.min.js │ ├── image-20230409165520988.png │ ├── index.js │ ├── package-lock.json │ ├── package.json │ ├── test │ │ ├── snap-to-grid.js │ │ ├── test1.html │ │ ├── test2.html │ │ └── test3.html │ ├── webpack.config.js │ └── webpack.prod.config.js ├── frame-rate │ ├── README.md │ ├── index.js │ └── package.json ├── graph │ ├── .gitignore │ ├── README.md │ ├── dist │ │ ├── graph.js │ │ └── graph.min.js │ ├── image-20230318092246915.png │ ├── image-20230318092739663.png │ ├── index.js │ ├── package-lock.json │ ├── package.json │ ├── src │ │ └── utils.js │ ├── test │ │ └── index.html │ ├── webpack.config.js │ └── webpack.prod.config.js ├── head-tracking │ ├── README.md │ ├── dist │ │ ├── head-tracking.js │ │ ├── head-tracking.js.map │ │ ├── head-tracking.min.js │ │ └── head-tracking.min.js.map │ ├── index.js │ ├── package-lock.json │ ├── package.json │ ├── src │ │ ├── face-detector.js │ │ └── head-tracker.js │ ├── test │ │ ├── index.html │ │ ├── rgbe-loader.js │ │ ├── window-camera.html │ │ ├── window-camera.js │ │ ├── window-camera2.html │ │ └── window-camera3.html │ └── webpack.config.js ├── hide-on-hover.js ├── label │ ├── README.md │ ├── image-20220811164727667.png │ ├── image-20220811164814125.png │ ├── index.js │ ├── package-lock.json │ ├── package.json │ └── test │ │ └── test-overwite.html ├── laser-manipulation │ ├── .gitignore │ ├── .npmignore │ ├── README.md │ ├── dist │ │ ├── laser-manipulation.js │ │ └── laser-manipulation.min.js │ ├── index.js │ ├── package-lock.json │ ├── package.json │ ├── webpack.config.js │ └── webpack.prod.config.js ├── low-cost-raycasting.js ├── mouse-manipulation │ ├── .gitignore │ ├── .npmignore │ ├── README.md │ ├── dist │ │ ├── mouse-manipulation.js │ │ └── mouse-manipulation.min.js │ ├── image-20220812163558569.png │ ├── index.js │ ├── package-lock.json │ ├── package.json │ ├── webpack.config.js │ └── webpack.prod.config.js ├── object-parent │ ├── README.md │ ├── index.js │ └── package.json ├── plug-socket │ ├── README.md │ ├── dist │ │ ├── plug-socket.js │ │ └── plug-socket.min.js │ ├── image-20230409165520988.png │ ├── image-20230410115732409.png │ ├── image-20230410120556987.png │ ├── index.js │ ├── package-lock.json │ ├── package.json │ ├── src │ │ ├── plug.js │ │ ├── socket-fabric.js │ │ ├── socket-system.js │ │ └── socket.js │ ├── test │ │ ├── test1.html │ │ ├── test10.html │ │ ├── test11.html │ │ ├── test12.html │ │ ├── test13.html │ │ ├── test14.html │ │ ├── test2.html │ │ ├── test3.html │ │ ├── test4.html │ │ ├── test5.html │ │ ├── test6.html │ │ ├── test7.html │ │ ├── test8.html │ │ ├── test9.html │ │ ├── unit-tests.html │ │ └── ut │ │ │ ├── common.js │ │ │ ├── ut-basic.js │ │ │ ├── ut-events.js │ │ │ └── ut-multi-plug.js │ ├── webpack.config.js │ └── webpack.prod.config.js ├── polygon-wireframe │ ├── README.md │ ├── image-20220816094651445.png │ ├── index.js │ ├── package.json │ └── test │ │ └── hidden-line-options.html ├── raycast-target │ ├── README.md │ ├── index.js │ └── package.json ├── raycaster-thresholds │ ├── README.md │ ├── index.js │ └── package.json ├── scale-on-hover.js ├── screen-position │ ├── README.md │ ├── index.js │ └── package.json ├── stats-panel │ ├── README.md │ ├── image-20221106161718921.png │ ├── index.js │ └── package.json ├── thumbstick-states │ ├── README.md │ ├── index.js │ └── package.json ├── window-3d │ ├── README.md │ └── index.js └── xr-room-physics │ ├── README.md │ ├── dist │ ├── xr-room-physics.js │ ├── xr-room-physics.min.js │ └── xr-room-physics.min.js.LICENSE.txt │ ├── image-20230702084154726.png │ ├── image-20230702090037281.png │ ├── image-20230702091131736.png │ ├── image-20230702092025851.png │ ├── image-20230702092318339.png │ ├── image-20230702100955317.png │ ├── index.js │ ├── package-lock.json │ ├── package.json │ ├── test │ ├── ammo-desktop-room.html │ ├── ammo-desktop-room2.html │ ├── ammo-desktop-room3.html │ ├── ammo-desktop-room4.html │ ├── ammo-desktop-room5.html │ ├── ammo-room.html │ ├── ammo-shape-test.html │ ├── cannon-desktop-room5.html │ ├── cannon-room.html │ ├── cannon-shape-test.html │ ├── index.html │ ├── physx-desktop-room5.html │ ├── physx-room.html │ ├── physx-shape-test.html │ ├── test-geometries.js │ └── toggle-debug.js │ └── webpack.config.js ├── compositions └── wrapped-present │ ├── README.md │ ├── assets │ └── sample-image.png │ ├── index.html │ ├── lib │ ├── FontLoader.js │ └── TextGeometry.js │ └── src │ ├── apply-url-params.js │ └── components.js ├── docs └── utility-components.md └── styles.css /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/node_modules/** 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 diarmidmackenzie 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /assets/by-the-ocean/by-the-ocean.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diarmidmackenzie/aframe-components/1dfa2f24b93dd68ff1f5fbaec558e28310db484c/assets/by-the-ocean/by-the-ocean.glb -------------------------------------------------------------------------------- /assets/cell/scene.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diarmidmackenzie/aframe-components/1dfa2f24b93dd68ff1f5fbaec558e28310db484c/assets/cell/scene.glb -------------------------------------------------------------------------------- /assets/eiffel/license.txt: -------------------------------------------------------------------------------- 1 | Model Information: 2 | * title: ( FREE ) La tour Eiffel 3 | * source: https://sketchfab.com/3d-models/free-la-tour-eiffel-8553f94d06e24cb4b0fde1080f281674 4 | * author: SDC (https://sketchfab.com/3Duae) 5 | 6 | Model License: 7 | * license type: CC-BY-4.0 (http://creativecommons.org/licenses/by/4.0/) 8 | * requirements: Author must be credited. Commercial use is allowed. 9 | 10 | If you use this 3D model in your project be sure to copy paste this credit wherever you share it: 11 | This work is based on "( FREE ) La tour Eiffel" (https://sketchfab.com/3d-models/free-la-tour-eiffel-8553f94d06e24cb4b0fde1080f281674) by SDC (https://sketchfab.com/3Duae) licensed under CC-BY-4.0 (http://creativecommons.org/licenses/by/4.0/) -------------------------------------------------------------------------------- /assets/eiffel/scene.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diarmidmackenzie/aframe-components/1dfa2f24b93dd68ff1f5fbaec558e28310db484c/assets/eiffel/scene.bin -------------------------------------------------------------------------------- /assets/icons/in-out-arrow.svg: -------------------------------------------------------------------------------- 1 | 2 | 16 | 18 | 38 | 42 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /assets/icons/left-arrow.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/icons/left-mouse.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /assets/icons/lock.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/icons/mouse-wheel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/icons/move-arrows.svg: -------------------------------------------------------------------------------- 1 | move-arrows -------------------------------------------------------------------------------- /assets/icons/pitch-yaw-arrow.svg: -------------------------------------------------------------------------------- 1 | 2 | 51 | -------------------------------------------------------------------------------- /assets/icons/yaw-arrow.svg: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /assets/milk-delivery/milk-delivery.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diarmidmackenzie/aframe-components/1dfa2f24b93dd68ff1f5fbaec558e28310db484c/assets/milk-delivery/milk-delivery.glb -------------------------------------------------------------------------------- /assets/starry-night/CoarseBristles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diarmidmackenzie/aframe-components/1dfa2f24b93dd68ff1f5fbaec558e28310db484c/assets/starry-night/CoarseBristles.png -------------------------------------------------------------------------------- /assets/starry-night/starry-night.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diarmidmackenzie/aframe-components/1dfa2f24b93dd68ff1f5fbaec558e28310db484c/assets/starry-night/starry-night.glb -------------------------------------------------------------------------------- /assets/t-rex/license.txt: -------------------------------------------------------------------------------- 1 | Model Information: 2 | * title: Tyrannosaurus Rex 3 | * source: https://sketchfab.com/3d-models/tyrannosaurus-rex-9eade2f07a8d4ae1aac8f53e5a3d0a7a 4 | * author: AVINAS (https://sketchfab.com/AVINAS) 5 | 6 | Model License: 7 | * license type: CC-BY-4.0 (http://creativecommons.org/licenses/by/4.0/) 8 | * requirements: Author must be credited. Commercial use is allowed. 9 | 10 | If you use this 3D model in your project be sure to copy paste this credit wherever you share it: 11 | This work is based on "Tyrannosaurus Rex" (https://sketchfab.com/3d-models/tyrannosaurus-rex-9eade2f07a8d4ae1aac8f53e5a3d0a7a) by AVINAS (https://sketchfab.com/AVINAS) licensed under CC-BY-4.0 (http://creativecommons.org/licenses/by/4.0/) -------------------------------------------------------------------------------- /assets/t-rex/scene.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diarmidmackenzie/aframe-components/1dfa2f24b93dd68ff1f5fbaec558e28310db484c/assets/t-rex/scene.bin -------------------------------------------------------------------------------- /assets/t-rex/textures/body_baseColor.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diarmidmackenzie/aframe-components/1dfa2f24b93dd68ff1f5fbaec558e28310db484c/assets/t-rex/textures/body_baseColor.jpeg -------------------------------------------------------------------------------- /assets/t-rex/textures/eyes_0_metallicRoughness.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diarmidmackenzie/aframe-components/1dfa2f24b93dd68ff1f5fbaec558e28310db484c/assets/t-rex/textures/eyes_0_metallicRoughness.png -------------------------------------------------------------------------------- /assets/t-rex/textures/eyes_baseColor.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diarmidmackenzie/aframe-components/1dfa2f24b93dd68ff1f5fbaec558e28310db484c/assets/t-rex/textures/eyes_baseColor.jpeg -------------------------------------------------------------------------------- /component-usage/bricks/colors.js: -------------------------------------------------------------------------------- 1 | COLORS = ['#05131D', 2 | '#0055BF', 3 | '#237841', 4 | '#008F9B', 5 | '#C91A09', 6 | '#C870A0', 7 | '#583927', 8 | '#9BA19D', 9 | '#6D6E5C', 10 | '#B4D2E3', 11 | '#4B9F4A', 12 | '#55A5AF', 13 | '#F2705E', 14 | '#FC97AC', 15 | '#F2CD37', 16 | '#FFFFFF', 17 | '#C2DAB8', 18 | '#FBE696', 19 | '#E4CD9E', 20 | '#C9CAE2', 21 | '#D4D5C9', 22 | '#81007B', 23 | '#2032B0', 24 | '#FE8A18', 25 | '#923978', 26 | '#BBE90B', 27 | '#958A73', 28 | '#E4ADC8', 29 | '#AC78BA', 30 | '#E1D5ED'] -------------------------------------------------------------------------------- /component-usage/bricks/falling.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 53 | 54 | 55 | 56 |
57 |

Building bricks with basic collision physics

58 |
59 | 62 | view code 63 | 64 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /component-usage/bricks/single-alt.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 |

A building base plate - use mouse to move it around

13 |
14 | 17 | view code 18 | 19 | 21 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /component-usage/bricks/single.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 |

Default building brick - use mouse to move it around

13 |
14 | 17 | view code 18 | 19 | 21 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /component-usage/connecting-line.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 |

Connecting lines between entities.

11 |

Specific offsets relative to the center of the entity can be specified.

12 |
13 | 16 | view code 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /component-usage/cursor-tracker.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | The cursor-tracker component ensures that an entity positioned at the camera stays aligned in 12 | orientation with the cursor.
13 | The orientation of this entity is shown in this example by a sphere mesh rotated so that one pole lies directly ahead.
14 | This is intended for use with RayOrigin: mouse.
15 | An example application of this is the mouse-manipulation component, where it is used together with 16 | object-parent to move an entity around in line with the mouse pointer.
17 | Note that this text overlay blocks the cursor 18 |
19 | 21 | 24 | 25 | 26 | 28 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 48 | view code 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /component-usage/dat-gui/torus.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 |

Controllable parameters for a-torus using aframe-dat-gui

12 |
13 | 16 | view code 17 | 18 | 19 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /component-usage/desktop-xr-hands/message.html: -------------------------------------------------------------------------------- 1 |

Hand tracking relies on an API proposal and is not yet part of the WebXR standard. Browser support is experimental, it might vary across vendors and it might require enabling the feature manually in settings.

-------------------------------------------------------------------------------- /component-usage/entity-screen-position.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 40 | 41 | 42 |
43 | This example displays the x/y screen position (same co-ordinates as mouse system) of the center of the cube
44 | Drag with mouse to look around, and use WASD to move
45 | Values are not necessarily valid when the cube is off-screen
46 | 47 |
48 | 51 | view code 52 | 53 | 54 | 55 | 56 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /component-usage/labels.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 |

Fixed size labels that face the camera. Left-mouse and WASD to move view.

11 |

Distanced labels maintain a fixed distance on camera from the point that they label.

12 |
13 | 16 | view code 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 29 | 30 | 31 | 35 | 36 | 39 | 40 | 41 | 42 | 43 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /component-usage/low-cost-raycasting.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 |

Low-cost raycasting. Still under development...

13 |
14 | 17 | view code 18 | 19 | 24 | 25 | 28 | 29 | 32 | 33 | 36 | 37 | 38 | 39 | 40 | 43 | 44 | 47 | 48 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /component-usage/raycaster-thresholds.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 22 | 23 | 24 | 25 |
26 |

By default, raycaster has a threshold of 1m for detecting intersection with a point or line.

27 |

The raycaster-thresholds component provide more flexibility for A-Frame applications.

28 | 33 |
34 |

When raycaster-threshold is not used, raycaster default of 1m is used, and the box will be hidden whenever the cursor is anywhere near or inside it.

35 |

When raycaster-thresholds is specified, raycaster-thresholds default of 0.01m is used, and the box will be hidden only when the cursor is on the line

36 |

Although not shown in this basic example, the "line" and "points" properties of raycaster-threshold can be used to set any desired threshold for raycasting against lines and points.

37 |
38 | 39 | 42 | view code 43 | 44 | 45 | 56 | 57 | -------------------------------------------------------------------------------- /component-usage/screen-position.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 |

Screen positions of entities. Move using left mouse or WASD to observe changes.

11 |

Small squares are 2D square overlays that show the tracked 2D position of each element on the screen.

12 |
13 | Cube: Calculating... 14 |
15 |
16 | Sphere: Calculating... 17 |
18 |
19 | Cylinder: Calculating... 20 |
21 |
22 | 25 | view code 26 | 27 |
32 |
33 |
38 |
39 |
44 |
45 | 46 | 47 | 48 | 49 | 50 | 53 | 54 | 57 | 58 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /component-usage/stats-collector.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 23 | 24 | 25 |
26 |

Collect and process stats from a data source, output in the style of A-Frame stats

27 |
28 | 31 | view code 32 | 33 | 34 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /component-usage/window-3d/by-the-ocean.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 18 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 34 | 35 | 40 | 41 | 42 | 43 |
44 | 45 |
46 |
47 |
48 |
49 |
50 |
51 | 52 | 53 | -------------------------------------------------------------------------------- /component-usage/window-3d/configure-example.js: -------------------------------------------------------------------------------- 1 | 2 | const DIORAMA_LIST = ["hello-aframe", "by-the-ocean", "starry-night", "milk-delivery"] 3 | 4 | AFRAME.registerComponent('configure-example', { 5 | 6 | schema: { 7 | diorama: {oneOf: DIORAMA_LIST, 8 | default: "by-the-ocean"}, 9 | width: {default: 600 }, 10 | height: {default: 400 }, 11 | xSensitivity: {default: 1 }, 12 | ySensitivity: {default: 1 }, 13 | headTracking: {default: false}, 14 | zSensitivity: {default: 3 }, 15 | stabilizationFactor: {default: 0.9}, 16 | moveSideToSide: {default: true}, 17 | }, 18 | 19 | init() { 20 | this.dioramas = {} 21 | DIORAMA_LIST.forEach((id) => { 22 | this.dioramas[id] = document.getElementById(`${id}-container`) 23 | }) 24 | 25 | this.viewport = document.getElementById('viewport1') 26 | 27 | this.cameraEntity = document.getElementById('camera1') 28 | }, 29 | 30 | update() { 31 | 32 | const { data } = this 33 | DIORAMA_LIST.forEach((id) => { 34 | this.dioramas[id].setAttribute("visible", "false") 35 | }) 36 | 37 | this.dioramas[data.diorama].setAttribute("visible", "true") 38 | 39 | this.viewport.style.width = `${data.width}px` 40 | this.viewport.style.height = `${data.height}px` 41 | 42 | const {xSensitivity, ySensitivity, zSensitivity } = data 43 | 44 | this.cameraEntity.setAttribute("window-3d", { xSensitivity, 45 | ySensitivity, 46 | zSensitivity}) 47 | 48 | 49 | if (this.data.headTracking) { 50 | const { stabilizationFactor } = data 51 | this.cameraEntity.setAttribute("face-detector", "") 52 | this.cameraEntity.setAttribute("head-tracker", { stabilizationFactor }) 53 | this.cameraEntity.setAttribute("head-tracker", "defaultPosition: 0 0 0.75") 54 | } 55 | else { 56 | this.cameraEntity.removeAttribute("face-detector") 57 | this.cameraEntity.removeAttribute("head-tracker") 58 | } 59 | } 60 | }) -------------------------------------------------------------------------------- /component-usage/window-3d/draggable-div.js: -------------------------------------------------------------------------------- 1 | let divLeft = 200; 2 | let divTop = 100; 3 | let mouseOffsetX = 0; 4 | let mouseOffsetY = 0; 5 | let dragging = false 6 | let direction = 1; 7 | 8 | const onMouseMove = (e) => { 9 | 10 | if (!dragging) return 11 | 12 | divLeft = e.clientX - mouseOffsetX 13 | divTop = e.clientY - mouseOffsetY 14 | const div = document.getElementById('viewport1') 15 | div.style.left = `${divLeft}px` 16 | div.style.top = `${divTop}px` 17 | } 18 | 19 | const onMouseDown = (e) => { 20 | dragging = true 21 | mouseOffsetX = e.offsetX; 22 | mouseOffsetY = e.offsetY; 23 | } 24 | 25 | const onMouseUp = (e) => { 26 | dragging = false 27 | const div = document.getElementById('viewport1') 28 | const rect = div.getBoundingClientRect() 29 | divLeft = rect.left 30 | divTop = rect.top 31 | } 32 | 33 | document.addEventListener('DOMContentLoaded', () => { 34 | const div = document.getElementById('viewport1') 35 | div.addEventListener("mousedown", onMouseDown) 36 | document.addEventListener("mouseup", onMouseUp) 37 | document.addEventListener("mousemove", onMouseMove) 38 | }) 39 | 40 | setInterval(() => { 41 | 42 | config = document.getElementById('Configuration')?.components["configure-example"]?.data 43 | 44 | if (!config?.moveSideToSide) return 45 | 46 | const div = document.getElementById('viewport1') 47 | if (!dragging) { 48 | const rect = div.getBoundingClientRect() 49 | 50 | const hitLeft = (rect.left < 5) 51 | const hitRight = (rect.right > window.innerWidth - 5) 52 | if (hitLeft) { 53 | direction = 1 54 | } 55 | if (hitRight) { 56 | direction = -1 57 | } 58 | 59 | if (!hitLeft || !hitRight) { 60 | divLeft += (direction * 4) 61 | div.style.left = `${divLeft}px` 62 | } 63 | } 64 | }, 40) 65 | -------------------------------------------------------------------------------- /component-usage/window-3d/hello-aframe.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 19 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 37 | 38 | 43 | 44 | 45 | 46 |
47 | 48 |
49 |
50 |
51 |
52 |
53 |
54 | 55 | 56 | -------------------------------------------------------------------------------- /component-usage/window-3d/milk-delivery.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 18 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 35 | 36 | 41 | 42 | 43 | 44 |
45 | 46 |
47 |
48 |
49 |
50 |
51 |
52 | 53 | 54 | -------------------------------------------------------------------------------- /component-usage/window-3d/starry-night.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 18 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 34 | 35 | 40 | 41 | 42 | 43 |
44 | 45 |
46 |
47 |
48 |
49 |
50 |
51 | 52 | 53 | -------------------------------------------------------------------------------- /component-usage/window-3d/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: grey; 3 | } 4 | 5 | video { 6 | visibility: hidden 7 | } 8 | 9 | #viewport1 { 10 | position: absolute; 11 | top: 100px; 12 | left: 200px; 13 | width: 600px; 14 | height:400px; 15 | margin: 10px; 16 | border-radius: 10px; 17 | border-width: 10px; 18 | border-color: #113; 19 | border-style: solid; 20 | box-shadow: inset 0px 0px 5px 2px #113; 21 | } 22 | 23 | #viewport-inner { 24 | width: 100%; 25 | height: 100%; 26 | } 27 | 28 | a { 29 | position: absolute; 30 | z-index: 10; 31 | bottom: 10px; 32 | left: 10px; 33 | } -------------------------------------------------------------------------------- /components/anchored/dist/anchored.min.js.LICENSE.txt: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright 2010-2023 Three.js Authors 4 | * SPDX-License-Identifier: MIT 5 | */ 6 | -------------------------------------------------------------------------------- /components/anchored/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aframe-anchored", 3 | "version": "0.0.1", 4 | "description": "Anchor A-Frame scene", 5 | "main": "index.js", 6 | "scripts": { 7 | "dist": "npm run dist:dev && npm run dist:prod", 8 | "dist:dev": "webpack --config webpack.config.js", 9 | "dist:prod": "webpack --config webpack.prod.config.js", 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "keywords": [ 13 | "A-Frame", 14 | "AR", 15 | "anchor", 16 | "WebXR" 17 | ], 18 | "author": "Diarmid Mackenzie", 19 | "license": "MIT", 20 | "dependencies": { 21 | "ratk": "^0.2.1" 22 | }, 23 | "devDependencies": { 24 | "webpack": "^5.75.0", 25 | "webpack-cli": "^5.1.4" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /components/anchored/test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 |

Anchored scene. Always anchored to a fixed position in world space.

12 |

Press A to toggle anchoring on/off.

13 |

Hold down oculus button to reset scene origin.

14 |
15 | 18 | view code 19 | 20 | 21 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /components/anchored/test/reset.js: -------------------------------------------------------------------------------- 1 | AFRAME.registerComponent('toggle-anchored', { 2 | 3 | schema: { 4 | anchoredEl: { type: 'selector', default: '[anchored]' } 5 | }, 6 | 7 | init() { 8 | this.toggleAnchored = this.toggleAnchored.bind(this) 9 | this.el.addEventListener('abuttondown', this.toggleAnchored) 10 | this.anchored = true 11 | 12 | }, 13 | 14 | toggleAnchored() { 15 | 16 | const el = this.data.anchoredEl 17 | const anchoredComponent = el.components['anchored'] 18 | this.anchored = !this.anchored 19 | 20 | if (this.anchored) { 21 | anchoredComponent.reAnchor() 22 | } 23 | else { 24 | anchoredComponent.unAnchor(false) 25 | } 26 | 27 | const plane = el.querySelector('a-plane') 28 | const color = this.anchored ? "#7BC8A4" : "white" 29 | plane.setAttribute('color', color) 30 | } 31 | }) 32 | -------------------------------------------------------------------------------- /components/anchored/webpack.config.js: -------------------------------------------------------------------------------- 1 | // Webpack uses this to work with directories 2 | const path = require('path'); 3 | 4 | // This is the main configuration object. 5 | // Here, you write different options and tell Webpack what to do 6 | module.exports = { 7 | 8 | // Path to your entry point. From this file Webpack will begin its work 9 | entry: './index.js', 10 | 11 | // Path and filename of your result bundle. 12 | // Webpack will bundle all JavaScript into this file 13 | output: { 14 | path: path.resolve(__dirname, 'dist'), 15 | publicPath: '', 16 | filename: 'anchored.js' 17 | }, 18 | 19 | resolve: { 20 | fallback: { 21 | "fs": false, 22 | "path": false 23 | }, 24 | }, 25 | 26 | // Default mode for Webpack is production. 27 | // Depending on mode Webpack will apply different things 28 | // on the final bundle. For now, we don't need production's JavaScript 29 | // minifying and other things, so let's set mode to development 30 | mode: 'development' 31 | }; -------------------------------------------------------------------------------- /components/anchored/webpack.prod.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var merge = require('webpack-merge').merge; 3 | var commonConfiguration = require('./webpack.config.js'); 4 | 5 | module.exports = merge(commonConfiguration, { 6 | output: { 7 | path: path.resolve(__dirname, 'dist'), 8 | publicPath: '/dist/', 9 | filename: 'anchored.min.js' 10 | }, 11 | mode: 'production' 12 | }); -------------------------------------------------------------------------------- /components/ball-blaster/image-20230701171702787.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diarmidmackenzie/aframe-components/1dfa2f24b93dd68ff1f5fbaec558e28310db484c/components/ball-blaster/image-20230701171702787.png -------------------------------------------------------------------------------- /components/ball-blaster/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aframe-ball-blaster", 3 | "version": "0.0.1", 4 | "description": "Simple blaster that shoots balls, for A-Frame", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "Diarmid Mackenzie", 10 | "license": "MIT" 11 | } 12 | -------------------------------------------------------------------------------- /components/ball-blaster/test/ammo-blaster.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Ball Blaster in Ammo Physics Engine. Press any key to shoot. 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |

Ball blaster with Ammo Physics. Left Blaster is directly in the scene. Press space to shoot.

17 |

Right blaster is a child of the right hand - only visible when in VR. Right trigger to shoot.

18 |
19 | 22 | view code 23 | 24 | 25 | 26 | 27 | 28 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /components/ball-blaster/test/cannon-blaster.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Ball Blaster in Cannon Physics Engine. Press any key to shoot. 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |

Ball blaster with Cannon Physics. Left Blaster is directly in the scene. Press space to shoot.

16 |

Right blaster is a child of the right hand - only visible when in VR. Right trigger to shoot.

17 |
18 | 21 | view code 22 | 23 | 24 | 25 | 26 | 27 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /components/ball-blaster/test/physx-blaster.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Ball Blaster in PhysX Physics Engine. Press any key to shoot. 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |

Ball blaster with PhysX Physics. Left Blaster is directly in the scene. Press space to shoot.

16 |

Right blaster is a child of the right hand - only visible when in VR. Right trigger to shoot.

17 |
18 | 21 | view code 22 | 23 | 24 | 25 | 26 | 27 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /components/bricks/image-20230318103822805.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diarmidmackenzie/aframe-components/1dfa2f24b93dd68ff1f5fbaec558e28310db484c/components/bricks/image-20230318103822805.png -------------------------------------------------------------------------------- /components/bricks/image-20230318113905914.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diarmidmackenzie/aframe-components/1dfa2f24b93dd68ff1f5fbaec558e28310db484c/components/bricks/image-20230318113905914.png -------------------------------------------------------------------------------- /components/bricks/image-20230418165028833.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diarmidmackenzie/aframe-components/1dfa2f24b93dd68ff1f5fbaec558e28310db484c/components/bricks/image-20230418165028833.png -------------------------------------------------------------------------------- /components/bricks/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aframe-bricks", 3 | "version": "0.0.1", 4 | "description": "Building Bricks for A-Frame", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [ 10 | "A-Frame", 11 | "bricks", 12 | "building" 13 | ], 14 | "author": "Diarmid Mackenzie", 15 | "license": "MIT" 16 | } 17 | -------------------------------------------------------------------------------- /components/bricks/test/basic-bricks.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | 15 | 16 |
17 |

Basic selection of bricks

18 |
19 | 22 | view code 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /components/bricks/test/brick-geometry.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 |

Basic brick geometry

11 |
12 | 15 | view code 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /components/bricks/test/single-brick.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 |

Single brick, showing underside

11 |
12 | 15 | view code 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /components/connecting-line/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aframe-connecting-line", 3 | "version": "0.3.1", 4 | "description": "Uses the A-Frame \"line\" component to draw a line between points on two entities.", 5 | "main": "index.js", 6 | "directories": { 7 | "example": "examples" 8 | }, 9 | "scripts": { 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/diarmidmackenzie/aframe-components/tree/main/components/connecting-line" 15 | }, 16 | "keywords": [ 17 | "A-Frame", 18 | "line" 19 | ], 20 | "bugs": { 21 | "url": "https://github.com/diarmidmackenzie/aframe-components/issues" 22 | }, 23 | "homepage": "https://diarmidmackenzie.github.io/aframe-components/components/connecting-line#readme", 24 | "author": "Diarmid Mackenzie", 25 | "license": "MIT" 26 | } 27 | -------------------------------------------------------------------------------- /components/cursor-tracker/README.md: -------------------------------------------------------------------------------- 1 | # cursor-tracker 2 | 3 | Set on an entity to track the orientation of the cursor's ray. 4 | 5 | Typically set on a entity that is a child of the camera that the cursor uses. This entity will then always point towards the cursor/mouse pointer. 6 | 7 | 8 | 9 | ## Schema 10 | 11 | | Property | Description | Default | 12 | | -------- | --------------------------------------- | ------- | 13 | | cursor | selector for the cursor entity to track | #cursor | 14 | 15 | 16 | 17 | ## Installation 18 | 19 | ``` 20 | 21 | ``` 22 | 23 | Or via [npm](https://www.npmjs.com/package/aframe-cursor-tracker) 24 | 25 | ``` 26 | npm install aframe-cursor-tracker 27 | ``` 28 | 29 | 30 | ## Examples 31 | 32 | [cursor-tracker.html](https://diarmidmackenzie.github.io/aframe-components/component-usage/cursor-tracker.html) 33 | 34 | Also Used by `mouse-manipulation` component to keep grabbed entities aligned with the mouse pointer. 35 | 36 | ## Code 37 | 38 | [cursor-tracker](https://github.com/diarmidmackenzie/aframe-components/blob/main/components/cursor-tracker/index.js) -------------------------------------------------------------------------------- /components/cursor-tracker/index.js: -------------------------------------------------------------------------------- 1 | // Set on an entity to track the orientation of the cursor's ray. 2 | // Typically set on a entity that is a child of the camera that the cursor uses. 3 | AFRAME.registerComponent('cursor-tracker', { 4 | 5 | schema: { 6 | cursor: {type: 'selector', default: "#cursor"}, 7 | }, 8 | 9 | init() { 10 | this.cursor = this.data.cursor 11 | this.raycaster = this.cursor.components['raycaster'].raycaster 12 | this.forward = new THREE.Vector3(0, 0, -1) 13 | this.localRayVector = new THREE.Vector3(); 14 | }, 15 | 16 | tick() { 17 | 18 | // Get ray direction vector in the space of this object. 19 | this.el.object3D.getWorldPosition(this.localRayVector) 20 | this.localRayVector.add(this.raycaster.ray.direction) 21 | this.el.object3D.parent.worldToLocal(this.localRayVector) 22 | this.localRayVector.normalize() 23 | this.el.object3D.quaternion.setFromUnitVectors(this.forward, this.localRayVector) 24 | } 25 | }); 26 | -------------------------------------------------------------------------------- /components/cursor-tracker/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aframe-cursor-tracker", 3 | "version": "0.1.0", 4 | "description": "Set on an entity to track the orientation of the cursor's ray.", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/diarmidmackenzie/aframe-components/tree/main/components/cursor-tracker" 12 | }, 13 | "keywords": [ 14 | "A-Frame", 15 | "Cursor" 16 | ], 17 | "bugs": { 18 | "url": "https://github.com/diarmidmackenzie/aframe-components/issues" 19 | }, 20 | "homepage": "https://diarmidmackenzie.github.io/aframe-components/components/cursor-tracker#readme", 21 | "author": "Diarmid Mackenzie", 22 | "license": "MIT" 23 | } 24 | -------------------------------------------------------------------------------- /components/dat-gui/image-20240304145551473.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diarmidmackenzie/aframe-components/1dfa2f24b93dd68ff1f5fbaec558e28310db484c/components/dat-gui/image-20240304145551473.png -------------------------------------------------------------------------------- /components/dat-gui/image-20240304150412011.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diarmidmackenzie/aframe-components/1dfa2f24b93dd68ff1f5fbaec558e28310db484c/components/dat-gui/image-20240304150412011.png -------------------------------------------------------------------------------- /components/dat-gui/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aframe-dat-gui", 3 | "version": "0.0.3", 4 | "description": "Dataarts dat.gui for A-Frame components", 5 | "main": "index.js", 6 | "scripts": { 7 | "dist": "npm run dist:dev && npm run dist:prod", 8 | "dist:dev": "webpack --config webpack.config.js", 9 | "dist:prod": "webpack --config webpack.prod.config.js", 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "keywords": [ 13 | "A-Frame", 14 | "dat.gui", 15 | "dat", 16 | "gui", 17 | "aframe" 18 | ], 19 | "author": "Diarmid Mackenzie", 20 | "license": "MIT", 21 | "devDependencies": { 22 | "webpack": "^5.75.0", 23 | "webpack-cli": "^5.0.1" 24 | }, 25 | "dependencies": { 26 | "dat.gui": "^0.7.9" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /components/dat-gui/test/bricks.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 30 | 31 | 32 |
33 |

Basic dat.gui example

34 |
35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /components/dat-gui/test/multiple-unnamed.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 23 | 24 | 25 |
26 |

dat.gui example with multiple unnamed entities

27 |
28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /components/dat-gui/webpack.config.js: -------------------------------------------------------------------------------- 1 | // Webpack uses this to work with directories 2 | const path = require('path'); 3 | 4 | // This is the main configuration object. 5 | // Here, you write different options and tell Webpack what to do 6 | module.exports = { 7 | 8 | // Path to your entry point. From this file Webpack will begin its work 9 | entry: './index.js', 10 | 11 | // Path and filename of your result bundle. 12 | // Webpack will bundle all JavaScript into this file 13 | output: { 14 | path: path.resolve(__dirname, 'dist'), 15 | publicPath: '', 16 | filename: 'dat-gui.js' 17 | }, 18 | 19 | resolve: { 20 | fallback: { 21 | "fs": false, 22 | "path": false 23 | }, 24 | }, 25 | 26 | // Default mode for Webpack is production. 27 | // Depending on mode Webpack will apply different things 28 | // on the final bundle. For now, we don't need production's JavaScript 29 | // minifying and other things, so let's set mode to development 30 | mode: 'development' 31 | }; -------------------------------------------------------------------------------- /components/dat-gui/webpack.prod.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var merge = require('webpack-merge').merge; 3 | var commonConfiguration = require('./webpack.config.js'); 4 | 5 | module.exports = merge(commonConfiguration, { 6 | output: { 7 | path: path.resolve(__dirname, 'dist'), 8 | publicPath: '/dist/', 9 | filename: 'dat-gui.min.js' 10 | }, 11 | mode: 'production' 12 | }); -------------------------------------------------------------------------------- /components/desktop-vr-controller/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /components/desktop-vr-controller/.npmignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | package-lock.json 3 | -------------------------------------------------------------------------------- /components/desktop-vr-controller/image-20220802185450073.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diarmidmackenzie/aframe-components/1dfa2f24b93dd68ff1f5fbaec558e28310db484c/components/desktop-vr-controller/image-20220802185450073.png -------------------------------------------------------------------------------- /components/desktop-vr-controller/image-20220802185543518.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diarmidmackenzie/aframe-components/1dfa2f24b93dd68ff1f5fbaec558e28310db484c/components/desktop-vr-controller/image-20220802185543518.png -------------------------------------------------------------------------------- /components/desktop-vr-controller/image-20220802185741939.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diarmidmackenzie/aframe-components/1dfa2f24b93dd68ff1f5fbaec558e28310db484c/components/desktop-vr-controller/image-20220802185741939.png -------------------------------------------------------------------------------- /components/desktop-vr-controller/image-20220802190100076.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diarmidmackenzie/aframe-components/1dfa2f24b93dd68ff1f5fbaec558e28310db484c/components/desktop-vr-controller/image-20220802190100076.png -------------------------------------------------------------------------------- /components/desktop-vr-controller/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aframe-desktop-vr-controller", 3 | "version": "0.1.3", 4 | "description": "Simulates a VR controller on the desktop, controllable using mouse & keyboard.", 5 | "main": "index.js", 6 | "scripts": { 7 | "dist": "npm run dist:dev && npm run dist:prod", 8 | "dist:dev": "webpack --config webpack.config.js", 9 | "dist:prod": "webpack --config webpack.prod.config.js", 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/diarmidmackenzie/aframe-components/tree/main/components/desktop-vr-controller" 15 | }, 16 | "keywords": [ 17 | "A-Frame", 18 | "Controller", 19 | "6DoF", 20 | "Keyboard", 21 | "Mouse", 22 | "Desktop" 23 | ], 24 | "bugs": { 25 | "url": "https://github.com/diarmidmackenzie/aframe-components/issues" 26 | }, 27 | "homepage": "https://diarmidmackenzie.github.io/aframe-components/components/desktop-vr-controller#readme", 28 | "author": "Diarmid Mackenzie", 29 | "license": "MIT", 30 | "devDependencies": { 31 | "webpack": "^5.75.0", 32 | "webpack-cli": "^5.0.1" 33 | }, 34 | "dependencies": { 35 | "aframe-connecting-line": "^0.1.0", 36 | "aframe-label": "^0.1.2", 37 | "aframe-mouse-manipulation": "^0.1.0", 38 | "aframe-raycaster-thresholds": "^0.1.0", 39 | "aframe-screen-display": "^0.1.1" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /components/desktop-vr-controller/webpack.config.js: -------------------------------------------------------------------------------- 1 | // Webpack uses this to work with directories 2 | const path = require('path'); 3 | 4 | // This is the main configuration object. 5 | // Here, you write different options and tell Webpack what to do 6 | module.exports = { 7 | 8 | // Path to your entry point. From this file Webpack will begin its work 9 | entry: './index.js', 10 | 11 | // Path and filename of your result bundle. 12 | // Webpack will bundle all JavaScript into this file 13 | output: { 14 | path: path.resolve(__dirname, 'dist'), 15 | publicPath: '', 16 | filename: 'desktop-vr-controller.js' 17 | }, 18 | 19 | resolve: { 20 | fallback: { 21 | "fs": false, 22 | "path": false 23 | }, 24 | }, 25 | 26 | // Default mode for Webpack is production. 27 | // Depending on mode Webpack will apply different things 28 | // on the final bundle. For now, we don't need production's JavaScript 29 | // minifying and other things, so let's set mode to development 30 | mode: 'development' 31 | }; -------------------------------------------------------------------------------- /components/desktop-vr-controller/webpack.prod.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var merge = require('webpack-merge').merge; 3 | var commonConfiguration = require('./webpack.config.js'); 4 | 5 | module.exports = merge(commonConfiguration, { 6 | output: { 7 | path: path.resolve(__dirname, 'dist'), 8 | publicPath: '/dist/', 9 | filename: 'desktop-vr-controller.min.js' 10 | }, 11 | mode: 'production' 12 | }); -------------------------------------------------------------------------------- /components/desktop-xr-hands/image-20230829170643885.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diarmidmackenzie/aframe-components/1dfa2f24b93dd68ff1f5fbaec558e28310db484c/components/desktop-xr-hands/image-20230829170643885.png -------------------------------------------------------------------------------- /components/desktop-xr-hands/image-20230829170851279.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diarmidmackenzie/aframe-components/1dfa2f24b93dd68ff1f5fbaec558e28310db484c/components/desktop-xr-hands/image-20230829170851279.png -------------------------------------------------------------------------------- /components/desktop-xr-hands/image-20230829175405101.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diarmidmackenzie/aframe-components/1dfa2f24b93dd68ff1f5fbaec558e28310db484c/components/desktop-xr-hands/image-20230829175405101.png -------------------------------------------------------------------------------- /components/desktop-xr-hands/index.js: -------------------------------------------------------------------------------- 1 | require('./src/hand-landmarker.js') 2 | require('./src/desktop-xr-hands.js') 3 | -------------------------------------------------------------------------------- /components/desktop-xr-hands/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aframe-desktop-xr-hands", 3 | "version": "0.0.4", 4 | "description": "Desktop simulation of WebXR Hand Tracking using Mediapipe", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "npm run dist", 8 | "deploy": "npm run build && netlify deploy --prod --dir=.", 9 | "dist": "npm run dist:min && npm run dist:max", 10 | "dist:max": "webpack", 11 | "dist:min": "cross-env NODE_ENV=production webpack", 12 | "prepublish": "npm run dist", 13 | "start": "cross-env webpack serve" 14 | }, 15 | "keywords": [ 16 | "aframe", 17 | "xr", 18 | "hand tracking" 19 | ], 20 | "author": "Diarmid Mackenzie", 21 | "license": "MIT", 22 | "dependencies": { 23 | "@mediapipe/drawing_utils": "^0.3.1675466124", 24 | "@mediapipe/tasks-vision": "^0.10.0", 25 | "aframe-screen-display": "^0.1.2" 26 | }, 27 | "devDependencies": { 28 | "cross-env": "^7.0.3", 29 | "webpack": "^5.75.0", 30 | "webpack-cli": "^5.0.1", 31 | "webpack-dev-server": "^4.15.1" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /components/desktop-xr-hands/test/dots.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Desktop simulation of WebXR Hand Tracking using Mediapipe. 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 |

Desktop simulation of WebXR Hand Tracking using Mediapipe.

12 |

Left Hand Red, Right Hand Green.

13 |

Enter full screen to enable hand tracking via webcam.

14 |
15 | 18 | view code 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /components/desktop-xr-hands/test/mesh.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Desktop simulation of WebXR Hand Tracking using Mediapipe. 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 |

Desktop simulation of WebXR Hand Tracking using Mediapipe.

12 |

Left Hand Red, Right Hand Green.

13 |

Enter full screen to enable hand tracking via webcam.

14 |
15 | 18 | view code 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /components/desktop-xr-hands/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | entry: './index.js', 5 | output: { 6 | libraryTarget: 'umd', 7 | path: path.resolve(__dirname, 'dist'), 8 | publicPath: '/dist/', 9 | filename: 10 | process.env.NODE_ENV === 'production' 11 | ? 'desktop-xr-hands.min.js' 12 | : 'desktop-xr-hands.js' 13 | }, 14 | externals: { 15 | // Stubs out `import ... from 'three'` so it returns `import ... from window.THREE` effectively using THREE global variable that is defined by AFRAME. 16 | three: 'THREE' 17 | }, 18 | devtool: 'source-map', 19 | mode: process.env.NODE_ENV === 'production' ? 'production' : 'development', 20 | devServer: { 21 | port: process.env.PORT || 5000, 22 | hot: false, 23 | liveReload: true, 24 | server: { 25 | type: 'https' 26 | }, 27 | static: { 28 | directory: path.resolve(__dirname + '../../..') 29 | } 30 | }, 31 | resolve: { 32 | alias: { 33 | three: 'super-three' 34 | } 35 | } 36 | }; -------------------------------------------------------------------------------- /components/desktop-xr-plane/README.md: -------------------------------------------------------------------------------- 1 | # desktop-xr-plane 2 | 3 | ## Overview 4 | 5 | A component to simulate WebXR XRPlanes, while running on a desktop system 6 | 7 | ## Schema 8 | 9 | This component has no schema. Simulated planes are configured via the plane geometry configured on the same element. 10 | 11 | ## Usage 12 | 13 | Configure an `a-plane` (or other element with a `plane` geometry) with the desired position, size, rotation and appearance (can be invisible if desired) 14 | 15 | Then add the `desktop-xr-plane`component 16 | 17 | ``` 18 | 25 | 26 | ``` 27 | 28 | 29 | 30 | ## Limitations 31 | 32 | Does not support the `scale` attribute on the element - scale is assumed to be `1 1 1` 33 | 34 | Only supports rectangles. The WebXR standard supports XRPlanes that are arbitrary polygons, so this is a limitation vs. the WebXR standard. However note that as of July 2023, the Meta Quest room setup process always generates rectangles (even for the ceiling and floor of a non-rectangular room). So it's possible to simulate all XRPlanes that can be generated by Meta Quest room setup. 35 | 36 | 37 | 38 | ## Installation 39 | 40 | Via CDN 41 | ``` 42 | 43 | ``` 44 | 45 | Or via [npm](https://www.npmjs.com/package/aframe-polygon-wireframe) 46 | 47 | ``` 48 | npm install aframe-desktop-xr-plane 49 | ``` 50 | 51 | 52 | 53 | ## Examples 54 | 55 | This component is used in tests for the `xr-room-physics` component, for example: 56 | 57 | [ammo-desktop-room5.html](https://diarmidmackenzie.github.io/aframe-components/components/xr-room-physics/test/ammo-desktop-room5.html) 58 | 59 | 60 | 61 | ## Code 62 | 63 | [desktop-xr--plane](https://github.com/diarmidmackenzie/aframe-components/blob/main/components/desktop-xr-plane/index.js) 64 | 65 | -------------------------------------------------------------------------------- /components/desktop-xr-plane/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aframe-desktop-xr-plane", 3 | "version": "0.0.1", 4 | "description": "Simulation of XR Planes on Desktop, for A-Frame", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [ 10 | "XRPlane", 11 | "A-Frame" 12 | ], 13 | "author": "Diarmid Mackenzie", 14 | "license": "MIT" 15 | } 16 | -------------------------------------------------------------------------------- /components/dynamic-snap/image-20230409165520988.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diarmidmackenzie/aframe-components/1dfa2f24b93dd68ff1f5fbaec558e28310db484c/components/dynamic-snap/image-20230409165520988.png -------------------------------------------------------------------------------- /components/dynamic-snap/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aframe-dynamic-snap", 3 | "version": "0.0.1", 4 | "description": "Dynamic snap-to-position for A-Frame", 5 | "main": "index.js", 6 | "scripts": { 7 | "dist": "npm run dist:dev && npm run dist:prod", 8 | "dist:dev": "webpack --config webpack.config.js", 9 | "dist:prod": "webpack --config webpack.prod.config.js", 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "keywords": [ 13 | "aframe", 14 | "snap", 15 | "dynamic", 16 | "position" 17 | ], 18 | "author": "Diarmid Mackenzie", 19 | "license": "MIT", 20 | "dependencies": { 21 | "aframe-polygon-wireframe": "^0.3.0" 22 | }, 23 | "devDependencies": { 24 | "webpack": "^5.75.0", 25 | "webpack-cli": "^5.0.1" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /components/dynamic-snap/test/snap-to-grid.js: -------------------------------------------------------------------------------- 1 | // Snap to grid component for use with dynamic-snap component. 2 | AFRAME.registerComponent('snap-to-grid', { 3 | dependencies: ['position', 'rotation'], 4 | 5 | schema: { 6 | offset: {type: 'vec3', default: {x: 0, y: 0, z: 0}}, 7 | snap: {type: 'vec3', default: {x: 0.5, y: 0.5, z: 0.5}} 8 | }, 9 | 10 | init() { 11 | 12 | this.transform = new THREE.Object3D() 13 | this.eventData = {worldTransform: this.transform} 14 | 15 | this.axis = new THREE.Vector3() 16 | }, 17 | 18 | tick() { 19 | 20 | const data = this.data; 21 | const transform = this.transform 22 | 23 | // get world transform of entity 24 | transform.position.set(0, 0, 0) 25 | transform.quaternion.identity() 26 | transform.scale.set(1, 1, 1) 27 | this.el.object3D.add(transform) 28 | this.el.sceneEl.object3D.attach(transform) 29 | 30 | // snap to position grid 31 | const pos = this.transform.position 32 | pos.x = Math.floor((pos.x + data.snap.x / 2) / data.snap.x) * data.snap.x + data.offset.x; 33 | pos.y = Math.floor((pos.y + data.snap.y / 2) / data.snap.y) * data.snap.y + data.offset.y; 34 | pos.z = Math.floor((pos.z + data.snap.z / 2) / data.snap.z) * data.snap.z + data.offset.z; 35 | 36 | // snap to 90 degree rotation 37 | // do this by converting to axis / angle as described here, then controlling the axis and angle. 38 | // https://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/index.htm 39 | const quat = transform.quaternion 40 | quat.normalize() 41 | const s = Math.sqrt(1-quat.w*quat.w) 42 | if (s < 0.01) { 43 | quat.identity() 44 | } 45 | else { 46 | let x = quat.x 47 | let y = quat.y 48 | let z = quat.z 49 | 50 | if (Math.abs(x) >= Math.abs(y) && Math.abs(x) >= Math.abs(z)) { 51 | x = Math.sign(x) 52 | y = 0 53 | z = 0 54 | } 55 | else if (Math.abs(y) >= Math.abs(x) && Math.abs(y) >= Math.abs(z)) { 56 | x = 0 57 | y = Math.sign(y) 58 | z = 0 59 | } 60 | else { 61 | x = 0 62 | y = 0 63 | z = Math.sign(z) 64 | } 65 | 66 | this.axis.set(x, y, z) 67 | 68 | let angle = 2 * Math.acos(quat.w) 69 | const rotationSnap = Math.PI / 2 70 | angle = Math.floor(angle / rotationSnap) * rotationSnap 71 | 72 | quat.setFromAxisAngle(this.axis, angle) 73 | } 74 | 75 | this.el.emit('snapStart', this.eventData) 76 | }, 77 | }); -------------------------------------------------------------------------------- /components/dynamic-snap/test/test1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 |

Dynamic snap to grid

13 |

Red Cone has transparent rendering for snap position, and wireframe rendering for precise position.

14 |

Blue Torus has transparent rendering for snap position, and object rendering for precise position (default).

15 |
16 | 19 | view code 20 | 21 | 23 | 27 | 28 | 29 | 36 | 37 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /components/dynamic-snap/test/test3.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 |

Dynamic snap to grid

15 |

Transparent rendering for precise position, wireframe rendering for snap position

16 |
17 | 20 | view code 21 | 22 | 24 | 28 | 29 | 30 | 35 | 36 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /components/dynamic-snap/webpack.config.js: -------------------------------------------------------------------------------- 1 | // Webpack uses this to work with directories 2 | const path = require('path'); 3 | 4 | // This is the main configuration object. 5 | // Here, you write different options and tell Webpack what to do 6 | module.exports = { 7 | 8 | // Path to your entry point. From this file Webpack will begin its work 9 | entry: './index.js', 10 | 11 | // Path and filename of your result bundle. 12 | // Webpack will bundle all JavaScript into this file 13 | output: { 14 | path: path.resolve(__dirname, 'dist'), 15 | publicPath: '', 16 | filename: 'dynamic-snap.js' 17 | }, 18 | 19 | resolve: { 20 | fallback: { 21 | "fs": false, 22 | "path": false 23 | }, 24 | }, 25 | 26 | // Default mode for Webpack is production. 27 | // Depending on mode Webpack will apply different things 28 | // on the final bundle. For now, we don't need production's JavaScript 29 | // minifying and other things, so let's set mode to development 30 | mode: 'development' 31 | }; -------------------------------------------------------------------------------- /components/dynamic-snap/webpack.prod.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var merge = require('webpack-merge').merge; 3 | var commonConfiguration = require('./webpack.config.js'); 4 | 5 | module.exports = merge(commonConfiguration, { 6 | output: { 7 | path: path.resolve(__dirname, 'dist'), 8 | publicPath: '/dist/', 9 | filename: 'dynamic-snap.min.js' 10 | }, 11 | mode: 'production' 12 | }); -------------------------------------------------------------------------------- /components/frame-rate/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aframe-frame-rate", 3 | "version": "0.1.0", 4 | "description": "Adjust WebXR frame rate for A-Frame applications", 5 | "main": "index.js", 6 | "directories": { 7 | "example": "examples" 8 | }, 9 | "scripts": { 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/diarmidmackenzie/aframe-components.git" 15 | }, 16 | "keywords": [ 17 | "A-Frame", 18 | "WebXR", 19 | "performance" 20 | ], 21 | "author": "Diarmid Mackenzie", 22 | "license": "MIT", 23 | "bugs": { 24 | "url": "https://github.com/diarmidmackenzie/aframe-components/issues" 25 | }, 26 | "homepage": "https://github.com/diarmidmackenzie/aframe-components/components/frame-rate#readme" 27 | } 28 | -------------------------------------------------------------------------------- /components/graph/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /components/graph/image-20230318092246915.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diarmidmackenzie/aframe-components/1dfa2f24b93dd68ff1f5fbaec558e28310db484c/components/graph/image-20230318092246915.png -------------------------------------------------------------------------------- /components/graph/image-20230318092739663.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diarmidmackenzie/aframe-components/1dfa2f24b93dd68ff1f5fbaec558e28310db484c/components/graph/image-20230318092739663.png -------------------------------------------------------------------------------- /components/graph/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aframe-graph", 3 | "version": "0.0.1", 4 | "description": "Dynamically Connected Graphs for A-Frame", 5 | "main": "index.js", 6 | "scripts": { 7 | "dist": "npm run dist:dev && npm run dist:prod", 8 | "dist:dev": "webpack --config webpack.config.js", 9 | "dist:prod": "webpack --config webpack.prod.config.js", 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "keywords": [ 13 | "A-Frame", 14 | "aframe", 15 | "link", 16 | "cut", 17 | "tree" 18 | ], 19 | "author": "Diarmid Mackenzie", 20 | "license": "MIT", 21 | "dependencies": { 22 | "aframe-connecting-line": "^0.1.0", 23 | "graphology": "^0.25.1", 24 | "graphology-shortest-path": "^2.0.2" 25 | }, 26 | "devDependencies": { 27 | "webpack": "^5.75.0", 28 | "webpack-cli": "^5.0.1" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /components/graph/src/utils.js: -------------------------------------------------------------------------------- 1 | const DFSStack = require('graphology-indices/dfs-stack'); 2 | 3 | /** 4 | * Return the set of nodes connected to a given node. 5 | * 6 | * @param {Graph} graph - Target graph. 7 | * @param {string} node - The ID of the node. 8 | * 9 | * Code based on forEachConnectedComponent() from graphology/components 10 | */ 11 | function getConnectedComponent(graph, node) { 12 | 13 | const stack = new DFSStack(graph); 14 | const push = stack.push.bind(stack); 15 | const component = [] 16 | 17 | stack.push(node); 18 | 19 | var source; 20 | 21 | while (stack.size !== 0) { 22 | source = stack.pop(); 23 | 24 | component.push(source); 25 | 26 | graph.forEachNeighbor(source, push); 27 | } 28 | 29 | return component 30 | } 31 | 32 | exports.getConnectedComponent = getConnectedComponent -------------------------------------------------------------------------------- /components/graph/test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 |

Dynamic Graph in A-Frame.

11 |
12 | 15 | view code 16 | 17 | 18 | 19 | 20 | 24 | 25 | 29 | 30 | 34 | 35 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /components/graph/webpack.config.js: -------------------------------------------------------------------------------- 1 | // Webpack uses this to work with directories 2 | const path = require('path'); 3 | 4 | // This is the main configuration object. 5 | // Here, you write different options and tell Webpack what to do 6 | module.exports = { 7 | 8 | // Path to your entry point. From this file Webpack will begin its work 9 | entry: './index.js', 10 | 11 | // Path and filename of your result bundle. 12 | // Webpack will bundle all JavaScript into this file 13 | output: { 14 | path: path.resolve(__dirname, 'dist'), 15 | publicPath: '', 16 | filename: 'graph.js' 17 | }, 18 | 19 | resolve: { 20 | fallback: { 21 | "fs": false, 22 | "path": false 23 | }, 24 | }, 25 | 26 | // Default mode for Webpack is production. 27 | // Depending on mode Webpack will apply different things 28 | // on the final bundle. For now, we don't need production's JavaScript 29 | // minifying and other things, so let's set mode to development 30 | mode: 'development' 31 | }; -------------------------------------------------------------------------------- /components/graph/webpack.prod.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var merge = require('webpack-merge').merge; 3 | var commonConfiguration = require('./webpack.config.js'); 4 | 5 | module.exports = merge(commonConfiguration, { 6 | output: { 7 | path: path.resolve(__dirname, 'dist'), 8 | publicPath: '/dist/', 9 | filename: 'graph.min.js' 10 | }, 11 | mode: 'production' 12 | }); -------------------------------------------------------------------------------- /components/head-tracking/index.js: -------------------------------------------------------------------------------- 1 | require('./src/face-detector.js') 2 | require('./src/head-tracker.js') 3 | -------------------------------------------------------------------------------- /components/head-tracking/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "head-tracking", 3 | "version": "0.0.1", 4 | "description": "Tracking head position using laptop webcam", 5 | "main": "index.js", 6 | "scripts": { 7 | "dist": "npm run dist:min && npm run dist:max", 8 | "dist:max": "webpack", 9 | "dist:min": "cross-env NODE_ENV=production webpack", 10 | "start": "cross-env webpack serve", 11 | "test": "echo \"Error: no test specified\" && exit 1" 12 | }, 13 | "author": "Diarmid Mackenzie", 14 | "license": "MIT", 15 | "dependencies": { 16 | "@mediapipe/tasks-vision": "^0.10.0" 17 | }, 18 | "devDependencies": { 19 | "cross-env": "^7.0.3", 20 | "webpack": "^5.75.0", 21 | "webpack-cli": "^5.0.1", 22 | "webpack-dev-server": "^4.15.0" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /components/head-tracking/src/face-detector.js: -------------------------------------------------------------------------------- 1 | const {FilesetResolver, FaceDetector} = require('@mediapipe/tasks-vision') 2 | 3 | AFRAME.registerComponent('face-detector', { 4 | 5 | init() { 6 | 7 | this.lastVideoTime = 0 8 | this.video = document.createElement('video') 9 | this.video.autoplay = true 10 | this.video.playsInline = true 11 | document.body.appendChild(this.video) 12 | 13 | this.eventData = { 14 | video: this.video, 15 | detections: null 16 | } 17 | 18 | this.predictWebcam = this.predictWebcam.bind(this) 19 | 20 | // start face detector (completes asynchronously) 21 | this.startFaceDetector() 22 | }, 23 | 24 | async startFaceDetector() { 25 | const vision = await FilesetResolver.forVisionTasks( 26 | "https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@latest/wasm" 27 | ); 28 | const faceDetector = await FaceDetector.createFromModelPath(vision, 29 | "https://storage.googleapis.com/mediapipe-tasks/face_detector/face_detection_short_range.tflite" 30 | ); 31 | 32 | await faceDetector.setOptions({ runningMode: "VIDEO" }); 33 | this.faceDetector = faceDetector 34 | 35 | // activate webcam 36 | stream = await navigator.mediaDevices.getUserMedia({ video: true }) 37 | this.video.srcObject = stream; 38 | this.video.addEventListener("loadeddata", this.predictWebcam); 39 | 40 | }, 41 | 42 | async predictWebcam() { 43 | 44 | // Detect faces using detectForVideo 45 | let startTimeMs = performance.now(); 46 | if (this.video.currentTime !== this.lastVideoTime) { 47 | this.lastVideoTime = this.video.currentTime; 48 | this.eventData.detections = this.faceDetector.detectForVideo(this.video, startTimeMs) 49 | .detections; 50 | 51 | this.el.emit('face-detected', this.eventData) 52 | } 53 | 54 | // Call this function again to keep predicting when the browser is ready 55 | window.requestAnimationFrame(this.predictWebcam); 56 | } 57 | }) -------------------------------------------------------------------------------- /components/head-tracking/test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 |

Basic head position detection

11 |
12 | 15 | view code 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /components/head-tracking/test/window-camera.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 |

Desktop screen simulating window into 3D world using head tracking

12 |
13 | 14 | 17 | view code 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /components/head-tracking/test/window-camera2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 |

Desktop screen simulating window into 3D world using head tracking

13 |
14 | 15 | 18 | view code 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /components/head-tracking/test/window-camera3.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 10 | 27 | 28 | 29 |
30 |

Desktop screen simulating window into 3D world using head tracking

31 |
32 | 33 | 36 | view code 37 | 38 | 39 | 40 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /components/head-tracking/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | entry: './index.js', 5 | output: { 6 | libraryTarget: 'umd', 7 | path: path.resolve(__dirname, 'dist'), 8 | publicPath: '/dist/', 9 | filename: 10 | process.env.NODE_ENV === 'production' 11 | ? 'head-tracking.min.js' 12 | : 'head-tracking.js' 13 | }, 14 | externals: { 15 | // Stubs out `import ... from 'three'` so it returns `import ... from window.THREE` effectively using THREE global variable that is defined by AFRAME. 16 | three: 'THREE' 17 | }, 18 | devtool: 'source-map', 19 | mode: process.env.NODE_ENV === 'production' ? 'production' : 'development', 20 | devServer: { 21 | port: process.env.PORT || 5000, 22 | hot: false, 23 | liveReload: true, 24 | server: { 25 | type: 'https' 26 | }, 27 | static: { 28 | directory: path.resolve(__dirname) 29 | } 30 | }, 31 | resolve: { 32 | alias: { 33 | three: 'super-three' 34 | } 35 | } 36 | }; -------------------------------------------------------------------------------- /components/hide-on-hover.js: -------------------------------------------------------------------------------- 1 | AFRAME.registerComponent("hide-on-hover", { 2 | 3 | init() { 4 | this.el.setAttribute("raycastable") 5 | }, 6 | 7 | events: { 8 | 'mouseenter': function (evt) { 9 | this.el.setAttribute("visible", false) 10 | }, 11 | 'mouseleave': function (evt) { 12 | this.el.setAttribute("visible", true) 13 | } 14 | } 15 | }) -------------------------------------------------------------------------------- /components/label/image-20220811164727667.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diarmidmackenzie/aframe-components/1dfa2f24b93dd68ff1f5fbaec558e28310db484c/components/label/image-20220811164727667.png -------------------------------------------------------------------------------- /components/label/image-20220811164814125.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diarmidmackenzie/aframe-components/1dfa2f24b93dd68ff1f5fbaec558e28310db484c/components/label/image-20220811164814125.png -------------------------------------------------------------------------------- /components/label/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aframe-label", 3 | "version": "0.1.2", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "aframe-label", 9 | "version": "0.1.2", 10 | "license": "MIT" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /components/label/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aframe-label", 3 | "version": "0.1.3", 4 | "description": "A set of components that can be used to add labels to entities in desktop & VR experiences.", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/diarmidmackenzie/aframe-components/tree/main/components/label" 12 | }, 13 | "keywords": [ 14 | "A-Frame", 15 | "Label", 16 | "VR", 17 | "Desktop" 18 | ], 19 | "bugs": { 20 | "url": "https://github.com/diarmidmackenzie/aframe-components/issues" 21 | }, 22 | "homepage": "https://diarmidmackenzie.github.io/aframe-components/components/label#readme", 23 | "author": "Diarmid Mackenzie", 24 | "license": "MIT" 25 | } 26 | -------------------------------------------------------------------------------- /components/laser-manipulation/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /components/laser-manipulation/.npmignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | package-lock.json 3 | -------------------------------------------------------------------------------- /components/laser-manipulation/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aframe-laser-manipulation", 3 | "version": "0.3.0", 4 | "description": "Enables the user to move objects around using a laser pointer.", 5 | "main": "index.js", 6 | "scripts": { 7 | "dist": "npm run dist:dev && npm run dist:prod", 8 | "dist:dev": "webpack --config webpack.config.js", 9 | "dist:prod": "webpack --config webpack.prod.config.js", 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/diarmidmackenzie/aframe-components/tree/main/components/laser-manipulation" 15 | }, 16 | "keywords": [ 17 | "A-Frame", 18 | "Laser Controls", 19 | "Manipulation", 20 | "VR", 21 | "Desktop", 22 | "Raycasting" 23 | ], 24 | "bugs": { 25 | "url": "https://github.com/diarmidmackenzie/aframe-components/issues" 26 | }, 27 | "homepage": "https://diarmidmackenzie.github.io/aframe-components/components/laser-manipulation#readme", 28 | "author": "Diarmid Mackenzie", 29 | "license": "MIT", 30 | "dependencies": { 31 | "aframe-object-parent": "^0.1.0", 32 | "aframe-thumbstick-states": "^0.1.0" 33 | }, 34 | "devDependencies": { 35 | "webpack": "^5.75.0", 36 | "webpack-cli": "^5.0.1" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /components/laser-manipulation/webpack.config.js: -------------------------------------------------------------------------------- 1 | // Webpack uses this to work with directories 2 | const path = require('path'); 3 | 4 | // This is the main configuration object. 5 | // Here, you write different options and tell Webpack what to do 6 | module.exports = { 7 | 8 | // Path to your entry point. From this file Webpack will begin its work 9 | entry: './index.js', 10 | 11 | // Path and filename of your result bundle. 12 | // Webpack will bundle all JavaScript into this file 13 | output: { 14 | path: path.resolve(__dirname, 'dist'), 15 | publicPath: '', 16 | filename: 'laser-manipulation.js' 17 | }, 18 | 19 | resolve: { 20 | fallback: { 21 | "fs": false, 22 | "path": false 23 | }, 24 | }, 25 | 26 | // Default mode for Webpack is production. 27 | // Depending on mode Webpack will apply different things 28 | // on the final bundle. For now, we don't need production's JavaScript 29 | // minifying and other things, so let's set mode to development 30 | mode: 'development' 31 | }; -------------------------------------------------------------------------------- /components/laser-manipulation/webpack.prod.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var merge = require('webpack-merge').merge; 3 | var commonConfiguration = require('./webpack.config.js'); 4 | 5 | module.exports = merge(commonConfiguration, { 6 | output: { 7 | path: path.resolve(__dirname, 'dist'), 8 | publicPath: '/dist/', 9 | filename: 'laser-manipulation.min.js' 10 | }, 11 | mode: 'production' 12 | }); -------------------------------------------------------------------------------- /components/mouse-manipulation/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /components/mouse-manipulation/.npmignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | package-lock.json 3 | -------------------------------------------------------------------------------- /components/mouse-manipulation/image-20220812163558569.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diarmidmackenzie/aframe-components/1dfa2f24b93dd68ff1f5fbaec558e28310db484c/components/mouse-manipulation/image-20220812163558569.png -------------------------------------------------------------------------------- /components/mouse-manipulation/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aframe-mouse-manipulation", 3 | "version": "0.3.0", 4 | "description": "Enables the user to move objects in 3D space using a mouse on desktop.", 5 | "main": "index.js", 6 | "scripts": { 7 | "dist": "npm run dist:dev && npm run dist:prod", 8 | "dist:dev": "webpack --config webpack.config.js", 9 | "dist:prod": "webpack --config webpack.prod.config.js", 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/diarmidmackenzie/aframe-components/tree/main/components/mouse-manipulation" 15 | }, 16 | "keywords": [ 17 | "A-Frame", 18 | "Mouse", 19 | "Desktop", 20 | "Raycasting" 21 | ], 22 | "bugs": { 23 | "url": "https://github.com/diarmidmackenzie/aframe-components/issues" 24 | }, 25 | "homepage": "https://diarmidmackenzie.github.io/aframe-components/components/mouse-manipulation#readme", 26 | "author": "Diarmid Mackenzie", 27 | "license": "MIT", 28 | "dependencies": { 29 | "aframe-cursor-tracker": "^0.1.0", 30 | "aframe-label": "^0.1.0", 31 | "aframe-object-parent": "^0.1.0" 32 | }, 33 | "devDependencies": { 34 | "webpack": "^5.75.0", 35 | "webpack-cli": "^5.0.1" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /components/mouse-manipulation/webpack.config.js: -------------------------------------------------------------------------------- 1 | // Webpack uses this to work with directories 2 | const path = require('path'); 3 | 4 | // This is the main configuration object. 5 | // Here, you write different options and tell Webpack what to do 6 | module.exports = { 7 | 8 | // Path to your entry point. From this file Webpack will begin its work 9 | entry: './index.js', 10 | 11 | // Path and filename of your result bundle. 12 | // Webpack will bundle all JavaScript into this file 13 | output: { 14 | path: path.resolve(__dirname, 'dist'), 15 | publicPath: '', 16 | filename: 'mouse-manipulation.js' 17 | }, 18 | 19 | resolve: { 20 | fallback: { 21 | "fs": false, 22 | "path": false 23 | }, 24 | }, 25 | 26 | // Default mode for Webpack is production. 27 | // Depending on mode Webpack will apply different things 28 | // on the final bundle. For now, we don't need production's JavaScript 29 | // minifying and other things, so let's set mode to development 30 | mode: 'development' 31 | }; -------------------------------------------------------------------------------- /components/mouse-manipulation/webpack.prod.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var merge = require('webpack-merge').merge; 3 | var commonConfiguration = require('./webpack.config.js'); 4 | 5 | module.exports = merge(commonConfiguration, { 6 | output: { 7 | path: path.resolve(__dirname, 'dist'), 8 | publicPath: '/dist/', 9 | filename: 'mouse-manipulation.min.js' 10 | }, 11 | mode: 'production' 12 | }); -------------------------------------------------------------------------------- /components/object-parent/index.js: -------------------------------------------------------------------------------- 1 | // Change the parent of an object without changing its transform. 2 | AFRAME.registerComponent('object-parent', { 3 | 4 | schema: { 5 | parent: {type: 'selector'}, 6 | }, 7 | 8 | update() { 9 | 10 | const matches = document.querySelectorAll(`#${parent.id}`) 11 | if (matches.length > 1) { 12 | console.warn(`object-parent matches duplicate entities for new parent ${parent.id}`) 13 | } 14 | 15 | const newParent = this.data.parent.object3D 16 | this.reparent(newParent) 17 | 18 | }, 19 | 20 | remove() { 21 | 22 | const originalParentEl = this.el.parentEl 23 | this.reparent(originalParentEl.object3D) 24 | 25 | }, 26 | 27 | reparent(newParent) { 28 | 29 | const object = this.el.object3D 30 | const oldParent = object.parent 31 | 32 | if (object.parent === newParent) { 33 | return; 34 | } 35 | 36 | objectEl = (o) => { 37 | if (o.type === 'Scene') { 38 | return (this.el.sceneEl) 39 | } 40 | else { 41 | return o.el 42 | } 43 | } 44 | 45 | //console.log(`Reparenting ${object.el.id} from ${objectEl(oldParent).id} to ${objectEl(newParent).id}`); 46 | 47 | newParent.attach(object); 48 | }, 49 | }); 50 | -------------------------------------------------------------------------------- /components/object-parent/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aframe-object-parent", 3 | "version": "0.1.0", 4 | "description": "Changes the object3D parent of an A-Frame entity.", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/diarmidmackenzie/aframe-components/tree/main/components/object-parent" 12 | }, 13 | "keywords": [ 14 | "A-Frame", 15 | "Threejs", 16 | "object3D", 17 | "parent" 18 | ], 19 | "bugs": { 20 | "url": "https://github.com/diarmidmackenzie/aframe-components/issues" 21 | }, 22 | "homepage": "https://diarmidmackenzie.github.io/aframe-components/components/object-parent#readme", 23 | "author": "Diarmid Mackenzie", 24 | "license": "MIT" 25 | } 26 | -------------------------------------------------------------------------------- /components/plug-socket/image-20230409165520988.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diarmidmackenzie/aframe-components/1dfa2f24b93dd68ff1f5fbaec558e28310db484c/components/plug-socket/image-20230409165520988.png -------------------------------------------------------------------------------- /components/plug-socket/image-20230410115732409.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diarmidmackenzie/aframe-components/1dfa2f24b93dd68ff1f5fbaec558e28310db484c/components/plug-socket/image-20230410115732409.png -------------------------------------------------------------------------------- /components/plug-socket/image-20230410120556987.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diarmidmackenzie/aframe-components/1dfa2f24b93dd68ff1f5fbaec558e28310db484c/components/plug-socket/image-20230410120556987.png -------------------------------------------------------------------------------- /components/plug-socket/index.js: -------------------------------------------------------------------------------- 1 | if (!AFRAME.components['polygon-wireframe']) require('aframe-polygon-wireframe') 2 | require('./src/socket-system.js') 3 | require('./src/socket-fabric.js') 4 | require('./src/socket.js') 5 | require('./src/plug.js') -------------------------------------------------------------------------------- /components/plug-socket/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aframe-plug-socket", 3 | "version": "0.0.1", 4 | "description": "Plugs & Sockets for A-Frame Entities", 5 | "main": "index.js", 6 | "scripts": { 7 | "dist": "npm run dist:dev && npm run dist:prod", 8 | "dist:dev": "webpack --config webpack.config.js", 9 | "dist:prod": "webpack --config webpack.prod.config.js", 10 | "test": "qunit" 11 | }, 12 | "keywords": [ 13 | "A-Frame", 14 | "aframe", 15 | "plug", 16 | "socket", 17 | "snap", 18 | "position" 19 | ], 20 | "author": "Diarmid Mackenzie", 21 | "license": "MIT", 22 | "dependencies": { 23 | "aframe-polygon-wireframe": "^0.2.1" 24 | }, 25 | "devDependencies": { 26 | "qunit": "^2.19.4", 27 | "webpack": "^5.75.0", 28 | "webpack-cli": "^5.0.1" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /components/plug-socket/src/plug.js: -------------------------------------------------------------------------------- 1 | AFRAME.registerComponent('plug', { 2 | 3 | init() { 4 | this.el.setAttribute('socket', {type: 'plug'}) 5 | } 6 | }) -------------------------------------------------------------------------------- /components/plug-socket/test/test1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 |

Simple plug-socket test - bricks should snap together

11 |
12 | 14 | 16 | 19 | 20 | 21 | 22 | 23 | 24 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /components/plug-socket/test/test10.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 |

Simple plug-socket test - bricks should snap together. Both have non-zero rotations.

11 |
12 | 14 | 16 | 20 | 21 | 22 | 23 | 24 | 25 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /components/plug-socket/test/test11.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 |

Simple plug-socket test - bricks should snap together. Both have non-zero rotations.

11 |
12 | 14 | 16 | 20 | 21 | 22 | 23 | 24 | 25 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /components/plug-socket/test/test12.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 |

8-stud plug-socket test - bricks at various offsets and angles

12 |

Snap distance: 0. Hit space to increase it (behaviour will degenerate above 0.9)

13 |
14 | 16 | 18 | 19 | 23 | 24 | 25 | 26 | 27 | 28 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /components/plug-socket/test/test13.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 |

Simple plug-socket test - bricks should snap together. Both have non-zero rotations.

11 |

Specific combination of rotations that seems to expose a problem...

12 |
13 | 15 | 17 | 22 | 23 | 24 | 25 | 26 | 27 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /components/plug-socket/test/test14.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 |

Simple plug-socket test - bricks should snap together. Both have non-zero rotations.

11 |

Another specific combination of rotations that exposed a problem...

12 |
13 | 15 | 17 | 18 | 23 | 24 | 25 | 26 | 27 | 28 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /components/plug-socket/test/test2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 |

2-stud plug-socket test - bricks should snap together

11 |
12 | 14 | 16 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /components/plug-socket/test/test3.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 |

4-stud plug-socket test - bricks should snap together, offset

11 |
12 | 15 | 17 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /components/plug-socket/test/test8.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 19 | 20 | 21 |
22 |

Simple plug-socket test - bricks move under user control and snap into place

23 |
24 | 26 | 27 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /components/plug-socket/test/test9.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 |

Plug-socket test with dynamic snap - bricks at various different positions

15 |

Can plug in above or below any block

16 |
17 | 20 | 21 | 36 | 37 | 41 | 42 | 43 | 44 | 45 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /components/plug-socket/test/unit-tests.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Test Suite 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /components/plug-socket/webpack.config.js: -------------------------------------------------------------------------------- 1 | // Webpack uses this to work with directories 2 | const path = require('path'); 3 | 4 | // This is the main configuration object. 5 | // Here, you write different options and tell Webpack what to do 6 | module.exports = { 7 | 8 | // Path to your entry point. From this file Webpack will begin its work 9 | entry: './index.js', 10 | 11 | // Path and filename of your result bundle. 12 | // Webpack will bundle all JavaScript into this file 13 | output: { 14 | path: path.resolve(__dirname, 'dist'), 15 | publicPath: '', 16 | filename: 'plug-socket.js' 17 | }, 18 | 19 | resolve: { 20 | fallback: { 21 | "fs": false, 22 | "path": false 23 | }, 24 | }, 25 | 26 | // Default mode for Webpack is production. 27 | // Depending on mode Webpack will apply different things 28 | // on the final bundle. For now, we don't need production's JavaScript 29 | // minifying and other things, so let's set mode to development 30 | mode: 'development' 31 | }; -------------------------------------------------------------------------------- /components/plug-socket/webpack.prod.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var merge = require('webpack-merge').merge; 3 | var commonConfiguration = require('./webpack.config.js'); 4 | 5 | module.exports = merge(commonConfiguration, { 6 | output: { 7 | path: path.resolve(__dirname, 'dist'), 8 | publicPath: '/dist/', 9 | filename: 'plug-socket.min.js' 10 | }, 11 | mode: 'production' 12 | }); -------------------------------------------------------------------------------- /components/polygon-wireframe/README.md: -------------------------------------------------------------------------------- 1 | # polygon-wireframe 2 | 3 | ## Overview 4 | 5 | A component to display wireframes composed of polygons, rather than triangles. 6 | 7 | ![image-20220816094651445](image-20220816094651445.png) 8 | 9 | 10 | 11 | ## Schema 12 | 13 | | Property | Description | Default | 14 | | ------------- | ------------------------------------------------------------ | ------- | 15 | | color | The color to use for the lines of the wireframe | grey | 16 | | opacity | The opacity with which to render the non-hidden parts of the wireframe. This should not be set to a lower value than hiddenOpacity (doing so will trigger a warning, and result in the whole wireframe being rendered with the hiddenOpacity opacity setting). | 1 | 17 | | hiddenOpacity | The opacity with which to render any hidden parts of the wireframe. Setting this to a non-zero value means that parts of the wireframe that would normally be hidden (i.e. that are behind other objects) are still visible. By setting it to a value lower than `opacity` allows for a visible difference between the hidden and non-hidden parts of the wireframe, even though both are visible. | 0 | 18 | | dashed | Whether to use dashed lines. If false, solid lines are used | false | 19 | | dashSize | If dashed lines are used, the length of the dashes relative to the gaps | 3 | 20 | | gapSize | If dashed lines are used, the length of the gaps relative to the dashes | 1 | 21 | | dashScale | If dashed lines are used, this determines the scale of the dashes / gaps. Whatever units are used for dashSize & gapSize, this is the number of units that will fit into a 1 unit of length (i.e. 1 meter if the entity has default scaling).

Larger values for dashScale result in smaller dashes / gaps.

With the default values for dashScale, dashSize and gapSize, and default entity scaling, dashes will be 10cm (3 x 100 / 30), and gaps 3.33cm (1 x 100 / 30). | 30 | 22 | 23 | ## Installation 24 | 25 | Via CDN 26 | ``` 27 | 28 | ``` 29 | 30 | Or via [npm](https://www.npmjs.com/package/aframe-polygon-wireframe) 31 | 32 | ``` 33 | npm install aframe-polygon-wireframe 34 | ``` 35 | 36 | 37 | ## Examples 38 | 39 | [polygon-wireframe.html](https://diarmidmackenzie.github.io/aframe-components/component-usage/polygon-wireframe.html) 40 | 41 | 42 | 43 | ## Code 44 | 45 | [polygon-wireframe](https://github.com/diarmidmackenzie/aframe-components/blob/main/components/polygon-wireframe/index.js) -------------------------------------------------------------------------------- /components/polygon-wireframe/image-20220816094651445.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diarmidmackenzie/aframe-components/1dfa2f24b93dd68ff1f5fbaec558e28310db484c/components/polygon-wireframe/image-20220816094651445.png -------------------------------------------------------------------------------- /components/polygon-wireframe/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aframe-polygon-wireframe", 3 | "version": "0.3.1", 4 | "description": "Render wireframes composed of polygons, rather than triangles.", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/diarmidmackenzie/aframe-components/tree/main/components/polygon-wireframe" 12 | }, 13 | "keywords": [ 14 | "A-Frame", 15 | "Laser Controls", 16 | "Manipulation", 17 | "VR", 18 | "Desktop" 19 | ], 20 | "bugs": { 21 | "url": "https://github.com/diarmidmackenzie/aframe-components/issues" 22 | }, 23 | "homepage": "https://diarmidmackenzie.github.io/aframe-components/components/polygon-wireframe#readme", 24 | "author": "Diarmid Mackenzie", 25 | "license": "MIT" 26 | } 27 | -------------------------------------------------------------------------------- /components/polygon-wireframe/test/hidden-line-options.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 14 | 15 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 28 | 29 | 33 | 34 | 35 | 38 | 39 | 40 | 44 | 45 | 46 | 49 | 50 | 54 | 55 | 56 | 59 | 60 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 74 | view code 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /components/raycast-target/README.md: -------------------------------------------------------------------------------- 1 | # raycast-target 2 | 3 | ## Overview 4 | 5 | A very simple utility component that is used to enable proxy raycasting. 6 | 7 | Proxy raycasting is a pattern where the entity used for raycasting is different from the entity that is selected or manipulated by raycasting. It enables performance improvements when raycasting against entities with complex geometry by allowing raycasting computations to be performed against an entity with simpler geometry, which can be substantially more efficient. 8 | 9 | The entity with simpler geometry could be a low-poly mesh that approximates the entity's geometry. Or in cases where precision raycasting is not required, it could be as simple as a bounding cube or sphere. 10 | 11 | 12 | 13 | ## Schema 14 | 15 | `raycast-target` has a single property schema 16 | 17 | The property is a selector for the target for which this entity is acting as a raycast proxy. If no value is specified, the entity will be set up as a raycast proxy for itself (i.e. no proxying takes place). 18 | 19 | 20 | 21 | ## Usage 22 | 23 | `raycast-target` exposes the target entity on the `target` property 24 | 25 | Here's some sample code that extracts a raycast target if available, and defaults to the entity itself if not. 26 | 27 | ``` 28 | getRaycastTarget(el) { 29 | if (el.components['raycast-target']) { 30 | return el.components['raycast-target'].target 31 | } 32 | else { 33 | return el 34 | } 35 | }, 36 | ``` 37 | 38 | 39 | 40 | ## Installation 41 | 42 | Via CDN 43 | ``` 44 | 45 | ``` 46 | 47 | Or via [npm](https://www.npmjs.com/package/aframe-raycast-target) 48 | 49 | ``` 50 | npm install aframe-raycast-target 51 | ``` 52 | 53 | 54 | ## Components supporting Proxy Raycasting 55 | 56 | The following high-level components support proxy raycasting using `raycast-target`. 57 | 58 | - [laser-manipulation](https://diarmidmackenzie.github.io/aframe-components/docs/laser-manipulation.html) 59 | 60 | - [mouse-manipulation](https://diarmidmackenzie.github.io/aframe-components/docs/mouse-manipulation.html) 61 | 62 | 63 | 64 | ## Examples 65 | 66 | To follow... 67 | 68 | 69 | 70 | ## Code 71 | 72 | [raycast-target](https://github.com/diarmidmackenzie/aframe-components/blob/main/components/raycast-target/index.js) -------------------------------------------------------------------------------- /components/raycast-target/index.js: -------------------------------------------------------------------------------- 1 | 2 | // Component used to mark an entity for raycasting, and optionally provide 3 | // another entity as a target (e.g. useful when raycasting against a low-poly mesh, rather than the target itself) 4 | // 5 | // Note that setting a target here doesn't change which entity will generate raycast / cursor events. 6 | // Applications written using cursor/raycaster need to be written to check for this configurattion and act on it. 7 | 8 | AFRAME.registerComponent('raycast-target', { 9 | schema: {type: 'selector'}, 10 | 11 | init() { 12 | this.target = this.data ? this.data : this.el 13 | } 14 | }) 15 | -------------------------------------------------------------------------------- /components/raycast-target/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aframe-raycast-target", 3 | "version": "0.1.0", 4 | "description": "Simple utility component that is used to enable proxy raycasting.", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/diarmidmackenzie/aframe-components/tree/main/components/raycast-target" 12 | }, 13 | "keywords": [ 14 | "A-Frame", 15 | "Raycasting", 16 | "Proxy" 17 | ], 18 | "bugs": { 19 | "url": "https://github.com/diarmidmackenzie/aframe-components/issues" 20 | }, 21 | "homepage": "https://diarmidmackenzie.github.io/aframe-components/components/raycast-target#readme", 22 | "author": "Diarmid Mackenzie", 23 | "license": "MIT" 24 | } 25 | -------------------------------------------------------------------------------- /components/raycaster-thresholds/README.md: -------------------------------------------------------------------------------- 1 | # raycaster-thresholds 2 | 3 | ## Overview 4 | 5 | A component that enables configuration of the proximity thresholds for raycasting against lines and points. 6 | 7 | This is useful because the default thresholds for raycasting against lines and points is 1m, which is far too large for many applications. See [this A-Frame issue](https://github.com/aframevr/aframe/issues/5072). 8 | 9 | 10 | 11 | ## Schema 12 | 13 | | Property | Description | Default | 14 | | -------- | ------------------------------------------------------------ | ------- | 15 | | line | The accuracy threshold (in meters) to use when raycasting against lines | 0.01 | 16 | | points | The accuracy threshold (in meters) to use when raycasting against points | 0.01 | 17 | 18 | 19 | 20 | ## Installation 21 | 22 | Via CDN 23 | ``` 24 | 25 | ``` 26 | 27 | Or via [npm](https://www.npmjs.com/package/aframe-raycaster-thresholds) 28 | 29 | ``` 30 | npm install aframe-raycaster-thresholds 31 | ``` 32 | 33 | ## Usage 34 | 35 | To use thresholds of 1cm rather than 1m, just set on a Entity that uses raycasting, like this: 36 | 37 | ``` 38 | 39 | ``` 40 | 41 | Or to specify specific non-default thresholds (for example): 42 | 43 | ``` 44 | 46 | ``` 47 | 48 | 49 | 50 | ## Examples 51 | 52 | [raycaster-thresholds.html](https://diarmidmackenzie.github.io/aframe-components/component-usage/raycaster-thresholds.html) 53 | 54 | 55 | 56 | ## Code 57 | 58 | [raycaster-thresholds](https://github.com/diarmidmackenzie/aframe-components/blob/main/components/raycaster-thresholds/index.js) -------------------------------------------------------------------------------- /components/raycaster-thresholds/index.js: -------------------------------------------------------------------------------- 1 | 2 | AFRAME.registerComponent('raycaster-thresholds', { 3 | 4 | dependencies: ['raycaster'], 5 | 6 | schema: { 7 | line: {type: 'number', default: 0.01}, 8 | points: {type: 'number', default: 0.01}, 9 | }, 10 | 11 | init() { 12 | this.oldLine = this.el.components.raycaster.raycaster.params.Line.threshold 13 | this.oldPoints = this.el.components.raycaster.raycaster.params.Points.threshold 14 | 15 | this.el.components.raycaster.raycaster.params.Line.threshold = this.data.line 16 | this.el.components.raycaster.raycaster.params.Points.threshold = this.data.points 17 | }, 18 | 19 | remove() { 20 | this.el.components.raycaster.raycaster.params.Line.threshold = this.oldLine 21 | this.el.components.raycaster.raycaster.params.Points.threshold = this.oldPoints 22 | } 23 | }) -------------------------------------------------------------------------------- /components/raycaster-thresholds/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aframe-raycaster-thresholds", 3 | "version": "0.1.0", 4 | "description": "Enables configuration of the proximity thresholds for raycasting against lines and points.", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/diarmidmackenzie/aframe-components/tree/main/components/raycaster-thresholds" 12 | }, 13 | "keywords": [ 14 | "A-Frame", 15 | "Raycasting", 16 | "Proxy" 17 | ], 18 | "bugs": { 19 | "url": "https://github.com/diarmidmackenzie/aframe-components/issues" 20 | }, 21 | "homepage": "https://diarmidmackenzie.github.io/aframe-components/components/raycaster-thresholds#readme", 22 | "author": "Diarmid Mackenzie", 23 | "license": "MIT" 24 | } 25 | -------------------------------------------------------------------------------- /components/scale-on-hover.js: -------------------------------------------------------------------------------- 1 | AFRAME.registerComponent("scale-on-hover", { 2 | 3 | init() { 4 | this.el.setAttribute("raycastable") 5 | }, 6 | 7 | events: { 8 | 'mouseenter': function (evt) { 9 | this.el.removeAttribute("animation__scale") 10 | this.el.setAttribute("animation__scale", "property:scale; to: 1.2 1.2 1.2; dur: 300") 11 | }, 12 | 'mouseleave': function (evt) { 13 | this.el.removeAttribute("animation__scale") 14 | this.el.setAttribute("animation__scale", "property:scale; to: 1 1 1; dur: 300") 15 | } 16 | } 17 | }) -------------------------------------------------------------------------------- /components/screen-position/README.md: -------------------------------------------------------------------------------- 1 | # screen-position 2 | 3 | ## Overview 4 | 5 | Components to track and report the 2D screen position of an A-Frame entity. 6 | 7 | 8 | 9 | ### screen-position 10 | 11 | This component makes the current screen position of an A-Frame entity (as per the active camera) available via a function 12 | 13 | `this.el.components['screen-position'].getScreenPosition(pos)` 14 | 15 | The parameter `pos` should contain a pre-allocated [`THREE.Vector2`](https://threejs.org/docs/index.html?q=vector2#api/en/math/Vector2). 16 | 17 | The x & y values are populated with the x & y co-ordinates on the current screen of the center of the entity. 18 | 19 | 20 | 21 | ### output-screen-position 22 | 23 | This is a component primarily intended for demonstration purposes. It can be added to an entity with the `screen-position` component, and outputs the screen position data in one of two ways: 24 | 25 | - As text, by writing to the `innerHTML` of a specified element 26 | - As screen position data, by writing to the `style.top` and `style.left` attributes of an element with absolute position. 27 | 28 | 29 | 30 | ## Schema 31 | 32 | | Property | Description | Default | 33 | | -------- | ------------------------------------------------------------ | ------- | 34 | | text | selector for an HTML element to write the x, y screen co-ordinates of the entity to, once every frame | | 35 | | tracker | selector for an HTML element with absolute position, whose 2D position will be updated once every frame to match the 2D position of the entity. | | 36 | 37 | 38 | 39 | ## Installation 40 | 41 | Via CDN 42 | ``` 43 | 44 | ``` 45 | 46 | Or via [npm](https://www.npmjs.com/package/aframe-screen-position) 47 | 48 | ``` 49 | npm install aframe-screen-position 50 | ``` 51 | 52 | ## Examples 53 | 54 | [screen-position.html](https://diarmidmackenzie.github.io/aframe-components/component-usage/screen-position.html) 55 | 56 | 57 | 58 | ## Code 59 | 60 | [screen-position](https://github.com/diarmidmackenzie/aframe-components/blob/main/components/screen-position/index.js) -------------------------------------------------------------------------------- /components/screen-position/index.js: -------------------------------------------------------------------------------- 1 | AFRAME.registerComponent('screen-position', { 2 | 3 | init() { 4 | this.vec3 = new THREE.Vector3() 5 | this.getScreenPosition = this.getScreenPosition.bind(this) 6 | }, 7 | 8 | getScreenPosition(pos) { 9 | 10 | this.el.object3D.getWorldPosition(this.vec3) 11 | 12 | const camera = this.el.sceneEl.camera 13 | this.vec3.project(camera) 14 | 15 | const bounds = document.body.getBoundingClientRect(); 16 | 17 | pos.x = bounds.width * (this.vec3.x + 1) / 2 18 | pos.y = bounds.height - bounds.height * (this.vec3.y + 1) / 2 19 | return pos 20 | } 21 | }); 22 | 23 | AFRAME.registerComponent('output-screen-position', { 24 | 25 | schema: { 26 | text: { type: 'selector'}, 27 | tracker: { type: 'selector'} 28 | }, 29 | 30 | init() { 31 | this.pos = new THREE.Vector2() 32 | this.getScreenPosition = this.el.components['screen-position'].getScreenPosition 33 | }, 34 | 35 | tick() { 36 | 37 | this.getScreenPosition(this.pos) 38 | 39 | if (this.data.text) { 40 | this.data.text.innerHTML = `x: ${this.pos.x.toFixed(0)}, y: ${this.pos.y.toFixed(0)}` 41 | } 42 | 43 | if (this.data.tracker) { 44 | 45 | const tracker = this.data.tracker 46 | 47 | const rect = tracker.getBoundingClientRect() 48 | tracker.style.top = `${this.pos.y - ( rect.height / 2 )}px` 49 | tracker.style.left = `${this.pos.x - ( rect.width / 2 )}px` 50 | } 51 | } 52 | }); 53 | -------------------------------------------------------------------------------- /components/screen-position/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aframe-screen-position", 3 | "version": "0.1.0", 4 | "description": "Components to track and report the 2D screen position of an A-Frame entity.", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/diarmidmackenzie/aframe-components/tree/main/components/screen-position" 12 | }, 13 | "keywords": [ 14 | "A-Frame", 15 | "Screen", 16 | "Position" 17 | ], 18 | "bugs": { 19 | "url": "https://github.com/diarmidmackenzie/aframe-components/issues" 20 | }, 21 | "homepage": "https://diarmidmackenzie.github.io/aframe-components/components/screen-position#readme", 22 | "author": "Diarmid Mackenzie", 23 | "license": "MIT" 24 | } 25 | -------------------------------------------------------------------------------- /components/stats-panel/image-20221106161718921.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diarmidmackenzie/aframe-components/1dfa2f24b93dd68ff1f5fbaec558e28310db484c/components/stats-panel/image-20221106161718921.png -------------------------------------------------------------------------------- /components/stats-panel/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aframe-stats-panel", 3 | "version": "0.2.3", 4 | "description": "A-Frame statistics panel for additional statistics, in the style of A-Frame stats", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [ 10 | "A-Frame", 11 | "statistics", 12 | "stats" 13 | ], 14 | "homepage": "https://diarmidmackenzie.github.io/aframe-components/components/stats-panel#readme", 15 | "author": "Diarmid Mackenzie", 16 | "license": "MIT" 17 | } 18 | -------------------------------------------------------------------------------- /components/thumbstick-states/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aframe-thumbstick-states", 3 | "version": "0.1.0", 4 | "description": "Simplify implementation of thumbstick-based controls in A-Frame.", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/diarmidmackenzie/aframe-components/tree/main/components/thumbstick-states" 12 | }, 13 | "keywords": [ 14 | "A-Frame", 15 | "Controller", 16 | "Thumbstick" 17 | ], 18 | "bugs": { 19 | "url": "https://github.com/diarmidmackenzie/aframe-components/issues" 20 | }, 21 | "homepage": "https://diarmidmackenzie.github.io/aframe-components/components/thumbstick-states#readme", 22 | "author": "Diarmid Mackenzie", 23 | "license": "MIT" 24 | } 25 | -------------------------------------------------------------------------------- /components/xr-room-physics/dist/xr-room-physics.min.js.LICENSE.txt: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright 2010-2023 Three.js Authors 4 | * SPDX-License-Identifier: MIT 5 | */ 6 | -------------------------------------------------------------------------------- /components/xr-room-physics/image-20230702084154726.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diarmidmackenzie/aframe-components/1dfa2f24b93dd68ff1f5fbaec558e28310db484c/components/xr-room-physics/image-20230702084154726.png -------------------------------------------------------------------------------- /components/xr-room-physics/image-20230702090037281.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diarmidmackenzie/aframe-components/1dfa2f24b93dd68ff1f5fbaec558e28310db484c/components/xr-room-physics/image-20230702090037281.png -------------------------------------------------------------------------------- /components/xr-room-physics/image-20230702091131736.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diarmidmackenzie/aframe-components/1dfa2f24b93dd68ff1f5fbaec558e28310db484c/components/xr-room-physics/image-20230702091131736.png -------------------------------------------------------------------------------- /components/xr-room-physics/image-20230702092025851.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diarmidmackenzie/aframe-components/1dfa2f24b93dd68ff1f5fbaec558e28310db484c/components/xr-room-physics/image-20230702092025851.png -------------------------------------------------------------------------------- /components/xr-room-physics/image-20230702092318339.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diarmidmackenzie/aframe-components/1dfa2f24b93dd68ff1f5fbaec558e28310db484c/components/xr-room-physics/image-20230702092318339.png -------------------------------------------------------------------------------- /components/xr-room-physics/image-20230702100955317.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diarmidmackenzie/aframe-components/1dfa2f24b93dd68ff1f5fbaec558e28310db484c/components/xr-room-physics/image-20230702100955317.png -------------------------------------------------------------------------------- /components/xr-room-physics/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aframe-xr-room-physics", 3 | "version": "0.0.1", 4 | "description": "Add physics objects for reported XR room layout", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "npm run dist", 8 | "deploy": "npm run build && netlify deploy --prod --dir=.", 9 | "dist": "npm run dist:min && npm run dist:max", 10 | "dist:max": "webpack", 11 | "dist:min": "cross-env NODE_ENV=production webpack", 12 | "prepublish": "npm run dist", 13 | "start": "cross-env webpack serve" 14 | }, 15 | "keywords": [ 16 | "aframe", 17 | "physics", 18 | "xr", 19 | "room", 20 | "planes" 21 | ], 22 | "author": "Diarmid Mackenzie", 23 | "license": "MIT", 24 | "dependencies": { 25 | "ratk": "^0.1.1" 26 | }, 27 | "devDependencies": { 28 | "cross-env": "^7.0.3", 29 | "webpack": "^5.75.0", 30 | "webpack-cli": "^5.0.1", 31 | "webpack-dev-server": "^4.15.1" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /components/xr-room-physics/test/ammo-desktop-room.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Configure Room Planes in Ammo Physics Engine 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 |

Test with a single desktop-emulated plane.

18 |
19 | 22 | view code 23 | 24 | 29 | 30 | 31 | 32 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /components/xr-room-physics/test/ammo-desktop-room2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Configure Room Planes in Ammo Physics Engine 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 |

Test with two desktop-emulated planes: walls. Space to shoot balls.

18 |
19 | 22 | view code 23 | 24 | 29 | 30 | 31 | 32 | 33 | 34 | 36 | 37 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /components/xr-room-physics/test/ammo-desktop-room3.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Configure Room Planes in Ammo Physics Engine 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 |

Test with two desktop-emulated planes: wall and floor. Space to shoot balls.

18 |
19 | 22 | view code 23 | 24 | 29 | 30 | 31 | 32 | 33 | 34 | 36 | 37 | 38 | 39 | 41 | 42 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /components/xr-room-physics/test/ammo-room.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Configure Room Planes in Ammo Physics Engine 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 |

Configured XR Planes automatically created as static bodies in Ammo Physics.

19 |
20 | 23 | view code 24 | 25 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /components/xr-room-physics/test/ammo-shape-test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Test generation of Ammo Physics Shape from 2 x parallel planes 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |

Generate Ammo Physics Shape from prism (ExtrudeGeometry)

16 |
17 | 20 | view code 21 | 22 | 23 | 24 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /components/xr-room-physics/test/cannon-room.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Configure Room Planes in Cannon Physics Engine 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |

Configured XR Planes automatically created as static bodies in Cannon Physics.

17 |
18 | 21 | view code 22 | 23 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /components/xr-room-physics/test/cannon-shape-test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Test generation of Cannon Physics Shape from 2 x parallel planes 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 |

Generate Cannon Physics Shape from prism (ExtrudeGeometry)

15 |
16 | 19 | view code 20 | 21 | 22 | 23 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /components/xr-room-physics/test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |

Test pages for xr-room-physics

5 |

Testing in VR, with a room layout pre-configured on your device

6 | 11 |

Testing on Desktop, with simulated room layout

12 | 13 |

Full test: Convex & concave corners; Desktop simulation of VR controllers; Toggle debug

14 | 19 |

Simpler tests (these all use Ammo physics)

20 | 26 | -------------------------------------------------------------------------------- /components/xr-room-physics/test/physx-room.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Configure Room Planes in PhysX Physics Engine 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |

Configured XR Planes automatically created as static bodies in PhysX Physics.

17 |
18 | 21 | view code 22 | 23 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /components/xr-room-physics/test/physx-shape-test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Test generation of PhysX Physics Shape from 2 x parallel planes 7 | 8 | 9 | 10 | 11 | 12 | 37 | 38 | 39 | 40 |
41 |

Generate PhysX Physics Shape from prism (ExtrudeGeometry)s

42 |
43 | 46 | view code 47 | 48 | 49 | 50 | 54 | 55 | 56 | 58 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /components/xr-room-physics/test/test-geometries.js: -------------------------------------------------------------------------------- 1 | AFRAME.registerComponent('two-planes', { 2 | 3 | schema: { 4 | engine: { type: 'string'} 5 | } 6 | , 7 | init() { 8 | const mesh = new THREE.Group() 9 | this.el.setObject3D('mesh', mesh) 10 | const planeGeometry = new THREE.PlaneGeometry() 11 | const material = new THREE.MeshBasicMaterial( {color: 0xffff00, side: THREE.DoubleSide} ); 12 | const frontPlane = new THREE.Mesh(planeGeometry, material) 13 | const rearPlane = new THREE.Mesh(planeGeometry, material) 14 | mesh.add(frontPlane) 15 | mesh.add(rearPlane) 16 | rearPlane.position.z -= 0.5 17 | 18 | if (this.data.engine === 'ammo') { 19 | mesh.position.z += 0.25 20 | } 21 | 22 | } 23 | }) 24 | 25 | AFRAME.registerComponent('prism', { 26 | 27 | schema: { 28 | engine: { type: 'string'} 29 | }, 30 | 31 | init() { 32 | 33 | const polygon = [ 34 | {x: -1, z: -1}, 35 | {x: -1, z: 1}, 36 | {x: 1, z: 1}, 37 | {x: 1, z: -1} 38 | ] 39 | 40 | const planeShape = new THREE.Shape() 41 | polygon.forEach((point, i) => { 42 | if (i == 0) { 43 | planeShape.moveTo(point.x, point.z); 44 | } else { 45 | planeShape.lineTo(point.x, point.z); 46 | } 47 | }); 48 | const geometry = new THREE.ExtrudeGeometry(planeShape, {depth: 0.3, bevelEnabled: false}); 49 | geometry.rotateX(-Math.PI / 2); 50 | geometry.center(); 51 | 52 | const material = new THREE.MeshBasicMaterial( {color: 0xffff00, side: THREE.DoubleSide} ); 53 | const mesh = new THREE.Mesh(geometry, material) 54 | this.el.setObject3D('mesh', mesh) 55 | } 56 | }) -------------------------------------------------------------------------------- /components/xr-room-physics/test/toggle-debug.js: -------------------------------------------------------------------------------- 1 | AFRAME.registerComponent('toggle-debug', { 2 | 3 | init() { 4 | this.debug = false; 5 | this.toggleDebug = this.toggleDebug.bind(this) 6 | this.keyUp = this.keyUp.bind(this) 7 | this.el.addEventListener('abuttondown', this.toggleDebug) 8 | this.el.addEventListener('xbuttondown', this.toggleDebug) 9 | window.addEventListener('keyup', this.keyUp) 10 | }, 11 | 12 | toggleDebug() { 13 | 14 | this.debug = !this.debug 15 | this.el.sceneEl.setAttribute('xr-room-physics', { debug: this.debug }) 16 | }, 17 | 18 | keyUp(e) { 19 | if (e.code === 'KeyQ') { 20 | this.toggleDebug() 21 | } 22 | } 23 | }); -------------------------------------------------------------------------------- /components/xr-room-physics/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | entry: './index.js', 5 | output: { 6 | libraryTarget: 'umd', 7 | path: path.resolve(__dirname, 'dist'), 8 | publicPath: '/dist/', 9 | filename: 10 | process.env.NODE_ENV === 'production' 11 | ? 'xr-room-physics.min.js' 12 | : 'xr-room-physics.js' 13 | }, 14 | externals: { 15 | // Stubs out `import ... from 'three'` so it returns `import ... from window.THREE` effectively using THREE global variable that is defined by AFRAME. 16 | three: 'THREE' 17 | }, 18 | devtool: 'source-map', 19 | mode: process.env.NODE_ENV === 'production' ? 'production' : 'development', 20 | devServer: { 21 | port: process.env.PORT || 5000, 22 | hot: false, 23 | liveReload: true, 24 | server: { 25 | type: 'https' 26 | }, 27 | static: { 28 | directory: path.resolve(__dirname + '../../..') 29 | } 30 | }, 31 | resolve: { 32 | alias: { 33 | three: 'super-three' 34 | } 35 | } 36 | }; -------------------------------------------------------------------------------- /compositions/wrapped-present/README.md: -------------------------------------------------------------------------------- 1 | # wrapped-present 2 | 3D animation giving someone a picture as a present. 3 | 4 | 5 | This can be customized with the following options as URL parameters. 6 | 7 | | Parameter | Value | Example | Default | 8 | | ------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------- | 9 | | gift-url | The URL of an image to include. Must be URL-encoded. | https%3A%2F%2Fcdn.aframe.io%2Fexamples
%2Fui%2FkarigurashiPoster.jpg | ./assets/sample-image.png | 10 | | box-color | The color to use for the gift box, as a hex string | #00ff00 (for green) | red (#ff0000) | 11 | | ribbon-color | The color to use for ribbon on the gift box, as a hex string. Note that metallic & roughness settings are automatically applied to this to make the ribbon shiny. | #0000ff (for blue) | gold (#ffd700) | 12 | | message | The message that appears at the top of the screen. This is displayed as a single line - long messages will be hard to read on mobile devices. Spaces need to be URL-encoded (%20) | Good%20%Luck | "Happy Birthday!" | 13 | 14 | 15 | 16 | Example: 17 | 18 | https://diarmidmackenzie.github.io/aframe-components/compositions/wrapped-present/?gift-url=https%3A%2F%2Fcdn.aframe.io%2Fexamples%2Fui%2FkarigurashiPoster.jpg&box-color=00ff00&ribbon-color=0000ff&message=Good%20%Luck%21 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /compositions/wrapped-present/assets/sample-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diarmidmackenzie/aframe-components/1dfa2f24b93dd68ff1f5fbaec558e28310db484c/compositions/wrapped-present/assets/sample-image.png -------------------------------------------------------------------------------- /compositions/wrapped-present/lib/TextGeometry.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Text = 3D Text 3 | * 4 | * parameters = { 5 | * font: , // font 6 | * 7 | * size: , // size of the text 8 | * height: , // thickness to extrude text 9 | * curveSegments: , // number of points on the curves 10 | * 11 | * bevelEnabled: , // turn on bevel 12 | * bevelThickness: , // how deep into text bevel goes 13 | * bevelSize: , // how far from text outline (including bevelOffset) is bevel 14 | * bevelOffset: // how far from text outline does bevel start 15 | * } 16 | */ 17 | 18 | 19 | const ExtrudeGeometry = THREE.ExtrudeGeometry; 20 | 21 | 22 | class TextGeometry extends ExtrudeGeometry { 23 | 24 | constructor( text, parameters = {} ) { 25 | 26 | const font = parameters.font; 27 | 28 | if ( font === undefined ) { 29 | 30 | super(); // generate default extrude geometry 31 | 32 | } else { 33 | 34 | const shapes = font.generateShapes( text, parameters.size ); 35 | 36 | // translate parameters to ExtrudeGeometry API 37 | 38 | parameters.depth = parameters.height !== undefined ? parameters.height : 50; 39 | 40 | // defaults 41 | 42 | if ( parameters.bevelThickness === undefined ) parameters.bevelThickness = 10; 43 | if ( parameters.bevelSize === undefined ) parameters.bevelSize = 8; 44 | if ( parameters.bevelEnabled === undefined ) parameters.bevelEnabled = false; 45 | 46 | super( shapes, parameters ); 47 | 48 | } 49 | 50 | this.type = 'TextGeometry'; 51 | 52 | } 53 | 54 | } 55 | 56 | 57 | THREE.TextGeometry = TextGeometry; 58 | //export { TextGeometry }; -------------------------------------------------------------------------------- /compositions/wrapped-present/src/apply-url-params.js: -------------------------------------------------------------------------------- 1 | // This is run when the page is loaded, to customize the scene based on URL parameters. 2 | // Assumes CONFIG global variable is alredy declared. 3 | 4 | const queryString = window.location.search; 5 | const urlParams = new URLSearchParams(queryString); 6 | 7 | const giftUrl = urlParams.get('gift-url') 8 | const boxColor = urlParams.get('box-color') 9 | const ribbonColor = urlParams.get('ribbon-color') 10 | const message = urlParams.get('message') 11 | 12 | if (boxColor) { 13 | CONFIG.boxColor = "#" + boxColor 14 | } 15 | 16 | if (ribbonColor) { 17 | CONFIG.ribbonColor = "#" + ribbonColor 18 | } 19 | 20 | if (giftUrl) { 21 | CONFIG.giftUrl = giftUrl; 22 | } 23 | 24 | if (message) { 25 | CONFIG.message = message; 26 | } 27 | -------------------------------------------------------------------------------- /docs/utility-components.md: -------------------------------------------------------------------------------- 1 | ## Utility Components 2 | 3 | A selection of simple utility components, used in various examples 4 | 5 | 6 | 7 | ### scale-on-hover 8 | 9 | This component scales an entity by 20% on the `mouseenter` event, and shrinks it back to normal size on the `mouseleave` event 10 | 11 | Used in various examples, including: 12 | 13 | - [polygon-wireframe.html](https://diarmidmackenzie.github.io/aframe-components/component-usage/polygon-wireframe.html) 14 | 15 | Code: [scale-on-hover.js](https://github.com/diarmidmackenzie/aframe-components/blob/main/components/scale-on-hover.js) 16 | 17 | Install: 18 | 19 | 20 | 21 | 22 | 23 | ### hide-on-hover 24 | 25 | This component hides an entity on the `mouseenter` event, and shows it again on the `mouseleave` event 26 | 27 | Used in various examples, including: 28 | 29 | - [raycaster-thresholds.html](https://diarmidmackenzie.github.io/aframe-components/component-usage/raycaster-thresholds.html) 30 | 31 | Code: [hide-on-hover.js](https://github.com/diarmidmackenzie/aframe-components/blob/main/components/hide-on-hover.js) 32 | 33 | Install: 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /styles.css: -------------------------------------------------------------------------------- 1 | 2 | .code-link { 3 | position: fixed; 4 | right: 50px; 5 | top: 50px; 6 | width: 70px; 7 | height: 70px; 8 | border-radius: 50%; 9 | display: flex; 10 | background: white; 11 | justify-content: center; 12 | align-items: center; 13 | font-size: 20px; 14 | text-decoration: none; 15 | text-align: center; 16 | color: black; 17 | font-family: courier; 18 | font-weight: bold; 19 | z-index: 10; 20 | } 21 | 22 | .text-overlay { 23 | background: black; 24 | color: white; 25 | width: 60%; 26 | position: absolute; 27 | top: 10px; 28 | right: 150px; 29 | z-index: 10; 30 | font-size: 20px; 31 | font-family: arial 32 | } 33 | 34 | .gui { 35 | position: absolute; 36 | top: 150px; 37 | right: 10px; 38 | } 39 | 40 | .buttons { 41 | position: absolute; 42 | top: 150px; 43 | right: 10px; 44 | display: flex; 45 | flex-direction: column; 46 | gap: 10px; 47 | z-index: 10; 48 | } --------------------------------------------------------------------------------