From 501746b156c9c839278b3fb33384e6dc43330858 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Mon, 29 Oct 2018 16:37:10 -0700 Subject: [PATCH] 3D Keyboard --- interface/resources/config/keyboard.json | 2476 +++++++++++++++++ interface/resources/meshes/drumstick.fbx | Bin 0 -> 63952 bytes .../resources/meshes/keyboard/SM_enter.fbx | Bin 0 -> 33248 bytes .../resources/meshes/keyboard/SM_key.fbx | Bin 0 -> 32448 bytes .../resources/meshes/keyboard/SM_space.fbx | Bin 0 -> 30400 bytes .../resources/meshes/keyboard/keyCap_F.png | Bin 0 -> 1670 bytes .../resources/meshes/keyboard/keyCap_a.png | Bin 0 -> 3081 bytes .../resources/meshes/keyboard/keyCap_b.png | Bin 0 -> 2744 bytes .../resources/meshes/keyboard/keyCap_c.png | Bin 0 -> 3421 bytes .../resources/meshes/keyboard/keyCap_d.png | Bin 0 -> 2648 bytes .../resources/meshes/keyboard/keyCap_e.png | Bin 0 -> 1689 bytes .../resources/meshes/keyboard/keyCap_g.png | Bin 0 -> 3298 bytes .../resources/meshes/keyboard/keyCap_h.png | Bin 0 -> 1616 bytes .../resources/meshes/keyboard/keyCap_i.png | Bin 0 -> 1581 bytes .../resources/meshes/keyboard/keyCap_j.png | Bin 0 -> 2213 bytes .../resources/meshes/keyboard/keyCap_k.png | Bin 0 -> 2800 bytes .../resources/meshes/keyboard/keyCap_l.png | Bin 0 -> 1631 bytes .../resources/meshes/keyboard/keyCap_m.png | Bin 0 -> 2714 bytes .../resources/meshes/keyboard/keyCap_n.png | Bin 0 -> 2230 bytes .../resources/meshes/keyboard/keyCap_o.png | Bin 0 -> 3508 bytes .../resources/meshes/keyboard/keyCap_p.png | Bin 0 -> 2282 bytes .../resources/meshes/keyboard/keyCap_q.png | Bin 0 -> 3681 bytes .../resources/meshes/keyboard/keyCap_r.png | Bin 0 -> 2833 bytes .../resources/meshes/keyboard/keyCap_s.png | Bin 0 -> 3405 bytes .../resources/meshes/keyboard/keyCap_t.png | Bin 0 -> 1641 bytes .../resources/meshes/keyboard/keyCap_u.png | Bin 0 -> 2537 bytes .../resources/meshes/keyboard/keyCap_v.png | Bin 0 -> 3024 bytes .../resources/meshes/keyboard/keyCap_w.png | Bin 0 -> 4149 bytes .../resources/meshes/keyboard/keyCap_x.png | Bin 0 -> 3327 bytes .../resources/meshes/keyboard/keyCap_y.png | Bin 0 -> 2701 bytes .../resources/meshes/keyboard/keyCap_z.png | Bin 0 -> 2394 bytes interface/resources/meshes/keyboard/key_0.png | Bin 0 -> 3132 bytes interface/resources/meshes/keyboard/key_1.png | Bin 0 -> 1909 bytes .../resources/meshes/keyboard/key_123.png | Bin 0 -> 4399 bytes interface/resources/meshes/keyboard/key_2.png | Bin 0 -> 2864 bytes interface/resources/meshes/keyboard/key_3.png | Bin 0 -> 3341 bytes interface/resources/meshes/keyboard/key_4.png | Bin 0 -> 2126 bytes interface/resources/meshes/keyboard/key_5.png | Bin 0 -> 3071 bytes interface/resources/meshes/keyboard/key_6.png | Bin 0 -> 3631 bytes interface/resources/meshes/keyboard/key_7.png | Bin 0 -> 1921 bytes interface/resources/meshes/keyboard/key_8.png | Bin 0 -> 3535 bytes interface/resources/meshes/keyboard/key_9.png | Bin 0 -> 3617 bytes interface/resources/meshes/keyboard/key_a.png | Bin 0 -> 3066 bytes .../resources/meshes/keyboard/key_abc.png | Bin 0 -> 5346 bytes .../meshes/keyboard/key_ampersand.png | Bin 0 -> 3695 bytes .../resources/meshes/keyboard/key_ast.png | Bin 0 -> 2196 bytes .../resources/meshes/keyboard/key_at.png | Bin 0 -> 4722 bytes interface/resources/meshes/keyboard/key_b.png | Bin 0 -> 2839 bytes .../meshes/keyboard/key_backspace.png | Bin 0 -> 1987 bytes interface/resources/meshes/keyboard/key_c.png | Bin 0 -> 2950 bytes .../resources/meshes/keyboard/key_cap.png | Bin 0 -> 2088 bytes .../resources/meshes/keyboard/key_caret.png | Bin 0 -> 2209 bytes .../meshes/keyboard/key_close_paren.png | Bin 0 -> 2621 bytes .../resources/meshes/keyboard/key_colon.png | Bin 0 -> 1640 bytes .../resources/meshes/keyboard/key_comma.png | Bin 0 -> 1785 bytes interface/resources/meshes/keyboard/key_d.png | Bin 0 -> 2962 bytes .../resources/meshes/keyboard/key_dollar.png | Bin 0 -> 3481 bytes .../resources/meshes/keyboard/key_dquote.png | Bin 0 -> 1918 bytes interface/resources/meshes/keyboard/key_e.png | Bin 0 -> 3029 bytes .../resources/meshes/keyboard/key_enter.png | Bin 0 -> 2201 bytes .../resources/meshes/keyboard/key_exclam.png | Bin 0 -> 1633 bytes .../resources/meshes/keyboard/key_exit.png | Bin 0 -> 2466 bytes interface/resources/meshes/keyboard/key_f.png | Bin 0 -> 2112 bytes interface/resources/meshes/keyboard/key_g.png | Bin 0 -> 3480 bytes interface/resources/meshes/keyboard/key_h.png | Bin 0 -> 2186 bytes .../resources/meshes/keyboard/key_hashtag.png | Bin 0 -> 2394 bytes interface/resources/meshes/keyboard/key_i.png | Bin 0 -> 1625 bytes interface/resources/meshes/keyboard/key_j.png | Bin 0 -> 2070 bytes interface/resources/meshes/keyboard/key_k.png | Bin 0 -> 2389 bytes interface/resources/meshes/keyboard/key_l.png | Bin 0 -> 1907 bytes interface/resources/meshes/keyboard/key_m.png | Bin 0 -> 2438 bytes .../resources/meshes/keyboard/key_min.png | Bin 0 -> 1557 bytes interface/resources/meshes/keyboard/key_n.png | Bin 0 -> 2165 bytes interface/resources/meshes/keyboard/key_o.png | Bin 0 -> 3054 bytes .../meshes/keyboard/key_open_paren.png | Bin 0 -> 2656 bytes interface/resources/meshes/keyboard/key_p.png | Bin 0 -> 2821 bytes .../meshes/keyboard/key_percentage.png | Bin 0 -> 4316 bytes .../resources/meshes/keyboard/key_period.png | Bin 0 -> 1591 bytes .../resources/meshes/keyboard/key_plus.png | Bin 0 -> 1635 bytes interface/resources/meshes/keyboard/key_q.png | Bin 0 -> 3012 bytes .../meshes/keyboard/key_question.png | Bin 0 -> 2727 bytes interface/resources/meshes/keyboard/key_r.png | Bin 0 -> 1897 bytes interface/resources/meshes/keyboard/key_s.png | Bin 0 -> 3080 bytes .../resources/meshes/keyboard/key_semi.png | Bin 0 -> 1797 bytes .../resources/meshes/keyboard/key_slash.png | Bin 0 -> 2580 bytes .../resources/meshes/keyboard/key_squote.png | Bin 0 -> 1783 bytes interface/resources/meshes/keyboard/key_t.png | Bin 0 -> 2038 bytes interface/resources/meshes/keyboard/key_u.png | Bin 0 -> 2232 bytes .../resources/meshes/keyboard/key_under.png | Bin 0 -> 1579 bytes interface/resources/meshes/keyboard/key_v.png | Bin 0 -> 2671 bytes interface/resources/meshes/keyboard/key_w.png | Bin 0 -> 3466 bytes interface/resources/meshes/keyboard/key_x.png | Bin 0 -> 2997 bytes interface/resources/meshes/keyboard/key_y.png | Bin 0 -> 3072 bytes interface/resources/meshes/keyboard/key_z.png | Bin 0 -> 2115 bytes .../meshes/keyboard/text_placard.png | Bin 0 -> 1308 bytes interface/resources/meshes/keyboard/white.png | Bin 0 -> 293 bytes .../resources/qml/controlsUit/Keyboard.qml | 20 +- .../qml/dialogs/TabletLoginDialog.qml | 4 + interface/resources/qml/hifi/AvatarApp.qml | 11 +- .../resources/qml/hifi/avatarapp/Settings.qml | 24 + .../qml/hifi/commerce/wallet/Wallet.qml | 4 + .../qml/hifi/dialogs/TabletRunningScripts.qml | 4 + interface/resources/qml/hifi/tablet/Edit.qml | 6 + .../qml/hifi/tablet/TabletAddressDialog.qml | 8 + .../tabletWindows/TabletPreferencesDialog.qml | 4 + interface/resources/sounds/keyboard_key.mp3 | Bin 0 -> 9089 bytes interface/src/Application.cpp | 17 +- interface/src/Menu.cpp | 2 + interface/src/Menu.h | 1 + .../src/raypick/PickScriptingInterface.cpp | 12 +- .../src/raypick/PointerScriptingInterface.cpp | 30 +- interface/src/raypick/RayPick.cpp | 8 +- interface/src/raypick/StylusPick.cpp | 18 +- interface/src/raypick/StylusPick.h | 9 +- interface/src/raypick/StylusPointer.cpp | 34 +- interface/src/raypick/StylusPointer.h | 7 +- .../src/scripting/HMDScriptingInterface.cpp | 7 +- .../src/scripting/HMDScriptingInterface.h | 2 + .../scripting/KeyboardScriptingInterface.cpp | 34 + .../scripting/KeyboardScriptingInterface.h | 43 + interface/src/ui/Keyboard.cpp | 799 ++++++ interface/src/ui/Keyboard.h | 152 + interface/src/ui/overlays/ModelOverlay.cpp | 2 +- interface/src/ui/overlays/Overlays.cpp | 53 +- interface/src/ui/overlays/Web3DOverlay.cpp | 2 + libraries/render-utils/src/Model.cpp | 2 +- .../ui/src/ui/TabletScriptingInterface.cpp | 6 + .../ui/src/ui/TabletScriptingInterface.h | 1 + .../controllerModules/inEditMode.js | 17 + .../nearParentGrabOverlay.js | 6 +- scripts/system/marketplaces/marketplaces.js | 5 + 131 files changed, 3777 insertions(+), 53 deletions(-) create mode 100644 interface/resources/config/keyboard.json create mode 100644 interface/resources/meshes/drumstick.fbx create mode 100644 interface/resources/meshes/keyboard/SM_enter.fbx create mode 100644 interface/resources/meshes/keyboard/SM_key.fbx create mode 100644 interface/resources/meshes/keyboard/SM_space.fbx create mode 100644 interface/resources/meshes/keyboard/keyCap_F.png create mode 100644 interface/resources/meshes/keyboard/keyCap_a.png create mode 100644 interface/resources/meshes/keyboard/keyCap_b.png create mode 100644 interface/resources/meshes/keyboard/keyCap_c.png create mode 100644 interface/resources/meshes/keyboard/keyCap_d.png create mode 100644 interface/resources/meshes/keyboard/keyCap_e.png create mode 100644 interface/resources/meshes/keyboard/keyCap_g.png create mode 100644 interface/resources/meshes/keyboard/keyCap_h.png create mode 100644 interface/resources/meshes/keyboard/keyCap_i.png create mode 100644 interface/resources/meshes/keyboard/keyCap_j.png create mode 100644 interface/resources/meshes/keyboard/keyCap_k.png create mode 100644 interface/resources/meshes/keyboard/keyCap_l.png create mode 100644 interface/resources/meshes/keyboard/keyCap_m.png create mode 100644 interface/resources/meshes/keyboard/keyCap_n.png create mode 100644 interface/resources/meshes/keyboard/keyCap_o.png create mode 100644 interface/resources/meshes/keyboard/keyCap_p.png create mode 100644 interface/resources/meshes/keyboard/keyCap_q.png create mode 100644 interface/resources/meshes/keyboard/keyCap_r.png create mode 100644 interface/resources/meshes/keyboard/keyCap_s.png create mode 100644 interface/resources/meshes/keyboard/keyCap_t.png create mode 100644 interface/resources/meshes/keyboard/keyCap_u.png create mode 100644 interface/resources/meshes/keyboard/keyCap_v.png create mode 100644 interface/resources/meshes/keyboard/keyCap_w.png create mode 100644 interface/resources/meshes/keyboard/keyCap_x.png create mode 100644 interface/resources/meshes/keyboard/keyCap_y.png create mode 100644 interface/resources/meshes/keyboard/keyCap_z.png create mode 100644 interface/resources/meshes/keyboard/key_0.png create mode 100644 interface/resources/meshes/keyboard/key_1.png create mode 100644 interface/resources/meshes/keyboard/key_123.png create mode 100644 interface/resources/meshes/keyboard/key_2.png create mode 100644 interface/resources/meshes/keyboard/key_3.png create mode 100644 interface/resources/meshes/keyboard/key_4.png create mode 100644 interface/resources/meshes/keyboard/key_5.png create mode 100644 interface/resources/meshes/keyboard/key_6.png create mode 100644 interface/resources/meshes/keyboard/key_7.png create mode 100644 interface/resources/meshes/keyboard/key_8.png create mode 100644 interface/resources/meshes/keyboard/key_9.png create mode 100644 interface/resources/meshes/keyboard/key_a.png create mode 100644 interface/resources/meshes/keyboard/key_abc.png create mode 100644 interface/resources/meshes/keyboard/key_ampersand.png create mode 100644 interface/resources/meshes/keyboard/key_ast.png create mode 100644 interface/resources/meshes/keyboard/key_at.png create mode 100644 interface/resources/meshes/keyboard/key_b.png create mode 100644 interface/resources/meshes/keyboard/key_backspace.png create mode 100644 interface/resources/meshes/keyboard/key_c.png create mode 100644 interface/resources/meshes/keyboard/key_cap.png create mode 100644 interface/resources/meshes/keyboard/key_caret.png create mode 100644 interface/resources/meshes/keyboard/key_close_paren.png create mode 100644 interface/resources/meshes/keyboard/key_colon.png create mode 100644 interface/resources/meshes/keyboard/key_comma.png create mode 100644 interface/resources/meshes/keyboard/key_d.png create mode 100644 interface/resources/meshes/keyboard/key_dollar.png create mode 100644 interface/resources/meshes/keyboard/key_dquote.png create mode 100644 interface/resources/meshes/keyboard/key_e.png create mode 100644 interface/resources/meshes/keyboard/key_enter.png create mode 100644 interface/resources/meshes/keyboard/key_exclam.png create mode 100644 interface/resources/meshes/keyboard/key_exit.png create mode 100644 interface/resources/meshes/keyboard/key_f.png create mode 100644 interface/resources/meshes/keyboard/key_g.png create mode 100644 interface/resources/meshes/keyboard/key_h.png create mode 100644 interface/resources/meshes/keyboard/key_hashtag.png create mode 100644 interface/resources/meshes/keyboard/key_i.png create mode 100644 interface/resources/meshes/keyboard/key_j.png create mode 100644 interface/resources/meshes/keyboard/key_k.png create mode 100644 interface/resources/meshes/keyboard/key_l.png create mode 100644 interface/resources/meshes/keyboard/key_m.png create mode 100644 interface/resources/meshes/keyboard/key_min.png create mode 100644 interface/resources/meshes/keyboard/key_n.png create mode 100644 interface/resources/meshes/keyboard/key_o.png create mode 100644 interface/resources/meshes/keyboard/key_open_paren.png create mode 100644 interface/resources/meshes/keyboard/key_p.png create mode 100644 interface/resources/meshes/keyboard/key_percentage.png create mode 100644 interface/resources/meshes/keyboard/key_period.png create mode 100644 interface/resources/meshes/keyboard/key_plus.png create mode 100644 interface/resources/meshes/keyboard/key_q.png create mode 100644 interface/resources/meshes/keyboard/key_question.png create mode 100644 interface/resources/meshes/keyboard/key_r.png create mode 100644 interface/resources/meshes/keyboard/key_s.png create mode 100644 interface/resources/meshes/keyboard/key_semi.png create mode 100644 interface/resources/meshes/keyboard/key_slash.png create mode 100644 interface/resources/meshes/keyboard/key_squote.png create mode 100644 interface/resources/meshes/keyboard/key_t.png create mode 100644 interface/resources/meshes/keyboard/key_u.png create mode 100644 interface/resources/meshes/keyboard/key_under.png create mode 100644 interface/resources/meshes/keyboard/key_v.png create mode 100644 interface/resources/meshes/keyboard/key_w.png create mode 100644 interface/resources/meshes/keyboard/key_x.png create mode 100644 interface/resources/meshes/keyboard/key_y.png create mode 100644 interface/resources/meshes/keyboard/key_z.png create mode 100644 interface/resources/meshes/keyboard/text_placard.png create mode 100644 interface/resources/meshes/keyboard/white.png create mode 100644 interface/resources/sounds/keyboard_key.mp3 create mode 100644 interface/src/scripting/KeyboardScriptingInterface.cpp create mode 100644 interface/src/scripting/KeyboardScriptingInterface.h create mode 100644 interface/src/ui/Keyboard.cpp create mode 100644 interface/src/ui/Keyboard.h diff --git a/interface/resources/config/keyboard.json b/interface/resources/config/keyboard.json new file mode 100644 index 0000000000..ba113da1f5 --- /dev/null +++ b/interface/resources/config/keyboard.json @@ -0,0 +1,2476 @@ +{ + "anchor": { + "dimensions": { + "x": 0.023600000888109207, + "y": 0.022600000724196434, + "z": 0.1274999976158142 + }, + "position": { + "x": 0.006292800903320312, + "y": 0.004300000742077827, + "z": 0.005427663803100586 + }, + "rotation": { + "w": 1.000, + "x": 0.000, + "y": 0.000, + "z": 0.000 + } + }, + "textDisplay": { + "dimensions": { + "x": 0.15, + "y": 0.045, + "z": 0.1 + }, + "localPosition": { + "x": -0.3032040786743164, + "y": 0.059300000742077827, + "z": 0.06454843521118164 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.976, + "z": 0.216 + }, + "leftMargin": 0.0, + "rightMargin": 0.0, + "topMargin": 0.0, + "bottomMargin": 0.0, + "lineHeight": 0.05 + }, + "useResourcesPath": true, + "layers": [ + [ + { + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.5332040786743164, + "y": 0.019300000742077827, + "z": 0.03745675086975098 + }, + "key": "p", + "texture": { + "file9": "meshes/keyboard/key_p.png", + "file10": "meshes/keyboard/key_p.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.42067813873291016, + "y": 0.019300000742077827, + "z": 0.03744244575500488 + }, + "key": "i", + "texture": { + "file9": "meshes/keyboard/key_i.png", + "file10": "meshes/keyboard/key_i.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "o", + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.47769832611083984, + "y": 0.019300000742077827, + "z": 0.03745675086975098 + }, + "texture": { + "file9": "meshes/keyboard/key_o.png", + "file10": "meshes/keyboard/key_o.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.49904823303222656, + "y": 0.019300000742077827, + "z": -0.01745915412902832 + }, + "key": "l", + "texture": { + "file9": "meshes/keyboard/key_l.png", + "file10": "meshes/keyboard/key_l.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.4439973831176758, + "y": 0.019300000742077827, + "z": -0.01745915412902832 + }, + "key": "k", + "texture": { + "file9": "meshes/keyboard/key_k.png", + "file10": "meshes/keyboard/key_k.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "y", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_y.png", + "file10": "meshes/keyboard/key_y.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.30902957916259766, + "y": 0.019300000742077827, + "z": 0.0374448299407959 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "r", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_r.png", + "file10": "meshes/keyboard/key_r.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.19474029541015625, + "y": 0.019300000742077827, + "z": 0.03745102882385254 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "d", + "texture": { + "file9": "meshes/keyboard/key_d.png", + "file10": "meshes/keyboard/key_d.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.16272640228271484, + "y": 0.019300000742077827, + "z": -0.01747274398803711 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "t", + "texture": { + "file9": "meshes/keyboard/key_t.png", + "file10": "meshes/keyboard/key_t.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.2517843246459961, + "y": 0.019300000742077827, + "z": 0.03744622692465782 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "f", + "texture": { + "file9": "meshes/keyboard/key_f.png", + "file10": "meshes/keyboard/key_f.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.2200756072998047, + "y": 0.019300000742077827, + "z": -0.01746511459350586 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "g", + "texture": { + "file9": "meshes/keyboard/key_g.png", + "file10": "meshes/keyboard/key_g.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.27622222900390625, + "y": 0.019300000742077827, + "z": -0.017457084730267525 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "w", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_w.png", + "file10": "meshes/keyboard/key_w.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.08203601837158203, + "y": 0.019300000742077827, + "z": 0.03743100166320801 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "q", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_q.png", + "file10": "meshes/keyboard/key_q.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.026292800903320312, + "y": 0.019300000742077827, + "z": 0.037427663803100586 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "a", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_a.png", + "file10": "meshes/keyboard/key_a.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.050909996032714844, + "y": 0.019300000742077827, + "z": -0.017487764358520508 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "e", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_e.png", + "file10": "meshes/keyboard/key_e.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.13773441314697266, + "y": 0.019300000742077827, + "z": 0.03745269775390625 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "s", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_s.png", + "file10": "meshes/keyboard/key_s.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.10659980773925781, + "y": 0.019300000742077827, + "z": -0.017481565475463867 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "u", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_u.png", + "file10": "meshes/keyboard/key_u.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.36423587799072266, + "y": 0.019300000742077827, + "z": 0.03743577003479004 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "h", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_h.png", + "file10": "meshes/keyboard/key_h.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.3321561813354492, + "y": 0.019300000742077827, + "z": -0.017461776733398438 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "j", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_j.png", + "file10": "meshes/keyboard/key_j.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.38753604888916016, + "y": 0.019300000742077827, + "z": -0.01746058464050293 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "c", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_c.png", + "file10": "meshes/keyboard/key_c.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.22023773193359375, + "y": 0.019300000742077827, + "z": -0.07282757759094238 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "v", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_v.png", + "file10": "meshes/keyboard/key_v.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.2767200469970703, + "y": 0.019300000742077827, + "z": -0.07291850447654724 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "b", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_b.png", + "file10": "meshes/keyboard/key_b.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.33254528045654297, + "y": 0.019300000742077827, + "z": -0.07283258438110352 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": " ", + "name": "space", + "dimensions": { + "x": 0.14597539603710175, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.27660465240478516, + "y": 0.019300000742077827, + "z": -0.12705934047698975 + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/white.png", + "file10": "meshes/keyboard/white.png" + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "z", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_z.png", + "file10": "meshes/keyboard/key_z.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.10669422149658203, + "y": 0.019300000742077827, + "z": -0.07285571098327637 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "n", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_n.png", + "file10": "meshes/keyboard/key_n.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.020292000845074654, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.38794422149658203, + "y": 0.019300000742077827, + "z": -0.0728309154510498 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "m", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_m.png", + "file10": "meshes/keyboard/key_m.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.01846799999475479, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.44464683532714844, + "y": 0.019300000742077827, + "z": -0.07282185554504395 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "x", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_x.png", + "file10": "meshes/keyboard/key_x.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.1630725860595703, + "y": 0.019300000742077827, + "z": -0.07284045219421387 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "DEL", + "type": "backspace", + "dimensions": { + "x": 0.04787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.53203323516845703, + "y": 0.019300000742077827, + "z": -0.07286686894893646 + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_backspace.png", + "file10": "meshes/keyboard/key_backspace.png" + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "Caps", + "type": "caps", + "switchToLayer": 1, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.02603323516845703, + "y": 0.019300000742077827, + "z": -0.07285571098327637 + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_cap.png", + "file10": "meshes/keyboard/key_cap.png" + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "Close", + "type": "close", + "dimensions": { + "x": 0.04787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.59333323516845703, + "y": 0.019300000742077827, + "z": 0.037454843521118164 + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_exit.png", + "file10": "meshes/keyboard/key_exit.png" + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "Enter", + "type": "enter", + "dimensions": { + "x": 0.08787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.5103323516845703, + "y": 0.019300000742077827, + "z": -0.127054843521118164 + }, + "modelURL": "meshes/keyboard/SM_enter.fbx", + "texture": { + "file10": "meshes/keyboard/key_enter.png", + "file11": "meshes/keyboard/key_enter.png" + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "numbers", + "type": "layer", + "switchToLayer": 2, + "dimensions": { + "x": 0.07787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.026323516845703, + "y": 0.019300000742077827, + "z": -0.127054843521118164 + }, + "modelURL": "meshes/keyboard/SM_enter.fbx", + "texture": { + "file10": "meshes/keyboard/key_123.png", + "file11": "meshes/keyboard/key_123.png" + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + } + ], + [ + { + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.5332040786743164, + "y": 0.019300000742077827, + "z": 0.037454843521118164 + }, + "key": "p", + "texture": { + "file9": "meshes/keyboard/keyCap_p.png", + "file10": "meshes/keyboard/keyCap_p.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.42067813873291016, + "y": 0.019300000742077827, + "z": 0.03744244575500488 + }, + "key": "i", + "texture": { + "file9": "meshes/keyboard/keyCap_i.png", + "file10": "meshes/keyboard/keyCap_i.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "o", + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.47769832611083984, + "y": 0.019300000742077827, + "z": 0.03745675086975098 + }, + "texture": { + "file9": "meshes/keyboard/keyCap_o.png", + "file10": "meshes/keyboard/keyCap_o.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.49904823303222656, + "y": 0.019300000742077827, + "z": -0.01745915412902832 + }, + "key": "l", + "texture": { + "file9": "meshes/keyboard/keyCap_l.png", + "file10": "meshes/keyboard/keyCap_l.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.4439973831176758, + "y": 0.019300000742077827, + "z": -0.01745915412902832 + }, + "key": "k", + "texture": { + "file9": "meshes/keyboard/keyCap_k.png", + "file10": "meshes/keyboard/keyCap_k.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "y", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/keyCap_y.png", + "file10": "meshes/keyboard/keyCap_y.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.30902957916259766, + "y": 0.019300000742077827, + "z": 0.0374448299407959 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "r", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/keyCap_r.png", + "file10": "meshes/keyboard/keyCap_r.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.19474029541015625, + "y": 0.019300000742077827, + "z": 0.03745102882385254 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "d", + "texture": { + "file9": "meshes/keyboard/keyCap_d.png", + "file10": "meshes/keyboard/keyCap_d.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.16272640228271484, + "y": 0.019300000742077827, + "z": -0.01747274398803711 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "t", + "texture": { + "file9": "meshes/keyboard/keyCap_t.png", + "file10": "meshes/keyboard/keyCap_t.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.2517843246459961, + "y": 0.019300000742077827, + "z": 0.03744622692465782 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "f", + "texture": { + "file9": "meshes/keyboard/keyCap_F.png", + "file10": "meshes/keyboard/keyCap_F.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.2200756072998047, + "y": 0.019300000742077827, + "z": -0.01746511459350586 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "g", + "texture": { + "file9": "meshes/keyboard/keyCap_g.png", + "file10": "meshes/keyboard/keyCap_g.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.27622222900390625, + "y": 0.019300000742077827, + "z": -0.017457084730267525 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "w", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/keyCap_w.png", + "file10": "meshes/keyboard/keyCap_w.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.08203601837158203, + "y": 0.019300000742077827, + "z": 0.03743100166320801 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "q", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/keyCap_q.png", + "file10": "meshes/keyboard/keyCap_q.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.026292800903320312, + "y": 0.019300000742077827, + "z": 0.037427663803100586 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "a", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/keyCap_a.png", + "file10": "meshes/keyboard/keyCap_a.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.050909996032714844, + "y": 0.019300000742077827, + "z": -0.017487764358520508 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "e", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/keyCap_e.png", + "file10": "meshes/keyboard/keyCap_e.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.13773441314697266, + "y": 0.019300000742077827, + "z": 0.03745269775390625 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "s", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/keyCap_s.png", + "file10": "meshes/keyboard/keyCap_s.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.10659980773925781, + "y": 0.019300000742077827, + "z": -0.017481565475463867 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "u", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/keyCap_u.png", + "file10": "meshes/keyboard/keyCap_u.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.36423587799072266, + "y": 0.019300000742077827, + "z": 0.03743577003479004 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "h", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/keyCap_h.png", + "file10": "meshes/keyboard/keyCap_h.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.3321561813354492, + "y": 0.019300000742077827, + "z": -0.017461776733398438 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "j", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/keyCap_j.png", + "file10": "meshes/keyboard/keyCap_j.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.38753604888916016, + "y": 0.019300000742077827, + "z": -0.01746058464050293 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "c", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/keyCap_c.png", + "file10": "meshes/keyboard/keyCap_c.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.22023773193359375, + "y": 0.019300000742077827, + "z": -0.07282757759094238 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "v", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/keyCap_v.png", + "file10": "meshes/keyboard/keyCap_v.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.2767200469970703, + "y": 0.019300000742077827, + "z": -0.07291850447654724 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "b", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/keyCap_b.png", + "file10": "meshes/keyboard/keyCap_b.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.33254528045654297, + "y": 0.019300000742077827, + "z": -0.07283258438110352 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": " ", + "name": "space", + "dimensions": { + "x": 0.14597539603710175, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.27660465240478516, + "y": 0.019300000742077827, + "z": -0.12705934047698975 + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/white.png", + "file10": "meshes/keyboard/white.png" + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "z", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/keyCap_z.png", + "file10": "meshes/keyboard/keyCap_z.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.10669422149658203, + "y": 0.019300000742077827, + "z": -0.07285571098327637 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "n", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/keyCap_n.png", + "file10": "meshes/keyboard/keyCap_n.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.020292000845074654, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.38794422149658203, + "y": 0.019300000742077827, + "z": -0.0728309154510498 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "m", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/keyCap_m.png", + "file10": "meshes/keyboard/keyCap_m.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.01846799999475479, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.44464683532714844, + "y": 0.019300000742077827, + "z": -0.07282185554504395 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "x", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/keyCap_x.png", + "file10": "meshes/keyboard/keyCap_x.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.1630725860595703, + "y": 0.019300000742077827, + "z": -0.07284045219421387 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "DEL", + "type": "backspace", + "dimensions": { + "x": 0.04787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.53203323516845703, + "y": 0.019300000742077827, + "z": -0.07286686894893646 + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_backspace.png", + "file10": "meshes/keyboard/key_backspace.png" + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "Caps", + "type": "caps", + "switchToLayer": 0, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.02603323516845703, + "y": 0.019300000742077827, + "z": -0.07285571098327637 + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_cap.png", + "file10": "meshes/keyboard/key_cap.png" + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "Close", + "type": "close", + "dimensions": { + "x": 0.04787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.59333323516845703, + "y": 0.019300000742077827, + "z": 0.037454843521118164 + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_exit.png", + "file10": "meshes/keyboard/key_exit.png" + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "Enter", + "type": "enter", + "dimensions": { + "x": 0.08787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.5103323516845703, + "y": 0.019300000742077827, + "z": -0.127054843521118164 + }, + "modelURL": "meshes/keyboard/SM_enter.fbx", + "texture": { + "file10": "meshes/keyboard/key_enter.png", + "file11": "meshes/keyboard/key_enter.png" + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "numbers", + "type": "layer", + "switchToLayer": 2, + "dimensions": { + "x": 0.07787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.026323516845703, + "y": 0.019300000742077827, + "z": -0.127054843521118164 + }, + "modelURL": "meshes/keyboard/SM_enter.fbx", + "texture": { + "file10": "meshes/keyboard/key_123.png", + "file11": "meshes/keyboard/key_123.png" + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + } + ], + [ + { + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.5332040786743164, + "y": 0.019300000742077827, + "z": 0.037454843521118164 + }, + "key": "0", + "texture": { + "file9": "meshes/keyboard/key_0.png", + "file10": "meshes/keyboard/key_0.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.42067813873291016, + "y": 0.019300000742077827, + "z": 0.03744244575500488 + }, + "key": "8", + "texture": { + "file9": "meshes/keyboard/key_8.png", + "file10": "meshes/keyboard/key_8.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "9", + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.47769832611083984, + "y": 0.019300000742077827, + "z": 0.03745675086975098 + }, + "texture": { + "file9": "meshes/keyboard/key_9.png", + "file10": "meshes/keyboard/key_9.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.47764823303222656, + "y": 0.019300000742077827, + "z": -0.01745915412902832 + }, + "key": "(", + "texture": { + "file9": "meshes/keyboard/key_open_paren.png", + "file10": "meshes/keyboard/key_open_paren.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.53364823303222656, + "y": 0.019300000742077827, + "z": -0.01745915412902832 + }, + "key": ")", + "texture": { + "file9": "meshes/keyboard/key_close_paren.png", + "file10": "meshes/keyboard/key_close_paren.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.59634823303222656, + "y": 0.019300000742077827, + "z": -0.01745915412902832 + }, + "key": "/", + "texture": { + "file9": "meshes/keyboard/key_slash.png", + "file10": "meshes/keyboard/key_slash.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.4206973831176758, + "y": 0.019300000742077827, + "z": -0.01745915412902832 + }, + "key": "+", + "texture": { + "file9": "meshes/keyboard/key_plus.png", + "file10": "meshes/keyboard/key_plus.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "6", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_6.png", + "file10": "meshes/keyboard/key_6.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.30902957916259766, + "y": 0.019300000742077827, + "z": 0.0374448299407959 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "4", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_4.png", + "file10": "meshes/keyboard/key_4.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.19474029541015625, + "y": 0.019300000742077827, + "z": 0.03745102882385254 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "$", + "texture": { + "file9": "meshes/keyboard/key_dollar.png", + "file10": "meshes/keyboard/key_dollar.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.13772640228271484, + "y": 0.019300000742077827, + "z": -0.01747274398803711 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "5", + "texture": { + "file9": "meshes/keyboard/key_5.png", + "file10": "meshes/keyboard/key_5.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.2517843246459961, + "y": 0.019300000742077827, + "z": 0.03744622692465782 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "%", + "texture": { + "file9": "meshes/keyboard/key_percentage.png", + "file10": "meshes/keyboard/key_percentage.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.1947756072998047, + "y": 0.019300000742077827, + "z": -0.01746511459350586 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "_", + "texture": { + "file9": "meshes/keyboard/key_under.png", + "file10": "meshes/keyboard/key_under.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.25172222900390625, + "y": 0.019300000742077827, + "z": -0.017457084730267525 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "2", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_2.png", + "file10": "meshes/keyboard/key_2.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.08203601837158203, + "y": 0.019300000742077827, + "z": 0.03743100166320801 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "1", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_1.png", + "file10": "meshes/keyboard/key_1.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.026292800903320312, + "y": 0.019300000742077827, + "z": 0.037427663803100586 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "@", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_at.png", + "file10": "meshes/keyboard/key_at.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.026209996032714844, + "y": 0.019300000742077827, + "z": -0.017487764358520508 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "3", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_3.png", + "file10": "meshes/keyboard/key_3.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.13773441314697266, + "y": 0.019300000742077827, + "z": 0.03745269775390625 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "#", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_hashtag.png", + "file10": "meshes/keyboard/key_hashtag.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.08209980773925781, + "y": 0.019300000742077827, + "z": -0.017481565475463867 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "7", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_7.png", + "file10": "meshes/keyboard/key_7.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.36423587799072266, + "y": 0.019300000742077827, + "z": 0.03743577003479004 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "&", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_ampersand.png", + "file10": "meshes/keyboard/key_ampersand.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.3090561813354492, + "y": 0.019300000742077827, + "z": -0.017461776733398438 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "-", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_min.png", + "file10": "meshes/keyboard/key_min.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.36433604888916016, + "y": 0.019300000742077827, + "z": -0.01746058464050293 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "'", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_squote.png", + "file10": "meshes/keyboard/key_squote.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.22023773193359375, + "y": 0.019300000742077827, + "z": -0.07282757759094238 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": ":", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_colon.png", + "file10": "meshes/keyboard/key_colon.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.2767200469970703, + "y": 0.019300000742077827, + "z": -0.07291850447654724 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": ";", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_semi.png", + "file10": "meshes/keyboard/key_semi.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.33254528045654297, + "y": 0.019300000742077827, + "z": -0.07283258438110352 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": " ", + "name": "space", + "dimensions": { + "x": 0.14597539603710175, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.27660465240478516, + "y": 0.019300000742077827, + "z": -0.12705934047698975 + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/white.png", + "file10": "meshes/keyboard/white.png" + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "*", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_ast.png", + "file10": "meshes/keyboard/key_ast.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.10669422149658203, + "y": 0.019300000742077827, + "z": -0.07285571098327637 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "!", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_exclam.png", + "file10": "meshes/keyboard/key_exclam.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.020292000845074654, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.38794422149658203, + "y": 0.019300000742077827, + "z": -0.0728309154510498 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "?", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_question.png", + "file10": "meshes/keyboard/key_question.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.01846799999475479, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.44464683532714844, + "y": 0.019300000742077827, + "z": -0.07282185554504395 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "\"", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_dquote.png", + "file10": "meshes/keyboard/key_dquote.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.1630725860595703, + "y": 0.019300000742077827, + "z": -0.07284045219421387 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "DEL", + "type": "backspace", + "dimensions": { + "x": 0.04787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.53203323516845703, + "y": 0.019300000742077827, + "z": -0.07286686894893646 + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_backspace.png", + "file10": "meshes/keyboard/key_backspace.png" + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "Caps", + "type": "caps", + "switchToLayer": 1, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.02603323516845703, + "y": 0.019300000742077827, + "z": -0.07285571098327637 + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_cap.png", + "file10": "meshes/keyboard/key_cap.png" + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "Close", + "type": "close", + "dimensions": { + "x": 0.04787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.59333323516845703, + "y": 0.019300000742077827, + "z": 0.037454843521118164 + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_exit.png", + "file10": "meshes/keyboard/key_exit.png" + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "Enter", + "type": "enter", + "dimensions": { + "x": 0.08787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.5103323516845703, + "y": 0.019300000742077827, + "z": -0.127054843521118164 + }, + "modelURL": "meshes/keyboard/SM_enter.fbx", + "texture": { + "file10": "meshes/keyboard/key_enter.png", + "file11": "meshes/keyboard/key_enter.png" + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "numbers", + "type": "layer", + "switchToLayer": 0, + "dimensions": { + "x": 0.07787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.026323516845703, + "y": 0.019300000742077827, + "z": -0.127054843521118164 + }, + "modelURL": "meshes/keyboard/SM_enter.fbx", + "texture": { + "file10": "meshes/keyboard/key_abc.png", + "file11": "meshes/keyboard/key_abc.png" + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": ".", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_period.png", + "file10": "meshes/keyboard/key_period.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.38794422149658203, + "y": 0.019300000742077827, + "z": -0.12705934047698975 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": ",", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_comma.png", + "file10": "meshes/keyboard/key_comma.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.1630725860595703, + "y": 0.019300000742077827, + "z": -0.12705934047698975 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + } + ] + ] +} diff --git a/interface/resources/meshes/drumstick.fbx b/interface/resources/meshes/drumstick.fbx new file mode 100644 index 0000000000000000000000000000000000000000..0243d9fd1b89f0dfe6572a7ec293a38ec571ce53 GIT binary patch literal 63952 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 literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/SM_enter.fbx b/interface/resources/meshes/keyboard/SM_enter.fbx new file mode 100644 index 0000000000000000000000000000000000000000..119e3fc5356a9dcaf271190d3e64912fdba4a3f3 GIT binary patch literal 33248 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 SA;rMJz~JfX=d#Wzp$Py6*tu~4 literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/keyCap_a.png b/interface/resources/meshes/keyboard/keyCap_a.png new file mode 100644 index 0000000000000000000000000000000000000000..f409254292dbe07c27c37341049f2c1f584f1f2e GIT binary patch literal 3081 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 literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/keyCap_d.png b/interface/resources/meshes/keyboard/keyCap_d.png new file mode 100644 index 0000000000000000000000000000000000000000..4eccee2dd533ea24e6bca5e6ab3114fce37db803 GIT binary patch literal 2648 zcmeHJZB&eD7{1@v$UChvW#|LVs1|9gLHf{C6Ka~3eAGyXKF~y%3Z*bLZ|UP`%0%a+ zXlccUP_n0dw39>#Vc?Hv4mbJm?sFLc#1ZyNSD%=+H|_F0Z5|-*d^zXcIwEUgmNnA{e1g^?YvWf>tea*$zxV>& zMkUh{4V#d%Z4J+!eZMbr)dt(kgRxW60xt(=<)}kjMm`M3FyhcW!5EzYDBu8808Cre zzlY=De09_}(y?(={MG5-Ob7HEtovNZ_?`cv<4YXpPXDActL_-1UWK72OL_8PiuyxR zWkHWKMa0S%6Vu)EQ&LhgHFg1+8Qj*I8n;jNfr%8pD+c^N81JY z#1h5Pi!c*oQEle^yJgA16rp`(G|7XK@>{n`=1`-qq6vYGr92Mj#EBEL{iFP6kO**a zE^YHKjjw~~Pj}n8PeAYvhnf1T|Ir+R;CGVJw!uD%qJE7YRp#QU7WR|?BD?L0GOB)CPGCV-Pft%NPriNU zX|IAcYt{sK90BHKy2{9{Usf^J{w`U^X7%;h1MhqT@CdQ$8yIM6-Y-?EUZu}RA++uT zzd|0}jLT%3gjzvQK^uj*Hn>TN7)`H6-yEcFrmE^WHd>#AORSeedL1iu@( zSM;>Hy1Fxho{hmd%lfgFmX`k7i^{ejbI8c9*=nFEr`~2_U}+U+Ahk*NrxM_?cNTZl zTr8;~fyZOMvxrd@R<2mzB2QLV0gh@GP}x!bEVIrR zFRJL~ka4;_%;-R3oza8CASV9Gn^A!lPfP^n!DTsW{|TwUJh-8d^U~mrh6))kUkgT^ z#MPZr53QXtnXJwt*v~e{GtCkOtJdAN$4+P_g$M{>*jUON?G@0?M0#^jup(X;jP_Qj zDFiU|Zi(V>`fIHukF__F7V*O6)Wc;CO?+p&(-@rlnJ0T4Y3kfHWoeElgFr^`;1JED z9b}C{P@utJAo}CN0AmgTe)rEFa?Dj9Rw{Spr-Jxqq4JijwSu;)K$9yqS^+N2rEhoJ z3I!cKE?mFv0g5%)@3OPAJ6xG3Yt=MLxdAt3dT%pQmG#Ck!T;1V$ zd19uzJx^>PTRGU)Jq3kpS&~=b74D02 zpGU{A8#F$5`f6r_wDSI;-9dA+^8{9U<3U!>CU{)cBzTu_M3RS&7u;qNxY1Kvs@ zK>ZjMB1%)w(p459brjZAy=idRXEHBxq34Klux)S*g8_-MW~_Jc)o3f4aCFV+km|Qq ziZv*XmC&3zO%OcJD%IRm-SIXFm?FZ7v~j8bRNW~p7lF9;O#fkeSI>a}a?+*)!x#Q~A9=Q?%3_owMY{W9vlW2_ae=xh zh*{lUL0>iGxZvOYJT8d*Kjy#Xi}PR1>2VI{zI_5B9k>f;9upnkMFjw0yLqj=x;!Z1 EPktEd`Tzg` literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/keyCap_e.png b/interface/resources/meshes/keyboard/keyCap_e.png new file mode 100644 index 0000000000000000000000000000000000000000..09bd5bc289d89475106bc4583820f10cdaa0f7f4 GIT binary patch literal 1689 zcmeAS@N?(olHy`uVBq!ia0y~yU}6Aa4iHr><-C@GfkCpwHKHUqKdq!Zu_%?nF(p4K zRlzeiF+DXXH8G{K@MNkD0|T42r;B4q#jQ7Y59YQQ2sm7{Sk$1I>Q#_F#ldsglD7#; zOB;Gc8b2JW{yz1?jHllFc7$FPXE^^kmxY0ep@D%xfq{VoWG(4Ta`YfsMWJ>MGF*&g zJH^@=m?#K3utUfXd6++7G?^x$Yd^pq-P4fy_IKUsQdgI>Tk(3=SDg}>@#SsK+i9;~ z?d>;wqd&cvg@qK$3IxKPiu`Bp%)LG5yqKUF2V;Y<{ieO;#SP{%jEv~klac}ttUt=J zW9jwRTi^eF+AkF$zP-Qi=8|@9&o09@ zT{ngYKN!xOkMBQCiZu@^x&;l^md5fjo_p+O(9m8)T6AEEIxIq@#DfBZ8|6k|Bt+6& uK#}%_f!3}-Ug3+BdB`kC>wgO-FjNN{?^~8w#mvCKz~JfX=d#Wzp$Py-;?-vW literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/keyCap_g.png b/interface/resources/meshes/keyboard/keyCap_g.png new file mode 100644 index 0000000000000000000000000000000000000000..9c52605428d9c8b82cf213068b1cb6e0ddde9f7d GIT binary patch literal 3298 zcmc&$TTl~b7Cs39BccH`aw(b!B8b!!wOoY+xfN{zS)_s(xrJCn5D2#rNCZ~_gHT1l z1q8Gfwty6}lC=SXEjG$UWLQ)#*&u?Dg>VhT07-W2OlO>(edyxDKAbtv02kl696`Qe&Om1fQ0vke(zEZUFrb9faZ3R_NSN#T2e?H1)RfULMeF9^C47< zFC`>ACH5)hFo4!2PgiHZ*K})hOCf@7i(NIH8v@WlnrbW8?xZNWs%sxhFsDf zwLM4~uQ6$6dZzaCH{VnYrQgOCju&cKcc%1+qkkf{G&h%mkE5z#BcK2fKmyQ)b;18H zUHjvb|9Cp;Bc%IH@%1bJd&7SB>wM`y=`7vVsI5Lb^78UZy7#pHD#KtfL}k^xlsf;_ zd9x+DLMr{+vPEl8W0%HQrd+zP5V~*f_<8P7ePw@9g92G}Hb#mooPLxxLqM}j>PQbj~^O@E?ITy~{OQX`$7!8h@4zEaY)5nF81nb6p^6;!I9=Ea~)0wRl7ma53 zGObI#+H+%=aD*GQUl^{BsQ4+=@BAS{xmQEjq)5;bVC=``a_yY@1M8O8)}~bm6e}mv zx}>JxqTZwC6#sD6GS$mIgh3u{~U_Hu5gitL)9BNK9LHy#4DB#>n`jq@vd5 zEHS?qw+}2VBKoLQYKr7|cC0*K7W!rLmw!E_9;{Bfx3sFbeoe`fukr}f2Mu%M0$f;F zxVP=#WU;b{CRO#u?D!=ss{MHk7vm3pfn>ckyO~K`dK!t?woCIzWW{7?htbvvsUUmT z=g13EB9$k!Hq5ngGR>z9oIPY8UEQY1I#S#-A;vU1yy2jkUhLTz#`iY2C*mF+S}}n+ zQHx4E(aU#yyOYdb$b2+XPMlq$MG{ zJB_wjhJ3+ve!Aypvh(ZJhFo)JwsQZnXGmJpM%(6rsKWFargNe~)i*vqKC=*SH7^Sc zQwQd^=>rq`;YFSIKHZ2U-D)3Jbrun_F{8R2`Oln#w|uH7BrMT|lNcmPJ}eTMIUV5+ zJuui+cA8`qI4UOw6gm=#4mG;9V17#=5VS8c2PI9tI23EalRtl~roYj^8O+P8s;b&O zF1*u2v3%*WZcb~Sa#_U!pT`gs6!hvc9twIsdD)>_!m_&2ND@|UB&P!p1-_%o^68uJ zcjUX%k!?~Oij{Lfu>nz0+nTQU%fi&e#6;6BG~6pl*VcdXtwY8mzTuWDSh z914^(D=YRzZlsWU9pS93?EcY5Y4nto2CQw4``vHTPhX+8w36$uE?0F&a7s!_WQzOT z-TuH}h_7ULlL^|2naU>CUE)cE50*-$KAisLmRFT{16K3EO6svtb&}~=!h=%%cuY!e z1+#Bf)~#qjvRnwrYrHM5qPbQp6S})%JZqskNB_Yhy*jK;%{aI_f`dM$tqmPx3n#8^ zMqA;CuNKc(Zhu(%40VO7r!CMsILYB~jBr1a>jgg>D{@?7u6DUb#SsTYr*!v$#drQS zubxR z&b0DJ8`0oaKpCLfe`OcE2@MqNKGw+%X8{d{fQJ5aq&k1jUJ_J?s=b&>Fv!~|h@V-a zlbuoUPeNzo$qn1aE}!8)NVIcwTw2%#`GYJm&E7Eg=zP@O6Ah8EQY3;kETy;TpWKxG z1>WN3n@^)jFyhT~K|UVlwho=3@QFxBHKK+U?l5>V?-VK?lq@eU##i3fMFTEzuXdkA zOruL3kIbWH$OGa$XnRewFDp*M?NhTF#b{(EBdrv&Ks;RN(lUy|bjd22wnl?WG#C-p zI>+OZvXYsbGK7-1NiO^}rvf3hgq8Q5GV+k2qm$FKiN*fNo1Jy)qgM6x^+S@TDPKJJ zT+?m08ImL>of1@4Rdq)!UUkeGNLyQZ{Me1r(%j4tg^N6$x0O0%9Jk}`Zg!Lo9Yq7+ z3NzaP2`m6S0Q&tH`oF{Va6Sly4Bq82x!yk#obhk#Nl(%M literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/keyCap_h.png b/interface/resources/meshes/keyboard/keyCap_h.png new file mode 100644 index 0000000000000000000000000000000000000000..f29323da1173bea026ac0f1f5f0ff97d51fae35c GIT binary patch literal 1616 zcmeAS@N?(olHy`uVBq!ia0y~yU}6Aa4iHr><-C@GfkCpwHKHUqKdq!Zu_%?nF(p4K zRlzeiF+DXXH8G{K@MNkD0|V9T==L4kpR945(n z4%F)@Cj<*7ay;Nh^CkY&m%#Xs;+hqM7 zaB*OGpR~+Cj`jxm7`FpDYfW!Ab}AYBGCpuv*Fd7J7*S1{qcOD~;GozDOjGek4r<6z nsC^h_L*hc@!R~$rW`?GV>faV^YB|8bz`)??>gTe~DWM4fe!z+W literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/keyCap_i.png b/interface/resources/meshes/keyboard/keyCap_i.png new file mode 100644 index 0000000000000000000000000000000000000000..721e6b84b1a7626aeadc8439c65bf580709b88db GIT binary patch literal 1581 zcmeAS@N?(olHy`uVBq!ia0y~yU}6Aa4iHr><-C@GfkCpwHKHUqKdq!Zu_%?nF(p4K zRlzeiF+DXXH8G{K@MNkD0|V=MPZ!6Kid%1P9OP|K5O8%g6-&}AVJmUUEZd}Jmhi~+ zz|7xq|DLMuc00m!;D3%9g8%~q3j+fqg98Iu%z@SJK$+ty*DgRo&_P0+?2v~=7>p*< zBn<6r+h#ZHoV)${^W$fGI2d}CZw*SU?7J{jvged zI2t%9O0Ja0IR4D?;z%CTvuQ!UKFCyUb88W0s5`;Uff2&^jX=*)6l?cE4nU-^B$`RI zLhQ+cLhTLYB{U>kDNJ(;l%;s`wV&5xWMepJC4PT@ov9oH0|SGntDnm{r-UW|>3f0w literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/keyCap_j.png b/interface/resources/meshes/keyboard/keyCap_j.png new file mode 100644 index 0000000000000000000000000000000000000000..7186df71c1026d15ea1426e95e77882ae4318d6c GIT binary patch literal 2213 zcmeAS@N?(olHy`uVBq!ia0y~yU}6Aa4iHr><-C@GfkCpwHKHUqKdq!Zu_%?nF(p4K zRlzeiF+DXXH8G{K@MNkD0|SS(hG8{;(RA-$d${;&Ov>W|(gxKG)|BSSdpIArFf%7)_>0=-L~4Y7JRFOg@_? z{P}TcsHF3z3%B2YcVqnbIr`%3qkneX*DQJ{B+$UXpuoVu0ZPW?Fv-z_WK{!$1QXGT z_JnI_C>tYVgN~_hvVZ`Ch1>b_=h+xRNxosbBo7-{%Jm-ei|@aW2{1P3>|}3XV5ncL z!PVTr;4p35?c6i{(^E5f1Q||D@^+BvzgM^4oa@8;^)BnhKR@1m_nec$97cz_NA_$v z-`p7e{y|8MT@=2rHMv*BvG z(zqA9cGaNJ=LqqfTI}4co`0zm>F4|@?M}ETIxZ9B_&$|^B zQWH1*I~-V3Tkxrt_g{vI)zAA?e?FX_wXCwTl5_pU{$D@meyqsxf5^iSWhTS-SUjUl z>qy%6+c$%sobXM!B>KN(TX{ikZLYr}!-Y*(v!?#A=4t;w`Q(v#P7DiDC&r)u#3$C> zde>#&9cG1x|K}FoKY!1(yS{*pVeX5Jqkhp{i+1eazkRjO6r*$7abfw>z;YY*I^4bsofXDu?mE43keE^;j4_S*U05#qBr!_m;c; zP>Fosm&4in@9Xp1Tw!Evc+<-B=bhBVPB|8ahWs^5Oz0Vx=o|qlvnkf@Oi>ZQz%;e}{g3sptlC@%BU|{fc^>bP0l+XkK^(*8l literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/keyCap_k.png b/interface/resources/meshes/keyboard/keyCap_k.png new file mode 100644 index 0000000000000000000000000000000000000000..69667f42e0c4bf63668dee1391701b5659cbddc6 GIT binary patch literal 2800 zcmd^>aa2=P9LIleXT^&UwlpCRxj7;&!C~&0A#4kQT#6_sXzMTop;}T=X<~R z`}y5_?%ltB-3zmP=lKG_EZ*waBmfc+4@$iD%v<#t06dl(mz=v%{7P=Va927!pC;as zPUne)8R<#s!nA^%i|J7SzVGm2pWh_CK58`P7~%Ag$T%^Ns&=Lq6K!4D9pff}U0LAMx3bExQv^^5c!l%F4<>Xwa!@fD1qY zAOj#XYW}x!K<;bu_b=z(2fAtEGbVqxU^6S+UD>^IbhJEFaq_Clpa36$!s0skU*GasNuNv->XLY z?Joem*RFF6ewUx=#3Z6LXwrF>%NRiU`eay~#&z`fulOovQRvVLr3ZzQoHK&d)Kpum>!?|*C+w@8=eNvr z%S>An$#8xxZtv&FyXAV8cbJP~(n~6jBt>V-T1o}9(Q?`qj#r8}(;kL;b0wVBC*X`a zHnX-oL4`ue=OV+HH0&9-LGwie3MGd}OU>a8lP~wj1$CDjRr07~H$ZJb+j!x&F z<2a8EeUMSdra~FZU7qEjbzUJ#_cp^SEm$l6vp=y>Yu#hn4_v>dAI5rF2Z_9~N>7u< zrL$!b#a-~NyV){+RExER042*=dwpE&3;_zSDja9td6zA6#(Ecoyt8|!vsLsOArUIo zjIEx4{HwDOMNsce=1KFigL?@}w6Slc;OEoxJFgTnfU@dXe*30rLc*x!vbp`^99;_m ze|G|2J5>JNcTBfNY^O$-Z=?X) z(E(&==(}AQzN>2y3SYH^b+Zz#B8Wmd9x!Q-07M~OQrjJ+I~;h*n4N+Z1Sx=f#4KMC ztW=w)*tR6IKi%k)oJ0oFiu!e?)KPZDPR~oYo)zA`Lr8_mzTTVo;A`8=*>qgU#UwrD z@i4G45oi4>pR6OKv(p6qRAP>Go&cGtRa95t78!!6#e@ld$dk$EAI9IaIY!%GB?E)t zlTY$YV6NE}Wn36&c~$unZQX=@akL*72Ad5v3}H}+rSb0Z<6q}o!fCgNgsb)V;Oi@ft-fajC(P`#ss~7btL#*EhLba= z|50b&NFo_g;7o9EzDnHE#aV2>KtN+WEr~4&EL{K;U4&nPK27k15s?yjqT+gdB?1-+ z#y0W!!)545TnJDS4H{D_-KO#aTpcF})--=&`-lpty6>eA2`u!6T%YyBWo6A4Z~O?E zX~a9B`47^8l33LeQ=6Vc40^7*Fle9*CBuzOgT^}Lvi$Cu_hux6geVRj2&N7av~E1L zYxB)+w@^VOM%C6SS750zyVbo{OzeJjSHQj~KQ7Q4l9>K=>WkhQ=vfI>rO1@~hvxNU z*!S353*=iqE=5o%*>pDrpC8xb8?Dk2{ORv^3?9<{LF4qcCFad^8)G_4Qd>-Y;6dO< zHC2>(8B5<1cFA+D{gSuaOQt%K!7%zHuO$QH*zx0C(AUe>;Kx;eF)Z1MJ_GlmD+*pvgV*ha$lJ$n|(dl`R_pfETwewmD||zTW_K C1v!ZT literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/keyCap_l.png b/interface/resources/meshes/keyboard/keyCap_l.png new file mode 100644 index 0000000000000000000000000000000000000000..d59c24f7e24a7223186b2907e536504da6623385 GIT binary patch literal 1631 zcmeAS@N?(olHy`uVBq!ia0y~yU}6Aa4iHr><-C@GfkCpwHKHUqKdq!Zu_%?nF(p4K zRlzeiF+DXXH8G{K@MNkD0|OhQr;B4q#jQ7Y&vLdHh`3!W+|V#3iT#*Y5_i}N)g>a< z6`RYA6_ozX*)0C#^dl#;Or=N62Y%Q1GAJ-GaDXgqU?7V*u-Y3ab3EnR6(|TgNQjdi z@~{Yl(PWy0q5V!oHq*NG*LJOY{qFqkb>5mY1M=Q+GdXSek`74-&Rs>;9j3 z|9fBO=bcPUL>u>@LayZ@o0sPKIwRHRZ>Ctr3k!xZR49f!kQs$=Z4{;@rUWMP0}~Pl zWXA5#SY8q@V-&SbB~z?aTTM_Tf<+MfNQGihP^&@&0f7Ly>e$p4 zh)64)u|j;HR13jYL~4W}3ZWEHgknVvHby`xBs?#0lXNbOfBL60*qQ$6oH?`m}=gKv~%OSDHo zOyu_H(CEmhl(_on_W=ZNoJzN>dPh&28*4@2`U=8b*j2Toq8ef~ReCVK` z-Bp!JRmq%1+OPZA1_ z>8t2CU2{*UX>82W^F@#ui_DWsrJvEx$S(IE!ra|&Y_^m^DTq4_hQgC9UP05|6=MhK zntRTIFph`bq(e0*zRe%k^PM0n0*W(nb)Ad}Ot|lY^Cfw3&fw#o}IrH%q`CCQCP%?7-?CF+Ow^10vC$XY42FJA_9!5yv!<%P~A0E z-L@T(&tk$eHqu(jMVvsl2brf(DBcb~7CJ~s5HTXS))`wT<3g7dY2l%P*kq!?LGwUo zu+`ZCTr{O4C0B;%2%eJ-O%@LDjEv;uq8GFb&6hH4QILYF* zOyap;sA+BehJv)E5*`UPTP|u7ccslJuM~j@jHWpC07FYeH0I3su($8b0Wh98k)NMr z*|*nR`;jrobM9u80s6$GB&TJ*+g*`ry=-hIDf0tmgH6}Xn>TSzM|bz4crv&^eGV@= z=DdFq>F6Y75#~}L&cDx>G<8~QBRz%aiI$}@fAI3q~gKY%+X8L zs0Jm>1Fo*f-R?n?DXwUOL~t+*kF`yqN>H=~nU`CCW*~PN+h6d6Kk|5|8%2p#B`TG4 zHj?x4^rPB_TU5nKgK>6BXvPKBk&*ef)R9wTDf2yucAI>29g5GD>%wK;=gKG3*C_Cy zN3EStpVm_7?hJ?e`uh5sn$?F{4(cHTK@b*8Wq|}0LFR@hDjTzv!H5`h8NDF@@#&4^ z?xoL3m29{-zx$+hc=+;wJg6YFWPG~-IyTOauw%@zt{7K28X(iIYJ#3+rSwNPVq>*RSJf3Ka}k;<A%Ff_8`^WCM&|sjH)fF%!xKTQcA-G$R?$Wr)7nIG8 zKU1&W>W*Yv)4U^SXN|%498|K!c@6bZN6ouF;X29X^5Qf{4JyuTu1R=uU0W51mA0Se zQ*698C!_88P<6F$tyfgpro_tO2C5-gXp$^zpGjA!5W($VAN%MTU${@`(M3#)swnnJ zcc5spqsKT~&5PfzUx!VIex32UmYuh93nI8G*J&Brc(NQyL3xeUL5VL0%eb0_hfj8w z&)pIBqZ^GU`b1z1nqC$5sY}J5g$hc6ExRtTI${bgLPr=o!^a&-nH1&{Z`9rDdKjyu zXV&dr+8%{O$=>*^C_x#P_qEw>phqr2%lcs z7n+ZvQ|$0B!d}=Z&U**=|F`fmKc#xlpW0c+O#m8=pU F_zNQ|_;COL literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/keyCap_n.png b/interface/resources/meshes/keyboard/keyCap_n.png new file mode 100644 index 0000000000000000000000000000000000000000..950fb88e41b66abd8f22d6ac9beb1a036f6cd853 GIT binary patch literal 2230 zcmc&#ZA?>F7=CXl?FCD7*;x1()f?CZ-GaodQ;~&REFdh!f+ny#D8&pF9? zpZ9&v^St+*Gt%S)AMYS<0PsoND^3Mq0s3$>eH65(2>=2Wap{Wvd50B6biNEk*?Czq zGBKAvB1@Ig*&oXLWzhh5dZJi#p!n`zV<~d$UcXbn);D2?@GL@fE-;;UjPqt$AN~sG z1&_~8dj3EJl?4=cwt~KOxcb7-TXy>1WTW|XsdYhSGdgCTZBP|26|w*W3V;A$!@8FL zFP!ZjdAH$!xgi~+`1+N18RnYTnQ1j#mlad5YPpqba?UM`)G0FT$4NM_ZTau<>Di0V z&O7I#&ssMvov`KQWQ;B^+Nz9u>`#-Fo?=16(RXWm1r%tf^4m+F_$R2~k>Hr=_t_Im zqgyDz!@gET)tfQ4O8anjK9ctkSd@6wAw1HN&&RslZ2(<6dS@5sr5I^?JZq7u_`5!|-dU8^IeIFhQ(#lEo zb>k-?AgydwKWcq__>K0hEGYQY-fkg3A7_8#__Gm*k%`B}e!-ggt{n(*8i}DBxfZDL zg)50By)(M5M5vBrfpC{Hd?sg*n-&K;zKly(ZHNQiW*K(^sYrp2-^-ma1GC%9rB}!! z^Cq90r&AikG#TjFH3H3O!HgHphQ^da;){lUvH~b!dCc~^%)*;**yvvllw^Q!*zzi> z{#zt!fwae>I{_-7gadp-EwCXc^$GGsTKO@T_Mw2hq|Y_V6PwMxlZ7EyL@w?i)FU;Z)N}R7POO~7$e~SLaX)>8+=ewRBR(_YcEe-I#7S(@# z$OUuF>%0jG_o}-Ocl$Y7qhl_eEZvKtJy3nwJXNqYegFU< MF)mr$9DAtjAC2nT7XSbN literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/keyCap_o.png b/interface/resources/meshes/keyboard/keyCap_o.png new file mode 100644 index 0000000000000000000000000000000000000000..2c1755c8877805917a20d32661f936d5988dcff2 GIT binary patch literal 3508 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 P000NOlgFOgps)M~_BGVb literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/keyCap_p.png b/interface/resources/meshes/keyboard/keyCap_p.png new file mode 100644 index 0000000000000000000000000000000000000000..366a9ef3ff594dac68e666b81bfa347f8fac82d4 GIT binary patch literal 2282 zcmeHIYfO`87=6BvQc5TGLv&$4X;Gt5SVxqR2?k0F+KM8VmB0{6)vBO!krv8S_+$f$ zVi;bi=%ks>IbebUTDdqbZexfH2}1@7t8yt=UwPlzZecAqFoa6|}Rdt-MC>DZxuIKyb+nVuJ&-#$tRIhC=RxvWF@Y z45R@eI(b3CLj=LCsX1FIrVfA=w(RRWO(($a<-vxjj}I?>nrBr%RGPOo?Yyw7DJxuX zt3<68xry~uxoWa2fA`bRtmvK#T zz}dOH%juA*TNEx>3C_;N9Ywn1gSwR1GbC8DbM6u%4zFI34RzyRMqN}TH=h#e%}p?v zCHn+a)HDo>Hf$jPvC{m2=0t=$5FjJkL$1)o&j1G2MbrG@Jytl>(Ql_(|J+!QfmCFr z{3enB8SA;r*srCntRA;pi$ZR+#_oFiGfR-hPZ^%A3nai53!DlleDb?q-#al-YsB{x z?nQ*9oDZ8TU4|mKt>oy@gBsGK11?4>8T-q-Dry=Ixo$M-8q{bi2jUr{l;tJ}UK-pf zo|743fDX!1=`}-N9~QH9Kwz(jQfAjJy;AJpX8hMx#+j(xEQN)jr2luFBuOMxI&e-bzJq4>!A| zG4~r{C1lcTk^J{Q{WrRH!1$Fl$2{`4>$Q#TF7LBvPgj2xwH=|edQtSvRyPL!@1BKr l_TN>%$mOvxI&PTu#Ss`v40Xs2eAx^D!1eL-E@4Hc{Q>m=0S5p8 literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/keyCap_q.png b/interface/resources/meshes/keyboard/keyCap_q.png new file mode 100644 index 0000000000000000000000000000000000000000..2d58bc2b46cb5d2cfdbf58abc76e3763756e9d1f GIT binary patch literal 3681 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 literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/keyCap_s.png b/interface/resources/meshes/keyboard/keyCap_s.png new file mode 100644 index 0000000000000000000000000000000000000000..365a833af2441a593c95a3b45e1b4ed3dac1cf6b GIT binary patch literal 3405 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 literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/keyCap_v.png b/interface/resources/meshes/keyboard/keyCap_v.png new file mode 100644 index 0000000000000000000000000000000000000000..1dbe395005c5da1f53826fc94752310af14369e2 GIT binary patch literal 3024 zcmds3X;4#H7Cu?%1QV9D2+}A47X&8?ibEq?0*bg{L>v*t4j@an4M_l77LgYbC*8x0 zAPz=oBsf-!3L23Ta0Ff?0%Am{Mx@z5dj$0_Jn5PSeRJmsblU+RC9Cxmd_fUu z8PX^e3MWUtRz+SdWsR}3gER(M3`rVD4%j`q!fq(uDOhfT#|U9&FGXDm`F{`G7N}sC9kvikoHO0*9Srq|gO6Q4mKn zOkfxKR}aJnOw;S8?eS9chMt7mk*2oj$_7pb;DUG8WDAla&eoVb2tXd8){rOSzrLQ; z=*cehpWm4^z#$4sFG-SLIZ)oeZ7H1G=7608{)L*yJneM!#!AX2wGTM^t2){JFYp8P zsI!KK>n*m(OjcEtvJ_aif3;24gm}|_5 zf(DA1WFVH=nOb6fBTa{+NWR`3G4GSA_U&j9%B(B@W@~JTZGP579fzCp&=RuvJUzv3 zUa53)Q_EZFI01UTlqIt#q|Go-A;-7h5ZruGTkCzlu%^4aJ6Q6v>^L?Mo6|M5BEzR1 z?KK1~&10##VS}(z@ig8`FCL!n_9*7&MtuI|A1)&?B*4(rb?hBw8Zt+obdA5z)OuoI zBm<Xtl9#*235AVhK-I`6h8a39u#kZ$rRH1GHrga4&IBPv(`Cbt?B^r7pP6daf ziqG`V@F=wEm{W;~Z`sdvI7v5GM17M{e;hf@Sm$_fjNPh@(pFax1mb5I`lD}2lnkUq zTB>SWTeFLpct8UbTQ4f6><1BJNVs_FUf#QR zuj)6mrY{Fp_xzCzk!$kwM#^wwzc5vQHpIN3cUjE_$4A|!tMafC4FL}_gS%Rp8JieJE@6l&R=)lnj+eT=`sK({*YmDoT4YZI6f5RI%)b1!S#Jwu_pA6L=|< zIjY^P`6D?493uSVGZQ4bdh6F&nT?K!ClQvcEoCW`(IH+`j0WGQ^$&-U=}MW-&8;xo zt5n6M0ge!{hUT+x0&~Nbwe@3U_$wnhw>ruS?U}gbsGs%aau)P58yNfBSspo9iF!|C z9=Yy)P*9Kwd$6m!yGm&p`Y^~8^%Gg#2z;dL3I z!SFAkCAM5?krT=H)aM3Gn+Z6CRJ@raVh;KS5(Qq*E#kV zi(r*mJT!}{#U$SgNX)z$9^6Mx$2Gs1HY5y$s{KzMTeY?0Jl!%; zp^ThytCeB=@)O38aJ4V6KN&ELbD4>hw9(7?%4?sTbzVG<`!zM-i&4RNcj( zRMg!lHwsJC9Y_I4uNc@a)bI#cUlA(PK?0{3&E)||x)_^!ziw%`>(>!KB^V7U$FC(2 zoP~(O5b6TdjEJdLX8T1GBDgrJBd9u|Z`MdVOCDgZ8Q5yrpKjgr^t;)}pBZ?^ZMAzI zOTu-E?iYpR%e%Pcx|i`~_>~;<4_mL|UPY|bo(I-Xzgp(Is?*Kyk)KE1X&db;tx6f? zJ4ZQL&^M1m!48eP(LWOhm+pXq1DXv4U9JTuG zcxbsKA)a*2CgOq6V8%P9$)Qi+_#0oN2--7|j&>2K5bw- ze;fD0?3`caERFBM<#JzFou2EE+@EE}h0~Vz?#;pDjRA-NIA8%RX1W;6N1=>?j{E&x q)xUkp|6BO~VTr~V5;$>|&b literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/keyCap_w.png b/interface/resources/meshes/keyboard/keyCap_w.png new file mode 100644 index 0000000000000000000000000000000000000000..a71de5124d9c153c076ceaa22550a6617a37b514 GIT binary patch literal 4149 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 Ypx$t3IWFmu2LJ#BQG)&JHpQ_14`e)Sg#Z8m literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/keyCap_y.png b/interface/resources/meshes/keyboard/keyCap_y.png new file mode 100644 index 0000000000000000000000000000000000000000..ed68e21384a58128e8b737f998abf650aa2b6bb1 GIT binary patch literal 2701 zcmeHJZB)}`6u+B)Sw%`f z2qZWHa|((nBB2Zhlv5XOqy!iWsB0q@o5afod+Cghed!yVPG9=)oO{o6p5J}${oQ+> z-}7m3kgtWgjX3~V`1^V90KkK3p(c}V<8=uDmZ$jaObJPdNlA@Nj)pB!3HzeS{)Z!D zqjy9{MlqA_L~jN#clP(*@_yPw<)cBmU8wD?L3a^e5y4$pF;58;%sgUU@H*)wpzNeb z7kYT=AQ+bkhv)5a~v?~RB4&3WKdUJfOw0RN6I-&bRF8Q!F zpW7bk@#ITwj37LnCtbtCsXgO%HTC!PrF^92@dj=P1mpIth_Lf3iCngm$?~E#>v%zY zYNk%T(6qfaw@(++S8IVwO-(&zM1fUdt6!SyqaoLqC0r1T#R0^#vg9E#6$O!MYHAa; z=SVENMr`~EbNMVZH1zQ@6dYDjA0LTA*KiH0{uZv~j;)nW2_Mp+BB-wSkZWnSWdVDr z`6aQ@_2r`c+k5ahq1CrserN{6K+$p_G5IdyQXbT9rtRB~AC^5 z!O>R4Y0X*FUNpn!vWBB$A_Uy``tdtfkr{?!Ws$5!W!R6$Y@9gvf3*r0kM?(x7K3he z3!O&sqG?yP|MKvCI{lQ<@T3F=R6a@0IhbzO-tomajD5SoyQrR-$^Y1rXJ;D{*K1fn z;A4tPU3=F#N$r?y^Q$GGC{5NZ+>R7wZ`g=D6SFBTE;!-M(TvCr3*6u++hy6s?)ZMa z2muvwb-ToGg$rIK(j=ji$KBT>+0=q^MhQeX)O1ycc+oN^ueko~F|{9$ zm1jHj4X?y+^2o;sSXEc~3}qftjDQuo@g#+uw6NikL@l*6kVqow-PhxU#I|+kimE0a zJqpvf?PW0Ju}1yt*3gqqXxZsl;QL);qyP0+o=viN9Whm%w|R?jnBa|KaTL17R~?0- zD(xB7LBI{9$VAes*;FO@jI&X!c~Zke!PaSWZnE0S%E=ht*#Fjs)&@*bs3tm^Dcd@n z{jcr5)K;^HLjZ%uK04XDV_puXzW>zq>P!j8vK!2M;?dg)0RxrB5~z5SPG24Wn;-{M zpUWF;!rOx;kCT=ho++B?R%}vlY#;>lla)ae$A9-gvc*Q%b2D+a=3vj$SnB%aZ#OxF zp5|@92rxluv)^dpq}Led8yXV&3rLcC&VeAksQv(_DzCCkUGbfQY7YnYx*{y5(UF}t zLDgLlB0y-Bf<#8op*PcS(i6ORYdHk4u^xFTeFgq5wV;Z<+}l>a9w&708|g8~t-jctRn8de zY%Io}CZ?ySGnq`am3n-*``H=Q;Njs`1oG7{8(NjRNc~aMgKzT5oe%c}Hu5eJ1M}Ql4eM_$|HO(50Yoa%d zxx|+p2vW~GwY4kV_RE*rgQBuQJiVbomuecbm0f+!E;GmZ?hVwMq33u6?5aA?f0_|l z7#bda5(}Z()^)_V5%Y%i?<$T7oDM#JxDSs7)l{!-qzfg!t>k(|~FM`Ok$ zX)%m>dB$~*A52_sZ>pXq`CRl=E|I$K_BEgq^TWDaG*6 zBV~YsmFGG!SWyu_l(Dv4GGrCmiNF{vnzVsKz%8~x_Z{J#mv)|9=O6FA&-1?T=kt7? z&-0#oCwBYtr7M>L!1AcKX}bXI0bN*hJGwHW9soRYBNKAt53+LeQ*)RQo_;WmNr+;n zW-xa#Q`0%wx0qxAtR;#TzPsR_e&FZq%X?h;sTX_gcW?Xk*wqdpvf zQpdSFB9SPj$259)qraWq^ZlF-UZ)wF&oE@x8&2v&TP`ZKBXN|XLVJEuNKNb!UY25` zcZ*Bspdk^5f>$*5ny2w|N=1Jvb(Qr|YRwcZ(0; zyU0q6_(1s%s^P=By4Hpri4wKjB_PxaGhOoMq(k`o`y5ROSk7|$;#h{#IKX#rDa%Xt z^-r@UU1X(P6HsJmLpmfhApA1UaQ)$-1A*yW%oxA4gKEggOZFX~#fY&W>uF*Svnf^E z!`1>mcF>3s>rZ-sCCgL(*?LswT>*1YIaydOJQq2yy4TwCig?X*v>yu$noP>6HXh4R z3e+tF^kE;2nD)C1zAYFRF;hIdoa+o&*7-gzio>$bYs1_vF4Z2em0od0LbQjrc={a@ zZlT=`-&qUf#8oMnj`q`flJh7Q$>sl=1eF`Kx_PsO~Hr_PA!NmIYGy9s*(^*2-Q!~GDpIV z>iqWczL1FIE4)GfE}ec2$6fo->+Gr=hmuvg2G3XUa73%oNW;Mnr~r5X1fCWy+y;+b zi0EM7xPF}+78c@Yn45fy#IMP5!2TEZ>5NaDE*kD>|5)M^#Ls`a(BaF0Ba64-*~5Fj jmYzBL{}pb!mv4uG?vkT|L)3sq0D!2-SX#rjl;eK`%4um; literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_0.png b/interface/resources/meshes/keyboard/key_0.png new file mode 100644 index 0000000000000000000000000000000000000000..ff852cda9dbc378f423851fc44298a9595a6d1d6 GIT binary patch literal 3132 zcmc(gYfuvC8pmG{)AiEP)I6EHaLP?nldQZU36|PwNv7p}H>E^t3C#=P70k+XQ_fj4 zDpM;fn#>F@*;y}OUOH;X8k&)UXcsd?F_eW6*r}QQaK7e;op;`Oo-e<5{_}fgp0_9j zx5w1T#s~ml8u)`>C;(9K`5JyM**3fD008UK{Ug)Ek_qXj;!_iV@6lv@0wVB4d}2aq zLj2LplYI$j0E|U}e!d4OuV#hP0~?~&({L!W*1_ZW&v+>63XM#3jFwWDu1)^_jtK z&CzwL9MWc*6}Wf;RY`6Y=5n=T%iW>ZIW=4akp0YHFj&&+X;ogKx!(Esm*4t*E714b z(sM1QSvuDlxAn@t9Jfda=jm0Ajv|6v>B*%CAY&1zrdSZy;(|mv<#oHAvo2T+K44r5 z30d#)A@Xn~&zHHF@p5wt6D<=6eV*ETW)A zME50|G!xHr($<>(S?J9Vb8~aQ2}!{Q>{u5e$`V5JK%r2MIdb+;Qd3o~cG*j-QYs(k zg)4CtghrK2Cfav|(IXNTaka!j@*T!x2^!8Q{O$w4YQt63H3I64XRn)Y!w38<% zYrBm=-tK2lSgOOrv1pqT{DwR((!1AbEv3`94etzHzG-Lm2C;R-YHaZAGcBu~9bJca zUFeVU)DpK{ab^tcB1kF53jsM5kr1q)LRX1P^r>c_aHK~d-;nuhA(0SH@?{)zrjc4d zx>xXTx6gi+aVn%hNwZP~E)B+4ut$Q+|CeLtvmoo8qAc)#o~ zSK2L*gVcR^GjN>9inR0TO4{KlV`h0*;?VI^WzPuPC!EXi)@J2p)2hx+->IDTSQlgX zp;jR-is5N_!3r$zx|A?ep*u@=+%(tCEID(F?PdlE#W0b9fV1Zb_ zNd1&frw^=4oH}-AwH^Y5JbXU*U~}CL1c-!OWW{TEwgn@fgGTHBi|F_vW*$YbL``rK{ z-k8b=HUr1+2iT7?$9YfPV1^(E`PQYvEgJIpiUSn*JIb|2r0F7n?Oc=mKKxZ%n}DcL zy88l3;hIyWj7QcE3*@R&2m;93yVbJ{a>d) z4(Qh|a0y{ge!A{FpjcYv3n)As4j1~JPX(8}^GOe$_0+>q|I^~);+wGRD7;07ki=F^ zatZRu$@o)U1M+3D81Z&pcx|~q2nVv$muer@ZD%r>oBzSiepJKNb?@b@nXs`Oa0QO`xtMHX5|T0SWX}kx#X?Ue z9rK9DkJkqn%dn&MqkCgv+bp5IRL}*O+=$S&ETYyM(5yfk`MK@t!Wmv;dy~?%jB+UmTLYmBQ4Fs%L{|i^(Ek0BXFu2HGFtB zSq^ss6!iv#=3}jO2CDKhqlL}Oqf+6>re7q572J2no7Vss5N&wWuh!%R8(^w=7RciT zLYDFQdy<{oQy)Ub8yyu%DPFef9wShEW#6vZ}1Fn{gIkJ3nc(IO?M7f%?%}p~w*iKKzR(C$+?g>#B?o4r}(@ zuq}mzYm8Zz)^{3#pyr?I)f%k} zY3-s7myqUD-4ea!Ff1H|Ji43{Q9IJpEus1Mq{ zbVMHUnG{@o$4$<97W_u%`W=OJhuIol79741CpBz2V(1SPM zNg1p3YmsaF`U00nztI|k;pbWHr6%JO6Ukb|RJB`m4?#OQ8PDXi+UWquR(lzmtnedU zG42Be5P${13;-DTs`l^2VJi#2VjMtLpze$DuQ4xfS^k~_Vs4-LBe!s>9{>OX{c(N| IcEz3k6MG5?0ssI2 literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_1.png b/interface/resources/meshes/keyboard/key_1.png new file mode 100644 index 0000000000000000000000000000000000000000..7115e92be8b2bdd84f739daefd0f55ed8623ff6d GIT binary patch literal 1909 zcmeAS@N?(olHy`uVBq!ia0y~yU}6Aa4iHr><-C@GfkCpwHKHUqKdq!Zu_%?nF(p4K zRlzeiF+DXXH8G{K@MNkD0|UFbr;B4q#jQ7YFXldS5OKJeI+aO-QR~1u28~881JM8` zXI2MRGg$}rcM9(qra$1DBlvrA|I{!g5AJ!z|JJUFTT-6xVeR7GW@&Op_?oE`UfjZ7&aVc(`9L^}THK z=f{(C5|TZE_rEW5Q2J!^-;<$icPz(22}z#y4IW3+Hvc^Njh(UK`{mNRSzA}FI;HWz zdOoAUpSSyC^ydCO$lI{xN~!H!zubBOhD$UOSgH6_^B8!-ua_#eye!H3-7L+(X(`wL`|rK>y$TE}C9n3!=oNn5 zo|`SDz@Va-_h;2AzvYvI?Kv1c=X{^NY~|};ReOK+$0{%^QEJ(6|NU9Ud8hpu896|i zvw?v@fq?X)P!Kuo=+*a6`9UuG zSRupzZ~pxG+w4I+`}{x2%sotZ7(6n*-p|{d%_GA0OAfp0%v<$H$X*nfcdUf4xYf=68juyxRfk%-h@6z5f5> z>SA`eIp?46E8nBAzJd6}RAE;y&{n=FLg&xshmn6gJsMmpSy>Q4jTGv{Gbz^YM!6BN zv_h6i6ndb6GTSNFu7HtcNc9q0jw4q4|8J@X7!2DsT@_%<6=h&xVDNPHb6Mw<&;$U< Cyhs-S literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_123.png b/interface/resources/meshes/keyboard/key_123.png new file mode 100644 index 0000000000000000000000000000000000000000..07a3187a70ec9b39052d4d5d7445c9c8c75a7613 GIT binary patch literal 4399 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 Fe*p@B`1k+- literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_2.png b/interface/resources/meshes/keyboard/key_2.png new file mode 100644 index 0000000000000000000000000000000000000000..99a7e650f1431b60189211951446818a086a3b61 GIT binary patch literal 2864 zcmc&#X;hO}8h#Vmj37&)ATC5uJ4ZrUMyV5)MA=7CVXqF7!1fBV1S{3k%A)0FbK*b|(h1GCqw8WhBt9(&2bWR4^UyafKE} zKS!s9B*pa5?E!F4Je-evB|e`M3uB(U12R7O3Rk2u4TphfT&t<3Coy?Ud_?%c>UteF}j*Kp{od6G@u^|V6O~K!m zj%-c**6H{ySof3Tn|A*Fg}slP%(O*1V1I4_AM^|x8KvlV&M*%5+N>)^`B7aH(sEYC z7|IUxtE4WwtYkZNd2h7d-0z}0&6o=^nZBA_X)IygKYaY2;NlTnON^H;xO*8*o9x@S z&%}iLjw#aCw$|8X#Ob=;)>`Ze_~IB=gP3Y(A9tBQS~EiEXjRk2LW}272|JLaAnkQk z=KN)CEhP+tK{&C*eA~Rf|{GNh~sT_;n)C!*c4)T@UM*t2?@D9li9na<1Mwmgcr=k zpnCu0kR#ixRh?%A)9;!Mt*M;Q9W|)cO0(Q1!rf)Xyk>;(f}=D1VZRv`!ggFpFzOlz zC$E?%6`;#&38Ye%W1(9kR#+Eh6KfWM5o$)YSpVx$@}8Idy?vn{yw2RID5j|b>NP*L z{)S_5yH^}(__}$mCnOl%hd~Y)FOWuUhE7vt(q!8_mmUv_em@=yhMqmF)#shzGTuaG z<9M#aV=WAFAngy#?p98za!sP2oFz@1t1^R}H2Z!UlQ$0wl_=|@LQ>$$c17`i(5b<=P*T5BYl7&1L|}woCh|9x4i46(6Zy`TzxI%;x4Z%JcM(@=Q&ObZIR6{Nk`L~s301~Gd{ zam)9No~k+AqZrBBJCtwy6oFc5*ZGaDpaRVDcd5+*Jj*dDi@wNqty1mq9F4He>NJ(u zrEt}68p}6cjA&kg&d1vgFpG3WYX7m=DQ`Cvx+$$*4=jpG$EvW%~`BSTX%Bvoacp%eNs=Qle+ur&tE4?UOH*&=o2-0cfqzmPz$;BXZM2-Q zA-)1G-k6X+n_-lsdaZRvDKiC!L+bsBHD&EbLC-+?9F?X!;grEUGMumQ{nRlkNM0;4 ze>P=g2Oi3}!=E&6!;&AJQXYpo3WvkliPZoqAQ1ig*~A$$Je2ht1nB^%fH{x>HV|U| zMkbrYBM_Y>+J^h_bJEE(6y)5ICW_Ey9y;~M@yd_|2VR;=9pxbcDgc=G)J?$6aDLSL zQ&pAL{lA?4V&zxEU%l~XfBx@~TcndWy#Fm7)Y)3~P1BLUwUY=2t{hU{x|wmT3;@8x M<+Ss+CxSlv0qVJa4gdfE literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_3.png b/interface/resources/meshes/keyboard/key_3.png new file mode 100644 index 0000000000000000000000000000000000000000..5ec80db3e01555227ff2b9b95886f333a519f00d GIT binary patch literal 3341 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 YK$iZ&p3I@_5dZ+U9Ujhio&3-I4|zN9)Bpeg literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_6.png b/interface/resources/meshes/keyboard/key_6.png new file mode 100644 index 0000000000000000000000000000000000000000..bf4e81a7a1171dfa395e69f9b845ef44c11ea632 GIT binary patch literal 3631 zcmb_ddol}eWtOGYw9<&v0jpP3vhD-?@lHSFY4 zL=>_kql;#xP*Xd?m_f{HTt*sWF!Os)y{B{by#Kv=|M;Hgb3W%h-{<)}-|sn}6!!z) zFI%j)7yy>J{;>Nn02)3Y>}QD8+jR#3bR+h7MjQz}6+u1`P6X$GP=BJaYw(HF#KXiB z0nz7Mi8}z6y>i{{d^DPbi;;yV|8xw6btuX$2@GW8D(b}*-W17)Q(CHsqYzycx|VJamuWc zN~H-);*MsunhqzpA-DTB=3uYVva&SH@K9L9oY&UY&dtr8%v~?PgvAFG@+ankO+f7B zf*f(pyzFA4iw8jg(aT<6E!AMiI*(RfULI0^R>K&?Y7UCTM}?11%9U>h>FGu_rel?$ zE~LT7(7@m=YY;i?h6Lh&ZFVWrid%7+l<&6op>Ne94vuhX_b)#`DH^+|1?S>t)m0Kz z67Gi7JLJFASKA4K3;U5?g+d{b)T(p_sG&~GhPH$^j7nl8K52_RAnrS_90LP`+5yH- zs>UFWmR4-0J`2ZQvTwD%7sNfEZQ=1G!(%mcqHSDPg|`S@YSuY8INXy3>LpVHIXb~@ zMF`>g%TrAzE{rHc7`wtBJ+W${k;Ouys7@50%Y1t@EVI>DE8qT!Zt6OkLf&4WBcGJ! zB0KH;8hrYCdtV5rN)Ilxda`~^pm8!akaq2{s;cS`zqDs8-Gz}B(xYp0&224bh3s zG=bZcY>iGDDLVSy{SjnHm`L!AzQ8fE0t+TXKGsw3Uv=lh14N{f_^2(CI+CFUze;9p za*n;{&c6{aB2;2Nw$Q$G!^6VdM`JxxTh{2gBf`rKMstctMc`eu0P~ZRh4^uoSyi-> z@F2xb9%N8|v9PFw6GS`KqFE^vbR2Qb^f`X~4)-j_2t{2S;N>CsG3a_z+ay^kD@ywf zQ<&mW{EAX~)BK4t>t`)k@1wkXP?ISY_V@R%LSB~bQrwDEYnd*1Wr zt~E^N+>lUL*<5QmlpUY^<_j zqlBpe)%kRyS9Nl}keNFR18K&`e%=LMW(H6b5 zKGD9@?po%wpd5d5@HM>EK{wr`uVzJ85Dc42?RA#=7l)Hbb!k#nlwgx3sc`t+{wRMo zOI)y+4>kcZnp==_dgtIATRw)H%4i zkl^%txyqDm$6xw6Xe_*0uihNHA1U2O6I4KOzooI>G~xYlE+mOWqApcRV>l8Yy>_Dq zxYLK-5Nma9V@Zso2r9tu-Zn$Vz2+q+$;==OcRN@?*)9yZ{tjk21WNU--%%M1lYA`{ zb<6%2>eEP|6P;dA@U8|6aV|okuw^_H)iX@$j5{pD1PpY{dq0(Fch=e6QcZm1Umdnn z2Sp`om^|Q57#SKih2w-n;kvrl4wPycX8d%=qgqWTgKdvW8?bfPyU7&=2N~^P(Jr_l z^(<2)#5svBoSyz`swGfU2s1OW@@kR66Eb528R~cTb%UoOgIEoZFLW+H+0OFc~%f-0WuP`RM4NRjc8y zmW{lAHoN@YIgeraG4*q6mB8|^3F*G^BIHoQ@t{0%m?joQ{gCFuh_O8~Jn*}JhocIL z8vJsod+%TmU|~l+i$xe7N={CWd{!dPqe^1N>;MgjIm_m>jZHeo?#>h^M|K`f1w64X ze!RY0naz&vJv}$$-LLF z0?Pbu)Kz*OK%0R0w?Ru|z~Yx^_Fd&}Q-T|!i0v)DGaI;XHPxEO$18)=0Kb{}OhWLQ z*y@CaA1&hyXC_!krHql&P<&8SN!|&C>7cnPUBW+c=f!?UZ}LM8G(=iVXIDyMj!(r; zx6N-gr>%gQq{l*9DJLk{DBLzr(`ih9pFRo?0`0|o;wDn-SqB^M;a5!(el1#wO8UW2 zGj=R&Yem0?OVD$lh@(*{7&LWH^I1l8&*?<2D6;j1C(0Pa-fT8IiQu5Kv~ITe?$p$j zt?G6E^Mfmml_0^&C7_>yw<%ztyl4HgTHg;03_R>sK{uC%g@+%WsrAYUW}w=JYUa27 zoWKelTm#}{XRl#VO?A0%FvfpY8uKtyHTSh$B+3E}6nDkyiC0txgR!?=^{IC@U165H z*}dUE>(P#LrnbAM$4a)gnjIe*`Qyyve4SaLP-vDhCmh)1Lm>22c%qu~PmyIN!-uYR zMH?+uQQp8yF+dU%6Ze)-bkbbv{^bF8AI+q%@OeutxSExfm4)+7%Hw;Ky&t;KaWiOp z{>yXjN?;#GB7Jx7L3h%g%YzeqmN_n77QN}!%+&C3$;Yb9vC+|W-z_Pntbi`J(G2#Q z!nX$rx*6=!(o%b~&x_*tN8c*$YxN0%dfJfb#`T?JB=0{|3kZn;OZ=6gSYKcN4I<#% zl1QY=sw$pL)e0=m+UM7b4*fAn+fPvf%PXd?qwe*^C}LB*3K}|E)FQ18ls)@I=4`p% zu?@OUJJF9tX3?K-8_&>!bB7X+6sQ<3`K2Lr)&%ZK4lzB~>1{s4vD++P0cE=q4m?=I zSHD#I=cqGt!(p@#0w2oad`C~wv;UUP4Ni9b{XGccE4ZT6)YP`Nw(T8D$xNP(JY8lC z;uKr)v!1<)famG{S+ma8T^4VWX`0e7(a?kiJKmYvUsle~OfuffKnwrcwV^o w{|6nv;N-pr{uLKK$G5)00)Bw~M;uB8##Uz2TD*#O0sy$~Ik3BOr{BeY0~vZD?f?J) literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_7.png b/interface/resources/meshes/keyboard/key_7.png new file mode 100644 index 0000000000000000000000000000000000000000..5d9765b37e87c4926961c38bc4d514643e8eab52 GIT binary patch literal 1921 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 CDBSe` literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_9.png b/interface/resources/meshes/keyboard/key_9.png new file mode 100644 index 0000000000000000000000000000000000000000..89a6397c8210e9b324bd21f6bb13e2482927de97 GIT binary patch literal 3617 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 literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_a.png b/interface/resources/meshes/keyboard/key_a.png new file mode 100644 index 0000000000000000000000000000000000000000..74d57d5bd4b1bb2b884a76011c21cb117ad612bd GIT binary patch literal 3066 zcmd^>Sy0ni7RFCvz_5s*En+~4(qgNa78*jJfdoY`vIK1rM8%cG7(tP+1(GOJW@Hfy zw3PycmKLnBfu^I-VaEc3Mr2SB0tRH86cQ335FnELr|c>o=LH}7VIFRM_vLr)t?%AC z=eyGJ$6!TM5SWMj6=umLK5OppTPrZCL zJoHHD*$W9*UxvB>801ks+>gfh$s|LU`{?G}A=MLP!pGmvy$y3+uW&ua8lThn5<|5H zYithw!i_9F{QA3ojS2sF{Dhus-$Lvit!7)yB{}Q3S-Do7?(ek>eA>Ml1_}@c8c;w1 zFb2TDinf0*j`_OFe>D!Mudwbf$5-zB@`Ekw3M+jjZv4N$O<}5=7$d6_Gb&G}k!xoP zo(GpMg!9EdS^!`*HS5gNZQwut zi{*NbT@HgdecHs}Mr2k0c+7AC(E$wswzv$rTz*a-HB|QqQx_7o*Q|NC@nHs0gIbp3ujGf0>!uR{iu!qm|6=Lo1>18jEwYfB6N?zl(nsTH|w=@dj9~!lSdn3{I8}kY(oezwalUn8;=Chguh}=zfj1wqlfz87Kp|9kYt>8#+wgbx zdy-E7IAA~EEKN&7w2~ayB?_P%J@tD2{!Nxl(IHj#iU=bIy=xh@Ol{(#t_k#hP&DYZ z$?LAMSSLQYt0E|GyblXUD#|loMPvV&7+?Q`N7lEUDSSSEvQ`l-NUwUM90@78QGw>7 zO;KXof>K}PH^-DFe8=vt;jq{s6y>q6b#}f>;`Uf%t&tH?J5i?dLLX-Ox>KsoncR_< zN~~}wE^Z>m8O?=8)-$mp)d5i4`7J=hQlPMP=H#p6HD0+}feUy5Sw4-3V#(&EbxbZUJRYb76sxbzEFrLRsbO z$X?^}Y}Yl#oe>^-&iQ_(y5^fuT>7LU)3azg{$y)_N?g?RN3K(NrWwD<1bXWpys0Rt ze|}<;_MQ=of!Nv}4x26Cx!WIm|N0U0o}L~ak5^0%LQU1jE;X<R z!`@ouFP}@y_ml3S&1x1Ka!OrYTzst8dmF2Ih28Yf-Tpf()(%8gDKoY9zRC~Hh?DNf zH0jWt9S5hW1Og$6e>CG79KAYE?l>Jq%pI&xHaFremT*+`z`#J|Vr@5lk!}p%bw=2~ z;+IP%V-*U8WY`kJRSN_H@q$8?Sl8cwA}&f7RmlE>U!JRx$bvF1W6_j7V}gm9E2L+N zwP?yIR~HwCYGaM0Y($HPuHIJii046W zb)X!N9@rnV3m=P(eOQk#XESA-SaESR&G#V zGFL1)&=35w~?j#p0Is#c%UAMPY%l-v==h_6vyiH(9a;WUg)(^EM8{O;*(gh76R&5!Hhv zv3W*o*=^tJFtsI=my+5}NuROz<#!@9+OIF)Zzt$Ks{dekyE@p$)X~w=d{Fd5SaMar ztE+1}xyh^Oa8j1zz0;Sf!qTX> zrPXI3vzOv;yVrI~I|}%5w5;Z66bjDF=dD`l^jsdg%eulWWemA1MVC)p))gkQuf+dP egV4eb#9%Mj<;D5XNV5q50MrA19%cK^-1slYRmbE2 literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_abc.png b/interface/resources/meshes/keyboard/key_abc.png new file mode 100644 index 0000000000000000000000000000000000000000..5b7f1bcb0fac2bbec87755d94c1c430b2a6cde6c GIT binary patch literal 5346 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 literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_b.png b/interface/resources/meshes/keyboard/key_b.png new file mode 100644 index 0000000000000000000000000000000000000000..50b607cbd3f6e5e6bec11845cca09a68200da6ec GIT binary patch literal 2839 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 KT-G@yGywpG#*C~0 literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_c.png b/interface/resources/meshes/keyboard/key_c.png new file mode 100644 index 0000000000000000000000000000000000000000..da8a4a4f2d531742844622ed80c5de4646949f6f GIT binary patch literal 2950 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 Jm$nEi{{oXY0UQ7T literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_close_paren.png b/interface/resources/meshes/keyboard/key_close_paren.png new file mode 100644 index 0000000000000000000000000000000000000000..6a93b0fe05348fdf8344a6162784517cdaea991d GIT binary patch literal 2621 zcmdT`ZA?>F7=G{N0xgJEtFR%$MUmha7Ly@k0(SxyBH&O_6qOdK1huG$2rJMQu^>3w zxCDzhi63E;34Tn`QKeYL&LQGt+CstN5VgXf3IY{-yIh@#Ci^k<$Fd(M=gm3qbIx;~ zJmD9W`+Hw5{Lmf*mHkHxbymJbA8oN`ii5&8bHJ~5@ zzyW9!tM?B-1oPHb9|@;@0OPlI|7bX%A0gc5M?Y-tQO+p*BP(!;Xv{j2b>=hb7#ktSij<~ zbj3Nb9B}Rx)@;Wj?dtyBTvU`7$u?FbCv0*+sMIdMs9>V^vTHDSLRf6{#2@d>tQjK7 zdo~HEC?X$%#)|1MZwlH@ug5?FEJWlE5x}7V6FgX0o}RAlANFN1!Q)2JWe$ojTQ|mr z2_`#B$Pezogkx2emD-)CXP~15h&EO8uG&d^foOYDTb$j?b72Pq3iN>mUNkV(DilR? zdgHYOrA8K)1_;i+A9q>ZKq*&n~GcY(P@jC4g7D@$+2?=0udqoLv{A^plU`@QYlY*!df<|+CH@TIC01h(dN-+b%;zpdr z6d_;$U;qdZ(Amta+dr9{9zC)>WBlHW7Pv^0&|gn+?yBO49h8AK)SE-(GV9|4y~d%S z)Jxrh672Pt1WCgTN&u&T4l9o9MO)&UWtbh0`jt2HR<#ikAUkHv?w-0uK z?D!UaU4!Gj_I7Om2B`<39ogX9j(M{GwwZ9A9O?~0G&uIqV3<*392&aY=MEec5YaIg zsal&~UwFhg#tQ|yeZ1v(Pg|cm$c8XR++!Qc5Sw(qS!OjR0z$RMf*?5i3hM%Cg%ZVW zHQ&rhGutAfvs1?AYZtPG7~Bptj}uyT_VwMgnF_KFgK$;JI#>jFOtwB>NOI4uZhd7a zegBnBWfiBzH1J4vQ5y$>T7FY{*$E1O*q5$Yn^C1x-jL7D^|7>A8WP5jn)16-GyT8o zb@zm@=Yv7%gUZxVCnk9C{ms$fDF$i~-aQ)5Fs;bH-CR3|x0xI&T()1YHnl`Mbe7RJ!L*3<5Q<-C@GfkCpwHKHUqKdq!Zu_%?nF(p4K zRlzeiF+DXXH8G{K@MNkD0|OhEr;B4q#jQ7Y4JWcFinv~UyH>D)MMZ&eiXtz!$H9a5 z3k{sNTs>>~(?9OQ_O2!CJAN=eu-UK1Ai%)D!oa}D;J`o@b6~YQQ092bwF^)XbdV4y zJLF*z2BXO|i9+oRj0BRvgZjNb2RN@q6`xx>ImpZFWC;huo!h@(-;cU&C7FCB%J+D4 zB@+`lspx@q9oKomqH}w{-xc2dF(=$5g7JaFyatL=GtLC@?m;S(--c_miu2}92@3LB z=FITm-o5z$*KS=qDY5ukmX+KYVSxq)1_cHN4p8PLo_YUca7EvQ;&*%B&%4o^zFX-K z7X$kz78cSCLa~%$?W5%ax%mm64Jpjn|Bpq2;k~KmZT~0xDj66U7(8A5T-G@y GGywn=Zk|K{ literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_comma.png b/interface/resources/meshes/keyboard/key_comma.png new file mode 100644 index 0000000000000000000000000000000000000000..69da507ec60038d15f5183d3bff4c588e0f70135 GIT binary patch literal 1785 zcmeAS@N?(olHy`uVBq!ia0y~yU}6Aa4iHr><-C@GfkCpwHKHUqKdq!Zu_%?nF(p4K zRlzeiF+DXXH8G{K@MNkD0|VP^PZ!6Kid%1PAIv>uAkh%GLP8?|>DDiB5-|^&s zlLeY~Dl!BtD3tlEFT=+kyeQ}ObAQF)1!k8`w&u>RT$nlQ_~XQY8;lO6_W%FeC@?WL z)W0m+`6RyJpEIuvW8mVudE&`2Rj>ZATQg_`dueX|wSLZj1%`lCe_s7Bv6}0{$=Ja3 zG{3=_qxSx6AtnaaQ}-FH|1Xr&Yg~D*Y`3qbuI=8q^|9Ay3Y9Sg%#uh>`^v8v@mG*R zBlsWFfuHmdKI;Vst04nX{djJ3c literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_d.png b/interface/resources/meshes/keyboard/key_d.png new file mode 100644 index 0000000000000000000000000000000000000000..557f3816f070ce467eca46e65e5bf8ce64f86204 GIT binary patch literal 2962 zcmc&$Sx}Q{7QKHGFb09N+W>6lk^G5TgyUCAKI64KWI%AVd^HR3HI?1Og;obQRU}IE4@W@YOjl=exJgt-9x) z@%H>)pI|}&0DbpuEe@hSQ|HgZRU|-w20#SB!JO!ya#YmEOhrYq;o;cYpa?Bzg862Um1Tb0zd7YQ z=*eE7j>?m~eY>95Ox5yA#2*9Zx3;!A7GxNa;4atyW_!G2fk-CU+M(r3{YNj0BVBY8 zYlEhG&d1qzVMBtSyiQ6^mN3W`*^Tquv|2xN^1@4>S=Gb*uya83*h--kGN%>og{rd` zU2;O7X8*>WO`JE$8jjvKH;YJ(cK(6B_Xza^sx=O@DP5S}cHBsFsJ+B>RvHtr*MR48Z zDvFJ544WO%_vp&Ry$*({PIR35ErYBlP;-wQ*<`dp^5rUT{G;Xbc*nJ41}-j<<$>c> zKVwyJUHpk#zLJ?gMx|0kbiQ4k%*s9MU5t@n;iJ<4uzR6UZ7%S1 zKW3zVx@!C?5iF06inx;%;>ZQMCwx>JKKm%YM%Wum+su!U^TZ{3Yo;X?} zwp7HfB!YpfhHVuT6yz^>V_{)Yw;2a42kf@2`Y;C)4E&VOMM55V62Tx|x5sAz9_BSI z033=9PazXQ>7!Y7xf{_%dT7``R;5rAqhp3cu~GsB4;*ieT_Xy@pe8|%DINygO2kAU z0*|2?EL1!%~ZU>%cmx1I8s(Wka7JS^^cQ^9noAU+1r|F*KZnA<`RPt(X`vXj=7JoCm(Gb*Py7ZG~#c)XL0 z2uji4k_+qht~+NkooOAK4npqeL_qX=%G# z7{8pk+`<(u|L86(yQ1VAq!bCHww~Kfs=DQ`%7U9U2g?{4afyl1Yge+K`O@09rHO4; z=dh!@Ivpt#jY_>}_)2DGrk9u3j&q+C&kb}>G-=X|ap>;m)(%6NHUM6);Gx%f_I4s| z>LV2H@}Bm_wr(o6;;)~aj}azs%le>H{vxzE{;wC&4~lozt;d1EK}I^Ih0a$85OCV#c$04 zLE}=ppX@Im*?dQ^Rki-ZAuHlanRyJ}DXf6+4Hli5QTcjV1RXy~CC7k&5={ zty{L#C{>N&w?gM(KpmpPkZ- zpSHWxwsf)t!Gm9^IP!q8rP9N;ykq!izW%hLlQFP;jCYM>*z7doUK*9iW>@T8M^!qe z7^yZFv`VGntpdfqvRB!SANueO0)ZRa~eHjrf-O_4y>BRq-KaN-1eRrjFI1R003}z^>n%OUC8m@0g|(h APyhe` literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_dollar.png b/interface/resources/meshes/keyboard/key_dollar.png new file mode 100644 index 0000000000000000000000000000000000000000..0105debb68db7fd3bd1c3d209956013ed76c3b52 GIT binary patch literal 3481 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 literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_e.png b/interface/resources/meshes/keyboard/key_e.png new file mode 100644 index 0000000000000000000000000000000000000000..1b356d9d5b4e2e207f7027410a16925164a01876 GIT binary patch literal 3029 zcmc&$Sx}Q%8ohrA5)ergP{55vK@o(g7&alu;($m55hKbfF~}AI$P$pmwrqq5Xlr*s zgy7aGjch7Si2`AhfeZwsK-oG1VptQ95Wpm4imvI0d6=m#dFZ-z>$@-K)>r4d_neb? z((Bj;H9a)|zy|k!xcC471FIXcT2l3#O8@}639k5rQ>2RtNg;6r;1os*CFr|HhY$%q zgpjZlax1|efb~i4E>6D5{nJCkr$@$ja6)Q`b-3r)=fTCW6UtcSqkpV$Aa{mlP4us`c}-n2$I>VH3*;;fZ&)g}9^hj~1~9I|^ttdJM> z)6KY(3)SX70?v-yC3J5~v138|2vJ5ZVH{I3T*qRuh(scV(-OZVlSS>Dtv6$ETf^e0 zR7IO7HK4*cywbn@;=%O(1_bc!NVc!7sWB%HDhR5!g&ob!u4Wohv2b9D79%qn^Q5Gi z?M6xr2IBw?a7^^R5dcKnUf{)&r&BM7@H7Z#6Uk(>j{5XQI5=FoCo69uC?Mdpz^G?x zp`c*vmh}dp+zCA$+?8vDVL51kXQAA(6cwM3)nx}(9Lxlr348iQ1{+UGerYGuh z27`eXmq{D&YGSF}=fEvOUuZ?FH|pn+Z9v;d~;u?Ynner<9}v^vKhjLx68 z$JuPwBKw+up)^0UmE)~Ni!$g&TPYiC2OHb zbSkj6ogL|i(V{GJNhH!t3>>uOZ>jPxG2iKH+RK z&*fUOx0aWZK8U@wgeolysuO;`V6xv1P8<5xXk4teDWjn;>adQwgxobM>_h-R;J%MG zL9daPrBf&`ruqyrXqH;SNC_5LYQbQD256U&2;l2G5})hzAJKkBipQ0zKs1km)(z9x zRT($s)T2H+EW#dcs!dz|>FZZBt^{qc7x^Mna*20016Mtrh`5oJ#}j0` zjSgAxUQA0DMTd>>P&svSfzswAd${M_Xhk~$#tDq5^xtc-QzPRy0|ZFjJ4g5Cj>Q$w zX*~nYI6Do9=DV;ZlTIr+LfGx5h^b1Ai+dk_=$pKjd+Vc}7wnWd+uLdkBc{}Jg}=H! zK~EC={Fc4{HYwK~pnLjkNyT~i~RFR+zw`>H6MB=JCRL>lQfjWYv z&aR@4Y-n+KI3&I*gKpL4Z?g3v#3o!VC^7`I_wrUSdf82P*aeZC>PHG3)6~x-lb`bA z3u{%+v8FvKDJl7Zx9xc*cidkSJapwqiR8B)1c<`1y4{(^Ey+VMRZ@BS-Ve$rSlxKW zuE2|WA@L5h$ymgVERVMbn;d&0Ds@tfOhu{7aj$P?J+?45ytKogrVZYf1eP%_TM=)5 zVy~B6SgGxe&|fFF=Z#)}vdyEvq4{aBGN+|0riyzcXZ0J(5&yEtJ#d26jd&Mzn7(K} zXGcJPTcjMZ(}HOH({0*LW#&-jvdt8^&=~HmWz>#p{kydZ+fCa1>H#ltzG0jf7^{tvMmR8&-VC(|ysaz6GnzU@HkNpZ z^R7fgCEM*$A+G9-rs9f?Bjg9twz+zr4+sbd3L+`!Ftj-6{CP3uNJ|Krj0rbh=NcqN*7=DO$v)L>!L4D zb@s$xi9QDh5;u78%E&+!iMum!S;`jaHWgFT(#}(dt&uRmX&F9n@^8*4P`T<_O?HhQ yy+-)|vG~6j9oHBV*BsOSVw?ZP6RlFVmjlM;&TMpcRVxPo0C!g}m(rubKl~e4s-)xq literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_enter.png b/interface/resources/meshes/keyboard/key_enter.png new file mode 100644 index 0000000000000000000000000000000000000000..cf935fe07e68d6d899b9ce200a13d0ddf56fb590 GIT binary patch literal 2201 zcmeHIe@v8h82`NQ9d|Dn-AmaV=jOe$g<)-8LrOH`y*y`7N+TIZirhJ0qXD}^Kw_QT zcSehxt+s5oMuwF-+j*QLCW4GNq$ygNweA{6a+m`3`s2zj;pFbx>!t(7ZJqRIe?0qs z_TBS&p3nF5-Ja*|7uT&xPSPd;NX~iAm=Dm9gOQKdwX2Q*NUdINs(#73wYt`_O@LvG zb+f?bR9IdS@&(J5y2?J`X@FUgV>GOPt^dj|zIXb~bKg8)X`~$LcEu=a&AIxA_{3^H z@nFJY_hDsjvqJqu{@h1)HP}<$YxR^Zy#Q@FtU*N;k|Gs zwVEZJxd+9Qy3d*pdKT8*780lPyUoK&aZX#sic2T=o)vQYQg8KjxT+HT!u&nLs%K8T z`q8J4HXYSjDQe0q`7JE&HQp$!iHHl$?8&H!W3W7e0fVTy7` zWGyk^G$Ao7-FeD1rISQYg$jlQ)hr|p2anGGRK}t)XD~1Q+mRdL`s=4fzQ|kS3ZzS# ziT(1A5A4@jsiGK0k>Ififyn{N9qBr-r)eHbY*I<^y*K(< zV9n5}a?$(isou2EL?n=&vQZZaw$$w#JTzG5pcpT*7rnsVj2aI8c(}q>GWfv9`5oO@ zeD;T`zuWEez8^~quQh0~`)e=K>hAw=wB}6LWA%kf4qC(t2HN<8s7mF6%@jc?QGsrz zwD>od1ac-VQ~t}i+n$j%ak8enol0lAoDkYx&RW7?NUJX{1m)jxvp@6(*rSw3rzDNZ z?g$5CISHyMZnMIf%^+pIIcz}V&yX}U->Wh?SS)-}oQd+*xy&j$l*t|V()749+wO8o z%L~Dr-oVE_Y^Ke?S*`@y|Nl;#=IEVW5csY>Ne{!0jE0Il#|HqAvwEG;^{jd4KSw<-C@GfkCpwHKHUqKdq!Zu_%?nF(p4K zRlzeiF+DXXH8G{K@MNkD0|Ohgr;B4q#jQ7Y5B4%E3b;BZ`!cjRaI`q2wXn_HYJ6kr z#G1@i?`nTaz4VM(=5mC);rAY21_cHN4v=LH3}i6}R(k_wj;CC^0tG<_330MR9u{FR znoN^WwNE&l%yj8?wfy~W_p;ZTsVo%|3{&_S9(U^F+oX;|dIF3<3-c zEDQ{c3=RxLG8^)3)fDzxKL41TeYPPbROt{GgE;X%v47fb@MQaUyM1r>UXRN4@T%iD z%o<;*d3<~JTEjM8&y&Ies9{KS03DE*6P|Ey&gUQ7wx&63(h^K$ZQ!nCVj|gls6j!o zc43MN1j_RiCB+dWCMe8P6l&){%Tq+B2Us$K(Ih(Lz<)a?9tQWfO6#x9|7FF%z`)?? L>gTe~DWM4fmm{&g literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_exit.png b/interface/resources/meshes/keyboard/key_exit.png new file mode 100644 index 0000000000000000000000000000000000000000..4c06660d569be01afdaf2bb290eb1525c39f5ff5 GIT binary patch literal 2466 zcmd5;eQXnD7=PYg?dtTTn=_7LvP&>VO>~lM;3lI7cG+g)n2f{*m3FwYu45@5(+MQp ziw1Ef>P%!yOr~eZ#JP@^NLC5V7RMUR5?3v@i`~`%%NQK6sX0gv?`qz+!Qf}K{X_n^ z+*PIIgY)!Rw9e@tVWhA%V<|hXLU~yZet*zd(xvjlnyAxJ7 zdNw(^n%0IU=Q?LYwmS?;@G>jxv(=ga!He0g^7mG16+WuC;#I&Q4E3OE2GDUBhc<^LOwvjd-f zIF|)=X~r{V{zk(vgpys(W6Ak%KEhX54Yo(i7A^*sPABBxM&IybUa9=;gUp3pYH|1I zxgTAYwb2=VD>I#xl6zeJ-wqB}h+!pg?Q{hoM_jQq^KZ$*cc8~Zk`En-*%=c|JtwfA z9M#Mi7N(RIzE_Te9$$;unTH301`bXHG;>|ES_)`-NRH#DBp)(jIjBwFTPlW?7SK6V zVGZN<1`S4t*_rKeJEI55hmJ`;q~qV*cHjPn$1i?gB8HU}m+Q}*6m+}_Ug=X5ON}bK zf9eMjnH_2#l<A%6+! zS@jmuEba*D5%~~p!on~PLR6n1Jelh=E4#I9CB$zo& zG}d{X$dA85IH%2fomtS#uG&GK!kfs82nnAftZED3n}L*N48WNr}QBu%c1U| zfV~1`4a;NnmHuOJs&J3rjKQIn@{9?*L4yl)SdIhBgI_a?h>mm#ppepn4IJ16mJloX z(BB7O)FPD8QNkiJv#WKf7~KGcQzLUw?G$_4<3r{56$^ zlskwT?_T0iZzE$!=7>yqipUM|%+=)~%-=Ww`OTLoCBg iH_pHe!{JbLKQM4ya~B+PKS2J*pr*3cdU%cF)n5UT`Lr_t literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_f.png b/interface/resources/meshes/keyboard/key_f.png new file mode 100644 index 0000000000000000000000000000000000000000..93306c60359d04f8e1c8a07dcc5167bbb0da082b GIT binary patch literal 2112 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 literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_h.png b/interface/resources/meshes/keyboard/key_h.png new file mode 100644 index 0000000000000000000000000000000000000000..c73f37c271fad09d1dfd91c54c42528baf87b75f GIT binary patch literal 2186 zcmeAS@N?(olHy`uVBq!ia0y~yU}6Aa4iHr><-C@GfkCpwHKHUqKdq!Zu_%?nF(p4K zRlzeiF+DXXH8G{K@MNkD0|SSSr;B4q#jQ7Y?*=|fkZ8DAdc68_<3{E;3M(B}9Ef0y za$LZ)foUt#My7>K&a6R9vMKu`)AkhHo!ZL6WcmD1_tgDAZ&c3z)2N;r`Hx{iZCw-- z10#b21A_nq0}BIL%z@R;LYdm|9|@Q>BoAzVMx?MX%u?-nX0Sk8{_p&G zO1_uhe%EqfSYXtAFu{EG^Tcok6F24zyKu(BKVia^Ml;`h@;q$ZHa~J&Wc?e)1NWBI z&st_Z@1HzxfgJ;5L#^L`Tfc{K>%Tu;e$s*A!Hnhg8zOX$J>K}8iLs%~_y5%2zkmPI zvtnXwIREb5JE@{SwO@ZSyyswI*!|&0baCAI6r(~l2F8Y4x?~H#Qr5`Te(U`|aLEFHTGQEWiA)V8=gsj^ESPw=T$(VOrEX`_KCI z>+|#TBd7h%{Z)TQ=g0Bff8IBi78q(UG5i*E`r&wu~q%EaHWC@>VP zI3&Oy{a0G1mH+07EPDqA0frk}bxt4pa{ovFw)U6*|NPnW_jCaV2V+C5|Nr*(c3pWD z&q;aPcRy`D^Sn5por$5{Zh6C3ZX>i^C7aDu$f8yWQ?v4xz_MdFlKQ8dCICE+-6BEPK&c{3U<=-uo zVqk1=%l~&hN?f~#!NJ-5|7M-l??0YDe_q#!iNW{E`|r9;91OWz{ys1Lf7X(Lg+Xf7 zyW}tHn4HKc2q`K&VdYQM<10cJObTV5SH64dc-QaLrY&3yx=*N?-H>t~(aZ*hh|%hS r++r108pCKZ(+Qc{f85h&WMc?yv=V4+Yq4TrU|{fc^>bP0l+XkKroGA8 literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_hashtag.png b/interface/resources/meshes/keyboard/key_hashtag.png new file mode 100644 index 0000000000000000000000000000000000000000..df673653b0416114f905965f1ade51d11897a0b3 GIT binary patch literal 2394 zcmc&!Yfw{X5Iy&jU_h{e8LXJpMihU5j}(hMhCmc8;se8@z)Tf`5~+ZM$kPGJMG@bb)2#v61}`=0^en6fM~B~-pCB|Unp42xsr8)SmOEzz;E z5LtB0HpLa055O4?T)H?cqqFbs4MnGrua3T{!8Mj*PL+qcEQaky2#M+#@*CS}xpwDs zAI)m?{lj^0h*fmcfY-^#tFHTX(9+6do8wUHcZpAefEeHcER5^;|H4@lYktCT5EG!z z%=q}39~ErWjx*E0;XU~xPuAR9zo%Hw%%UIYYCg!HnO0&n8c%#TFh9#%D|KJJuR`r4 z^7Z|MEv@e0PuX?CS)Y>=<=h*gch>LB^46;72@+^JvhsGFimq1hv%NWp3DawRV&@D+ z*gtm5g*dOMsOXMitFERl;@n68%IjUJ^P$OoE>`KKVhFYq;Gl+fTyvGAAWpS`7)k8n zC)xy15U*>qf&EKp9YCVPLelffYYwUnwS)j&6^3PxLz90z_yZG?x`i$aDS|y%czP-s zJwy@*Vd-qiCP+<15J$-@Aqyu6s(@!`_5c@=`EiqArKBKkZrs+{>@&26N}%n~>Y69% z`DM6aMFMQV#ff-R_cRDFh0sU=l5(XAc*)%mqkO(B@hT4jBpGyc^DIh?$b2`Ie&fQM zsfP);XC`rK>+^QG4i9U;O}a5SG!!l^sdJn$BfwF)`4FFdHeRLcH)gi{cGI)4<=zcN zIaJhZ^1QyR)W!3<{$m-s{zY*mXCK*zcO)zdR@z(^juZssI$?0vzg zr|jrQ797O*xF2xD(K93oyAATI#}6N{WmqX>gfl+O+M6l&T@c)pX(>RlV&jpiSWoz) z+`EY|Qzd`G5pc0zkSCEsSZU8g1US%M7?H2A#VX1Ut;%b1v@0_#`6N=;5l8QqD(FcB zRX@Ay&;o-)RntJWch~o~K3-i&{@l>iG{+}qo)@e?(y6Ztwyu5Z{Z80DO-EJ=-$D>>6DvtxZ;sF z?Of|LU{N0T!0`O}bFbcr!j_GFuJvuF$qS0fU!MBZG&LhOhlQtKW&34zZmVl|Vg6hm zrFT|e7C!r&i&ir!N2=Pcw_Fln(9c8vHN%xg@&M9l`88XZ9SKWe7pKMhra9=COfJds zzXAkR*8S3h8SAT;fUn4trL7n+^{4St&}i|9GLJ6-Vt@12sky6NFQ5o8d7^ r{<-C@GfkCpwHKHUqKdq!Zu_%?nF(p4K zRlzeiF+DXXH8G{K@MNkD0|V=CPZ!6Kid%2)9^`B>5NNwN_b5|Plj@R|lC(_E^kmM{ z&z}9bXFFZ~;o$~}nLimH)b3Mb5MW?nVPIfna9|*dIk4ItD04jJ+65>GI!K6<9rCaU zgVAJ~1k>JdicdJ;UR3t)ocDXTra60co!P>4bpEG~x0{R48Mg6io)i^mU|>*SVBi3y zOd^?o&iX%GdA@)BL1~aC1qRII)}a1L)!^&et=rCjyBD2pwq(hNsWx9JwHC#RL^>5k z50VcX7-qo3h{7O-YnOX%!1y%w`qtWSHvY^(p()Em8RAZm6TT>p9)unxtz>~EMR+)o lrk#Otq?Sf?yICa|3>y_Lb9_0?!oa}5;OXk;vd$@?2>=FFoU{M{ literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_j.png b/interface/resources/meshes/keyboard/key_j.png new file mode 100644 index 0000000000000000000000000000000000000000..f723de1f55df97011773af238e7fe85d00f387bc GIT binary patch literal 2070 zcmeAS@N?(olHy`uVBq!ia0y~yU}6Aa4iHr><-C@GfkCpwHKHUqKdq!Zu_%?nF(p4K zRlzeiF+DXXH8G{K@MNkD0|WaGPZ!6Kid%2)KFpo%z|e4U*(-*g2RRMu9P<>;v2>~z z%wRBbym8c#5?%Fj5e7;1DN2h^Sfy7eBeo|z^!d>%ip}|HB6aQBg%4ldxXxcWiQKf0!30IrJU7H>M9IyVq-L5 zA<^F$VT3!_4n*AAX3C+!P~)c9a__jBXA*-T!;g~9^;4FuG<0Ejpp(1(wqkoUqd}s0 z>~%@jWvg;;pFVwB@jSzy#spS|^vtbMyfgo9)-i75VCY$|b9&XPe_7wwzpgqVY0Hps z>kiW+n-+m*pDX{Q?u@asvpbiz+5XS-imUUV|Ga$p@`1_k%B;H?SQy;I(*O7Wu6+0I z-MaPblX>dq{(8LU|A!9-HSg;usjOeW-c63N!7wybbp4C^-~O4i_T^vcoc!;A?|k{A z$8yZ1oBJ6KWbm~+@A+f@Z+YeS-+R*;d}g;Fn6s zh^tNaO?hnwi;GJg<~{%a=eEKoo!dEP-+n7IFdbOg&3Gek`~N4!iKm|y?TxEG%gw>? zd`a%@UF+u0pI^P_g`w}|+qZ99{NDYFo$nw^b)2C1W@p=3E#-AS-%~-v9^_SjC zc@~C-`EQt*$jq!TZ7`b5qFaE$nfT0$k%+;0p6I+!6YUMemOR*Pr(C-Nu|?pA`L*H+ a4ENTmO$eFAah!pHfx*+&&t;ucLK6VsNvXB~ literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_k.png b/interface/resources/meshes/keyboard/key_k.png new file mode 100644 index 0000000000000000000000000000000000000000..aa3e806a8d135bf1b79703fde235ea1d067f9b96 GIT binary patch literal 2389 zcmc(feN0nV7{=e*b=i$fq@dv=OiO8CP9jQFn23~*A{Z%?iAutD!4=$=p+#Y^fl&FX zfZ7g(3D}h_C=7;d5MeNFMZ^}IA_E}^6R0KbQniH@TP*EeZk@(te~o6@A2;{hbDrP( zKIb_%r|3P-j^)c-mI1)>i15&801A)~O1?5(LYe@;IhDOX_5H(1sp$zv5+R6t_&_2( zB01q;Vsv5x_luO?#BBhmJ|Z+|PsY7RzYnA+_pan8oEuGlVEguQ+ml=Q%idhIzX9r5 zM_#{keEfJ!?#>!&95Qf~>Fpe4Z(D8fn$VvabuJSddD+H`rx$r!#bGcmnQ%q;0CWJ9 zw6m1qQkYj-S&0M1D)ZLkd@G`R9>bE8f0^Ra4(=uP0$=RQ_M=E!r5d69K^o@&?$JVR zxnK}itu(Y1fA>e{lg{Q3nn$W{-Eh9PFAhPhwEa)~>an%OO1DWI|6Y?RKLISC#=8yb zPKD=CT7Z}%W>TPXPt2tWjl1N>vBwoR&zFS)y(~fJ8|ko+KV3!-v}cR0L9>_qwCB5D%2}TRdHNl=ijgEc4^sshc4Z z6jasbu7YMf_0uUEgwH+HqMe_eonq1;e2Xd12K!pDJz!1*E`1sJfNJu}Ap3ls1@p{+>I50@DFAxP%c&ttq7_aP2WtvU*_$&?j_4@2pG7YI46! zsdoC?C@MsAa%?a+k$dUoD#^;FxN*CmNzgIT)`Saz)q-Oy+1ii~F)Tn=1S}7UMADTuQPSSt zK6xKdSgfnMP!vTBtf}t4?SSwzhxAu`&F;XfYa2`E6*y6$rC1hhONHpIuD0F%$Zdqw zY>Lz-2h%}RUP7nBlc5c!yv80ni0&FIkFtQ?s8Am(TtoTc4R;Xz_Tb`)5+K=fp7C)8`q zNAEibfYpcR>>iot`gA-iYaeTR0i0T&Yw7dZGwj!-))ud3GnNtMN`bX?M9-I3PRCI< zM}8FGgz&Qlb5slz;bUR36~xooZzkCyvV^oiE|;&9zLP=Inazht;Tff4(AeYQOr;%! z_mV#J>oRCVX<||x-Py|)>94ziC|b+-SQU_hf@;-e)Fqoz-_W;_0iviyM*Sx)nxYV( zpRbOi#;ynh`uR!rohcrVj9}+%NsTF6LvpniH%5}aKpgmqcal7F89LK>+tPV$HqBC= zJ58GQUnN6ljQ1@oxQAnqC6Aq5B{g5w1=ApW#=|kFyB{I{k1XZit9xflYI+V#F-{Nz zD|TE>e>yiex44)!?~4b{YqqQwj<-C@GfkCpwHKHUqKdq!Zu_%?nF(p4K zRlzeiF+DXXH8G{K@MNkD0|UFLr;B4q#jQ7YFXr9~kZ8EL>=Z*9(+9SD491PNOg;?~ zjl8E=W^g7jpJCh3IFrGcaj#N-?iGzKr9HLhlp7;{E&cI%>-rO?-c#5?%Fj5e7;1DN2QBhRH7 zKXzhR5PV1}!D!~5-I^MEB{IP!i z=4=kehCD9yzxMHuxf%SA>nd1j`(9S#FSn36Bg5DbCeiEWEBEgEY^MuF`rB`x6?w7h zU_!w4(&YBtF?xX_lGb4iAHGWM<4zH5U~q^|iBw}yVEA&e#47jGyfZILjEY$pZY9~x zH!st%cC6HsW4c^e8hgC(&W*(h^#Ku#4$&@wTjJL1GC453SZs5C`Q=RGg<;+fmW7O|k#XyFVdQ&MBb@0R0&yz5oCK literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_m.png b/interface/resources/meshes/keyboard/key_m.png new file mode 100644 index 0000000000000000000000000000000000000000..2e83b7b214666b41f24db1021b1ea570d9ece81a GIT binary patch literal 2438 zcmeAS@N?(olHy`uVBq!ia0y~yU}6Aa4iHr><-C@GfkCpwHKHUqKdq!Zu_%?nF(p4K zRlzeiF+DXXH8G{K@MNkD0|Tdqr;B4q#jQ7YANp!#iZmp?ezmHWrpLbSvZyhx={f)T@SQhqHbO;lGaR2?ud%z&@ZkB+ z)3FQ+3=A9~*EBGY#T;1e4U{>aa_tHf1RW&A$qso~gu!SsO`=dc0~78f@Z`nz-3?+t ze$V~?@BZ1dXY;n-uDtpo;r#jYn{Tf9GwW)C0>hG!yj}0>!nb}Ex6iPed++Yuqx`+~ zIzM7J7IZJNu&{XXwd&*hUs)z{{iko*@m^ZR*4+PWg2$Q-T>S?g7VP-ZUr|-HYu~;; z-JO4w?ekKMW~%)8n!K&MySu_||Cx}De}Dh|_xSPRc41xthHJ_U8-@M-{U_U7_wnfL z&wVVGl&wBTtL|)mso<(Yp&55g(%`X6jUCG45eX$UTzRtA4#V_y2Y_olhnE zYLf2V$Vp}1!{@;8LokW^_$lwY9rXv->VT$u2&V$Y|y@mBWs{|hFh zo6UY|5Y6+fu!-S<+02|9=HcPt@8gBvynp}P;OdvD?%YfctB-s;|4QZg*|TSF+eFT7 zsEt<>V7PYg=FP~w{NkS8_BBcX8iRl6E#mWHh%aXL?*JbB_DF3@dTU0ND?|9EkoVT6z9W>d2n3=`VC zl{VbsKBTiFvHkGF%a<=JCHtsNKDKVvj#US%_Rc#lefl$3!=2LIcQ0ma`BB=u*m+}? z^bd=jqI}PqAA(|KQ(0A2Rek;cyQ_~K&*}?}ex2~QuD-R|lD~)HLecf>*Tu!fr_P_p z-+uTP8@D7M8Rxf4%#|=>F_KTU;g`bYRHn{59>(vDb!k zPTh|dajng^x*8qg$PkhIUVqUf(f3y!85jf@7+4q>7#SQ$U<-C@GfkCpwHKHUqKdq!Zu_%?nF(p4K zRlzeiF+DXXH8G{K@MNkD0|V=JPZ!6Kid%1P9OP|q;Ba*`{N}o%<=^TAVeb!1-uZCO zKkVHx(~{xF`AjATMg|841_1^J76!7I1FM~dGRIS_oq>^ppaX|E`5_OBFc?jyNfc@q zATQY<*-BChYKRU!YQ2M%iJ^gkL4kpR1C%=mGB<2I%~BIyp1w@>!OR(w+6*}-xH%|F z^c1HX2L>PFosZprMEjm5+8c-sd+fGTu3dqsn8#^5<=Q#$8csyQM0MK-e(wfGhC@Gi W<;|Dv&tPC+VDNPHb6Mw<&;$VfXLQd1 literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_n.png b/interface/resources/meshes/keyboard/key_n.png new file mode 100644 index 0000000000000000000000000000000000000000..be1a6a9e7a52b5b517fee281f23fd19d6fe9d257 GIT binary patch literal 2165 zcmeAS@N?(olHy`uVBq!ia0y~yU}6Aa4iHr><-C@GfkCpwHKHUqKdq!Zu_%?nF(p4K zRlzeiF+DXXH8G{K@MNkD0|SS+r;B4q#jQ7YF9tr!5ovh%b^cy+)gxR-xTbKma82Nv z!X>0Fpyi-Ff$sv>6|OBUB@=9wjjgT!S+DV8n*Xd)Z%+Y>k-YJ9UQdJS(%AnD3#$KZ zW?^7rXkcJaU|`??xrlToIeL(+qEI^r87@Y$onq|_OcVqi*dgSHJj@?3noN@@)UJR# z**J97MKM)8ei|AY+TY(_|F=1A{q*ORf3}2iFqmYXeG;p$Rx~Yje)Z9#Nguy_5m{ed zUjF?b`@A`GWaQ=f*_aI`j*y&DG~?&*8LaekI|cMIMIH~a{1!_&z?Oi-dh>m zbTOl(tnAs_$J~W{4SKuw?dv-xBO~LZHu>!G|5{OxKU&P&JTaY_u_4T@{>)#AYw}iC zKL6v9k(Kpb9{jq8!NDoXYQ5Y4)2F>x>x<0SdK9W-@v>NG#{2cFR?RrkeKcwIeB(#Y zYwWiF;9z)DbiCd|#*KN~pIuT8zhsymS!KSxaar(s^g5iQFH4)lqoP=So?B+(f3ocn;0BkpW5V;{%if4KOQ0+4EN*T zWUE``Ca<2u>AoP=r&Ek>UKO4Hf6LkR3mX|OSZd0rb>@Dq zeq;scCE~2oo@Szii#f>Vvk+xn6N`agt1|+W|xxN z|KIQLN^FhLo9=t}bs&QP!;QupH~a7Yys`5Ck>tn1&dS?s%SAXC8*;m2f83aOKc(aT zy?beq+a9K={5EA}Vc7TfOMmPS-s9!-o8(*`VwgMo zpMBhVZYdtNFj2;aD_^Vju3o)5ZT{ykUrHvo?>1y$Vp!X~_v_jI-~Z!-?{hLRHf$4d zy?CN^cK(yfdlexL3=2*g-o3|p+iVvT6GQLYf7`!5VQOG-*tPq=@89Qb`)}R9zx@OU z!;*~8>$4jf4otuDZRsyQMn#4RD{Rh3*VctIDw0v$k(s?<+934YkC%le%(6M(y=X(_ zg>8Fxty|0Fz;4OH0#ikL`AdyLM1f%%g98JD00RRH0|O&j%z@R;z(|qfDc9~mLC`@$ hoXn8__jWo1Gs8q@CF|#@%RtQu22WQ%mvv4FO#n-)*EawF literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_o.png b/interface/resources/meshes/keyboard/key_o.png new file mode 100644 index 0000000000000000000000000000000000000000..883feec8715fc0621edb9d9150f2f51643195407 GIT binary patch literal 3054 zcmeHJYc$l07T+^xXmY$WLSC_SDsJe3$jne)d8O2$bFNojx$;UVGiGp4Xol&Wipulk zNDqw8Tt$OXIF7_5hh#92Ms770w@AtU|s3|mX6uj z@EfKBY6H^!Tk-WP|8-$sah;tuNLT#NXH#?qSu*5{tLy4A2{~;Om+FuS$*a))x7&l) z9N4_28CxhC)=#SDa2Pg}s?&8-A+$T`YMMGaPYPFpJ}rG9;PIlr&1E-zNHWXi4GlFF z);v}>ObsQLc1_2zg(FF=Sn!Ofr;y3kw_F}CE18NMzUkm{%^j`~!EgDnBy|6P=9saA z5q6KUv6hA{@U`T`gh2lL_b3qu7>cYqeNtPzT;O)J<#^GU)#g*s9lpRwt~#DZMjXoE-F(f%FO|y&L;~yl zdzJDHGBC`WlDK-P(3+or1`PNdyn(m0d@w%6=?piUoUf5S<|>c!IC~&}mBJxKpWBHB zW7Wfx+brn#TQ2ydwW-11QZ&(b?wIwSh*;FG=S0Upbl(!TUxU_sXQzy>AsQ%^KFlA= z-#Gh@e#%R(9-NJ{%nxRLo{!Sh(NM2uvvb>n`w5l-`wF>amS>y3CwF2ZXqYmY8?tza zvgvNu*`-aZwV|7eFsH!N?PWb9D{F6uh*?q5(b3@(uNG#a_V%<~Q^BD_hpP3`-$yCm ze4(CcudYzrbaVA-CuM$|);!ijK<2ma2uZ!KVhF<@7AO{)e+&`Y?(VqeWF%#Tu0&Hc zTqG^y(G{J9COuuVP$eo3JjtTM=#PbDtz#;7Mrcy67pj5sGMKfH8O!FKqa(Q&h`$+F z$9N;R{iGUlmAfQO&JjlwkkcZo&3YbP)7`z9I3VGQ!$eX!rpqHsyWT8J*p)$;n5vI~ zz3lpWw;@|LFROL&@$x>rf%y3`hh#-q`CinO@cRg%woc(`O^q;bh|kxJ z4)A%`7&yXDA6->tD{oLEuefc~!hO-agnh3g1WF4kpKcL})Tmv^Uo1)~7 zu?5{$p-@o&mM|wQGKmF z78!y?-l+H=y5Q+J7HW~(eiVT^E6BA=&ugA!R(h*wo^PDUkkbXu|%3rIZgf+WOz8s zzL>6XJ4@>(ce4IR3?L>e1qB7~c)@gzI7z}tyZL73!b=g8$z*ZP@JC0VP5WX1DeIjn zAwP9$OAfnhe<+}vCkA`&;64z*n8bWJDZ8>3;7W)sNoDs|6&Qolw~4yYcf zHGDTW7qxF;RZ0(_c2@-&z0RlkmG%|aX`vM1{|0O$=JUVdpw_cOz8#|E2U+19Dv6bL P4gkQ(&h1ErtzXJtJnXg| literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_open_paren.png b/interface/resources/meshes/keyboard/key_open_paren.png new file mode 100644 index 0000000000000000000000000000000000000000..9dcee0b9a0b0e84bea376265a735a0a07238fb65 GIT binary patch literal 2656 zcmc&!TToM16y5g{@bZq}qgoypwW%$JfVLJzNkptLWzbQBwFrb~w1_+dQo&q65lTl; zoF<&gkMZs*eWs~??_4?oV?`(v-Q*IsAN zF5Mcw+0kyX9RN6nh6HZ|K!G|K6%H=mcm)6!Wo`OAYkO)^R!(e2B5X)VjZ4HsQ)2mv z+Y)0Fa?^iITnAu>h6ZogaiCu}@JqTXVgV8BJ)h%F%oo>kwpyd!^xeo6yFFZ7f`-l) z*D15C3%&8RzH52=Pb~VWtE;v*Ft5Z}pYMDwfpI`K^t3|;1!diTjM)&AlWi`5QF;gf z9Ns8>yKsDV;%5)XW?|io<8SW#zYq3rd_yKOIG`imTXwf5{lTY~f zm0aSxGiJpW5JaV3S2d;7NE%j(fpl|ng zG{3&|f{MQJae+>H{^-%8h7olnhX&jFM-1BWwP`#qw`1s+RY4b@dq87$PR_!BtGg`$ z(@UY^(dcNVZthRjd*yYFL;~NwRT~k zP@yuL^L2j;8z1{m$D>4TaqMytSzr}qiARYs5h?vUTN7>R6-4=vOq>DY0S%em{KHpb zP-@u!Ts(=0^suzhVAU;?LGR3fv`J?Mh#zW%s-`Xr)j2cTw9ALo%A1PCfM3#=v8$@n zCpH5v6^TTB1yv8*sgb$H#u!b%A|=wk1e7`-GBq{zibwZzhVS+sgZaxJzINA6uow`c zd;Qukn@2HYknr)bp+kyt_k)?4@yJ}bCplw|-y)3QJ`$X@4yO7}2x~o1*yY&vvmsyi z`7|4YWeWz)1|u5<*%5>AbhY#=ip9jtt~Gnoz{P%q+`&yG=ODzL)_P&ujK<7!q)aYv zATUS}_`9`soCZ3C=pKkzQmd-!*W;Thm>Ov!i+~fCukIKmN84Fc2{X^?g5*Pl1Guz4 zMj;Kfc}MEN4Q|Jm?}f>Kk;?HeWT#$nYbj!GKrIx+%PK!|HyROq6@Fn^KYy zKZFzUtnx+_HF>j4R_eZIA%+mA!ePiikj-N~)Eg|2indcCtfJ2!XDvG}LT>b-$N%f6X5QN4Ga*10+5 z&wR9o{(Y;T#d~^sGV$&t`;yY;+qaz*FD4JH>k*_l`XNePF5C1Hh!}8b##A65OM4$&p)(7+td%<;J#<${ za7SC&cB4sH7*s^ADX)+4-Uwn4pUrS_R@*Lrt@o!hAf$7M!$W!1U3qTz=!7jeAUu>f zyiwWKR^$a?(7D4n%t^huWS%QroERIM7dT{>Af7e8`ni<6eP zYs*@~ikNtkq)t1W{cb?hH%`;$!Nru`_VnFXBBaeN(M|=n;1clS_|MgQ9 z@X23^5%LQzSJf154y$6~#s?WP6MyODGgA-Pefky}j5er-<#nU2d>RlRG+NVUxxQx& z{}wm^bO0Iv@~g#$7x@w10>W?|jmj{mRomi0nAUpU@=Swk!G?*ae_-4q^N7sM<4 E3thGQkN^Mx literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_p.png b/interface/resources/meshes/keyboard/key_p.png new file mode 100644 index 0000000000000000000000000000000000000000..8d55d351cedabfda1e3a0883cb13f8d2f5f617b0 GIT binary patch literal 2821 zcmchWYf#fy8phv*ATfdjTah4Zf+7{_5~wUviXo!hrYsj55fKQcTx!)2E(VZG{t#SI zK@b)gxvGqWl>$XT!eW{@5CQ@MDjRE>k`O3Rpp>RXAPw1JJJZ?j%&VD#^-Y>s- zpYuG=xfbB(WrQ=w0f3Q@_wf?|Xjnhk^()o7oB8oZ(L7U9c53 ziuh=6eX4$tK_<@KV6M}C$~XGqu3S5;+Le>cj^=iI9~$$owwr#DA=DW|`;^cLg#eHM zuj7~YT zEu`hkgQ{tcVV_E+DkalbwQGcrO!08wnBnj|_r=gsgd?o+1WPgYY1=dYJ=UY(Q=c_Yzd}Idb}i7kXWa3R{Y`$I@|%A_!VnPY4@9?^ zT_GnsS=XZFOz8{>15fv8HQF_?u!qsizmQD@`&GJcS*YGLf z3b&9T;EwZB(;R8H(KNer$pj56<-XQCg&N8iRjJ2J;6P;ZNgF)WHQHA5+(Ge;mF~p_ zryJqY=oT9_&qhH{u{3PIA5?$IrBb8Om@RTRO&{itoGWXT&Wag<-NtE@BS*v!{L@lV zZx3!eTUVeC$0n>kS&OjG8PRofgdq(#WApp8=MR>Cs425$;d1iH+m}iWc=>CRTbC)- zLV;j1Rg{so`+j8>P?erFfh9+OHp`z$EK|vdVOyziC)goU@~X1mXt_&iZ(1z9!YBd^ z;E37;*VKdx+{7p{H5H@5qL6!@x~~@I2I89aoPTJ_UM&h9k^Uk6PC@h$J-9h0Q184+9ZSlK}(csC! z1D`yB}VsBl&o!dG;~}W5sodC zetyq+X^0|Y>6@Vt*Wa7o*x`o})AniH2LKPfoJ6}H8;2K@E7Dhfx-8cR48%W@&A*;! zA6+;@ZwTu6iurOTan1$qg!rU^+}uP9D!MR?F_p!tRYBz#M9n9?;eVcLBp=1J%4`uJSVS< z^PtOybYLjarOgBny*kBT$9X~Hug3CqBq$J?X}xL!i#4>#rmG;jR6l3Q?w*;P6t~ea zfTIn@v0W-xl?E8NezHkArA`?wo9Lip;Aw%CPj1J~mG`=o!_)pzDccd_Y3FZB)XZLv zeWjoLr+g3%{y}t`_+ZF}M2Xic>5p2GW`CR)8yoA%!GN8OPode3{>O(~U(Ecxyx4Wb z4GW>+g7Ewl>Q=WWrv24K{umL3;0XB=ab?N-lxz!wwXfATf$G6SlgVTUU#~4cxOnSy z!^F%uMLviwMUvL4(nT3*JXIP+x1#3Vt)8893Sf@JVce=rLVkClT{ak9R68>!SU&7i zm?LSA9lIG@*lc71{L6ir^CF{o9F_`z2fzTJU?byinPoghhM{>CTE%c4g*VBN@9nCW zG=c_TaVya1&8z{`rd0j|FaH;@|FxdqzV-i&|1T`qc+S7n<3HDg-|B`j1LElY1A)D? Q5dZ)m55MD$?h#pk2i4?4z5oCK literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_percentage.png b/interface/resources/meshes/keyboard/key_percentage.png new file mode 100644 index 0000000000000000000000000000000000000000..fbe1fa959975b50926d92b6a67e70340b36a9493 GIT binary patch literal 4316 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 literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_period.png b/interface/resources/meshes/keyboard/key_period.png new file mode 100644 index 0000000000000000000000000000000000000000..ff726df39b64f8e6650da997339242afc8383173 GIT binary patch literal 1591 zcmeAS@N?(olHy`uVBq!ia0y~yU}6Aa4iHr><-C@GfkCpwHKHUqKdq!Zu_%?nF(p4K zRlzeiF+DXXH8G{K@MNkD0|V<#PZ!6Kid%2)Zsa|zAkcQv;*>%U(_~p?BQEJVi@yXq zWqyvUKO6sJaz;(cGv{rdXs+?Og! zRqvNQK4!znNKVf9AaCEA&b@R>M}*bNt+#rst&J{oF-U)6VHw!0LP0pef)_@U7|bwj zFq%y54&+5LlC9*V5hU#bWF#?aXm7ah$H>OOsw8pdSzFp}1_lNOPgg&ebxsLQ0A@&y ADF6Tf literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_plus.png b/interface/resources/meshes/keyboard/key_plus.png new file mode 100644 index 0000000000000000000000000000000000000000..3eab84c34d621c9b3431a897f0b740cf8bd3fee0 GIT binary patch literal 1635 zcmeAS@N?(olHy`uVBq!ia0y~yU}6Aa4iHr><-C@GfkCpwHKHUqKdq!Zu_%?nF(p4K zRlzeiF+DXXH8G{K@MNkD0|OhYr;B4q#jQ7Y5ArrENHko$RlBro^9PJ3(6=GWnTfV#~t7z{udhz#zcDK#*Bya3#vO z@wBJr-`m-`hR5b?UVKoVuvL)s^x>-!)4Gg#&ogK7>D~KYdwW~kdY#ioeUA?wRb+X7 zXlsQ)+rOznL0-Jl#pev&n3sgFYar|%u!A3j$h9o2S8OSby=^v2PWH0`&jH_^OiaZ1 z2aks-)~-ys5%}CkFvckKfCFW=Q?6Zrgmj5L9*IuY*tN3|4rBb0;K0DJWBzsqW(JAN X+Ddy0r>bP0l+XkK`dOi4 literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_q.png b/interface/resources/meshes/keyboard/key_q.png new file mode 100644 index 0000000000000000000000000000000000000000..fc733fd7fd9918056c61702f3233b4ffae05893b GIT binary patch literal 3012 zcmc(hdr;F?7ROJ1gohC@T13q9h$0VN3n~F4MiLZ-(2^p>6;LXIAgGvt7$1=2Cn^dG z8BwD&25=Z&?G^-z6k|+Wgj7O6i-16RD56z}LfMdnN3vr(&d%=a&g{zU>>qc|y?5q& z=gjAR&zzGP%<#9s5pV!tK@ac?0YKpWGJ79V30uAcfTby0!&1Iv#-$wFn;Z+i`);?^9?=TGuk(s)DuCFJyW-SN~<82q|=Olv>U{&UbUtI z%AgE(pQ0PzUmR90`@zUNYT#_1Xf){U#TX9p(ZtN9hd*x=>SYjM*QQ+fB3Ku@grZFqn!5dZG$>bDe$avwm35X zm7L0(mC|!l#C<}en6n`3?mb*LqMjNac5!%FWzl6pANTg3JjJ$)%(f+qqiy`1ViLY{Qz3{@$gCi^*cT4xY2Xvul{iFJ9Q zB(>p?Pl22nUfI=Ti$U9a4|z$xVtR`@t%YK-IP5?ejtH#4q(NS4>MldKF6_XK7n*dC z@%Rs7a1|<fOyS=r<(YzR%Kb$>sjj=WAZ>WsyQ{r`OMfP9LZZ%zcpm7aTNy z|5fF;dI59g%ZYl?S)e84lJO%%^6# zUzbSlshtqKR5Lv6#I2p)n6SY8%*}9b5u^S~R_=;$!6phNZOqR7G1?ft{dHZA1fP+? zvOrD3zx&g0t3K(8VQR`-sPxg}nzWbRnfCXb2O1|Zt#g;Hmg)w!P2l4A36_5q&Oy*&&+Ig}3O3#-F-vfkLdf zuR=RZJ<4b+HGYi4hcf6ao#^wpdabRGeH^~gFcFfFtG25Obh-KR%uvH?JF42vz zpudrNxLDh&cP9g8Fn4j9zODSNBE=n#fQuzfIVAU1qF`@v}1v123?N!wa`>AVz}InDkTCd?gCn*VsEsy zj9JBx(eWuBZkCgdHkY!=W15Q_-1>FJ`mwyZGW0$N&Hnq^;WZK}4n!x@IG@Yx# zv!rxsC2&BmriVMCpD9rwqGN58NVK8WC=6mP;!4wNU@!q2TzP`o~SuhbE zd5@^?bd5wtMm82~Gs2)=)t3L?-SZq^!PZ%%kqX}7ZhSg z)F;Wd+^G_x!MqAVk9gXc_xJZ#ulBGC!_YoUtm|!zUN}FU|Fd1nkw+?aWB?5YlSkQN zN?V<&+_2~qPVNe+-VO_BM7_!H1^kZ`K9u!;a{DhJ{&(_>k8STCaDRv&Iqc0*4w$*l j8UMR6<#!_FT7%gdu#6#iJr$G{0RYgqGW;qwN2UJ_a}S@( literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_question.png b/interface/resources/meshes/keyboard/key_question.png new file mode 100644 index 0000000000000000000000000000000000000000..57f518721333f4698abdc2e415d2ce44a97bd49e GIT binary patch literal 2727 zcmds3ZBP?t65fO$DV8KkR2nltl`j!=v-%df7H`|_v4-2nRlPvXZL+) zcZ-7pc37Ab%mKiHzSAcJ011l+y(n@Bw0Z#eAj3BzBQzy0Ba@LH3*KL*#KhXtlNj-_ zA+d}vv(xUydI6Zz=|0}OvmTGj)k%+;R-C~hzR53^-HBict53Wa;{XmEKR5&N$h)W$ z@7Y=&vzT1s(Qqp`Y-S84OGwI%qFmT@Tzc2%S9QuhQALYmHaJ+x2UZ-PjOugCUy!5g`rAiPfjPBEH`&QdiJej&c^bCQ7&9bh24ZT<_ zmZ4d-5PlXV6biq&X_6GXNxm?XO2u3yqF;f~NGCZp&9{O+G{EZ82^H~_6?D}C~Ib%zM6_I@@#Hy{%L$# z#JaZr-aSA;`_?mlC6q^_HAzC5n}1$JcRr2UO|$x!{%g@n3KQ?^;^LCL{Eju8x-z$q zYqMF>bUS8nt=Jk)@miOvmF}7umONQ|EmSbCH_Xj>jK_Z;y(%=Y}S$@o$NSOSgaDuy8AE;oGt4H@< z(FyB6gdj%-(P(%-saR_Kv3G3v+7CMb2Tpye_tgyr=}tbyQsdCkM30mQcE@?K*@mv%iBc+W0UA7Rls zCbfC_{ZG@H+$fw`1NVb8&p!oTaXRI(^mYvzx@BT9 zt;O{E2UZ9UL6$6JU~Sv-NuT)wuRmQ>-alo70%oE+afGZN8)2;P{A_TL+vR3~xGPM= z`?e_`c(z(Eev*CHKjC-&p(pFi;;-LWK8~Q;`#DtPOGUk7XAHX)j&;!DNyIRwSFuYa z8J>bojb3Ybz=+ZAsEzUe6Nezq{y356%sN49N+16a5~k~H-tK4c2oS=kfi z4eJWp(=nWpGQ;hdG&Z|>VqFSF5J{?<_XS>8ms^#egMFD{zn8TmJw_g~wi9vRS*G6d zcSDzr#%4SF(SnfGXa-^UE80Raqh*w3=+7l%D~%6b1xh>D<0sB1)b*^-)9ncU`HOl# z_Bev-yLG{_f{v!D6wUJFZDXZCJzUEcDU3zEa|QJVRmSs@Kok(%^h$TkYJ+BG80l`t zQL+0AjLE`}^^IoG6E@@@O<7T3$R%gC+8{W)_ByY$kDjz_OS95PPu?0%TWiI2kTiwY zg|$bu1mllyujxyStB=6~b3miHcuF%BFiH=T&r|#d zaJW^&Q&S&ln}YEjqlW^gfAk~7JtVtsV)`r$$5FD0dYO?yl!J%*$f+UO@S>!AsvkkeL zkTzv7Trz{>vV|0(Y|)$lP`eEZ2+t*4mvE&2NH8w4$f=|=FZgp}+r!oVBX+J-ngMnajF`U4QEK?uJ(X1IIQ}LwY+N0^-qK@9)QPNXwwZkDanhJmeKmj1&4b{$(bCZ!} zstl(6jrr_#UT;9-wPXC%S-b%}9PwJCR~x+g_O*M#JHr3(=Jp%!Q41orsTJgNI1GY` RSrh;O-8aDJ;-~xbe+OGEGsXY_ literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_r.png b/interface/resources/meshes/keyboard/key_r.png new file mode 100644 index 0000000000000000000000000000000000000000..6277c640978d83298abdb27c9c8db0ee56a9c428 GIT binary patch literal 1897 zcmeAS@N?(olHy`uVBq!ia0y~yU}6Aa4iHr><-C@GfkCpwHKHUqKdq!Zu_%?nF(p4K zRlzeiF+DXXH8G{K@MNkD0|Ps^r;B4q#jQ7Y59Zz~5IOL0$tl_X2ZCqV(wIK5888|& zeq@=!nZUe7%2!kaEOy1@~{Yl(PWxLp>_e>$>u@*mo%0Sp2r`5 zEZX_!*JPd3U5gAPSs3;?MIFo(wEuAA&$Gs*_e<|SEV%Lhd;Py{I;ZdEJ$H3vc+kyv zn7#QRv;UgcRU0Gz{Jx)WHv4MU*N7Ykh6mAJtM_Nl`usCTgoD9l;4zS zhLuG=0C|@6xkd1{^_^)V`o?x8=g-o z`&(e4leeatg<;KPC2`lYc8S z2=p1-b1=A=%s%_>{tE^shQ^f0ubP0l+XkKpqEGn literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_s.png b/interface/resources/meshes/keyboard/key_s.png new file mode 100644 index 0000000000000000000000000000000000000000..1fc108539141e31c575f9ff41b1eaef9c22bf2e5 GIT binary patch literal 3080 zcmc(hdr;F?7ROIwf)S%UL}{_5FUAS*qzS!$L>rQ{^;DfbIzT6zW3bw zIroo~{q?nEnCxFE_JzSlA5?{RQ8HssuWF@mNct~x2EG-6`rkk??4hNP)d9yca zcEn=amXmGBl@{00oUY~^`nryFcl0f`ixq1MIwrp{XdHUJeV4m=fWQBj@Ig7nTMhaE z7yuNIVNUK}D<{wI^7EJL&ja0mh|ih)9|ilv&heyq$^rGizd-gIWGM>CQY07O2v?aP z2!nB+gDA1Jej~!N{WBaNn@eu&zS7YLXHt`rVvG{2E?OleCuch&^~XXdCw}WxC=#f= zQCBF%$TVCoH`0NxQ;YnzPDBFhEjsv51TBBJWZ+uF{8z@c{Mbt*stz28~W3zMz z8p0fm@#_ja?4`k+&2HLua+SCMg@I0L{V|$jF||a&2Q;)kl`DC&QAvPXj)pBL?}S_=R$0o$C}wI7 zPj)xwX097IBi))P2J75@TQ%pVk4=T>5iiY}*qC6qnV-7L<#JlQ{a&?v8&@ zS63%_u&tA)J^65GU(hPT=Sz_@g;lNQk6L-;^=V~gWs$v!2vWj-qdiU+BsvyHITUGs z@M`hl_Hc0UdSmwjGt#^7G~;L1UPvSI0yeH7RgTTP*87pVDAP_AHa$u`C(n-4iXsxO z7uNJdWpMk36T(@f?@?rp9kNSXJ|vr22rZ=nk~@>2B~W4SN>geEm0q=_rdew z1eCcM9t~ysY^2HG9ItZ&{lRQo`hjy=uPr^eiDES`p`jN_=R8MrQ*2s;o)I7b{}OE2C`3Z&LhtP3{aVPSG?sS10CMyDCV_$_4V5& z8Pg|g{aQ*Tk*mgxO=hX)Xavk<+Na{JmO?ZJI>QOZ_zZ5GdP2Qm;1Zyiya@Mc-^LJm6F43zcHQgiWn70!XKYNcG5*A3+ zdxh~1MNfvtEuFAG`BsdWuaKW{25J07tE9xlOj1{hX(b8=s;0HS00wjqqz}X|8fJXTNojb`kO}0=o`lO@V}52geJhHdw59EaQv2`t1E-0PLxu!Z0i0sIN}UpyrRz8k_v( z#_Uti*4h=+Z`Rmy@nDx7r|o5og&}99d#cl1Dz_iiI)lZGgr37eG_4oXSS%K$tU#=T zA7wKdraxv>R#i1+;UL&xLGrn3x$)g$MY0#JO%^j~*iy5G(Y~6EoT&AC<(l2G6?DI7 zdvBbsr`;}!s1=-O=}0^^Bo4T8BOI?DpHh9;(b-SG<-0+?1`W})>oW}Mu}UAC184bs zz8FbmFwS2kT`Z8T{rlVa2)#uZcopeTl;BuA_JEO>?hF=e7K^n^uFS7$4eCR~?mG&8ra!ta3 literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_semi.png b/interface/resources/meshes/keyboard/key_semi.png new file mode 100644 index 0000000000000000000000000000000000000000..5cb1b495a4b4f79db88a8a913014169269d1153c GIT binary patch literal 1797 zcmeAS@N?(olHy`uVBq!ia0y~yU}6Aa4iHr><-C@GfkCpwHKHUqKdq!Zu_%?nF(p4K zRlzeiF+DXXH8G{K@MNkD0|VP~PZ!6Kid%2)9xS}&AkvVi?aa7{`9%X?gjNIJl~ zl%%{PW?%35Hh%D19=!HzaQlxefqAM542%p83=9Gc3@i-9Gyk1E#&IGca@ymM8GIjh zul%0J#lRWK#6+Sg=r&^4{)5lV&4F#}HGP>-FGDM)*;VhCIsD=OWm5Z4<33-~W)>Fm z0{?*iU;d?2J}OQ>ee{k11ILPMR`(v(*v%GVVt9G(-#Z2YhK`k6zdgKJKQF!6i9uo3 z>#tV#4*ueo^x0b;tT6j;-Tw+3Hfx*t{^blim>V+W?0L&J{A*@#@R9$=v`42~Q;|X7 z!rLTxNve}U7Q^oE- z_3!Hwo>kh(@VU8Y)iE=8oDvcsy%a%7gcNI+V5Haxa&r?n9Z+cdFe+38ZmniuX4t0L Wsxx)s?;Q*b3=E#GelF{r5}E*Qq!^|E literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_slash.png b/interface/resources/meshes/keyboard/key_slash.png new file mode 100644 index 0000000000000000000000000000000000000000..be7b0fecb486566ae909baecac777cdb4e78e496 GIT binary patch literal 2580 zcmeHJT}+c#7=Bx;{S+%7!d6h!4jB?tEr<(>MCvR*O%Vo)0tyvqVa%}#gF!QBD{=B6 zL_lIeP@6dsBLM>_3}JjAVilr9$Y>NTEr{6QPeEKM{af0N-Rur8cC(9<_gtLkoaa36 z^PcwS+QmX08rQ=tVjSNSO;PaKd#wZ4*)Kifzg@!1<9G&2^oCY!xJR(Y3#Iw z6n-Q>fhSCV!1o7O-oj?>Igs=8_4Dqm>S!m4n)k?wxjd45kQ_0fXM!;nK)>`P(ik*z1FPdbiQB5?OgYE{D&k+E%G zdHIVxXF|8%sRRT!9E$p&)vCON8)Pz>>>`m=H7RU=zSVN-2oE2X4Wtwl`U1wjG#X9{u%Vq0zo0B*H0e#4nTtm%RtxtB)hOMsZHg z4%xtZi{3Mcsr_`{&`Eo7SZ#TG01>=ho_<9?wgH8g6fCKRURdlXHFYw zbA?UxylDzS@*;YE;*1NSTU<3MY#oWap(rh)x0+L{Ts^Feg_p=mQ#1X2otOk}J{?F$ zNK}+m)D{!O)G}-bdvfqAT?k@MJy*lEJh4*(ZCgb@Uud*zbVcVs5 z{>@|8#jvo|1W9FP9M#(SB_Ux$+tnQ0tStg2?_T=r+gr76opbl{yohd})zM9F zb%yu)*Iv`hz&nPoQEb|P0v2l3Tpb+5bnhNyAW-P%x@vsKsUzKk44oXo42Kk1s^^w+ zwY?H(ep1mM2stoE9i^KxZWK#?P2tBh%=G$hDgNpxt`ETsH~Xw>#wajW zKH0|TJOQN_)Pbr%X}cx4Ilc0k_eR>=hnqRLeEhMm7zM1jhC3G+#C&$HVTx|b_l2{~ zeS?ApJ0&J+sPVlBwktw$tA>lhS)VgWL)&e7ep4*LF4b_g%b1t@nr_b85t$jM5?&d0 zYu%(GcDV!UP;D)P*!iUbaG#u>_SU||{pZ%2X@I(LaP&TcdDhQZ1xF-+%A%yAfyrjM zI5L^U${miK&(a0k%pH)cX}!LggC8ZaLT&6;-|{yco?l9!?dHoBu0&j) zq@i{YP}%K1xME^YQ_erMqr1-FcTxy8AF@ zqDWv(4K^@7o}_wuNXicn4;Sf|SOySmWLpQ)f!Pq}r#@6o3RQupFYj1ivdU3p-G6oG zZ2OaMucpq-*((8KE3(uaymM|Dk;&Uy!LcR!PMtF$-SLO^IMq`{#+2;p19rYIBsTdQ z3K*vO-vbaRi{bwzkTy1mnd{EMRTK&99N#5Jr|b1aVeB9K+X<3Mcjc7}FVhvz(=BQ1 zJP`11oVDBV2*K=+YN#ZMlU^oL2RRd_m2^`+l)f4l&rO<3<-C@GfkCpwHKHUqKdq!Zu_%?nF(p4K zRlzeiF+DXXH8G{K@MNkD0|VPkPZ!6Kid%2)9?U&tAky$KptNCw<4lGd3O;Po2F%h2 z*wUE$CkXCw)Y{tawU)*I(Us@#t!JN}d`REAam}<;Nro5aXA3fLfUIs{U{GKni#f2` z6)1B&<=QzY2s&_xlOOW12!qjNngrACkoMlqAwO&F+O5a4?e@(O-MMyJsv+yHO>yhz zvpuM=ndhgzS;^ep;mgt~pT6AwU(KEn#=)?pX!l*E(<}{g7EFuuXP-@*_k8*381Wm7 z4qBh5FF#$jdv2s6!-P{`t9o^c|1~o>m>ra6xc&Cp8UcnAi?@b(b*`DS?Bossh7-ya z|BNk|oEQ{l{{AZ$Y1X%xL4o1IW4;5*4jc?AzrXeWVTfQ5U^t;x@z1z|g@du7#=7Bw zmpQ|W_Wex?`=egh?dR7kb4_4psAQ48C<5FaNMJHrPaEZ@u;)sl4H3 zS2cr5xR<32-$&U4>wORWtSQrT3A@M1WB>18e94FF%pc?}nV19^7+4q>7#SQG7>HrU zJ)Am0#k}vaiSPQFJ3J>(c|T;dddk5;q;6a`QLMd(awCZHK8-xUz(|qplxue&Efru1 z1}s9vq$eb8M5jR{JxELnwX=|$f*v&bGqN!}`CF{Y;b1t2fq{X+)78&qol`;+02ll3 A_W%F@ literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_t.png b/interface/resources/meshes/keyboard/key_t.png new file mode 100644 index 0000000000000000000000000000000000000000..c2082b8f51223e7033afe73b82a3cca584bdd803 GIT binary patch literal 2038 zcmeAS@N?(olHy`uVBq!ia0y~yU}6Aa4iHr><-C@GfkCpwHKHUqKdq!Zu_%?nF(p4K zRlzeiF+DXXH8G{K@MNkD0|WaEPZ!6Kid%2)ehj>oAaUT~rDBFV3ONVZS~!nz_6QgF z2^e)6G+HnwHs>VFIFK`8S;gDRZJ`Taxlelj^R#tK!Fsd(YVj(~yVmVzykLL7m%)L7 zL4bjQg@J*QEat#!XJDkr@sw+KpdjcVAx?J4!y}Bmh=XgxrTsz4TuDaRV3DI|&z`;L ze?P}8t%JcqJ2Z6a^PQFlX3t?_q9oZg>@`+iP5yz-AS7F z)h;vETd5l(QXP+(xd$&5exmmy=mMPGbn$@bg2 z(@!ssxADEqUt?Tw>JERyx|5%4tmex7E%~qXf`54fr9n?r%rz9OT4lt-!tiT_2A8t~ z!-D=(n@m95jIB|UOiT>5Q{Kz|n)0o*NRVt0+sNjS9vW&Y z)w}N>UpzlQ|H5_7tnLgSE*Dtb$uZ+&a$tC|_+v#)b#*r1hj2f}i?y%6{`&Rn*XHxA zu7M`=&ren()fB$XSQ~mSuz2Ek|wU2!9 z@^!#`Lv|*H1MWqV8U&ITq4YsQTzdnavVoA_AqsF7EqJsS fOps^dVc5Uw-;Y%q?4KAI7#KWV{an^LB{Ts5EkS@@ literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_u.png b/interface/resources/meshes/keyboard/key_u.png new file mode 100644 index 0000000000000000000000000000000000000000..657527f6c0068fc41a5ecbdcdff232defe858b02 GIT binary patch literal 2232 zcmeAS@N?(olHy`uVBq!ia0y~yU}6Aa4iHr><-C@GfkCpwHKHUqKdq!Zu_%?nF(p4K zRlzeiF+DXXH8G{K@MNkD0|Q5tr;B4q#jQ7YFBabNU}#7zp3D@|WW^BP@PeVdLHj_6 zLJZ?FrgNeUq6%6YtPE5yG;U(npLx1fF@ih$_iwpBA|{)r+^pX^$#r?=ENg~+^Xp}p z7#JBG7#IW?7+4s{Vh*f!7Rnq?v33SV3W5$C;^c=sEW%(knI=)FT>x*g@i@lc-moMj zwDeaTP56&enYJ zXeZZiv2PbN)nunNQ0hd=eE6g5+wZ;23`_^gL#}UaW?*7?d;4u!Xs9J02ZKub?z?Vx z|2=-ZxVeYHVUh8d3hDKHGAS$!Lc9)3ejiBOyzD;*gNp7Ym0F{jE_?SdFgEPAIdA{z z)aLx zMowE>!pG9EUd`vg?W(oW5`WV-M`|2=$+ISG_4Vu5KYY&GpS=FMbpLThHa~OoFNYNs zOkD%Fh&DX_+i)^PXxq)VWrxqlKmUEOZ1>)i+;WHXU(_+IV|cLtP{$uxxqUjPi>&5a z-Muqm_JJi8tOfrL{pBscG}DP;LG9s(1|LuUKK?(Bk%i&k@d_Ki%6IuM*%%uls`kds z%GRlzr$60WorAHV9Fo+j}&r zaP@4)%{O&={?$jl_52AE-PiH7sPgH{GLLh=e^>ALXYn^bhDCuv;QGg(r!OBajPCRQ z)!zBvGCzkeTs@BsWN)FP+P&Fd4hP7weKGiGUmy4WW&jJrx+NN2+qZAuq!V4kWt@{` zBDGgGrr_|;8x5~x<~r=&!#B%6bN~JIVXMDZ)qdQ*qeZ*Z;PU(LzjN0Mp-}q>EzN&u+p|hAEW8%^h0lIP2?GNI NgQu&X%Q~loCIIFy+yVdq literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_under.png b/interface/resources/meshes/keyboard/key_under.png new file mode 100644 index 0000000000000000000000000000000000000000..3694dd31090a560a802191acaf243c676263769a GIT binary patch literal 1579 zcmeAS@N?(olHy`uVBq!ia0y~yU}6Aa4iHr><-C@GfkCpwHKHUqKdq!Zu_%?nF(p4K zRlzeiF+DXXH8G{K@MNkD0|V<>PZ!6Kid%2)9^`FM5O8%g72C2lkW)91Np|hD2Z{Vb zX<7>Rk4!Ikc;*aq!`~P+1_1^J76t}J1_uVRm;$Fc?jy zNfc^lU>x8iAiyBZzyZoB4Gatl3}i9M(f;9XE|b`$sM*E;{!9?OSp9vY0K5ZvO*tS%9LEcio5p7Y4%FAXkBh+d_C?fqvQybAv^rq0v zIkniDUMSM^Vk%OpMqAP}Ct)VD8k#0zMq_?^aQwS}u;=VQ`^P=cJ@?$t^W6J=o^zj@ z?eDkB#E4=9046@Gy*B_LFfLdK{hKlCiafps0Rqqf$N>5yPj74Igw&*R-{@#obV-4bDD`5pRzO4>vV6z1%mT$V-IheS*4sFH~dGM$Z` z=kKZsoWKW*WBKXl1Krv~4J%(;igyQ6adUXHwQ}?1@M4K>f>gtq28PhJQGJkQ>dcNg z;^yw|?%~m^vw^Fkqop*Jw!RP3hvh-)FmX^RO{^_V_(=~DHpkMmZ-c0$Q%!YJDjC%L zd=p32@eVJ=SNCtnjJb&fL^!n~WWI0Q=_)7UlMb{gqE>4_U{QQOoq4!)iR7=t7f~;1 zp^)^hr>AG{a!y2x+OhwJq&x8@DZE%f4jmt(Jo!BZxx3n=5aswZ`<#9cD<$?ijadBnuA@*cA;QKsVE0Yh(llAghr-LI>QrqwyKkbW>=K=wDqcmxjE+K zb5pq!Z^|*RP$C?iV82W69g0JN4R1 zGVs#b8Q&QkI*l*-F%EGwzb57jc+DA2v6X_GH}~`}WGknnYW0Gkv9P3sc28SBjM+)p z5%!dp9o8lts1?ov8n->k=6#q-u@7$;-mvi2#BYkpXaQb5U<-{k%-Q>P)!}YLqbgj2 zvw?=UC)r?rnIBsqVZasvuK)+)z%IL1DH@TfBrLL1x#bl;i@6gX5f;{zcA^^(Aqf3z z<=#QTxS)qxirT=U`Qpo3g+d|dp-OPerM5GFn6rF##p?|ZIyoT(VQcbLm-l@bALv_g_Xf{R zDQxQ`Gic3mK#(!M+N0R09b*qC!~Z{k@%(=NjTSz}6sq5#2nqPP7Zfg+cQgY4_^kBv JzPy6I|8IdU_TK;i literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_w.png b/interface/resources/meshes/keyboard/key_w.png new file mode 100644 index 0000000000000000000000000000000000000000..15de9b25a8cfe59c3350c498126de9eafdf30158 GIT binary patch literal 3466 zcmc(hSyYqP8iscwQ)O-|qEbVl1=BJWgh0RqtcuhE4$z1L1PR8&V60F;f&39HLP4-1 zpalY_)`AJsA`BvsKv7W!LyZW5LKIAx!jQy<=}-Q%I9;pfT%8NJI2U{E@8BScaf%GQ}THFt-}qa$k7dyEbR27_V% zU=D!Jnx^%X>tX)usSV-)y&>@%#~B;2?k^kG?fl;^u5WOEV;jVE{^ty%uJe*LV#D#p z1R_y8WJ$DDTRpX>^(j}WnBc47gK%2*sgMvVbTx!WpPfzT2rH(IyuG~xMTlJPxO)0I zpFhsw@%A`E&d4#ZhY!;}($f+S5C{aVRL1M{yT6yi0f`U<#l^kquz!(Nl%12q$(fs* ztHPEOiRf2g^6QwfO#Z&c#zvKytOQbHkxm|6#cDO>OKNLFcw(vn+OvQQAauhMl@t;va?({;fBX| zNo1Yl-?qSsmo80J8=%M&+Ni)>r|9hC38h0N*1?ajdlP$&BL~Vuf80;TD`D8fRIhf` zi^>VQa6tE)!vi~aEiq+s8vL!VRqmS$ll}&lpkaT^nb}KN*V~Cf?ybk%kq`LEdDT8G zUnISm*GG|?A8|ZeE5EPWb9mPO1tO=xA9oaC&0WUczNO%cwL+E_7H;erz?3P5{SH$d2>kryvVGd{FOl*$Dso3PF4`gnKuZo7$b_dnQYX`{uOtP@{n zZw(v2uJ|bu>Fo4|PMD%Ukkh4)^wCT@tnenk4H4k?48)v2K7(2B^SGS1{2 z_v~SJWUc2iMuTzs1!DEAzVL02e`laVE}P#%ONv%tG#DcSL{%E-T~w0Ie%>v6YDHfz z=!WoAh$KS%c?_# zVo_9Zx0rYUNNc1|Iwc z1~_s}!JxLVJ|<}#F4J#u{dDvPH+n_8n22s8r1P1>ipeOxKk_7}@CvB;uGZ;d(jrAr zDAn)i=AP^yxfdf-RrJ1zT64;JY(|)1Qr>az_0q}5#Fk}@OP7U1a`S@+I(F9@L$R*G z>fW7K&p)vY8Tm$fmy>)YM>98qG&+v)7ET%^p$= z_u3#9z*Ga~&(21>RxE_I@X+U6uwz;^Ypec z8h41|7_@4BvR@Y)3E4ZK$(Tp{=6pRgzDY%FuH!RF=^|&t13>zVEFtOg*>S9Q*-Xtk< z@r_={zqO4RK1}$OX^U{5`79q@fb)3m>@I>3)oO_9t4<7Kpl#iZc_)$l z3TyS%7mSLXj*QD5xW(4weBTXaf!CD@k);{RasrGD1)JrS4Wv!CoOuKq_VsF}cDGG~iD=&Ru% z99r|H9C#%|v7H zw78^%Iw3NcALCopO%3c|0J2eyj6E;)n3M8ftqgm&8R(-^*z*RB!RNUB=XSG3N&~>) ziH1}y*TcUufCd04;0b^cSXa4Gyv_Un94t4I$^VLU8w{2k#LfQ`FxS~>ow;;I^?4h} XH&uO{;pSNe002Jw{q|MuC8zugEve^% literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_x.png b/interface/resources/meshes/keyboard/key_x.png new file mode 100644 index 0000000000000000000000000000000000000000..d81a423f3b3651de72b207bed25a6dc7d38976f4 GIT binary patch literal 2997 zcmdT`Yg7{07Cs;)7AAVrn~VZ!jP_8nOv_71Dm6)inW?F$J;2ZmwU9JX8EbrGjWylG zu?&0pno+aI#Yd%HDst3B$-1P4T8f5>PZ%X+maa8x&97Pa=CAwXti9LT>)ZSL_Bm(m zowb*`%Xk@T834d|_cvbq0Dyw|1D`iZD9>^LfF+xJfbAC(!9I291Ox1##~fy0cOO4= zl(CO-h@KeN#2^5$JaD(yj{OO5hu@9I^*u*7#3@?W^UOZ3*kdU9ZV;^7S_1qaY+&8s z&vWCgD1^Dia=4o{j)^~*sC?jATw*HS@uX?MSWAkGfzyqv)oNHneOv0IB>)O|0AK-t z&y)J!-7mrXsnjCj@GoHer|OG_1Lz`Jw?O2}JO77{KkOG~S|l9%m(NCm(ob7R(Ek49 z6&S=)syU2Dot@KZqaOZBvqxP%r)x7i5j}#;}F|hKUFs{$~7d_{qjaRd_-DE_-PL-IelmsJ!j5+ z&hWw<9B_O3`x!N~2uzrNr&6gLp~gH6CI{$?rIA6oDa{Yg;;?U-Tdj6Jmx+2WdhF8% zS?7q^hh-SObnS>H38Oa!zwJTM1peuv5vS=4shDxKY<%AU7a8Lf+MkEyMbhE7A3j-DLHMEYqNkqZq z3jDdg*sZVb4WPTPMak_(=th}p?=u6Ox>h3L%8{xAPkoNKTqRqi4r{#tR@~g&%q6xF zxetR!ICU?K_WPUHY?VFndD~)!v_{IzQ9<7sRqT*yvMhlYIl$M3Z-}?Z9&Xn+^_f6H z9NE{)(d7MO?I+*HXM!0nOK#_I)F|`istY@1%V)zM`_N*?#ZP=%Kp|B*?WUy)!b9QS z`Z)C<=FvH07>Gh2s0{CD3fWYK+b>x|A+cMcU7Ex4>myd%Cwz5tJYo2QI}`}bY8*8} zO;$Eny7$9Fu>?>^Y#(}3ze1DM7435AnP7x-Y-$(iNl#m#W@3j_Om3UF9j+w~Sy4!A ze|q_V=os7f%~xIX8l3H_kyAqLQ}Xp`rra(9NFF#Fd^5=#7Nrt5_HcXq`<+P_}jygXOjhR(Czmz z@d6%?U%Og%D!F95^sNpL3wie zWF*s$LLy{3Te<$rGPQ<=(m5IMI9W6__s+cy!me^53c}+U5u23xiRsA#0)j#!{4+#; zrSar@Q^2fjJcz6b9JDKvmL>}bW)u>k=!NGIB7H%e@ig)3GabHx?`d$`34vVRmcP&2)NI!R`nE%J5V#$a3SbIyv{gCr>KB^oO z3Vzi3IRd;bDJ$!ZO^{-$-m#kRO$FEji4DKGe`aRpwaF6Y^yy-0Q^j614~IpI>6;>Z zE?pejb$+L<$^{CJR|S`|bQ|sp%F0eBCr@9O#^>@9VB!x|a+T|6Pm_!@p*Qj%4ckK< z`E|6?f1oa)4#YtrywsI4%{Fw-eau9^g(>>@w;aeDzZ(kf@zDzx%VsZEM$ z9)xr&P0%Q92wh3ehwLeq*0s1dAi$HV#{>;H-DKv;`;3Y(M~(<3vei zTVT>_WxN_bYGDco-2t&xj2fB+dE6)U>fE|5SnacSHfgLXm>1tFN8B5mm#jM;l>3G& zyBe|Tk|C*<41EmUywq zlo2%RZv)1bG>+sw4pYT$<(`lQ3aJKBM~yPo`K=K$(Wq40k?J5~p&D)ZhH_=r6)a$P z4uqKE|1ACD@zw0Qui7YuDe#RcgC%mK%7Pb zwBJ~+No#cVdDXJ`Y2WCWJr4K^C^^t%P6wpyaJy8B~?w_E3n zEz1}|`7kU1-5EG6fB+HzNC05qi`>P+$1D~*L>D=O7YYCC5&qw-75}GOT!TutwgJo@ W?s*+|hRFf|fZb%OSB1yHwBG^ucD9EA literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_y.png b/interface/resources/meshes/keyboard/key_y.png new file mode 100644 index 0000000000000000000000000000000000000000..cb85af5b322234afe8e41e3d2c24f2ff8aafc1a4 GIT binary patch literal 3072 zcmc(hYfzJC7RS%~a*LpbtO!D_NtG%Q0YzL{MG{)kQWWG;i9`YXxi%TfTaW_bp&0+{<)hr_548ox=?Gk%0b0jfw=ZhIZ8Hnm`rlI|Ga-u+V$24`{EF>Rx7*(bO3Vz zLjdHX!ao)VWJbl$9H-Afy5AK4Xyt!z*vB>QcWj2Z;r|=~8#R%MJSgv;Mb00sdM@LZ z(H0o;ml`Y8k}OqI`sG9p#|;y1STH+ZC^Rt1l&E`pb_v&y_8+S3O04n!_lP?{5t;E2}l8S7vOwa;_<&vg-cyU2a znPw_irjg)66jAQJ%Gu%ATG4KN_r_{whf71SY*{*pM|}mJe#eQ#kgK}Vu{FkWo*_&; zxlcW%)u;btZmP;*-pgF{Ub|((r$Q&h>6I4-}0 zKG2MJ?iu`m5WcRp*sZd#tH~dmv@(Qe{+sOPkC<8fEp3>#f9Vp+PW8)|%j+c(pC48p z#NQ70n!C15o7lrw&Nkm?PCsZkW3>wR0LnMg3sq-Aj^A^H+qenB52R-K&tmrCf==U@ zMfg#mukpV|m%ni77tWROI0pz2NMX>b?^4_3@&KjXJG^c02zr@O6Vk_wjt-YNTBZll zq!mM~b*YQ88C^#jCQgSL!IMORcVoFWOM)+kZH4Dre^ljq>=`G&!`osu(W`tXO?Fs4 zqHYUacG*oPXe}g~*+a3vv|XAK^;yl}-^K2%5!?d`ELBq;1_6|DvQIY2ywsx3L32;1 z(|Lr*>J%eC&=&XH@Uu)mku}R6ii2|)s-%N+yB~cvF)us>Cqc2_q-|20g7L$q!S*hc_c_Q$@WbN zA19LyQIOPZ&^B{9Q#hA2fRPQsheH}0u&}#{>`t6*ghI(35h2&7D9O=`+hF*LM1e8) zJH3r~($B>EydnG;h?BnCs=}+)yFv|aM>&JKN$JiK$;Zp|F*- zeu*gWy$;S-h8y0UWjdrzDDRO*tz58>by7>*4s9#Sy&F@CXg^P<&j%OpN&m4Q-1Ng9 znKM?XJeD|ZhQe0X@!YtBxL1!g5_?fvF_ci3;@vp48gDd(i^U_FxD&T70mAQoawI)d zOI#)H>pb}(_+n^dtx=Th$BqGA{v?*#saG;GRa!)U zh!LRbOJ>m>t15go3Z;H3YR`wqvz-G4OlQ3STPs#v{}GwsZGr;E@(bwOBMok{h6@C! z^76BIp@|moNZ=~>Ys9XfK)X=BS4k^j+}9=TmSssm?sou@2lML%>F!1fwr%S zL}FS5FN@~^1c>ZoO>G}=S8Z+yayz#%R! z?(C%Lhq7cHWg+v{;a7bZLf!?z*+Os!ykKC~&wYe=$+@v`0Ip zYHH=xV`=TJImELqwy&Cb`kjfqjAJ+Ldv^>6P7=SFv}2n?v1fk~magaz$>OQ{Y6-KM zA2#5jU%5J&4$7en$sQ((LWE{wqNZbzO*RKtCw4wh`}*D#AH?jIiXjYpS{Y&vuItsW zI>$^9e&x7YLKIDZ0pc#QA>`>)tS-}|@hYFQJTL7CCV=8Dvr zz7O+A<`H|Rl6fvR7bcHEo3p3i^t3ka&$-zQ_VaHsu85bM9HlfUW82yek55m#31zod zC9<9Gi}H0-uQ}&ev|>jNoWG>=0GP~87Xu1>015y>@BjY)@MD<&_zG1z{J~~vCO-2w qn=(VQG(+6{-=^umGZkFGEJC6Cjbp}9M~DRg@bdK8c;k!EqrU>>4D2BQ literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_z.png b/interface/resources/meshes/keyboard/key_z.png new file mode 100644 index 0000000000000000000000000000000000000000..462531351db199b49c68afdcebcd304daa202f80 GIT binary patch literal 2115 zcmeAS@N?(olHy`uVBq!ia0y~yU}6Aa4iHr><-C@GfkCpwHKHUqKdq!Zu_%?nF(p4K zRlzeiF+DXXH8G{K@MNkD0|Wb0PZ!6Kid%2)2Ik&&kU8+MM2o@ML-Gbs7Mo%7EXUpj znoVtvXBMOd7@u%7U@<+49oBH=goWM_rG_e zywG!@8EgFizeophSQsu{Eu9OZH*LQE{`bEA=-s#9+S%E?QCwNf?$Fosw}D~8VgaYn zP>^v2{(G2|^e>xSEtTC{Q~9m@momeId6n~KFEjkc%;Zp3bBmvY!SdCvJc+g)TaVT4 z@YZWQ*|bq!fT1Ym4g0!JTphpd|Mr3moLf2X_BK@@6)6)MJ$~?X%^;?l$uW_gU zu{sBa6Bj={kgxri{ieO9g7wH=PDK-6;k|cm@G~)Z8iDjKd$r39Y)0PCHRd2+E_qd8 z;ZlDzIdaFEt)RHpmIC0WM(Hb;0R>yawm^zQ~d-x#qM zjf@*uAt|-seT{_7>#cY5?%%uj?!opC%Twmo$e1`R+rz{(umz(4Lp07Dg*~5Ph~mnS z7z)vIEJOi?+8cH#K6E{Tf|$zz`!6`;u=vBoS#-wo>-L1 z;Fyx1l&avFo0y&&l$w}QS$HzlhJk@)pQnpsNX4x;cQ*DeH4t&Q_+NbyOS-@%#?%0& zt$|Fb7Z?>&^bShCzUm)-D}n99y7Ma!9Ma#hXq>!o+MoxQ`rV88VJ??j1R>nUT zkl}%qVoT;MF3*ihRz59^45G_!gmcbMjuaDQW4NOn?w;2#3%_SV@XfcF4&_PBa$s)N1&2ea4+>XQH^Z`sSFsBm?SD4dD zaO->q^dLkFQ8X?*fXn7?%e~9U5Rl$(b0<-e#hHQO%1uT7@`%eOwfAQ;GdxJ!=}^=j zayRj&0goFa!-@?K^WJY*_u8hDk)iz5+lkyR?SIYs7P}fUJdoRIRa8BtVAA)>d8%`i zmzta}?zvdNz%bqIhKX-~e%sWmiStFGr={LDQ)Xg_S@`~Up$vEDhD}y)lJ9>njlC+t z@L@Bn8ILokb)swe4R()y}pNo?{(JaZG%Q-e|yQz{EjrrIztFdX-EaSW-r_2!}>BLf4+VS@wvpDQx=g*aXI^eXRV zWnkc7U|?iWU|?VYnJ>V=z{KD{nIN^yK{!J`ijj>WA&vQO32T210|Nttr>mdKI;Vst E0L)M=YXATM literal 0 HcmV?d00001 diff --git a/interface/resources/qml/controlsUit/Keyboard.qml b/interface/resources/qml/controlsUit/Keyboard.qml index 9d4fd33022..c38631ff79 100644 --- a/interface/resources/qml/controlsUit/Keyboard.qml +++ b/interface/resources/qml/controlsUit/Keyboard.qml @@ -36,13 +36,29 @@ Rectangle { readonly property int raisedHeight: keyboardHeight + (showMirrorText ? keyboardRowHeight : 0) - height: enabled && raised ? raisedHeight : 0 - visible: enabled && raised + height: 0 + visible: false property bool shiftMode: false property bool numericShiftMode: false + + onPasswordChanged: { + var use3DKeyboard = (typeof MenuInterface === "undefined") ? false : MenuInterface.isOptionChecked("Use 3D Keyboard"); + if (use3DKeyboard) { + KeyboardScriptingInterface.password = password; + } + } + onRaisedChanged: { + var use3DKeyboard = (typeof MenuInterface === "undefined") ? false : MenuInterface.isOptionChecked("Use 3D Keyboard"); + if (!use3DKeyboard) { + keyboardBase.height = raised ? raisedHeight : 0; + keyboardBase.visible = raised; + } else { + KeyboardScriptingInterface.raised = raised; + KeyboardScriptingInterface.password = raised ? password : false; + } mirroredText = ""; } diff --git a/interface/resources/qml/dialogs/TabletLoginDialog.qml b/interface/resources/qml/dialogs/TabletLoginDialog.qml index 6314921286..08aa903ae9 100644 --- a/interface/resources/qml/dialogs/TabletLoginDialog.qml +++ b/interface/resources/qml/dialogs/TabletLoginDialog.qml @@ -127,6 +127,10 @@ TabletModalWindow { } } + Component.onDestruction: { + loginKeyboard.raised = false; + } + Keyboard { id: loginKeyboard raised: root.keyboardEnabled && root.keyboardRaised diff --git a/interface/resources/qml/hifi/AvatarApp.qml b/interface/resources/qml/hifi/AvatarApp.qml index 39590748cf..9635681c34 100644 --- a/interface/resources/qml/hifi/AvatarApp.qml +++ b/interface/resources/qml/hifi/AvatarApp.qml @@ -19,7 +19,7 @@ Rectangle { HifiControls.Keyboard { id: keyboard z: 1000 - raised: parent.keyboardEnabled && parent.keyboardRaised + raised: parent.keyboardEnabled && parent.keyboardRaised && HMD.active numeric: parent.punctuationMode anchors { left: parent.left @@ -204,7 +204,8 @@ Rectangle { property bool isInManageState: false - Component.onCompleted: { + Component.onDestruction: { + keyboard.raised = false; } AvatarAppStyle { @@ -235,6 +236,8 @@ Rectangle { avatarIconVisible: mainPageVisible settingsButtonVisible: mainPageVisible onSettingsClicked: { + displayNameInput.focus = false; + root.keyboardRaised = false; settings.open(currentAvatarSettings, currentAvatar.avatarScale); } } @@ -344,6 +347,10 @@ Rectangle { emitSendToScript({'method' : 'changeDisplayName', 'displayName' : text}) focus = false; } + + onFocusChanged: { + root.keyboardRaised = focus; + } } ShadowImage { diff --git a/interface/resources/qml/hifi/avatarapp/Settings.qml b/interface/resources/qml/hifi/avatarapp/Settings.qml index bad1394133..cd892c17b1 100644 --- a/interface/resources/qml/hifi/avatarapp/Settings.qml +++ b/interface/resources/qml/hifi/avatarapp/Settings.qml @@ -14,6 +14,22 @@ Rectangle { signal scaleChanged(real scale); + property bool keyboardEnabled: true + property bool keyboardRaised: false + property bool punctuationMode: false + + HifiControlsUit.Keyboard { + id: keyboard + z: 1000 + raised: parent.keyboardEnabled && parent.keyboardRaised + numeric: parent.punctuationMode + anchors { + left: parent.left + right: parent.right + bottom: parent.bottom + } + } + property alias onSaveClicked: dialogButtons.onYesClicked property alias onCancelClicked: dialogButtons.onNoClicked @@ -314,6 +330,10 @@ Rectangle { anchors.left: parent.left anchors.right: parent.right placeholderText: 'user\\file\\dir' + + onFocusChanged: { + keyboardRaised = (avatarAnimationUrlInputText.focus || avatarCollisionSoundUrlInputText.focus); + } } } @@ -340,6 +360,10 @@ Rectangle { anchors.left: parent.left anchors.right: parent.right placeholderText: 'https://hifi-public.s3.amazonaws.com/sounds/Collisions-' + + onFocusChanged: { + keyboardRaised = (avatarAnimationUrlInputText.focus || avatarCollisionSoundUrlInputText.focus); + } } } diff --git a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml index ed4ba66b2b..874c1c53b7 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml @@ -40,6 +40,10 @@ Rectangle { source: "images/wallet-bg.jpg"; } + Component.onDestruction: { + keyboard.raised = false; + } + Connections { target: Commerce; diff --git a/interface/resources/qml/hifi/dialogs/TabletRunningScripts.qml b/interface/resources/qml/hifi/dialogs/TabletRunningScripts.qml index 6cd220307d..b0f17ff841 100644 --- a/interface/resources/qml/hifi/dialogs/TabletRunningScripts.qml +++ b/interface/resources/qml/hifi/dialogs/TabletRunningScripts.qml @@ -88,6 +88,10 @@ Rectangle { checkMenu.restart(); } + Component.onDestruction: { + keyboard.raised = false; + } + function updateRunningScripts() { function simplify(path) { // trim URI querystring/fragment diff --git a/interface/resources/qml/hifi/tablet/Edit.qml b/interface/resources/qml/hifi/tablet/Edit.qml index 4acced86ce..099c53cda2 100644 --- a/interface/resources/qml/hifi/tablet/Edit.qml +++ b/interface/resources/qml/hifi/tablet/Edit.qml @@ -49,5 +49,11 @@ StackView { if (currentItem && currentItem.fromScript) currentItem.fromScript(message); } + + Component.onDestruction: { + if (KeyboardScriptingInterface.raised) { + KeyboardScriptingInterface.raised = false; + } + } } diff --git a/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml b/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml index 0f26ba20aa..b8972378ad 100644 --- a/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml +++ b/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml @@ -56,10 +56,13 @@ StackView { Qt.callLater(function() { addressBarDialog.keyboardEnabled = HMD.active; addressLine.forceActiveFocus(); + addressBarDialog.raised = true; }) } + Component.onDestruction: { root.parentChanged.disconnect(center); + keyboard.raised = false; } function center() { @@ -218,6 +221,11 @@ StackView { leftMargin: 8; verticalCenter: addressLineContainer.verticalCenter; } + + onFocusChanged: { + addressBarDialog.raised = focus; + } + onTextChanged: { updateLocationText(text.length > 0); } diff --git a/interface/resources/qml/hifi/tablet/tabletWindows/TabletPreferencesDialog.qml b/interface/resources/qml/hifi/tablet/tabletWindows/TabletPreferencesDialog.qml index 57ca705352..a5d7b23df6 100644 --- a/interface/resources/qml/hifi/tablet/tabletWindows/TabletPreferencesDialog.qml +++ b/interface/resources/qml/hifi/tablet/tabletWindows/TabletPreferencesDialog.qml @@ -242,6 +242,10 @@ Item { keyboardEnabled = HMD.active; } + Component.onDestruction: { + keyboard.raised = false; + } + onKeyboardRaisedChanged: { if (keyboardEnabled && keyboardRaised) { var delta = mouseArea.mouseY - (dialog.height - footer.height - keyboard.raisedHeight -hifi.dimensions.controlLineHeight); diff --git a/interface/resources/sounds/keyboard_key.mp3 b/interface/resources/sounds/keyboard_key.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..e2cec81032389f719f90dd60b0ae4ca10719e725 GIT binary patch literal 9089 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 literal 0 HcmV?d00001 diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index bd126048dd..ccefb3c772 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -187,6 +187,7 @@ #include "scripting/SelectionScriptingInterface.h" #include "scripting/WalletScriptingInterface.h" #include "scripting/TTSScriptingInterface.h" +#include "scripting/KeyboardScriptingInterface.h" #if defined(Q_OS_MAC) || defined(Q_OS_WIN) #include "SpeechRecognizer.h" #endif @@ -205,6 +206,7 @@ #include "ui/UpdateDialog.h" #include "ui/overlays/Overlays.h" #include "ui/DomainConnectionModel.h" +#include "ui/Keyboard.h" #include "Util.h" #include "InterfaceParentFinder.h" #include "ui/OctreeStatsProvider.h" @@ -953,6 +955,8 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) { DependencyManager::set(); DependencyManager::set(); + DependencyManager::set(); + DependencyManager::set(); return previousSessionCrashed; } @@ -2327,6 +2331,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo // Preload Tablet sounds DependencyManager::get()->preloadSounds(); + DependencyManager::get()->createKeyboard(); _pendingIdleEvent = false; _pendingRenderEvent = false; @@ -2436,11 +2441,17 @@ QString Application::getUserAgent() { } void Application::toggleTabletUI(bool shouldOpen) const { - auto tabletScriptingInterface = DependencyManager::get(); auto hmd = DependencyManager::get(); if (!(shouldOpen && hmd->getShouldShowTablet())) { auto HMD = DependencyManager::get(); HMD->toggleShouldShowTablet(); + + if (!HMD->getShouldShowTablet()) { + DependencyManager::get()->setRaised(false); + _window->activateWindow(); + auto tablet = DependencyManager::get()->getTablet(SYSTEM_TABLET); + tablet->unfocus(); + } } } @@ -2633,6 +2644,7 @@ void Application::cleanupBeforeQuit() { // it accesses the PickManager to delete its associated Pick DependencyManager::destroy(); DependencyManager::destroy(); + DependencyManager::destroy(); qCDebug(interfaceapp) << "Application::cleanupBeforeQuit() complete"; } @@ -3113,6 +3125,7 @@ void Application::onDesktopRootContextCreated(QQmlContext* surfaceContext) { surfaceContext->setContextProperty("Vec3", new Vec3()); surfaceContext->setContextProperty("Uuid", new ScriptUUID()); surfaceContext->setContextProperty("Assets", DependencyManager::get().data()); + surfaceContext->setContextProperty("Keyboard", DependencyManager::get().data()); surfaceContext->setContextProperty("AvatarList", DependencyManager::get().data()); surfaceContext->setContextProperty("Users", DependencyManager::get().data()); @@ -6848,6 +6861,8 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe scriptEngine->registerGlobalObject("LODManager", DependencyManager::get().data()); + scriptEngine->registerGlobalObject("Keyboard", DependencyManager::get().data()); + scriptEngine->registerGlobalObject("Paths", DependencyManager::get().data()); scriptEngine->registerGlobalObject("HMD", DependencyManager::get().data()); diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 16e8af5683..2ca997a1fc 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -356,6 +356,8 @@ Menu::Menu() { qApp->setHmdTabletBecomesToolbarSetting(action->isChecked()); }); + addCheckableActionToQMenuAndActionHash(uiOptionsMenu, MenuOption::Use3DKeyboard, 0, false); + // Developer > Render >>> MenuWrapper* renderOptionsMenu = developerMenu->addMenu("Render"); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 1e9955a760..f1d56825b5 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -210,6 +210,7 @@ namespace MenuOption { const QString TurnWithHead = "Turn using Head"; const QString UseAudioForMouth = "Use Audio for Mouth"; const QString UseCamera = "Use Camera"; + const QString Use3DKeyboard = "Use 3D Keyboard"; const QString VelocityFilter = "Velocity Filter"; const QString VisibleToEveryone = "Everyone"; const QString VisibleToFriends = "Friends"; diff --git a/interface/src/raypick/PickScriptingInterface.cpp b/interface/src/raypick/PickScriptingInterface.cpp index 26b5aacac5..6e979d2d91 100644 --- a/interface/src/raypick/PickScriptingInterface.cpp +++ b/interface/src/raypick/PickScriptingInterface.cpp @@ -31,6 +31,9 @@ #include +static const float WEB_TOUCH_Y_OFFSET = 0.105f; // how far forward (or back with a negative number) to slide stylus in hand +static const glm::vec3 TIP_OFFSET = glm::vec3(0.0f, StylusPick::WEB_STYLUS_LENGTH - WEB_TOUCH_Y_OFFSET, 0.0f); + unsigned int PickScriptingInterface::createPick(const PickQuery::PickType type, const QVariant& properties) { switch (type) { case PickQuery::PickType::Ray: @@ -137,7 +140,12 @@ unsigned int PickScriptingInterface::createStylusPick(const QVariant& properties maxDistance = propMap["maxDistance"].toFloat(); } - return DependencyManager::get()->addPick(PickQuery::Stylus, std::make_shared(side, filter, maxDistance, enabled)); + glm::vec3 tipOffset = TIP_OFFSET; + if (propMap["tipOffset"].isValid()) { + tipOffset = vec3FromVariant(propMap["tipOffset"]); + } + + return DependencyManager::get()->addPick(PickQuery::Stylus, std::make_shared(side, filter, maxDistance, enabled, tipOffset)); } // NOTE: Laser pointer still uses scaleWithAvatar. Until scaleWithAvatar is also deprecated for pointers, scaleWithAvatar should not be removed from the pick API. @@ -416,4 +424,4 @@ void PickScriptingInterface::setParentTransform(std::shared_ptr pick, pick->parentTransform = std::make_shared(pickID); } } -} \ No newline at end of file +} diff --git a/interface/src/raypick/PointerScriptingInterface.cpp b/interface/src/raypick/PointerScriptingInterface.cpp index a44d14b4a6..f0edb4d9ef 100644 --- a/interface/src/raypick/PointerScriptingInterface.cpp +++ b/interface/src/raypick/PointerScriptingInterface.cpp @@ -16,6 +16,11 @@ #include "LaserPointer.h" #include "StylusPointer.h" #include "ParabolaPointer.h" +#include "StylusPick.h" + +static const glm::quat X_ROT_NEG_90{ 0.70710678f, -0.70710678f, 0.0f, 0.0f }; +static const glm::vec3 DEFAULT_POSITION_OFFSET{0.0f, 0.0f, -StylusPick::WEB_STYLUS_LENGTH / 2.0f}; +static const glm::vec3 DEFAULT_MODEL_DIMENSIONS{0.01f, 0.01f, StylusPick::WEB_STYLUS_LENGTH}; void PointerScriptingInterface::setIgnoreItems(unsigned int uid, const QScriptValue& ignoreItems) const { DependencyManager::get()->setIgnoreItems(uid, qVectorQUuidFromScriptValue(ignoreItems)); @@ -50,6 +55,8 @@ unsigned int PointerScriptingInterface::createPointer(const PickQuery::PickType& * @typedef {object} Pointers.StylusPointerProperties * @property {boolean} [hover=false] If this pointer should generate hover events. * @property {boolean} [enabled=false] + * @property {Vec3} [tipOffset] The specified offset of the from the joint index. + * @property {object} [model] Data to replace the default model url, positionOffset and rotationOffset. */ unsigned int PointerScriptingInterface::createStylus(const QVariant& properties) const { QVariantMap propertyMap = properties.toMap(); @@ -64,7 +71,28 @@ unsigned int PointerScriptingInterface::createStylus(const QVariant& properties) enabled = propertyMap["enabled"].toBool(); } - return DependencyManager::get()->addPointer(std::make_shared(properties, StylusPointer::buildStylusOverlay(propertyMap), hover, enabled)); + glm::vec3 modelPositionOffset = DEFAULT_POSITION_OFFSET; + glm::quat modelRotationOffset = X_ROT_NEG_90; + glm::vec3 modelDimensions = DEFAULT_MODEL_DIMENSIONS; + + if (propertyMap["model"].isValid()) { + QVariantMap modelData = propertyMap["model"].toMap(); + + if (modelData["positionOffset"].isValid()) { + modelPositionOffset = vec3FromVariant(modelData["positionOffset"]); + } + + if (modelData["rotationOffset"].isValid()) { + modelRotationOffset = quatFromVariant(modelData["rotationOffset"]); + } + + if (modelData["dimensions"].isValid()) { + modelDimensions = vec3FromVariant(modelData["dimensions"]); + } + } + + return DependencyManager::get()->addPointer(std::make_shared(properties, StylusPointer::buildStylusOverlay(propertyMap), hover, enabled, modelPositionOffset, + modelRotationOffset, modelDimensions)); } /**jsdoc diff --git a/interface/src/raypick/RayPick.cpp b/interface/src/raypick/RayPick.cpp index ad12db4df2..b6adba4a12 100644 --- a/interface/src/raypick/RayPick.cpp +++ b/interface/src/raypick/RayPick.cpp @@ -10,6 +10,7 @@ #include "Application.h" #include "EntityScriptingInterface.h" #include "ui/overlays/Overlays.h" +#include "ui/Keyboard.h" #include "avatar/AvatarManager.h" #include "scripting/HMDScriptingInterface.h" #include "DependencyManager.h" @@ -40,9 +41,12 @@ PickResultPointer RayPick::getEntityIntersection(const PickRay& pick) { PickResultPointer RayPick::getOverlayIntersection(const PickRay& pick) { bool precisionPicking = !(getFilter().doesPickCoarse() || DependencyManager::get()->getForceCoarsePicking()); + auto keyboard = DependencyManager::get(); + QVector ignoreItems = keyboard->getKeysID(); + ignoreItems.append(getIgnoreItemsAs()); RayToOverlayIntersectionResult overlayRes = qApp->getOverlays().findRayIntersectionVector(pick, precisionPicking, - getIncludeItemsAs(), getIgnoreItemsAs(), !getFilter().doesPickInvisible(), !getFilter().doesPickNonCollidable()); + getIncludeItemsAs(), ignoreItems, !getFilter().doesPickInvisible(), !getFilter().doesPickNonCollidable()); if (overlayRes.intersects) { return std::make_shared(IntersectionType::OVERLAY, overlayRes.overlayID, overlayRes.distance, overlayRes.intersection, pick, overlayRes.surfaceNormal, overlayRes.extraInfo); } else { @@ -118,4 +122,4 @@ glm::vec2 RayPick::projectOntoOverlayXYPlane(const QUuid& overlayID, const glm:: glm::vec2 RayPick::projectOntoEntityXYPlane(const QUuid& entityID, const glm::vec3& worldPos, bool unNormalized) { auto props = DependencyManager::get()->getEntityProperties(entityID); return projectOntoXYPlane(worldPos, props.getPosition(), props.getRotation(), props.getDimensions(), props.getRegistrationPoint(), unNormalized); -} \ No newline at end of file +} diff --git a/interface/src/raypick/StylusPick.cpp b/interface/src/raypick/StylusPick.cpp index c495ddd194..0416563272 100644 --- a/interface/src/raypick/StylusPick.cpp +++ b/interface/src/raypick/StylusPick.cpp @@ -21,11 +21,7 @@ #include using namespace bilateral; - -// TODO: make these configurable per pick -static const float WEB_STYLUS_LENGTH = 0.2f; -static const float WEB_TOUCH_Y_OFFSET = 0.105f; // how far forward (or back with a negative number) to slide stylus in hand -static const glm::vec3 TIP_OFFSET = glm::vec3(0.0f, WEB_STYLUS_LENGTH - WEB_TOUCH_Y_OFFSET, 0.0f); +float StylusPick::WEB_STYLUS_LENGTH = 0.2f; struct SideData { QString avatarJoint; @@ -64,8 +60,8 @@ bool StylusPickResult::checkOrFilterAgainstMaxDistance(float maxDistance) { return distance < maxDistance; } -StylusPick::StylusPick(Side side, const PickFilter& filter, float maxDistance, bool enabled) : - Pick(StylusTip(side), filter, maxDistance, enabled) +StylusPick::StylusPick(Side side, const PickFilter& filter, float maxDistance, bool enabled, const glm::vec3& tipOffset) : + Pick(StylusTip(side), filter, maxDistance, enabled), _tipOffset(tipOffset) { } @@ -90,7 +86,7 @@ static StylusTip getFingerWorldLocation(Side side) { } // controllerWorldLocation is where the controller would be, in-world, with an added offset -static StylusTip getControllerWorldLocation(Side side) { +static StylusTip getControllerWorldLocation(Side side, const glm::vec3& tipOffset) { static const std::array INPUTS{ { UserInputMapper::makeStandardInput(SIDES[0].channel), UserInputMapper::makeStandardInput(SIDES[1].channel) } }; const auto sideIndex = index(side); @@ -114,7 +110,7 @@ static StylusTip getControllerWorldLocation(Side side) { // add to the real position so the grab-point is out in front of the hand, a bit result.position += result.orientation * (sideData.grabPointSphereOffset * sensorScaleFactor); // move the stylus forward a bit - result.position += result.orientation * (TIP_OFFSET * sensorScaleFactor); + result.position += result.orientation * (tipOffset * sensorScaleFactor); auto worldControllerPos = avatarPosition + avatarOrientation * pose.translation; // compute tip velocity from hand controller motion, it is more accurate than computing it from previous positions. @@ -131,7 +127,7 @@ StylusTip StylusPick::getMathematicalPick() const { if (qApp->getPreferAvatarFingerOverStylus()) { result = getFingerWorldLocation(_mathPick.side); } else { - result = getControllerWorldLocation(_mathPick.side); + result = getControllerWorldLocation(_mathPick.side, _tipOffset); } return result; } @@ -236,4 +232,4 @@ Transform StylusPick::getResultTransform() const { Transform transform; transform.setTranslation(stylusResult->intersection); return transform; -} \ No newline at end of file +} diff --git a/interface/src/raypick/StylusPick.h b/interface/src/raypick/StylusPick.h index cd01df20e9..3e0ee452e9 100644 --- a/interface/src/raypick/StylusPick.h +++ b/interface/src/raypick/StylusPick.h @@ -58,7 +58,7 @@ public: class StylusPick : public Pick { using Side = bilateral::Side; public: - StylusPick(Side side, const PickFilter& filter, float maxDistance, bool enabled); + StylusPick(Side side, const PickFilter& filter, float maxDistance, bool enabled, const glm::vec3& tipOffset); StylusTip getMathematicalPick() const override; PickResultPointer getDefaultResult(const QVariantMap& pickVariant) const override; @@ -71,6 +71,11 @@ public: bool isLeftHand() const override { return _mathPick.side == Side::Left; } bool isRightHand() const override { return _mathPick.side == Side::Right; } bool isMouse() const override { return false; } + + static float WEB_STYLUS_LENGTH; + +private: + glm::vec3 _tipOffset; }; -#endif // hifi_StylusPick_h \ No newline at end of file +#endif // hifi_StylusPick_h diff --git a/interface/src/raypick/StylusPointer.cpp b/interface/src/raypick/StylusPointer.cpp index 4ba3813c4a..caa3151cc5 100644 --- a/interface/src/raypick/StylusPointer.cpp +++ b/interface/src/raypick/StylusPointer.cpp @@ -17,9 +17,6 @@ #include "PickScriptingInterface.h" #include -// TODO: make these configurable per pointer -static const float WEB_STYLUS_LENGTH = 0.2f; - static const float TABLET_MIN_HOVER_DISTANCE = -0.1f; static const float TABLET_MAX_HOVER_DISTANCE = 0.1f; static const float TABLET_MIN_TOUCH_DISTANCE = -0.1f; @@ -28,9 +25,15 @@ static const float TABLET_MAX_TOUCH_DISTANCE = 0.005f; static const float HOVER_HYSTERESIS = 0.01f; static const float TOUCH_HYSTERESIS = 0.001f; -StylusPointer::StylusPointer(const QVariant& props, const OverlayID& stylusOverlay, bool hover, bool enabled) : +static const QString DEFAULT_STYLUS_MODEL_URL = PathUtils::resourcesUrl() + "/meshes/tablet-stylus-fat.fbx"; + +StylusPointer::StylusPointer(const QVariant& props, const OverlayID& stylusOverlay, bool hover, bool enabled, + const glm::vec3& modelPositionOffset, const glm::quat& modelRotationOffset, const glm::vec3& modelDimensions) : Pointer(DependencyManager::get()->createStylusPick(props), enabled, hover), - _stylusOverlay(stylusOverlay) + _stylusOverlay(stylusOverlay), + _modelPositionOffset(modelPositionOffset), + _modelDimensions(modelDimensions), + _modelRotationOffset(modelRotationOffset) { } @@ -42,9 +45,19 @@ StylusPointer::~StylusPointer() { OverlayID StylusPointer::buildStylusOverlay(const QVariantMap& properties) { QVariantMap overlayProperties; - // TODO: make these configurable per pointer + // TODO: make these configurable per pointe + QString modelUrl = DEFAULT_STYLUS_MODEL_URL; + + if (properties["model"].isValid()) { + QVariantMap modelData = properties["model"].toMap(); + + if (modelData["url"].isValid()) { + modelUrl = modelData["url"].toString(); + } + } + overlayProperties["name"] = "stylus"; - overlayProperties["url"] = PathUtils::resourcesUrl() + "/meshes/tablet-stylus-fat.fbx"; + overlayProperties["url"] = modelUrl; overlayProperties["loadPriority"] = 10.0f; overlayProperties["solid"] = true; overlayProperties["visible"] = false; @@ -72,13 +85,12 @@ void StylusPointer::updateVisuals(const PickResultPointer& pickResult) { void StylusPointer::show(const StylusTip& tip) { if (!_stylusOverlay.isNull()) { QVariantMap props; - static const glm::quat X_ROT_NEG_90{ 0.70710678f, -0.70710678f, 0.0f, 0.0f }; - auto modelOrientation = tip.orientation * X_ROT_NEG_90; + auto modelOrientation = tip.orientation * _modelRotationOffset; auto sensorToWorldScale = DependencyManager::get()->getMyAvatar()->getSensorToWorldScale(); - auto modelPositionOffset = modelOrientation * (vec3(0.0f, 0.0f, -WEB_STYLUS_LENGTH / 2.0f) * sensorToWorldScale); + auto modelPositionOffset = modelOrientation * (_modelPositionOffset * sensorToWorldScale); props["position"] = vec3toVariant(tip.position + modelPositionOffset); props["rotation"] = quatToVariant(modelOrientation); - props["dimensions"] = vec3toVariant(sensorToWorldScale * vec3(0.01f, 0.01f, WEB_STYLUS_LENGTH)); + props["dimensions"] = vec3toVariant(sensorToWorldScale * _modelDimensions); props["visible"] = true; qApp->getOverlays().editOverlay(_stylusOverlay, props); } diff --git a/interface/src/raypick/StylusPointer.h b/interface/src/raypick/StylusPointer.h index ff60fd78e5..64e2a38bed 100644 --- a/interface/src/raypick/StylusPointer.h +++ b/interface/src/raypick/StylusPointer.h @@ -21,7 +21,8 @@ class StylusPointer : public Pointer { using Ptr = std::shared_ptr; public: - StylusPointer(const QVariant& props, const OverlayID& stylusOverlay, bool hover, bool enabled); + StylusPointer(const QVariant& props, const OverlayID& stylusOverlay, bool hover, bool enabled, + const glm::vec3& modelPositionOffset, const glm::quat& modelRotationOffset, const glm::vec3& modelDimensions); ~StylusPointer(); void updateVisuals(const PickResultPointer& pickResult) override; @@ -81,6 +82,10 @@ private: bool _showing { true }; + glm::vec3 _modelPositionOffset; + glm::vec3 _modelDimensions; + glm::quat _modelRotationOffset; + }; #endif // hifi_StylusPointer_h diff --git a/interface/src/scripting/HMDScriptingInterface.cpp b/interface/src/scripting/HMDScriptingInterface.cpp index ea24d6c793..f2f8d3b8d4 100644 --- a/interface/src/scripting/HMDScriptingInterface.cpp +++ b/interface/src/scripting/HMDScriptingInterface.cpp @@ -119,8 +119,11 @@ void HMDScriptingInterface::toggleShouldShowTablet() { } void HMDScriptingInterface::setShouldShowTablet(bool value) { - _showTablet = value; - _tabletContextualMode = false; + if (_showTablet != value) { + _showTablet = value; + _tabletContextualMode = false; + emit showTabletChanged(value); + } } QScriptValue HMDScriptingInterface::getHUDLookAtPosition2D(QScriptContext* context, QScriptEngine* engine) { diff --git a/interface/src/scripting/HMDScriptingInterface.h b/interface/src/scripting/HMDScriptingInterface.h index 2c0a3fe45f..6cc695762b 100644 --- a/interface/src/scripting/HMDScriptingInterface.h +++ b/interface/src/scripting/HMDScriptingInterface.h @@ -355,6 +355,8 @@ signals: */ bool shouldShowHandControllersChanged(); + void showTabletChanged(bool showTablet); + public: HMDScriptingInterface(); static QScriptValue getHUDLookAtPosition2D(QScriptContext* context, QScriptEngine* engine); diff --git a/interface/src/scripting/KeyboardScriptingInterface.cpp b/interface/src/scripting/KeyboardScriptingInterface.cpp new file mode 100644 index 0000000000..b26e1ec378 --- /dev/null +++ b/interface/src/scripting/KeyboardScriptingInterface.cpp @@ -0,0 +1,34 @@ +// +// KeyboardScriptingInterface.cpp +// interface/src/scripting +// +// Created by Dante Ruiz on 2018-08-27. +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "KeyboardScriptingInterface.h" +#include "ui/Keyboard.h" + +bool KeyboardScriptingInterface::isRaised() { + return DependencyManager::get()->isRaised(); +} + +void KeyboardScriptingInterface::setRaised(bool raised) { + DependencyManager::get()->setRaised(raised); +} + + +bool KeyboardScriptingInterface::isPassword() { + return DependencyManager::get()->isPassword(); +} + +void KeyboardScriptingInterface::setPassword(bool password) { + DependencyManager::get()->setPassword(password); +} + +void KeyboardScriptingInterface::loadKeyboardFile(const QString& keyboardFile) { + DependencyManager::get()->loadKeyboardFile(keyboardFile); +} diff --git a/interface/src/scripting/KeyboardScriptingInterface.h b/interface/src/scripting/KeyboardScriptingInterface.h new file mode 100644 index 0000000000..1ab91ea7c3 --- /dev/null +++ b/interface/src/scripting/KeyboardScriptingInterface.h @@ -0,0 +1,43 @@ +// +// KeyboardScriptingInterface.h +// interface/src/scripting +// +// Created by Dante Ruiz on 2018-08-27. +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_KeyboardScriptingInterface_h +#define hifi_KeyboardScriptingInterface_h + +#include + +#include "DependencyManager.h" + +/**jsdoc + * The Keyboard API provides facilities to use 3D Physical keyboard. + * @namespace Keyboard + * + * @hifi-interface + * @hifi-client-entity + * + * @property {bool} raised - true If the keyboard is visible false otherwise + * @property {bool} password - true Will show * instead of characters in the text display false otherwise + */ +class KeyboardScriptingInterface : public QObject, public Dependency { + Q_OBJECT + Q_PROPERTY(bool raised READ isRaised WRITE setRaised) + Q_PROPERTY(bool password READ isPassword WRITE setPassword) + +public: + Q_INVOKABLE void loadKeyboardFile(const QString& string); +private: + bool isRaised(); + void setRaised(bool raised); + + bool isPassword(); + void setPassword(bool password); +}; +#endif diff --git a/interface/src/ui/Keyboard.cpp b/interface/src/ui/Keyboard.cpp new file mode 100644 index 0000000000..677d384a17 --- /dev/null +++ b/interface/src/ui/Keyboard.cpp @@ -0,0 +1,799 @@ +// +// Keyboard.cpp +// interface/src/scripting +// +// Created by Dante Ruiz on 2018-08-27. +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "Keyboard.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ui/overlays/Overlays.h" +#include "ui/overlays/Overlay.h" +#include "ui/overlays/ModelOverlay.h" +#include "ui/overlays/Cube3DOverlay.h" +#include "ui/overlays/Text3DOverlay.h" +#include "avatar/AvatarManager.h" +#include "avatar/MyAvatar.h" +#include "avatar/AvatarManager.h" +#include "raypick/PickScriptingInterface.h" +#include "scripting/HMDScriptingInterface.h" +#include "scripting/WindowScriptingInterface.h" +#include "DependencyManager.h" + +#include "raypick/StylusPointer.h" +#include "GLMHelpers.h" +#include "Application.h" + +static const int LEFT_HAND_CONTROLLER_INDEX = 0; +static const int RIGHT_HAND_CONTROLLER_INDEX = 1; + +static const float MALLET_LENGTH = 0.4f; +static const float MALLET_TOUCH_Y_OFFSET = 0.105f; +static const float MALLET_Y_OFFSET = 0.35f; + +static const glm::quat MALLET_ROTATION_OFFSET{0.70710678f, 0.0f, -0.70710678f, 0.0f}; +static const glm::vec3 MALLET_MODEL_DIMENSIONS{0.05f, MALLET_LENGTH, 0.05f}; +static const glm::vec3 MALLET_POSITION_OFFSET{0.0f, -MALLET_Y_OFFSET / 2.0f, 0.0f}; +static const glm::vec3 MALLET_TIP_OFFSET{0.0f, MALLET_LENGTH - MALLET_TOUCH_Y_OFFSET, 0.0f}; + + +static const glm::vec3 Z_AXIS {0.0f, 0.0f, 1.0f}; +static const glm::vec3 KEYBOARD_TABLET_OFFSET{0.28f, -0.3f, -0.05f}; +static const glm::vec3 KEYBOARD_TABLET_DEGREES_OFFSET{-45.0f, 0.0f, 0.0f}; +static const glm::vec3 KEYBOARD_TABLET_LANDSCAPE_OFFSET{-0.2f, -0.27f, -0.05f}; +static const glm::vec3 KEYBOARD_TABLET_LANDSCAPE_DEGREES_OFFSET{-45.0f, 0.0f, -90.0f}; +static const glm::vec3 KEYBOARD_AVATAR_OFFSET{-0.6f, 0.3f, -0.7f}; +static const glm::vec3 KEYBOARD_AVATAR_DEGREES_OFFSET{0.0f, 180.0f, 0.0f}; + +static const QString SOUND_FILE = PathUtils::resourcesUrl() + "sounds/keyboard_key.mp3"; +static const QString MALLET_MODEL_URL = PathUtils::resourcesUrl() + "meshes/drumstick.fbx"; + +static const float PULSE_STRENGTH = 0.6f; +static const float PULSE_DURATION = 3.0f; + +static const int KEY_PRESS_TIMEOUT_MS = 100; +static const int LAYER_SWITCH_TIMEOUT_MS = 200; + +static const QString CHARACTER_STRING = "character"; +static const QString CAPS_STRING = "caps"; +static const QString CLOSE_STRING = "close"; +static const QString LAYER_STRING = "layer"; +static const QString BACKSPACE_STRING = "backspace"; +static const QString SPACE_STRING = "space"; +static const QString ENTER_STRING = "enter"; + +std::pair calculateKeyboardPositionAndOrientation() { + auto myAvatar = DependencyManager::get()->getMyAvatar(); + auto hmd = DependencyManager::get(); + + std::pair keyboardLocation = std::make_pair(glm::vec3(), glm::quat()); + float sensorToWorldScale = myAvatar->getSensorToWorldScale(); + QUuid tabletID = hmd->getCurrentTabletFrameID(); + if (!tabletID.isNull() && hmd->getShouldShowTablet()) { + Overlays& overlays = qApp->getOverlays(); + auto tabletOverlay = std::dynamic_pointer_cast(overlays.getOverlay(tabletID)); + if (tabletOverlay) { + auto tablet = DependencyManager::get()->getTablet("com.highfidelity.interface.tablet.system"); + bool landscapeMode = tablet->getLandscape(); + glm::vec3 keyboardOffset = landscapeMode ? KEYBOARD_TABLET_LANDSCAPE_OFFSET : KEYBOARD_TABLET_OFFSET; + glm::vec3 keyboardDegreesOffset = landscapeMode ? KEYBOARD_TABLET_LANDSCAPE_DEGREES_OFFSET : KEYBOARD_TABLET_DEGREES_OFFSET; + glm::vec3 tabletWorldPosition = tabletOverlay->getWorldPosition(); + glm::quat tabletWorldOrientation = tabletOverlay->getWorldOrientation(); + glm::vec3 scaledKeyboardTabletOffset = keyboardOffset * sensorToWorldScale; + + keyboardLocation.first = tabletWorldPosition + (tabletWorldOrientation * scaledKeyboardTabletOffset); + keyboardLocation.second = tabletWorldOrientation * glm::quat(glm::radians(keyboardDegreesOffset)); + } + + } else { + glm::vec3 avatarWorldPosition = myAvatar->getWorldPosition(); + glm::quat avatarWorldOrientation = myAvatar->getWorldOrientation(); + glm::vec3 scaledKeyboardAvatarOffset = KEYBOARD_AVATAR_OFFSET * sensorToWorldScale; + + keyboardLocation.first = avatarWorldPosition + (avatarWorldOrientation * scaledKeyboardAvatarOffset); + keyboardLocation.second = avatarWorldOrientation * glm::quat(glm::radians(KEYBOARD_AVATAR_DEGREES_OFFSET)); + } + + return keyboardLocation; +} + +void Key::saveDimensionsAndLocalPosition() { + Overlays& overlays = qApp->getOverlays(); + auto model3DOverlay = std::dynamic_pointer_cast(overlays.getOverlay(_keyID)); + + if (model3DOverlay) { + _originalLocalPosition = model3DOverlay->getLocalPosition(); + _originalDimensions = model3DOverlay->getDimensions(); + _currentLocalPosition = _originalLocalPosition; + } +} + +void Key::scaleKey(float sensorToWorldScale) { + Overlays& overlays = qApp->getOverlays(); + auto model3DOverlay = std::dynamic_pointer_cast(overlays.getOverlay(_keyID)); + + if (model3DOverlay) { + glm::vec3 scaledLocalPosition = _originalLocalPosition * sensorToWorldScale; + glm::vec3 scaledDimensions = _originalDimensions * sensorToWorldScale; + _currentLocalPosition = scaledLocalPosition; + + QVariantMap properties { + { "dimensions", vec3toVariant(scaledDimensions) }, + { "localPosition", vec3toVariant(scaledLocalPosition) } + }; + + overlays.editOverlay(_keyID, properties); + } +} + +void Key::startTimer(int time) { + if (_timer) { + _timer->start(time); + _timer->setSingleShot(true); + } +} + +bool Key::timerFinished() { + if (_timer) { + return (_timer->remainingTime() <= 0); + } + return false; +} + +QString Key::getKeyString(bool toUpper) const { + return toUpper ? _keyString.toUpper() : _keyString; +} + +int Key::getScanCode(bool toUpper) const { + QString character = toUpper ? _keyString.toUpper() : _keyString; + auto utf8Key = character.toUtf8(); + return (int)utf8Key[0]; +} + +Key::Type Key::getKeyTypeFromString(const QString& keyTypeString) { + if (keyTypeString == SPACE_STRING) { + return Type::SPACE; + } else if (keyTypeString == BACKSPACE_STRING) { + return Type::BACKSPACE; + } else if (keyTypeString == LAYER_STRING) { + return Type::LAYER; + } else if (keyTypeString == CAPS_STRING) { + return Type::CAPS; + } else if (keyTypeString == CLOSE_STRING) { + return Type::CLOSE; + } else if (keyTypeString == ENTER_STRING) { + return Type::ENTER; + } + + return Type::CHARACTER; +} + +Keyboard::Keyboard() { + auto pointerManager = DependencyManager::get(); + auto windowScriptingInterface = DependencyManager::get(); + auto myAvatar = DependencyManager::get()->getMyAvatar(); + connect(pointerManager.data(), &PointerManager::triggerBeginOverlay, this, &Keyboard::handleTriggerBegin, Qt::QueuedConnection); + connect(pointerManager.data(), &PointerManager::triggerContinueOverlay, this, &Keyboard::handleTriggerContinue, Qt::QueuedConnection); + connect(pointerManager.data(), &PointerManager::triggerEndOverlay, this, &Keyboard::handleTriggerEnd, Qt::QueuedConnection); + connect(myAvatar.get(), &MyAvatar::sensorToWorldScaleChanged, this, &Keyboard::scaleKeyboard, Qt::QueuedConnection); + connect(windowScriptingInterface.data(), &WindowScriptingInterface::domainChanged, [&]() { setRaised(false); }); +} + + +void Keyboard::createKeyboard() { + auto pointerManager = DependencyManager::get(); + + QVariantMap modelProperties { + { "url", MALLET_MODEL_URL } + }; + + QVariantMap leftStylusProperties { + { "hand", LEFT_HAND_CONTROLLER_INDEX }, + { "filter", PickScriptingInterface::PICK_OVERLAYS() }, + { "model", modelProperties }, + { "tipOffset", vec3toVariant(MALLET_TIP_OFFSET) } + }; + + QVariantMap rightStylusProperties { + { "hand", RIGHT_HAND_CONTROLLER_INDEX }, + { "filter", PickScriptingInterface::PICK_OVERLAYS() }, + { "model", modelProperties }, + { "tipOffset", vec3toVariant(MALLET_TIP_OFFSET) } + }; + + _leftHandStylus = pointerManager->addPointer(std::make_shared(leftStylusProperties, StylusPointer::buildStylusOverlay(leftStylusProperties), true, true, + MALLET_POSITION_OFFSET, MALLET_ROTATION_OFFSET, MALLET_MODEL_DIMENSIONS)); + _rightHandStylus = pointerManager->addPointer(std::make_shared(rightStylusProperties, StylusPointer::buildStylusOverlay(rightStylusProperties), true, true, + MALLET_POSITION_OFFSET, MALLET_ROTATION_OFFSET, MALLET_MODEL_DIMENSIONS)); + + pointerManager->disablePointer(_rightHandStylus); + pointerManager->disablePointer(_leftHandStylus); + + QString keyboardSvg = PathUtils::resourcesUrl() + "config/keyboard.json"; + loadKeyboardFile(keyboardSvg); + + _keySound = DependencyManager::get()->getSound(SOUND_FILE); +} + +bool Keyboard::isRaised() const { + return resultWithReadLock([&] { return _raised; }); +} + +void Keyboard::setRaised(bool raised) { + + bool isRaised; + withReadLock([&] { isRaised = _raised; }); + if (isRaised != raised) { + raiseKeyboardAnchor(raised); + raiseKeyboard(raised); + raised ? enableStylus() : disableStylus(); + withWriteLock([&] { + _raised = raised; + _layerIndex = 0; + _capsEnabled = false; + _typedCharacters.clear(); + }); + + updateTextDisplay(); + } +} + +void Keyboard::updateTextDisplay() { + Overlays& overlays = qApp->getOverlays(); + + auto myAvatar = DependencyManager::get()->getMyAvatar(); + float sensorToWorldScale = myAvatar->getSensorToWorldScale(); + float textWidth = (float) overlays.textSize(_textDisplay.overlayID, _typedCharacters).width(); + + glm::vec3 scaledDimensions = _textDisplay.dimensions; + scaledDimensions *= sensorToWorldScale; + float leftMargin = (scaledDimensions.x / 2); + scaledDimensions.x += textWidth; + + + QVariantMap textDisplayProperties { + { "dimensions", vec3toVariant(scaledDimensions) }, + { "leftMargin", leftMargin }, + { "text", _typedCharacters }, + { "lineHeight", (_textDisplay.lineHeight * sensorToWorldScale) } + }; + + overlays.editOverlay(_textDisplay.overlayID, textDisplayProperties); +} + +void Keyboard::raiseKeyboardAnchor(bool raise) const { + Overlays& overlays = qApp->getOverlays(); + OverlayID anchorOverlayID = _anchor.overlayID; + auto anchorOverlay = std::dynamic_pointer_cast(overlays.getOverlay(anchorOverlayID)); + if (anchorOverlay) { + std::pair keyboardLocation = calculateKeyboardPositionAndOrientation(); + anchorOverlay->setWorldPosition(keyboardLocation.first); + anchorOverlay->setWorldOrientation(keyboardLocation.second); + anchorOverlay->setVisible(raise); + + QVariantMap textDisplayProperties { + { "visible", raise } + }; + + overlays.editOverlay(_textDisplay.overlayID, textDisplayProperties); + } +} + +void Keyboard::scaleKeyboard(float sensorToWorldScale) { + Overlays& overlays = qApp->getOverlays(); + + glm::vec3 scaledDimensions = _anchor.originalDimensions * sensorToWorldScale; + auto volume3DOverlay = std::dynamic_pointer_cast(overlays.getOverlay(_anchor.overlayID)); + + if (volume3DOverlay) { + volume3DOverlay->setDimensions(scaledDimensions); + } + + for (auto& keyboardLayer: _keyboardLayers) { + for (auto& key: keyboardLayer) { + key.scaleKey(sensorToWorldScale); + } + } + + + + glm::vec3 scaledLocalPosition = _textDisplay.localPosition * sensorToWorldScale; + glm::vec3 textDisplayScaledDimensions = _textDisplay.dimensions * sensorToWorldScale; + + QVariantMap textDisplayProperties { + { "localPosition", vec3toVariant(scaledLocalPosition) }, + { "dimensions", vec3toVariant(textDisplayScaledDimensions) }, + { "lineHeight", (_textDisplay.lineHeight * sensorToWorldScale) } + }; + + overlays.editOverlay(_textDisplay.overlayID, textDisplayProperties); +} + +void Keyboard::startLayerSwitchTimer() { + if (_layerSwitchTimer) { + _layerSwitchTimer->start(LAYER_SWITCH_TIMEOUT_MS); + _layerSwitchTimer->setSingleShot(true); + } +} + +bool Keyboard::isLayerSwitchTimerFinished() { + if (_layerSwitchTimer) { + return (_layerSwitchTimer->remainingTime() <= 0); + } + return false; +} + +void Keyboard::raiseKeyboard(bool raise) const { + if (_keyboardLayers.empty()) { + return; + } + Overlays& overlays = qApp->getOverlays(); + for (const auto& key: _keyboardLayers[_layerIndex]) { + auto base3DOverlay = std::dynamic_pointer_cast(overlays.getOverlay(key.getID())); + if (base3DOverlay) { + base3DOverlay->setVisible(raise); + } + } +} + +bool Keyboard::isPassword() const { + return resultWithReadLock([&] { return _password; }); +} + +void Keyboard::setPassword(bool password) { + if (_password != password) { + withWriteLock([&] { + _password = password; + _typedCharacters.clear(); + }); + } + + updateTextDisplay(); +} + +void Keyboard::switchToLayer(int layerIndex) { + if (layerIndex >= 0 && layerIndex < (int)_keyboardLayers.size()) { + Overlays& overlays = qApp->getOverlays(); + + OverlayID currentAnchorOverlayID = _anchor.overlayID; + + glm::vec3 currentOverlayPosition; + glm::quat currentOverlayOrientation; + + auto currentAnchorOverlay = std::dynamic_pointer_cast(overlays.getOverlay(currentAnchorOverlayID)); + if (currentAnchorOverlay) { + currentOverlayPosition = currentAnchorOverlay->getWorldPosition(); + currentOverlayOrientation = currentAnchorOverlay->getWorldOrientation(); + } + + raiseKeyboardAnchor(false); + raiseKeyboard(false); + + setLayerIndex(layerIndex); + + raiseKeyboardAnchor(true); + raiseKeyboard(true); + + OverlayID newAnchorOverlayID = _anchor.overlayID; + auto newAnchorOverlay = std::dynamic_pointer_cast(overlays.getOverlay(newAnchorOverlayID)); + if (newAnchorOverlay) { + newAnchorOverlay->setWorldPosition(currentOverlayPosition); + newAnchorOverlay->setWorldOrientation(currentOverlayOrientation); + } + + startLayerSwitchTimer(); + } +} + +void Keyboard::handleTriggerBegin(const OverlayID& overlayID, const PointerEvent& event) { + if (_keyboardLayers.empty() || !isLayerSwitchTimerFinished()) { + return; + } + + auto pointerID = event.getID(); + auto buttonType = event.getButton(); + + for (auto index = _keyboardLayers[_layerIndex].begin(); index != _keyboardLayers[_layerIndex].end(); index++) { + Key& key = *index; + if (key.getID() == overlayID && (pointerID == _leftHandStylus || pointerID == _rightHandStylus) && + buttonType == PointerEvent::PrimaryButton) { + + + if (key.timerFinished()) { + + auto handIndex = (pointerID == _leftHandStylus) ? controller::Hand::LEFT : controller::Hand::RIGHT; + auto userInputMapper = DependencyManager::get(); + userInputMapper->triggerHapticPulse(PULSE_STRENGTH, PULSE_DURATION, handIndex); + + Overlays& overlays = qApp->getOverlays(); + auto base3DOverlay = std::dynamic_pointer_cast(overlays.getOverlay(overlayID)); + + glm::vec3 keyWorldPosition; + if (base3DOverlay) { + keyWorldPosition = base3DOverlay->getWorldPosition(); + } + + AudioInjectorOptions audioOptions; + audioOptions.localOnly = true; + audioOptions.position = keyWorldPosition; + audioOptions.volume = 0.4f; + + AudioInjector::playSound(_keySound->getByteArray(), audioOptions); + + int scanCode = key.getScanCode(_capsEnabled); + QString keyString = key.getKeyString(_capsEnabled); + + auto tablet = DependencyManager::get()->getTablet("com.highfidelity.interface.tablet.system"); + + switch (key.getKeyType()) { + case Key::Type::CLOSE: + setRaised(false); + tablet->unfocus(); + return; + + case Key::Type::CAPS: + _capsEnabled = !_capsEnabled; + switchToLayer(key.getSwitchToLayerIndex()); + return; + case Key::Type::LAYER: + _capsEnabled = false; + switchToLayer(key.getSwitchToLayerIndex()); + return; + case Key::Type::BACKSPACE: + scanCode = Qt::Key_Backspace; + keyString = "\x08"; + _typedCharacters = _typedCharacters.left(_typedCharacters.length() -1); + updateTextDisplay(); + break; + case Key::Type::ENTER: + scanCode = Qt::Key_Return; + keyString = "\x0d"; + _typedCharacters.clear(); + updateTextDisplay(); + break; + case Key::Type::CHARACTER: + if (keyString != " ") { + _typedCharacters.push_back((_password ? "*" : keyString)); + } else { + _typedCharacters.clear(); + } + updateTextDisplay(); + break; + + default: + break; + } + + QKeyEvent* pressEvent = new QKeyEvent(QEvent::KeyPress, scanCode, Qt::NoModifier, keyString); + QKeyEvent* releaseEvent = new QKeyEvent(QEvent::KeyRelease, scanCode, Qt::NoModifier, keyString); + QCoreApplication::postEvent(QCoreApplication::instance(), pressEvent); + QCoreApplication::postEvent(QCoreApplication::instance(), releaseEvent); + + key.startTimer(KEY_PRESS_TIMEOUT_MS); + } + + break; + } + } +} + +void Keyboard::handleTriggerEnd(const OverlayID& overlayID, const PointerEvent& event) { + if (_keyboardLayers.empty() || !isLayerSwitchTimerFinished()) { + return; + } + + auto pointerID = event.getID(); + + for (auto index = _keyboardLayers[_layerIndex].begin(); index != _keyboardLayers[_layerIndex].end(); index++) { + Key& key = *index; + + if (key.getID() == overlayID && (pointerID == _leftHandStylus || pointerID == _rightHandStylus)) { + Overlays& overlays = qApp->getOverlays(); + + auto base3DOverlay = std::dynamic_pointer_cast(overlays.getOverlay(overlayID)); + + if (base3DOverlay) { + base3DOverlay->setLocalPosition(key.getCurrentLocalPosition()); + } + + key.setIsPressed(false); + if (key.timerFinished()) { + key.startTimer(KEY_PRESS_TIMEOUT_MS); + } + break; + } + } + +} + +void Keyboard::handleTriggerContinue(const OverlayID& overlayID, const PointerEvent& event) { + if (_keyboardLayers.empty() || !isLayerSwitchTimerFinished()) { + return; + } + + auto pointerID = event.getID(); + + for (auto index = _keyboardLayers[_layerIndex].begin(); index != _keyboardLayers[_layerIndex].end(); index++) { + Key& key = *index; + + if (key.getID() == overlayID && (pointerID == _leftHandStylus || pointerID == _rightHandStylus)) { + Overlays& overlays = qApp->getOverlays(); + + if (!key.isPressed()) { + auto pointerManager = DependencyManager::get(); + auto pickResult = pointerManager->getPrevPickResult(pointerID); + + auto base3DOverlay = std::dynamic_pointer_cast(overlays.getOverlay(overlayID)); + + if (base3DOverlay) { + auto pickResultVariant = pickResult->toVariantMap(); + auto stylusTipVariant = pickResultVariant["stylusTip"]; + auto stylusTipPositionVariant = stylusTipVariant.toMap()["position"]; + glm::vec3 stylusTipPosition = vec3FromVariant(stylusTipPositionVariant); + + glm::quat overlayOrientation = base3DOverlay->getWorldOrientation(); + glm::vec3 overlayPosition = base3DOverlay->getWorldPosition(); + + glm::mat4 overlayWorldMat = createMatFromQuatAndPos(overlayOrientation, overlayPosition); + glm::mat4 overlayWorldToLocalMat = glm::inverse(overlayWorldMat); + + glm::vec3 stylusTipInOverlaySpace = transformPoint(overlayWorldToLocalMat, stylusTipPosition); + + static const float PENATRATION_THRESHOLD = 0.025f; + if (stylusTipInOverlaySpace.z < PENATRATION_THRESHOLD) { + static const float Z_OFFSET = 0.002f; + glm::vec3 overlayYAxis = overlayOrientation * Z_AXIS; + glm::vec3 overlayYOffset = overlayYAxis * Z_OFFSET; + glm::vec3 localPosition = key.getCurrentLocalPosition() - overlayYOffset; + base3DOverlay->setLocalPosition(localPosition); + key.setIsPressed(true); + } + } + } + break; + } + } +} + +void Keyboard::disableStylus() { + auto pointerManager = DependencyManager::get(); + pointerManager->setRenderState(_leftHandStylus, "events off"); + pointerManager->disablePointer(_leftHandStylus); + pointerManager->setRenderState(_rightHandStylus, "events off"); + pointerManager->disablePointer(_rightHandStylus); +} + +void Keyboard::setLayerIndex(int layerIndex) { + if (layerIndex >= 0 && layerIndex < (int)_keyboardLayers.size()) { + _layerIndex = layerIndex; + } else { + _layerIndex = 0; + } +} + +void Keyboard::loadKeyboardFile(const QString& keyboardFile) { + if (keyboardFile.isEmpty()) { + return; + } + + auto request = DependencyManager::get()->createResourceRequest(this, keyboardFile); + + if (!request) { + qCWarning(interfaceapp) << "Could not create resource for Keyboard file" << keyboardFile; + } + + + connect(request, &ResourceRequest::finished, this, [=]() { + if (request->getResult() != ResourceRequest::Success) { + qCWarning(interfaceapp) << "Keyboard file failed to download"; + return; + } + + clearKeyboardKeys(); + Overlays& overlays = qApp->getOverlays(); + auto requestData = request->getData(); + + QVector includeItems; + + QJsonParseError parseError; + QJsonDocument jsonDoc = QJsonDocument::fromJson(requestData, &parseError); + + if (parseError.error != QJsonParseError::NoError) { + qCWarning(interfaceapp) << "Failed to parse keyboard json file - Error: " << parseError.errorString(); + return; + } + QJsonObject jsonObject = jsonDoc.object(); + QJsonArray layer = jsonObject["Layer1"].toArray(); + QJsonObject anchorObject = jsonObject["anchor"].toObject(); + bool useResourcePath = jsonObject["useResourcesPath"].toBool(); + QString resourcePath = PathUtils::resourcesUrl(); + + + if (anchorObject.isEmpty()) { + qCWarning(interfaceapp) << "No Anchor specified. Not creating keyboard"; + return; + } + + QVariantMap anchorProperties { + { "name", "KeyboardAnchor"}, + { "isSolid", true }, + { "visible", false }, + { "grabbable", true }, + { "ignoreRayIntersection", false }, + { "dimensions", anchorObject["dimensions"].toVariant() }, + { "position", anchorObject["position"].toVariant() }, + { "orientation", anchorObject["rotation"].toVariant() } + }; + + glm::vec3 dimensions = vec3FromVariant(anchorObject["dimensions"].toVariant()); + + Anchor anchor; + anchor.overlayID = overlays.addOverlay("cube", anchorProperties); + anchor.originalDimensions = dimensions; + _anchor = anchor; + + const QJsonArray& keyboardLayers = jsonObject["layers"].toArray(); + int keyboardLayerCount = keyboardLayers.size(); + _keyboardLayers.reserve(keyboardLayerCount); + + + for (int keyboardLayerIndex = 0; keyboardLayerIndex < keyboardLayerCount; keyboardLayerIndex++) { + const QJsonValue& keyboardLayer = keyboardLayers[keyboardLayerIndex].toArray(); + + std::vector keyboardLayerKeys; + foreach (const QJsonValue& keyboardKeyValue, keyboardLayer.toArray()) { + + QVariantMap textureMap; + if (!keyboardKeyValue["texture"].isNull()) { + textureMap = keyboardKeyValue["texture"].toObject().toVariantMap(); + + if (useResourcePath) { + for (auto iter = textureMap.begin(); iter != textureMap.end(); iter++) { + QString modifiedPath = resourcePath + iter.value().toString(); + textureMap[iter.key()] = modifiedPath; + } + } + } + + QString modelUrl = keyboardKeyValue["modelURL"].toString(); + QString url = (useResourcePath ? (resourcePath + modelUrl) : modelUrl); + + QVariantMap properties { + { "dimensions", keyboardKeyValue["dimensions"].toVariant() }, + { "position", keyboardKeyValue["position"].toVariant() }, + { "visible", false }, + { "isSolid", true }, + { "emissive", true }, + { "parentID", _anchor.overlayID }, + { "url", url }, + { "textures", textureMap }, + { "grabbable", false }, + { "localOrientation", keyboardKeyValue["localOrientation"].toVariant() } + }; + + OverlayID overlayID = overlays.addOverlay("model", properties); + + QString keyType = keyboardKeyValue["type"].toString(); + QString keyString = keyboardKeyValue["key"].toString(); + + Key key; + if (!keyType.isNull()) { + Key::Type type= Key::getKeyTypeFromString(keyType); + key.setKeyType(type); + + if (type == Key::Type::LAYER || type == Key::Type::CAPS) { + int switchToLayer = keyboardKeyValue["switchToLayer"].toInt(); + key.setSwitchToLayerIndex(switchToLayer); + } + } + key.setID(overlayID); + key.setKeyString(keyString); + key.saveDimensionsAndLocalPosition(); + + includeItems.append(key.getID()); + _itemsToIgnore.append(key.getID()); + keyboardLayerKeys.push_back(key); + } + + _keyboardLayers.push_back(keyboardLayerKeys); + + } + + TextDisplay textDisplay; + QJsonObject displayTextObject = jsonObject["textDisplay"].toObject(); + + QVariantMap displayTextProperties { + { "dimensions", displayTextObject["dimensions"].toVariant() }, + { "localPosition", displayTextObject["localPosition"].toVariant() }, + { "localOrientation", displayTextObject["localOrientation"].toVariant() }, + { "leftMargin", displayTextObject["leftMargin"].toVariant() }, + { "rightMargin", displayTextObject["rightMargin"].toVariant() }, + { "topMargin", displayTextObject["topMargin"].toVariant() }, + { "bottomMargin", displayTextObject["bottomMargin"].toVariant() }, + { "lineHeight", displayTextObject["lineHeight"].toVariant() }, + { "visible", false }, + { "emissive", true }, + { "grabbable", false }, + { "text", ""}, + { "parentID", _anchor.overlayID } + }; + + textDisplay.overlayID = overlays.addOverlay("text3d", displayTextProperties); + textDisplay.localPosition = vec3FromVariant(displayTextObject["localPosition"].toVariant()); + textDisplay.dimensions = vec3FromVariant(displayTextObject["dimensions"].toVariant()); + textDisplay.lineHeight = (float) displayTextObject["lineHeight"].toDouble(); + + _textDisplay = textDisplay; + + _ignoreItemsLock.withWriteLock([&] { + _itemsToIgnore.push_back(_textDisplay.overlayID); + _itemsToIgnore.push_back(_anchor.overlayID); + }); + _layerIndex = 0; + auto pointerManager = DependencyManager::get(); + pointerManager->setIncludeItems(_leftHandStylus, includeItems); + pointerManager->setIncludeItems(_rightHandStylus, includeItems); + }); + + request->send(); +} + +QVector Keyboard::getKeysID() { + return _ignoreItemsLock.resultWithReadLock>([&] { + return _itemsToIgnore; + }); +} + +void Keyboard::clearKeyboardKeys() { + Overlays& overlays = qApp->getOverlays(); + + for (const auto& keyboardLayer: _keyboardLayers) { + for (const Key& key: keyboardLayer) { + overlays.deleteOverlay(key.getID()); + } + } + + overlays.deleteOverlay(_anchor.overlayID); + overlays.deleteOverlay(_textDisplay.overlayID); + + _keyboardLayers.clear(); + + _ignoreItemsLock.withWriteLock([&] { + _itemsToIgnore.clear(); + }); +} + +void Keyboard::enableStylus() { + auto pointerManager = DependencyManager::get(); + pointerManager->setRenderState(_leftHandStylus, "events on"); + pointerManager->enablePointer(_leftHandStylus); + pointerManager->setRenderState(_rightHandStylus, "events on"); + pointerManager->enablePointer(_rightHandStylus); + +} diff --git a/interface/src/ui/Keyboard.h b/interface/src/ui/Keyboard.h new file mode 100644 index 0000000000..662a51c2da --- /dev/null +++ b/interface/src/ui/Keyboard.h @@ -0,0 +1,152 @@ +// +// Keyboard.h +// interface/src/scripting +// +// Created by Dante Ruiz on 2018-08-27. +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_Keyboard_h +#define hifi_Keyboard_h + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "ui/overlays/Overlay.h" + +class PointerEvent; + + +class Key { +public: + Key() = default; + ~Key() = default; + + enum Type { + CHARACTER, + CAPS, + CLOSE, + LAYER, + BACKSPACE, + SPACE, + ENTER + }; + + + static Key::Type getKeyTypeFromString(const QString& keyTypeString); + + OverlayID getID() const { return _keyID; } + void setID(OverlayID overlayID) { _keyID = overlayID; } + + void startTimer(int time); + bool timerFinished(); + + void setKeyString(QString keyString) { _keyString = keyString; } + QString getKeyString(bool toUpper) const; + int getScanCode(bool toUpper) const; + + bool isPressed() const { return _pressed; } + void setIsPressed(bool pressed) { _pressed = pressed; } + + void setSwitchToLayerIndex(int layerIndex) { _switchToLayer = layerIndex; } + int getSwitchToLayerIndex() const { return _switchToLayer; } + + Type getKeyType() const { return _type; } + void setKeyType(Type type) { _type = type; } + + glm::vec3 getCurrentLocalPosition() const { return _currentLocalPosition; } + + void saveDimensionsAndLocalPosition(); + + void scaleKey(float sensorToWorldScale); +private: + Type _type { Type::CHARACTER }; + + int _switchToLayer { 0 }; + bool _pressed { false }; + + OverlayID _keyID; + QString _keyString; + + glm::vec3 _originalLocalPosition; + glm::vec3 _originalDimensions; + glm::vec3 _currentLocalPosition; + + std::shared_ptr _timer { std::make_shared() }; +}; + +class Keyboard : public Dependency, public QObject, public ReadWriteLockable { +public: + Keyboard(); + void createKeyboard(); + bool isRaised() const; + void setRaised(bool raised); + + bool isPassword() const; + void setPassword(bool password); + + void loadKeyboardFile(const QString& keyboardFile); + QVector getKeysID(); + +public slots: + void handleTriggerBegin(const OverlayID& overlayID, const PointerEvent& event); + void handleTriggerEnd(const OverlayID& overlayID, const PointerEvent& event); + void handleTriggerContinue(const OverlayID& overlayID, const PointerEvent& event); + void scaleKeyboard(float sensorToWorldScale); + +private: + struct Anchor { + OverlayID overlayID; + glm::vec3 originalDimensions; + }; + + struct TextDisplay { + float lineHeight; + OverlayID overlayID; + glm::vec3 localPosition; + glm::vec3 dimensions; + }; + + void raiseKeyboard(bool raise) const; + void raiseKeyboardAnchor(bool raise) const; + + void setLayerIndex(int layerIndex); + void enableStylus(); + void disableStylus(); + void clearKeyboardKeys(); + void switchToLayer(int layerIndex); + void updateTextDisplay(); + + void startLayerSwitchTimer(); + bool isLayerSwitchTimerFinished(); + + bool _raised { false }; + bool _password { false }; + bool _capsEnabled { false }; + int _layerIndex { 0 }; + unsigned int _leftHandStylus { 0 }; + unsigned int _rightHandStylus { 0 }; + SharedSoundPointer _keySound { nullptr }; + std::shared_ptr _layerSwitchTimer { std::make_shared() }; + + QString _typedCharacters; + TextDisplay _textDisplay; + Anchor _anchor; + + mutable ReadWriteLockable _ignoreItemsLock; + QVector _itemsToIgnore; + std::vector> _keyboardLayers; +}; + +#endif diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp index 190d9c3895..fd3ff69691 100644 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -766,4 +766,4 @@ render::ItemKey ModelOverlay::getKey() { builder.withMetaCullGroup(); } return builder.build(); -} \ No newline at end of file +} diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index f4c98bb9e0..35228c6247 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -35,6 +35,7 @@ #include "RectangleOverlay.h" #include "Text3DOverlay.h" #include "Web3DOverlay.h" +#include "ui/Keyboard.h" #include #include @@ -868,8 +869,13 @@ void Overlays::mousePressPointerEvent(const OverlayID& overlayID, const PointerE QMetaObject::invokeMethod(thisOverlay.get(), "handlePointerEvent", Q_ARG(PointerEvent, event)); } - // emit to scripts - emit mousePressOnOverlay(overlayID, event); + + auto keyboard = DependencyManager::get(); + // Do not send keyboard key event to scripts to prevent malignant scripts from gathering what you typed + if (!keyboard->getKeysID().contains(overlayID)) { + // emit to scripts + emit mousePressOnOverlay(overlayID, event); + } } bool Overlays::mouseDoublePressEvent(QMouseEvent* event) { @@ -902,8 +908,12 @@ void Overlays::hoverEnterPointerEvent(const OverlayID& overlayID, const PointerE QMetaObject::invokeMethod(thisOverlay.get(), "hoverEnterOverlay", Q_ARG(PointerEvent, event)); } - // emit to scripts - emit hoverEnterOverlay(overlayID, event); + auto keyboard = DependencyManager::get(); + // Do not send keyboard key event to scripts to prevent malignant scripts from gathering what you typed + if (!keyboard->getKeysID().contains(overlayID)) { + // emit to scripts + emit hoverEnterOverlay(overlayID, event); + } } void Overlays::hoverOverPointerEvent(const OverlayID& overlayID, const PointerEvent& event) { @@ -917,8 +927,12 @@ void Overlays::hoverOverPointerEvent(const OverlayID& overlayID, const PointerEv QMetaObject::invokeMethod(thisOverlay.get(), "handlePointerEvent", Q_ARG(PointerEvent, event)); } - // emit to scripts - emit hoverOverOverlay(overlayID, event); + auto keyboard = DependencyManager::get(); + // Do not send keyboard key event to scripts to prevent malignant scripts from gathering what you typed + if (!keyboard->getKeysID().contains(overlayID)) { + // emit to scripts + emit hoverOverOverlay(overlayID, event); + } } void Overlays::hoverLeavePointerEvent(const OverlayID& overlayID, const PointerEvent& event) { @@ -932,8 +946,12 @@ void Overlays::hoverLeavePointerEvent(const OverlayID& overlayID, const PointerE QMetaObject::invokeMethod(thisOverlay.get(), "hoverLeaveOverlay", Q_ARG(PointerEvent, event)); } - // emit to scripts - emit hoverLeaveOverlay(overlayID, event); + auto keyboard = DependencyManager::get(); + // Do not send keyboard key event to scripts to prevent malignant scripts from gathering what you typed + if (!keyboard->getKeysID().contains(overlayID)) { + // emit to scripts + emit hoverLeaveOverlay(overlayID, event); + } } bool Overlays::mouseReleaseEvent(QMouseEvent* event) { @@ -962,8 +980,12 @@ void Overlays::mouseReleasePointerEvent(const OverlayID& overlayID, const Pointe QMetaObject::invokeMethod(thisOverlay.get(), "handlePointerEvent", Q_ARG(PointerEvent, event)); } - // emit to scripts - emit mouseReleaseOnOverlay(overlayID, event); + auto keyboard = DependencyManager::get(); + // Do not send keyboard key event to scripts to prevent malignant scripts from gathering what you typed + if (!keyboard->getKeysID().contains(overlayID)) { + // emit to scripts + emit mouseReleaseOnOverlay(overlayID, event); + } } bool Overlays::mouseMoveEvent(QMouseEvent* event) { @@ -1014,8 +1036,13 @@ void Overlays::mouseMovePointerEvent(const OverlayID& overlayID, const PointerEv QMetaObject::invokeMethod(thisOverlay.get(), "handlePointerEvent", Q_ARG(PointerEvent, event)); } - // emit to scripts - emit mouseMoveOnOverlay(overlayID, event); + auto keyboard = DependencyManager::get(); + + // Do not send keyboard key event to scripts to prevent malignant scripts from gathering what you typed + if (!keyboard->getKeysID().contains(overlayID)) { + // emit to scripts + emit mouseMoveOnOverlay(overlayID, event); + } } QVector Overlays::findOverlays(const glm::vec3& center, float radius) { @@ -1035,7 +1062,7 @@ QVector Overlays::findOverlays(const glm::vec3& center, float radius) { OverlayID thisID = i.key(); auto overlay = std::dynamic_pointer_cast(i.value()); // FIXME: this ignores overlays with ignorePickIntersection == true, which seems wrong - if (overlay && overlay->getVisible() && !overlay->getIgnorePickIntersection() && overlay->isLoaded()) { + if (overlay && overlay->getVisible() && overlay->isLoaded()) { // get AABox in frame of overlay glm::vec3 dimensions = overlay->getDimensions(); glm::vec3 low = dimensions * -0.5f; diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index f13d25f22c..e7a0c5934e 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -41,6 +41,7 @@ #include "scripting/AssetMappingsScriptingInterface.h" #include "scripting/MenuScriptingInterface.h" #include "scripting/SettingsScriptingInterface.h" +#include "scripting/KeyboardScriptingInterface.h" #include #include #include @@ -273,6 +274,7 @@ void Web3DOverlay::setupQmlSurface(bool isTablet) { _webSurface->getSurfaceContext()->setContextProperty("HiFiAbout", AboutUtil::getInstance()); _webSurface->getSurfaceContext()->setContextProperty("WalletScriptingInterface", DependencyManager::get().data()); _webSurface->getSurfaceContext()->setContextProperty("ResourceRequestObserver", DependencyManager::get().data()); + _webSurface->getSurfaceContext()->setContextProperty("KeyboardScriptingInterface", DependencyManager::get().data()); // Override min fps for tablet UI, for silky smooth scrolling setMaxFPS(90); diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 6f285a9f0c..7da7a45e83 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -1180,9 +1180,9 @@ int Model::getLastFreeJointIndex(int jointIndex) const { void Model::setTextures(const QVariantMap& textures) { if (isLoaded()) { - _pendingTextures.clear(); _needsFixupInScene = true; _renderGeometry->setTextures(textures); + _pendingTextures.clear(); } else { _pendingTextures = textures; } diff --git a/libraries/ui/src/ui/TabletScriptingInterface.cpp b/libraries/ui/src/ui/TabletScriptingInterface.cpp index 1081f8c4e7..52d359ad0d 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.cpp +++ b/libraries/ui/src/ui/TabletScriptingInterface.cpp @@ -888,6 +888,12 @@ void TabletProxy::desktopWindowClosed() { gotoHomeScreen(); } +void TabletProxy::unfocus() { + if (_qmlOffscreenSurface) { + _qmlOffscreenSurface->lowerKeyboard(); + } +} + QQuickItem* TabletProxy::getQmlTablet() const { if (!_qmlTabletRoot) { diff --git a/libraries/ui/src/ui/TabletScriptingInterface.h b/libraries/ui/src/ui/TabletScriptingInterface.h index 2d37402d01..9821ad1263 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.h +++ b/libraries/ui/src/ui/TabletScriptingInterface.h @@ -232,6 +232,7 @@ public: const QString getName() const { return _name; } bool getToolbarMode() const { return _toolbarMode; } void setToolbarMode(bool toolbarMode); + void unfocus(); /**jsdoc * @function TabletProxy#gotoMenuScreen diff --git a/scripts/system/controllers/controllerModules/inEditMode.js b/scripts/system/controllers/controllerModules/inEditMode.js index 2b17f447a0..6adfa88fb2 100644 --- a/scripts/system/controllers/controllerModules/inEditMode.js +++ b/scripts/system/controllers/controllerModules/inEditMode.js @@ -175,6 +175,23 @@ Script.include("/~/system/libraries/utils.js"); return this.exitModule(); } } + + var stopRunning = false; + + if ((controllerData.triggerClicks[this.hand] === 0 && controllerData.secondaryValues[this.hand] === 0)) { + var stopRunning = false; + controllerData.nearbyOverlayIDs[this.hand].forEach(function(overlayID) { + var overlayName = Overlays.getProperty(overlayID, "name"); + if (overlayName === "KeyboardAnchor") { + stopRunning = true; + } + }); + + if (stopRunning) { + return this.exitModule(); + } + } + this.sendPickData(controllerData); return this.isReady(controllerData); }; diff --git a/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js b/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js index 763a0a0a27..9bddeb236a 100644 --- a/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js +++ b/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js @@ -46,6 +46,10 @@ Script.include("/~/system/libraries/utils.js"); return this.getOtherModule().thisHandIsParent(props); }; + this.isGrabbedThingVisible = function() { + return Overlays.getProperty(this.grabbedThingID, "visible"); + }; + this.thisHandIsParent = function(props) { if (props.parentID !== MyAvatar.sessionUUID && props.parentID !== MyAvatar.SELF_ID) { return false; @@ -198,7 +202,7 @@ Script.include("/~/system/libraries/utils.js"); }; this.run = function (controllerData) { - if (controllerData.triggerClicks[this.hand] === 0 && controllerData.secondaryValues[this.hand] === 0) { + if ((controllerData.triggerClicks[this.hand] === 0 && controllerData.secondaryValues[this.hand] === 0) || !this.isGrabbedThingVisible()) { this.endNearParentingGrabOverlay(); this.robbed = false; return makeRunningValues(false, [], []); diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index 8bfd776971..74c1e4baf0 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -726,9 +726,14 @@ var onTabletScreenChanged = function onTabletScreenChanged(type, url) { filterText = ""; } + var wasIsOpen = ui.isOpen; ui.isOpen = (onMarketplaceScreen || onCommerceScreen) && !onWalletScreen; ui.buttonActive(ui.isOpen); + if (wasIsOpen !== ui.isOpen && Keyboard.raised) { + Keyboard.raised = false; + } + if (type === "Web" && url.indexOf(MARKETPLACE_URL) !== -1) { ContextOverlay.isInMarketplaceInspectionMode = true; } else {