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 0000000000..0243d9fd1b Binary files /dev/null and b/interface/resources/meshes/drumstick.fbx differ diff --git a/interface/resources/meshes/keyboard/SM_enter.fbx b/interface/resources/meshes/keyboard/SM_enter.fbx new file mode 100644 index 0000000000..119e3fc535 Binary files /dev/null and b/interface/resources/meshes/keyboard/SM_enter.fbx differ diff --git a/interface/resources/meshes/keyboard/SM_key.fbx b/interface/resources/meshes/keyboard/SM_key.fbx new file mode 100644 index 0000000000..02684a42d8 Binary files /dev/null and b/interface/resources/meshes/keyboard/SM_key.fbx differ diff --git a/interface/resources/meshes/keyboard/SM_space.fbx b/interface/resources/meshes/keyboard/SM_space.fbx new file mode 100644 index 0000000000..77632eb795 Binary files /dev/null and b/interface/resources/meshes/keyboard/SM_space.fbx differ diff --git a/interface/resources/meshes/keyboard/keyCap_F.png b/interface/resources/meshes/keyboard/keyCap_F.png new file mode 100644 index 0000000000..fba21a7d77 Binary files /dev/null and b/interface/resources/meshes/keyboard/keyCap_F.png differ diff --git a/interface/resources/meshes/keyboard/keyCap_a.png b/interface/resources/meshes/keyboard/keyCap_a.png new file mode 100644 index 0000000000..f409254292 Binary files /dev/null and b/interface/resources/meshes/keyboard/keyCap_a.png differ diff --git a/interface/resources/meshes/keyboard/keyCap_b.png b/interface/resources/meshes/keyboard/keyCap_b.png new file mode 100644 index 0000000000..5ab85290d1 Binary files /dev/null and b/interface/resources/meshes/keyboard/keyCap_b.png differ diff --git a/interface/resources/meshes/keyboard/keyCap_c.png b/interface/resources/meshes/keyboard/keyCap_c.png new file mode 100644 index 0000000000..9a020163ab Binary files /dev/null and b/interface/resources/meshes/keyboard/keyCap_c.png differ diff --git a/interface/resources/meshes/keyboard/keyCap_d.png b/interface/resources/meshes/keyboard/keyCap_d.png new file mode 100644 index 0000000000..4eccee2dd5 Binary files /dev/null and b/interface/resources/meshes/keyboard/keyCap_d.png differ diff --git a/interface/resources/meshes/keyboard/keyCap_e.png b/interface/resources/meshes/keyboard/keyCap_e.png new file mode 100644 index 0000000000..09bd5bc289 Binary files /dev/null and b/interface/resources/meshes/keyboard/keyCap_e.png differ diff --git a/interface/resources/meshes/keyboard/keyCap_g.png b/interface/resources/meshes/keyboard/keyCap_g.png new file mode 100644 index 0000000000..9c52605428 Binary files /dev/null and b/interface/resources/meshes/keyboard/keyCap_g.png differ diff --git a/interface/resources/meshes/keyboard/keyCap_h.png b/interface/resources/meshes/keyboard/keyCap_h.png new file mode 100644 index 0000000000..f29323da11 Binary files /dev/null and b/interface/resources/meshes/keyboard/keyCap_h.png differ diff --git a/interface/resources/meshes/keyboard/keyCap_i.png b/interface/resources/meshes/keyboard/keyCap_i.png new file mode 100644 index 0000000000..721e6b84b1 Binary files /dev/null and b/interface/resources/meshes/keyboard/keyCap_i.png differ diff --git a/interface/resources/meshes/keyboard/keyCap_j.png b/interface/resources/meshes/keyboard/keyCap_j.png new file mode 100644 index 0000000000..7186df71c1 Binary files /dev/null and b/interface/resources/meshes/keyboard/keyCap_j.png differ diff --git a/interface/resources/meshes/keyboard/keyCap_k.png b/interface/resources/meshes/keyboard/keyCap_k.png new file mode 100644 index 0000000000..69667f42e0 Binary files /dev/null and b/interface/resources/meshes/keyboard/keyCap_k.png differ diff --git a/interface/resources/meshes/keyboard/keyCap_l.png b/interface/resources/meshes/keyboard/keyCap_l.png new file mode 100644 index 0000000000..d59c24f7e2 Binary files /dev/null and b/interface/resources/meshes/keyboard/keyCap_l.png differ diff --git a/interface/resources/meshes/keyboard/keyCap_m.png b/interface/resources/meshes/keyboard/keyCap_m.png new file mode 100644 index 0000000000..a6bc6729a8 Binary files /dev/null and b/interface/resources/meshes/keyboard/keyCap_m.png differ diff --git a/interface/resources/meshes/keyboard/keyCap_n.png b/interface/resources/meshes/keyboard/keyCap_n.png new file mode 100644 index 0000000000..950fb88e41 Binary files /dev/null and b/interface/resources/meshes/keyboard/keyCap_n.png differ diff --git a/interface/resources/meshes/keyboard/keyCap_o.png b/interface/resources/meshes/keyboard/keyCap_o.png new file mode 100644 index 0000000000..2c1755c887 Binary files /dev/null and b/interface/resources/meshes/keyboard/keyCap_o.png differ diff --git a/interface/resources/meshes/keyboard/keyCap_p.png b/interface/resources/meshes/keyboard/keyCap_p.png new file mode 100644 index 0000000000..366a9ef3ff Binary files /dev/null and b/interface/resources/meshes/keyboard/keyCap_p.png differ diff --git a/interface/resources/meshes/keyboard/keyCap_q.png b/interface/resources/meshes/keyboard/keyCap_q.png new file mode 100644 index 0000000000..2d58bc2b46 Binary files /dev/null and b/interface/resources/meshes/keyboard/keyCap_q.png differ diff --git a/interface/resources/meshes/keyboard/keyCap_r.png b/interface/resources/meshes/keyboard/keyCap_r.png new file mode 100644 index 0000000000..053151aada Binary files /dev/null and b/interface/resources/meshes/keyboard/keyCap_r.png differ diff --git a/interface/resources/meshes/keyboard/keyCap_s.png b/interface/resources/meshes/keyboard/keyCap_s.png new file mode 100644 index 0000000000..365a833af2 Binary files /dev/null and b/interface/resources/meshes/keyboard/keyCap_s.png differ diff --git a/interface/resources/meshes/keyboard/keyCap_t.png b/interface/resources/meshes/keyboard/keyCap_t.png new file mode 100644 index 0000000000..6ee42e3ae4 Binary files /dev/null and b/interface/resources/meshes/keyboard/keyCap_t.png differ diff --git a/interface/resources/meshes/keyboard/keyCap_u.png b/interface/resources/meshes/keyboard/keyCap_u.png new file mode 100644 index 0000000000..731467227a Binary files /dev/null and b/interface/resources/meshes/keyboard/keyCap_u.png differ diff --git a/interface/resources/meshes/keyboard/keyCap_v.png b/interface/resources/meshes/keyboard/keyCap_v.png new file mode 100644 index 0000000000..1dbe395005 Binary files /dev/null and b/interface/resources/meshes/keyboard/keyCap_v.png differ diff --git a/interface/resources/meshes/keyboard/keyCap_w.png b/interface/resources/meshes/keyboard/keyCap_w.png new file mode 100644 index 0000000000..a71de5124d Binary files /dev/null and b/interface/resources/meshes/keyboard/keyCap_w.png differ diff --git a/interface/resources/meshes/keyboard/keyCap_x.png b/interface/resources/meshes/keyboard/keyCap_x.png new file mode 100644 index 0000000000..232725dd6c Binary files /dev/null and b/interface/resources/meshes/keyboard/keyCap_x.png differ diff --git a/interface/resources/meshes/keyboard/keyCap_y.png b/interface/resources/meshes/keyboard/keyCap_y.png new file mode 100644 index 0000000000..ed68e21384 Binary files /dev/null and b/interface/resources/meshes/keyboard/keyCap_y.png differ diff --git a/interface/resources/meshes/keyboard/keyCap_z.png b/interface/resources/meshes/keyboard/keyCap_z.png new file mode 100644 index 0000000000..a1fe8d4181 Binary files /dev/null and b/interface/resources/meshes/keyboard/keyCap_z.png differ diff --git a/interface/resources/meshes/keyboard/key_0.png b/interface/resources/meshes/keyboard/key_0.png new file mode 100644 index 0000000000..ff852cda9d Binary files /dev/null and b/interface/resources/meshes/keyboard/key_0.png differ diff --git a/interface/resources/meshes/keyboard/key_1.png b/interface/resources/meshes/keyboard/key_1.png new file mode 100644 index 0000000000..7115e92be8 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_1.png differ diff --git a/interface/resources/meshes/keyboard/key_123.png b/interface/resources/meshes/keyboard/key_123.png new file mode 100644 index 0000000000..07a3187a70 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_123.png differ diff --git a/interface/resources/meshes/keyboard/key_2.png b/interface/resources/meshes/keyboard/key_2.png new file mode 100644 index 0000000000..99a7e650f1 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_2.png differ diff --git a/interface/resources/meshes/keyboard/key_3.png b/interface/resources/meshes/keyboard/key_3.png new file mode 100644 index 0000000000..5ec80db3e0 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_3.png differ diff --git a/interface/resources/meshes/keyboard/key_4.png b/interface/resources/meshes/keyboard/key_4.png new file mode 100644 index 0000000000..e97261f2cd Binary files /dev/null and b/interface/resources/meshes/keyboard/key_4.png differ diff --git a/interface/resources/meshes/keyboard/key_5.png b/interface/resources/meshes/keyboard/key_5.png new file mode 100644 index 0000000000..59e059fbf4 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_5.png differ diff --git a/interface/resources/meshes/keyboard/key_6.png b/interface/resources/meshes/keyboard/key_6.png new file mode 100644 index 0000000000..bf4e81a7a1 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_6.png differ diff --git a/interface/resources/meshes/keyboard/key_7.png b/interface/resources/meshes/keyboard/key_7.png new file mode 100644 index 0000000000..5d9765b37e Binary files /dev/null and b/interface/resources/meshes/keyboard/key_7.png differ diff --git a/interface/resources/meshes/keyboard/key_8.png b/interface/resources/meshes/keyboard/key_8.png new file mode 100644 index 0000000000..f905e2220c Binary files /dev/null and b/interface/resources/meshes/keyboard/key_8.png differ diff --git a/interface/resources/meshes/keyboard/key_9.png b/interface/resources/meshes/keyboard/key_9.png new file mode 100644 index 0000000000..89a6397c82 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_9.png differ diff --git a/interface/resources/meshes/keyboard/key_a.png b/interface/resources/meshes/keyboard/key_a.png new file mode 100644 index 0000000000..74d57d5bd4 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_a.png differ diff --git a/interface/resources/meshes/keyboard/key_abc.png b/interface/resources/meshes/keyboard/key_abc.png new file mode 100644 index 0000000000..5b7f1bcb0f Binary files /dev/null and b/interface/resources/meshes/keyboard/key_abc.png differ diff --git a/interface/resources/meshes/keyboard/key_ampersand.png b/interface/resources/meshes/keyboard/key_ampersand.png new file mode 100644 index 0000000000..e8e06892f8 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_ampersand.png differ diff --git a/interface/resources/meshes/keyboard/key_ast.png b/interface/resources/meshes/keyboard/key_ast.png new file mode 100644 index 0000000000..1c6f03ed4a Binary files /dev/null and b/interface/resources/meshes/keyboard/key_ast.png differ diff --git a/interface/resources/meshes/keyboard/key_at.png b/interface/resources/meshes/keyboard/key_at.png new file mode 100644 index 0000000000..0d0e9019a8 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_at.png differ diff --git a/interface/resources/meshes/keyboard/key_b.png b/interface/resources/meshes/keyboard/key_b.png new file mode 100644 index 0000000000..50b607cbd3 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_b.png differ diff --git a/interface/resources/meshes/keyboard/key_backspace.png b/interface/resources/meshes/keyboard/key_backspace.png new file mode 100644 index 0000000000..db56841a4a Binary files /dev/null and b/interface/resources/meshes/keyboard/key_backspace.png differ diff --git a/interface/resources/meshes/keyboard/key_c.png b/interface/resources/meshes/keyboard/key_c.png new file mode 100644 index 0000000000..da8a4a4f2d Binary files /dev/null and b/interface/resources/meshes/keyboard/key_c.png differ diff --git a/interface/resources/meshes/keyboard/key_cap.png b/interface/resources/meshes/keyboard/key_cap.png new file mode 100644 index 0000000000..1e3c4c1b9f Binary files /dev/null and b/interface/resources/meshes/keyboard/key_cap.png differ diff --git a/interface/resources/meshes/keyboard/key_caret.png b/interface/resources/meshes/keyboard/key_caret.png new file mode 100644 index 0000000000..05011b905a Binary files /dev/null and b/interface/resources/meshes/keyboard/key_caret.png differ 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 0000000000..6a93b0fe05 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_close_paren.png differ diff --git a/interface/resources/meshes/keyboard/key_colon.png b/interface/resources/meshes/keyboard/key_colon.png new file mode 100644 index 0000000000..a4f025349c Binary files /dev/null and b/interface/resources/meshes/keyboard/key_colon.png differ diff --git a/interface/resources/meshes/keyboard/key_comma.png b/interface/resources/meshes/keyboard/key_comma.png new file mode 100644 index 0000000000..69da507ec6 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_comma.png differ diff --git a/interface/resources/meshes/keyboard/key_d.png b/interface/resources/meshes/keyboard/key_d.png new file mode 100644 index 0000000000..557f3816f0 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_d.png differ diff --git a/interface/resources/meshes/keyboard/key_dollar.png b/interface/resources/meshes/keyboard/key_dollar.png new file mode 100644 index 0000000000..0105debb68 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_dollar.png differ diff --git a/interface/resources/meshes/keyboard/key_dquote.png b/interface/resources/meshes/keyboard/key_dquote.png new file mode 100644 index 0000000000..393ab9b748 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_dquote.png differ diff --git a/interface/resources/meshes/keyboard/key_e.png b/interface/resources/meshes/keyboard/key_e.png new file mode 100644 index 0000000000..1b356d9d5b Binary files /dev/null and b/interface/resources/meshes/keyboard/key_e.png differ diff --git a/interface/resources/meshes/keyboard/key_enter.png b/interface/resources/meshes/keyboard/key_enter.png new file mode 100644 index 0000000000..cf935fe07e Binary files /dev/null and b/interface/resources/meshes/keyboard/key_enter.png differ diff --git a/interface/resources/meshes/keyboard/key_exclam.png b/interface/resources/meshes/keyboard/key_exclam.png new file mode 100644 index 0000000000..e9f68ced1a Binary files /dev/null and b/interface/resources/meshes/keyboard/key_exclam.png differ diff --git a/interface/resources/meshes/keyboard/key_exit.png b/interface/resources/meshes/keyboard/key_exit.png new file mode 100644 index 0000000000..4c06660d56 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_exit.png differ diff --git a/interface/resources/meshes/keyboard/key_f.png b/interface/resources/meshes/keyboard/key_f.png new file mode 100644 index 0000000000..93306c6035 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_f.png differ diff --git a/interface/resources/meshes/keyboard/key_g.png b/interface/resources/meshes/keyboard/key_g.png new file mode 100644 index 0000000000..9fda692c04 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_g.png differ diff --git a/interface/resources/meshes/keyboard/key_h.png b/interface/resources/meshes/keyboard/key_h.png new file mode 100644 index 0000000000..c73f37c271 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_h.png differ diff --git a/interface/resources/meshes/keyboard/key_hashtag.png b/interface/resources/meshes/keyboard/key_hashtag.png new file mode 100644 index 0000000000..df673653b0 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_hashtag.png differ diff --git a/interface/resources/meshes/keyboard/key_i.png b/interface/resources/meshes/keyboard/key_i.png new file mode 100644 index 0000000000..6277d8085c Binary files /dev/null and b/interface/resources/meshes/keyboard/key_i.png differ diff --git a/interface/resources/meshes/keyboard/key_j.png b/interface/resources/meshes/keyboard/key_j.png new file mode 100644 index 0000000000..f723de1f55 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_j.png differ diff --git a/interface/resources/meshes/keyboard/key_k.png b/interface/resources/meshes/keyboard/key_k.png new file mode 100644 index 0000000000..aa3e806a8d Binary files /dev/null and b/interface/resources/meshes/keyboard/key_k.png differ diff --git a/interface/resources/meshes/keyboard/key_l.png b/interface/resources/meshes/keyboard/key_l.png new file mode 100644 index 0000000000..4b31a84f32 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_l.png differ diff --git a/interface/resources/meshes/keyboard/key_m.png b/interface/resources/meshes/keyboard/key_m.png new file mode 100644 index 0000000000..2e83b7b214 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_m.png differ diff --git a/interface/resources/meshes/keyboard/key_min.png b/interface/resources/meshes/keyboard/key_min.png new file mode 100644 index 0000000000..e45beb4e7c Binary files /dev/null and b/interface/resources/meshes/keyboard/key_min.png differ diff --git a/interface/resources/meshes/keyboard/key_n.png b/interface/resources/meshes/keyboard/key_n.png new file mode 100644 index 0000000000..be1a6a9e7a Binary files /dev/null and b/interface/resources/meshes/keyboard/key_n.png differ diff --git a/interface/resources/meshes/keyboard/key_o.png b/interface/resources/meshes/keyboard/key_o.png new file mode 100644 index 0000000000..883feec871 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_o.png differ 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 0000000000..9dcee0b9a0 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_open_paren.png differ diff --git a/interface/resources/meshes/keyboard/key_p.png b/interface/resources/meshes/keyboard/key_p.png new file mode 100644 index 0000000000..8d55d351ce Binary files /dev/null and b/interface/resources/meshes/keyboard/key_p.png differ diff --git a/interface/resources/meshes/keyboard/key_percentage.png b/interface/resources/meshes/keyboard/key_percentage.png new file mode 100644 index 0000000000..fbe1fa9599 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_percentage.png differ diff --git a/interface/resources/meshes/keyboard/key_period.png b/interface/resources/meshes/keyboard/key_period.png new file mode 100644 index 0000000000..ff726df39b Binary files /dev/null and b/interface/resources/meshes/keyboard/key_period.png differ diff --git a/interface/resources/meshes/keyboard/key_plus.png b/interface/resources/meshes/keyboard/key_plus.png new file mode 100644 index 0000000000..3eab84c34d Binary files /dev/null and b/interface/resources/meshes/keyboard/key_plus.png differ diff --git a/interface/resources/meshes/keyboard/key_q.png b/interface/resources/meshes/keyboard/key_q.png new file mode 100644 index 0000000000..fc733fd7fd Binary files /dev/null and b/interface/resources/meshes/keyboard/key_q.png differ diff --git a/interface/resources/meshes/keyboard/key_question.png b/interface/resources/meshes/keyboard/key_question.png new file mode 100644 index 0000000000..57f5187213 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_question.png differ diff --git a/interface/resources/meshes/keyboard/key_r.png b/interface/resources/meshes/keyboard/key_r.png new file mode 100644 index 0000000000..6277c64097 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_r.png differ diff --git a/interface/resources/meshes/keyboard/key_s.png b/interface/resources/meshes/keyboard/key_s.png new file mode 100644 index 0000000000..1fc1085391 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_s.png differ diff --git a/interface/resources/meshes/keyboard/key_semi.png b/interface/resources/meshes/keyboard/key_semi.png new file mode 100644 index 0000000000..5cb1b495a4 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_semi.png differ diff --git a/interface/resources/meshes/keyboard/key_slash.png b/interface/resources/meshes/keyboard/key_slash.png new file mode 100644 index 0000000000..be7b0fecb4 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_slash.png differ diff --git a/interface/resources/meshes/keyboard/key_squote.png b/interface/resources/meshes/keyboard/key_squote.png new file mode 100644 index 0000000000..3239c19ceb Binary files /dev/null and b/interface/resources/meshes/keyboard/key_squote.png differ diff --git a/interface/resources/meshes/keyboard/key_t.png b/interface/resources/meshes/keyboard/key_t.png new file mode 100644 index 0000000000..c2082b8f51 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_t.png differ diff --git a/interface/resources/meshes/keyboard/key_u.png b/interface/resources/meshes/keyboard/key_u.png new file mode 100644 index 0000000000..657527f6c0 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_u.png differ diff --git a/interface/resources/meshes/keyboard/key_under.png b/interface/resources/meshes/keyboard/key_under.png new file mode 100644 index 0000000000..3694dd3109 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_under.png differ diff --git a/interface/resources/meshes/keyboard/key_v.png b/interface/resources/meshes/keyboard/key_v.png new file mode 100644 index 0000000000..c061ca6fa0 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_v.png differ diff --git a/interface/resources/meshes/keyboard/key_w.png b/interface/resources/meshes/keyboard/key_w.png new file mode 100644 index 0000000000..15de9b25a8 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_w.png differ diff --git a/interface/resources/meshes/keyboard/key_x.png b/interface/resources/meshes/keyboard/key_x.png new file mode 100644 index 0000000000..d81a423f3b Binary files /dev/null and b/interface/resources/meshes/keyboard/key_x.png differ diff --git a/interface/resources/meshes/keyboard/key_y.png b/interface/resources/meshes/keyboard/key_y.png new file mode 100644 index 0000000000..cb85af5b32 Binary files /dev/null and b/interface/resources/meshes/keyboard/key_y.png differ diff --git a/interface/resources/meshes/keyboard/key_z.png b/interface/resources/meshes/keyboard/key_z.png new file mode 100644 index 0000000000..462531351d Binary files /dev/null and b/interface/resources/meshes/keyboard/key_z.png differ diff --git a/interface/resources/meshes/keyboard/text_placard.png b/interface/resources/meshes/keyboard/text_placard.png new file mode 100644 index 0000000000..b0a5953a30 Binary files /dev/null and b/interface/resources/meshes/keyboard/text_placard.png differ diff --git a/interface/resources/meshes/keyboard/white.png b/interface/resources/meshes/keyboard/white.png new file mode 100644 index 0000000000..9673f508fc Binary files /dev/null and b/interface/resources/meshes/keyboard/white.png differ 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 0000000000..e2cec81032 Binary files /dev/null and b/interface/resources/sounds/keyboard_key.mp3 differ 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 {