{"id":5265,"date":"2024-10-06T06:24:29","date_gmt":"2024-10-06T06:24:29","guid":{"rendered":"https:\/\/mixedrealitynow.com\/?p=5265"},"modified":"2024-10-20T06:39:09","modified_gmt":"2024-10-20T06:39:09","slug":"spatial-drawing-with-stylus-input-device-for-quest-logitech-mx-ink","status":"publish","type":"post","link":"https:\/\/mixedrealitynow.com\/ko\/spatial-drawing-with-stylus-input-device-for-quest-logitech-mx-ink","title":{"rendered":"Building app with Spatial Stylus Input Device for Quest &#8211; Logitech MX Ink"},"content":{"rendered":"\n<p><\/p>\n\n\n\n<figure class=\"wp-block-video\"><video height=\"720\" style=\"aspect-ratio: 1280 \/ 720;\" width=\"1280\" controls src=\"https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/MXInk_HL_1014_720p.mp4\"><\/video><\/figure>\n\n\n\n<p> As a designer, being able to put a brush stroke on a three-dimensional canvas was one of the most exciting experiences I had when I first tried Virtual Reality. I became a huge fan of spatial painting apps like TiltBrush, spending hours sketching large-scale pieces.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1920\" height=\"1080\" src=\"https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/MXInkCover.jpg\" alt=\"\" class=\"wp-image-5284\" srcset=\"https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/MXInkCover.jpg 1920w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/MXInkCover-300x169.jpg 300w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/MXInkCover-1024x576.jpg 1024w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/MXInkCover-768x432.jpg 768w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/MXInkCover-1536x864.jpg 1536w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/MXInkCover-18x10.jpg 18w\" sizes=\"auto, (max-width: 1920px) 100vw, 1920px\" \/><\/figure>\n\n\n\n<p>However, one of the biggest challenges was using bulky controllers for extended periods. It was difficult to create smaller, precise sketches with the large motion controllers. I\u2019ve often thought how incredible it would be if a stylus input device could be integrated into VR.<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h4 class=\"wp-block-heading\">Some of my paintings with old bulky controllers with HTC Vive and Windows MR devices<\/h4>\n\n\n\n<div class=\"wp-block-columns has-3-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex\">\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\">\n<figure class=\"wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio\"><div class=\"wp-block-embed__wrapper\">\n<iframe loading=\"lazy\" title=\"STAR WARS Tilt Brush - Spaceships battle paint drawing in VR\" width=\"640\" height=\"360\" src=\"https:\/\/www.youtube.com\/embed\/9Bx6nDjxC24?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen><\/iframe>\n<\/div><figcaption class=\"wp-element-caption\"><a href=\"http:\/\/dongyoonpark.com\/drawing-star-wars-in-vr-world-with-tilt-brush\">Star Wars VR painting with Tilt Brush<\/a><br>Received Steam Community Award : )<\/figcaption><\/figure>\n<\/div>\n\n\n\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\">\n<figure class=\"wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio\"><div class=\"wp-block-embed__wrapper\">\n<iframe loading=\"lazy\" title=\"Dragon Ball Tilt Brush - Drawing Freeza in VR #tiltbrush\" width=\"640\" height=\"360\" src=\"https:\/\/www.youtube.com\/embed\/1wPR8eV4kTw?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen><\/iframe>\n<\/div><figcaption class=\"wp-element-caption\"><a href=\"https:\/\/youtu.be\/1wPR8eV4kTw\" target=\"_blank\" rel=\"noreferrer noopener\">Dragon Ball Z &#8211; Frieza with Tilt Brush<\/a><br>Tilt Brush VR painting<\/figcaption><\/figure>\n<\/div>\n<\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Dream has Come True: Spatial Stylus Input Device for Meta Quest<\/h2>\n\n\n\n<figure class=\"wp-block-video\"><video height=\"480\" style=\"aspect-ratio: 854 \/ 480;\" width=\"854\" autoplay controls loop muted src=\"https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/MXInkHL.mp4\"><\/video><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>Finally, the dream has come true. <a href=\"https:\/\/www.logitech.com\/en-us\/products\/vr\/mx-ink.html\" target=\"_blank\" rel=\"noopener\" title=\"\">Logitech has introduced a new stylus device MX Ink <\/a>that\u2019s spatially tracked just like 6DoF controllers, allowing you to perform spatial drawing in 3D space with a much more familiar and comfortable pen-like form factor, instead of bulky controllers.<\/p>\n\n\n\n<p>In addition to its precision and spatial tracking, the stylus offers exciting features\u2014a pressure-sensitive tip and button. With the pressure-sensitive middle button, you can control the thickness of brush strokes in real time.<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"957\" height=\"347\" src=\"https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/Beta1-StylusControls.png\" alt=\"\" class=\"wp-image-5280\" style=\"object-fit:cover\" srcset=\"https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/Beta1-StylusControls.png 957w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/Beta1-StylusControls-300x109.png 300w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/Beta1-StylusControls-768x278.png 768w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/Beta1-StylusControls-18x7.png 18w\" sizes=\"auto, (max-width: 957px) 100vw, 957px\" \/><\/figure>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Start Building with MX Ink Sample App<\/h2>\n\n\n\n<p><a href=\"https:\/\/logitech.github.io\/mxink\/\" target=\"_blank\" rel=\"noopener\" title=\"\">Logitech&#8217;s Github page provides comprehensive resources and tools<\/a> for building apps with MX Ink. <\/p>\n\n\n\n<p><a href=\"https:\/\/logitech.github.io\/mxink\/Assets\/MX_Ink_Sample_App.zip\" target=\"_blank\" rel=\"noopener\" title=\"\">MX Ink Sample App<\/a> provides a sample scene that demonstrates how to create a simple spatial drawing experience with stroke thickness controls. The project includes Meta XR Core SDK v68 through pacakge manager which support MX Ink.<\/p>\n\n\n\n<p>Inside <strong>SampleScene.untiy<\/strong> scene, you can find <strong>MX_Ink <\/strong>prefab and basic Camera Rig with Passthrough capability configured through Building Blocks.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"2663\" height=\"1362\" src=\"https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/2024-10-05-14_22_10-MXInkSample-SampleScene-Windows-Mac-Linux-Unity-2022.3.20f1-_DX11_.png\" alt=\"\" class=\"wp-image-5290\" srcset=\"https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/2024-10-05-14_22_10-MXInkSample-SampleScene-Windows-Mac-Linux-Unity-2022.3.20f1-_DX11_.png 2663w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/2024-10-05-14_22_10-MXInkSample-SampleScene-Windows-Mac-Linux-Unity-2022.3.20f1-_DX11_-300x153.png 300w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/2024-10-05-14_22_10-MXInkSample-SampleScene-Windows-Mac-Linux-Unity-2022.3.20f1-_DX11_-1024x524.png 1024w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/2024-10-05-14_22_10-MXInkSample-SampleScene-Windows-Mac-Linux-Unity-2022.3.20f1-_DX11_-768x393.png 768w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/2024-10-05-14_22_10-MXInkSample-SampleScene-Windows-Mac-Linux-Unity-2022.3.20f1-_DX11_-1536x786.png 1536w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/2024-10-05-14_22_10-MXInkSample-SampleScene-Windows-Mac-Linux-Unity-2022.3.20f1-_DX11_-2048x1047.png 2048w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/2024-10-05-14_22_10-MXInkSample-SampleScene-Windows-Mac-Linux-Unity-2022.3.20f1-_DX11_-18x9.png 18w\" sizes=\"auto, (max-width: 2663px) 100vw, 2663px\" \/><\/figure>\n\n\n\n<p>As described in the MX Ink Unity Integration documentation, you need to assign <strong>MxInkActions.asset<\/strong> file in the <strong>Edit &gt; Project Settings &gt; Meta XR &gt; Input Actions<\/strong> to see the stylus device model. Otherwise, it will show Quest controllers.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1337\" height=\"995\" src=\"https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/2024-10-12-13_31_18-Project-Settings.png\" alt=\"\" class=\"wp-image-5371\" srcset=\"https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/2024-10-12-13_31_18-Project-Settings.png 1337w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/2024-10-12-13_31_18-Project-Settings-300x223.png 300w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/2024-10-12-13_31_18-Project-Settings-1024x762.png 1024w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/2024-10-12-13_31_18-Project-Settings-768x572.png 768w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/2024-10-12-13_31_18-Project-Settings-16x12.png 16w\" sizes=\"auto, (max-width: 1337px) 100vw, 1337px\" \/><\/figure>\n\n\n\n<p><strong>MxInkActions.asset<\/strong> file shows the definition of actions that MX Ink device supports. To learn more about Input Actions, check out <a href=\"https:\/\/developers.meta.com\/horizon\/documentation\/unity\/unity-inputactions\/\" target=\"_blank\" rel=\"noopener\" title=\"\">this documentation<\/a>.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full is-resized\"><img loading=\"lazy\" decoding=\"async\" width=\"1008\" height=\"894\" src=\"https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/2024-10-13-10_09_49-Settings.png\" alt=\"\" class=\"wp-image-5391\" style=\"width:629px;height:auto\" srcset=\"https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/2024-10-13-10_09_49-Settings.png 1008w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/2024-10-13-10_09_49-Settings-300x266.png 300w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/2024-10-13-10_09_49-Settings-768x681.png 768w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/2024-10-13-10_09_49-Settings-14x12.png 14w\" sizes=\"auto, (max-width: 1008px) 100vw, 1008px\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Brush Stroke Thickness with Pressure Sensitive Button <\/h2>\n\n\n\n<p>When you run the <strong>SampleScene.unity<\/strong>, you can see varying line thickness based on the analog value of the Middle button. It just works like the analog trigger button of the controllers.<\/p>\n\n\n\n<figure class=\"wp-block-video\"><video height=\"480\" style=\"aspect-ratio: 854 \/ 480;\" width=\"854\" autoplay controls loop src=\"https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/MXInk_Analog1.mp4\"><\/video><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Mixed Reality + Pressure Senstive Tip<\/h2>\n\n\n\n<p>What does it mean to have pressure sensitive tip? By combining with Quest&#8217;s Passthrough capability, you can draw virtual strokes on the physical surfaces. In addition to it, since Quest&#8217;s Meta Horizon OS provides sophisticated Scene Understanding and environmental awareness, you can leverage physical surfaces in fully immersive experiences as well. Check out my other post &#8216;<a href=\"https:\/\/mixedrealitynow.com\/building-mr-apps-using-physical-surfaces-has-never-been-easier-how-to-use-meta-mixed-reality-utility-kit-mruk\" target=\"_blank\" rel=\"noopener\" title=\"\">Building MR apps using physical surfaces has never been easier! How to use Meta Mixed Reality Utility Kit (MRUK)<\/a>&#8216; to learn more about Scene Understanding.<\/p>\n\n\n\n<figure class=\"wp-block-video\"><video height=\"480\" style=\"aspect-ratio: 854 \/ 480;\" width=\"854\" autoplay controls src=\"https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/MXInk_SurfaceWriting.mp4\"><\/video><\/figure>\n\n\n\n<p><strong>Drawing <\/strong>prefab object includes <strong>LineDrawing<\/strong>.cs script which demonstrates how to create lines with <strong>Unity Line Renderer<\/strong> based on the input values.<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" data-code=\"\nfloat analogInput = \nMathf.Max(_stylusHandler.CurrentState.tip_value,_stylusHandler.CurrentState.cluster_middle_value);\n\nif (analogInput &gt; 0 &amp;&amp; _stylusHandler.CanDraw())\n{\n    if (!_isDrawing)\n    {\n        StartNewLine();\n        _isDrawing = true;\n    }\n    AddPoint(_stylusHandler.CurrentState.inkingPose.position, _lineWidthIsFixed ? 1.0f : analogInput);\n}\n\" style=\"color:#d8dee9ff;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki nord\" style=\"background-color: #2e3440ff\" tabindex=\"0\"><code><span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9\">float<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">analogInput<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9\">Mathf<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">Max<\/span><span style=\"color: #D8DEE9FF\">(<\/span><span style=\"color: #D8DEE9\">_stylusHandler<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9\">CurrentState<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9\">tip_value<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9\">_stylusHandler<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9\">CurrentState<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9\">cluster_middle_value<\/span><span style=\"color: #D8DEE9FF\">)<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #81A1C1\">if<\/span><span style=\"color: #D8DEE9FF\"> (<\/span><span style=\"color: #D8DEE9\">analogInput<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">&gt;<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">&amp;&amp;<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">_stylusHandler<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">CanDraw<\/span><span style=\"color: #D8DEE9FF\">())<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ECEFF4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #81A1C1\">if<\/span><span style=\"color: #D8DEE9FF\"> (<\/span><span style=\"color: #81A1C1\">!<\/span><span style=\"color: #D8DEE9\">_isDrawing<\/span><span style=\"color: #D8DEE9FF\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #ECEFF4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #88C0D0\">StartNewLine<\/span><span style=\"color: #D8DEE9FF\">()<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #D8DEE9\">_isDrawing<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">true;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #ECEFF4\">}<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #88C0D0\">AddPoint<\/span><span style=\"color: #D8DEE9FF\">(<\/span><span style=\"color: #D8DEE9\">_stylusHandler<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9\">CurrentState<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9\">inkingPose<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9\">position<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">_lineWidthIsFixed<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">?<\/span><span style=\"color: #D8DEE9FF\"> 1<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9FF\">0<\/span><span style=\"color: #D8DEE9\">f<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">:<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">analogInput<\/span><span style=\"color: #D8DEE9FF\">)<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ECEFF4\">}<\/span><\/span>\n<span class=\"line\"><\/span><\/code><\/pre><\/div>\n\n\n\n<p>Inside <strong>VrStylusHandler<\/strong>.cs script under MX_Ink prefab, you can find how to retrieve the values from the stylus input, which is <a href=\"https:\/\/logitech.github.io\/mxink\/UnityIntegration.html\" target=\"_blank\" rel=\"noopener\" title=\"\">documented in this page<\/a>.<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" data-code=\"  OVRPlugin.GetActionStateFloat(&quot;tip&quot;, out stylus_tip_value);\n  OVRPlugin.GetActionStateBoolean(&quot;front&quot;, out bool stylus_front_button);\n  OVRPlugin.GetActionStateFloat(&quot;middle&quot;, out stylus_middle_value);\n  OVRPlugin.GetActionStateBoolean(&quot;back&quot;, out bool stylus_back_button)\n  OVRPlugin.GetActionStateBoolean(&quot;dock&quot;, out _stylus_docked)\" style=\"color:#d8dee9ff;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki nord\" style=\"background-color: #2e3440ff\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #D8DEE9FF\">  <\/span><span style=\"color: #D8DEE9\">OVRPlugin<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">GetActionStateFloat<\/span><span style=\"color: #D8DEE9FF\">(<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">tip<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">out<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">stylus_tip_value<\/span><span style=\"color: #D8DEE9FF\">)<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">  <\/span><span style=\"color: #D8DEE9\">OVRPlugin<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">GetActionStateBoolean<\/span><span style=\"color: #D8DEE9FF\">(<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">front<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">out<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">bool<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">stylus_front_button<\/span><span style=\"color: #D8DEE9FF\">)<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">  <\/span><span style=\"color: #D8DEE9\">OVRPlugin<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">GetActionStateFloat<\/span><span style=\"color: #D8DEE9FF\">(<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">middle<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">out<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">stylus_middle_value<\/span><span style=\"color: #D8DEE9FF\">)<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">  <\/span><span style=\"color: #D8DEE9\">OVRPlugin<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">GetActionStateBoolean<\/span><span style=\"color: #D8DEE9FF\">(<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">back<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">out<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">bool<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">stylus_back_button<\/span><span style=\"color: #D8DEE9FF\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">  <\/span><span style=\"color: #D8DEE9\">OVRPlugin<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">GetActionStateBoolean<\/span><span style=\"color: #D8DEE9FF\">(<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">dock<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">out<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">_stylus_docked<\/span><span style=\"color: #D8DEE9FF\">)<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" data-code=\"void Update()\n{\n    OVRInput.Update();\n    UpdatePose();\n\n    if (!OVRPlugin.GetActionStateFloat(MX_Ink_TipForce, out _stylus.tip_value))\n    {\n        Debug.LogError($&quot;MX_Ink: Error getting action name: {MX_Ink_TipForce}&quot;);\n    }\n\n    if (!OVRPlugin.GetActionStateFloat(MX_Ink_MiddleForce, out _stylus.cluster_middle_value))\n    {\n        Debug.LogError($&quot;MX_Ink: Error getting action name: {MX_Ink_TipForce}&quot;);\n    }\n\n    if (!OVRPlugin.GetActionStateBoolean(MX_Ink_ClusterFront, out _stylus.cluster_front_value))\n    {\n        Debug.LogError($&quot;MX_Ink: Error getting action name: {MX_Ink_ClusterFront}&quot;);\n    }\n    \n    ...\" style=\"color:#d8dee9ff;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki nord\" style=\"background-color: #2e3440ff\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #81A1C1\">void<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">Update<\/span><span style=\"color: #D8DEE9FF\">()<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ECEFF4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #D8DEE9\">OVRInput<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">Update<\/span><span style=\"color: #D8DEE9FF\">()<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #88C0D0\">UpdatePose<\/span><span style=\"color: #D8DEE9FF\">()<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #81A1C1\">if<\/span><span style=\"color: #D8DEE9FF\"> (<\/span><span style=\"color: #81A1C1\">!<\/span><span style=\"color: #D8DEE9\">OVRPlugin<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">GetActionStateFloat<\/span><span style=\"color: #D8DEE9FF\">(<\/span><span style=\"color: #D8DEE9\">MX_Ink_TipForce<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">out<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">_stylus<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9\">tip_value<\/span><span style=\"color: #D8DEE9FF\">))<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #ECEFF4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #D8DEE9\">Debug<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">LogError<\/span><span style=\"color: #D8DEE9FF\">(<\/span><span style=\"color: #D8DEE9\">$<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">MX_Ink: Error getting action name: {MX_Ink_TipForce}<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #D8DEE9FF\">)<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #ECEFF4\">}<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #81A1C1\">if<\/span><span style=\"color: #D8DEE9FF\"> (<\/span><span style=\"color: #81A1C1\">!<\/span><span style=\"color: #D8DEE9\">OVRPlugin<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">GetActionStateFloat<\/span><span style=\"color: #D8DEE9FF\">(<\/span><span style=\"color: #D8DEE9\">MX_Ink_MiddleForce<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">out<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">_stylus<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9\">cluster_middle_value<\/span><span style=\"color: #D8DEE9FF\">))<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #ECEFF4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #D8DEE9\">Debug<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">LogError<\/span><span style=\"color: #D8DEE9FF\">(<\/span><span style=\"color: #D8DEE9\">$<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">MX_Ink: Error getting action name: {MX_Ink_TipForce}<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #D8DEE9FF\">)<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #ECEFF4\">}<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #81A1C1\">if<\/span><span style=\"color: #D8DEE9FF\"> (<\/span><span style=\"color: #81A1C1\">!<\/span><span style=\"color: #D8DEE9\">OVRPlugin<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">GetActionStateBoolean<\/span><span style=\"color: #D8DEE9FF\">(<\/span><span style=\"color: #D8DEE9\">MX_Ink_ClusterFront<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">out<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">_stylus<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9\">cluster_front_value<\/span><span style=\"color: #D8DEE9FF\">))<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #ECEFF4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #D8DEE9\">Debug<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">LogError<\/span><span style=\"color: #D8DEE9FF\">(<\/span><span style=\"color: #D8DEE9\">$<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">MX_Ink: Error getting action name: {MX_Ink_ClusterFront}<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #D8DEE9FF\">)<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #ECEFF4\">}<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #81A1C1\">...<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Visualizing brush stroke<\/h2>\n\n\n\n<p>Unity&#8217;s <strong>LineRenderer <\/strong>provides a decent drawing experiences stroke thickness variations based on analog input values. However, due to its tape-like shapes, it feels it is not great for representing stroke of letters when writing on 2D surfaces.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1570\" height=\"551\" src=\"https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/MXInk_LineComp.jpg\" alt=\"\" class=\"wp-image-5302\" srcset=\"https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/MXInk_LineComp.jpg 1570w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/MXInk_LineComp-300x105.jpg 300w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/MXInk_LineComp-1024x359.jpg 1024w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/MXInk_LineComp-768x270.jpg 768w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/MXInk_LineComp-1536x539.jpg 1536w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/MXInk_LineComp-18x6.jpg 18w\" sizes=\"auto, (max-width: 1570px) 100vw, 1570px\" \/><\/figure>\n\n\n\n<p>Setting the Alignment property to <strong>View<\/strong> makes it little bit better but still not great for surface writing.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"2674\" height=\"1829\" src=\"https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/MXInk_LineRenderer_Alignment-1.png\" alt=\"\" class=\"wp-image-5373\" srcset=\"https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/MXInk_LineRenderer_Alignment-1.png 2674w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/MXInk_LineRenderer_Alignment-1-300x205.png 300w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/MXInk_LineRenderer_Alignment-1-1024x700.png 1024w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/MXInk_LineRenderer_Alignment-1-768x525.png 768w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/MXInk_LineRenderer_Alignment-1-1536x1051.png 1536w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/MXInk_LineRenderer_Alignment-1-2048x1401.png 2048w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/MXInk_LineRenderer_Alignment-1-18x12.png 18w\" sizes=\"auto, (max-width: 2674px) 100vw, 2674px\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Button Visual Feedback<\/h2>\n\n\n\n<p>Sample App shows an example of button highlights which can improve the interaction confidence. You can find the code in <strong>VrStylusHandler<\/strong>.cs script file. Based on the button values, it changes the button&#8217;s material color through MeshRenderer.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" data-code=\"        _tip.GetComponent&lt;MeshRenderer&gt;().material.color = _stylus.tip_value &gt; 0 ? active_color : default_color;\n        _cluster_front.GetComponent&lt;MeshRenderer&gt;().material.color = _stylus.cluster_front_value ? active_color : default_color;\n        _cluster_middle.GetComponent&lt;MeshRenderer&gt;().material.color = _stylus.cluster_middle_value &gt; 0 ? active_color : default_color;\n        if (_stylus.cluster_back_value)\n        {\n            _cluster_back.GetComponent&lt;MeshRenderer&gt;().material.color = _stylus.cluster_back_value ? active_color : default_color;\n        }\n        else\n        {\n            _cluster_back.GetComponent&lt;MeshRenderer&gt;().material.color = _stylus.cluster_back_double_tap_value ? double_tap_active_color : default_color;\n        }\" style=\"color:#d8dee9ff;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki nord\" style=\"background-color: #2e3440ff\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #D8DEE9\">_tip<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">GetComponent<\/span><span style=\"color: #ECEFF4\">&lt;<\/span><span style=\"color: #D8DEE9FF\">MeshRenderer<\/span><span style=\"color: #ECEFF4\">&gt;<\/span><span style=\"color: #D8DEE9FF\">()<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9\">material<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9\">color<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">_stylus<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9\">tip_value<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">&gt;<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">?<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">active_color<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">:<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">default_color<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #D8DEE9\">_cluster_front<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">GetComponent<\/span><span style=\"color: #ECEFF4\">&lt;<\/span><span style=\"color: #D8DEE9FF\">MeshRenderer<\/span><span style=\"color: #ECEFF4\">&gt;<\/span><span style=\"color: #D8DEE9FF\">()<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9\">material<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9\">color<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">_stylus<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9\">cluster_front_value<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">?<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">active_color<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">:<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">default_color<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #D8DEE9\">_cluster_middle<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">GetComponent<\/span><span style=\"color: #ECEFF4\">&lt;<\/span><span style=\"color: #D8DEE9FF\">MeshRenderer<\/span><span style=\"color: #ECEFF4\">&gt;<\/span><span style=\"color: #D8DEE9FF\">()<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9\">material<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9\">color<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">_stylus<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9\">cluster_middle_value<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">&gt;<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">?<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">active_color<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">:<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">default_color<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #81A1C1\">if<\/span><span style=\"color: #D8DEE9FF\"> (<\/span><span style=\"color: #D8DEE9\">_stylus<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9\">cluster_back_value<\/span><span style=\"color: #D8DEE9FF\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #ECEFF4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">            <\/span><span style=\"color: #D8DEE9\">_cluster_back<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">GetComponent<\/span><span style=\"color: #ECEFF4\">&lt;<\/span><span style=\"color: #D8DEE9FF\">MeshRenderer<\/span><span style=\"color: #ECEFF4\">&gt;<\/span><span style=\"color: #D8DEE9FF\">()<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9\">material<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9\">color<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">_stylus<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9\">cluster_back_value<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">?<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">active_color<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">:<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">default_color<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #ECEFF4\">}<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #81A1C1\">else<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #ECEFF4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">            <\/span><span style=\"color: #D8DEE9\">_cluster_back<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">GetComponent<\/span><span style=\"color: #ECEFF4\">&lt;<\/span><span style=\"color: #D8DEE9FF\">MeshRenderer<\/span><span style=\"color: #ECEFF4\">&gt;<\/span><span style=\"color: #D8DEE9FF\">()<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9\">material<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9\">color<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">_stylus<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9\">cluster_back_double_tap_value<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">?<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">double_tap_active_color<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">:<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">default_color<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #ECEFF4\">}<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Haptic Feedback<\/h2>\n\n\n\n<p>MX Ink Sample App also includes an example of haptic feedback. In <strong>VrStylusHandler<\/strong>.cs you can find it under PlayHapticClick() functions which uses <strong>OVRPlugin.TriggerVibrationAction<\/strong>()<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" data-code=\"    private void PlayHapticClick(float analogValue, ref bool hasVibrated, OVRPlugin.Hand hand)\n    {\n        if (analogValue &gt;= _hapticClickMinThreshold)\n        {\n            if (!hasVibrated)\n            {\n                OVRPlugin.TriggerVibrationAction(MX_Ink_Haptic_Pulse, hand,\n                _hapticClickDuration, _hapticClickAmplitude);\n                hasVibrated = true;\n            }\n        }\n        if (analogValue &lt; _hapticClickMinThreshold)\n        {\n            hasVibrated = false;\n        }\n    }\" style=\"color:#d8dee9ff;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki nord\" style=\"background-color: #2e3440ff\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #D8DEE9\">private<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">void<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">PlayHapticClick<\/span><span style=\"color: #D8DEE9FF\">(<\/span><span style=\"color: #D8DEE9\">float<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">analogValue<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">ref<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">bool<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">hasVibrated<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">OVRPlugin<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9\">Hand<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">hand<\/span><span style=\"color: #D8DEE9FF\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #ECEFF4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #81A1C1\">if<\/span><span style=\"color: #D8DEE9FF\"> (<\/span><span style=\"color: #D8DEE9\">analogValue<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">&gt;=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">_hapticClickMinThreshold<\/span><span style=\"color: #D8DEE9FF\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #ECEFF4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">            <\/span><span style=\"color: #81A1C1\">if<\/span><span style=\"color: #D8DEE9FF\"> (<\/span><span style=\"color: #81A1C1\">!<\/span><span style=\"color: #D8DEE9\">hasVibrated<\/span><span style=\"color: #D8DEE9FF\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">            <\/span><span style=\"color: #ECEFF4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">                <\/span><span style=\"color: #D8DEE9\">OVRPlugin<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">TriggerVibrationAction<\/span><span style=\"color: #D8DEE9FF\">(<\/span><span style=\"color: #D8DEE9\">MX_Ink_Haptic_Pulse<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">hand<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">                <\/span><span style=\"color: #D8DEE9\">_hapticClickDuration<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">_hapticClickAmplitude<\/span><span style=\"color: #D8DEE9FF\">)<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">                <\/span><span style=\"color: #D8DEE9\">hasVibrated<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">true;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">            <\/span><span style=\"color: #ECEFF4\">}<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #ECEFF4\">}<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #81A1C1\">if<\/span><span style=\"color: #D8DEE9FF\"> (<\/span><span style=\"color: #D8DEE9\">analogValue<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">&lt;<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">_hapticClickMinThreshold<\/span><span style=\"color: #D8DEE9FF\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #ECEFF4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">            <\/span><span style=\"color: #D8DEE9\">hasVibrated<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">false;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #ECEFF4\">}<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #ECEFF4\">}<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Adding Interactions with Meta XR Interaction SDK<\/h2>\n\n\n\n<p>In addition to drawing, your app probably needs various spatial interactions such as grabbing and moving objects or interacting with UI. For example, you might want allow the user to interact with UI for color palette or brush types, or grab and manipulate 3D brush strokes. With high precision, stylus could be also used selecting small objects in space.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Using MX Ink with Interaction SDK&#8217;s Comprehensive Rig<\/h3>\n\n\n\n<p><strong><a href=\"https:\/\/assetstore.unity.com\/packages\/tools\/integration\/meta-xr-interaction-sdk-265014?srsltid=AfmBOopiTiD8sTn-mywvsuDhE9EhUMpKkQBg5Vw4ORPhvksZEvMbTIyB\" target=\"_blank\" rel=\"noopener\" title=\"\">Meta XR Interaction SDK<\/a><\/strong> provides comprehensive set of spatial interactions with various input modality support. Interaction SDK&#8217;s <strong>OVRCameraRigInteraction<\/strong>.prefab has all input modalities configured in a single prefab including camera. Please check out Interaction SDK&#8217;s rich interaction support in this article &#8211; <strong><a href=\"https:\/\/mixedrealitynow.com\/getting-started-with-meta-xr-interaction-sdk-quest-3-how-to-crucial-interactions\" target=\"_blank\" rel=\"noopener\" title=\"\">How To Create Spatial Interactions with Meta XR Interaction SDK<\/a><\/strong><\/p>\n\n\n\n<p>To use MX Ink with <strong>OVRCameraRigInteraction<\/strong>.prefab, you can assign <strong>OVRCameraRig &gt; OVRInteractionComprehensive &gt; OVRControllers &gt; Left\/RightController<\/strong> to VrStylersHandler script. <\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"2401\" height=\"1166\" src=\"https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/MXInk_ISDK_ComprehensiveRig-2.png\" alt=\"\" class=\"wp-image-5397\" srcset=\"https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/MXInk_ISDK_ComprehensiveRig-2.png 2401w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/MXInk_ISDK_ComprehensiveRig-2-300x146.png 300w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/MXInk_ISDK_ComprehensiveRig-2-1024x497.png 1024w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/MXInk_ISDK_ComprehensiveRig-2-768x373.png 768w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/MXInk_ISDK_ComprehensiveRig-2-1536x746.png 1536w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/MXInk_ISDK_ComprehensiveRig-2-2048x995.png 2048w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/MXInk_ISDK_ComprehensiveRig-2-18x9.png 18w\" sizes=\"auto, (max-width: 2401px) 100vw, 2401px\" \/><\/figure>\n\n\n\n<p>If you don&#8217;t assign these controller fields, you will see both stylus and controller models rendered together.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full is-resized\"><img loading=\"lazy\" decoding=\"async\" width=\"1295\" height=\"857\" src=\"https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/2024-10-12-19_47_57-ISDKv69-SampleScene-Android-Unity-2022.3.23f1-_DX11_.png\" alt=\"\" class=\"wp-image-5387\" style=\"width:577px;height:auto\" srcset=\"https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/2024-10-12-19_47_57-ISDKv69-SampleScene-Android-Unity-2022.3.23f1-_DX11_.png 1295w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/2024-10-12-19_47_57-ISDKv69-SampleScene-Android-Unity-2022.3.23f1-_DX11_-300x199.png 300w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/2024-10-12-19_47_57-ISDKv69-SampleScene-Android-Unity-2022.3.23f1-_DX11_-1024x678.png 1024w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/2024-10-12-19_47_57-ISDKv69-SampleScene-Android-Unity-2022.3.23f1-_DX11_-768x508.png 768w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/2024-10-12-19_47_57-ISDKv69-SampleScene-Android-Unity-2022.3.23f1-_DX11_-18x12.png 18w\" sizes=\"auto, (max-width: 1295px) 100vw, 1295px\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Adding Poke, Ray, Grab Interactors to MX Ink Stylus<\/h3>\n\n\n\n<p>To enable poke, ray, grab interactions in your experiences, you need to add corresponding Interactors (on input source) and Interactables (on target object). We can do this easily by copying existing one that is already configured for controllers in <strong>OVRCameraRigInteraction<\/strong>.prefab.<\/p>\n\n\n\n<p>Duplicate <strong>ControllerInteractors <\/strong>object under <strong>OVRInteractionComprehensive &gt; OVRControllers &gt; RightController<\/strong> and move them into <strong>MX_Ink <\/strong>object.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1593\" height=\"844\" src=\"https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/MXInk_ISDK_Interactors.png\" alt=\"\" class=\"wp-image-5394\" srcset=\"https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/MXInk_ISDK_Interactors.png 1593w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/MXInk_ISDK_Interactors-300x159.png 300w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/MXInk_ISDK_Interactors-1024x543.png 1024w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/MXInk_ISDK_Interactors-768x407.png 768w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/MXInk_ISDK_Interactors-1536x814.png 1536w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/MXInk_ISDK_Interactors-18x10.png 18w\" sizes=\"auto, (max-width: 1593px) 100vw, 1593px\" \/><\/figure>\n\n\n\n<p>As you can see, ControllerInteractors object contains all crucial interactors such as Poke, Ray, and Grab interactions. Since I don&#8217;t intend to use locomotion, and distance grab, I removed those interactors.<\/p>\n\n\n\n<p>One thing we need to modify is the <strong>Selector <\/strong>for Ray and Poke interactors. Since we would like to perform select action with stylus device&#8217;s button, we need to provide a new selector script for stylus.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1523\" height=\"653\" src=\"https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/MXInk_ISDK_Selector1.png\" alt=\"\" class=\"wp-image-5398\" srcset=\"https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/MXInk_ISDK_Selector1.png 1523w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/MXInk_ISDK_Selector1-300x129.png 300w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/MXInk_ISDK_Selector1-1024x439.png 1024w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/MXInk_ISDK_Selector1-768x329.png 768w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/MXInk_ISDK_Selector1-18x8.png 18w\" sizes=\"auto, (max-width: 1523px) 100vw, 1523px\" \/><\/figure>\n\n\n\n<p>For this, I simply duplicated <strong>ControllerSelector<\/strong>.cs, renamed it as <strong>StylusSelector<\/strong>.cs and modified the script to retrieve stylus button states and use it as a selector. <\/p>\n\n\n\n<p>In this example, I used <strong>_stylusHandler.CurrentState.cluster_front_value<\/strong> to use the front button&#8217;s click as selector.<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" data-code=\"        \/\/ Added this line to get VrStylusHandler.cs script\n        [SerializeField]\n        private StylusHandler _stylusHandler;\n\n        ...\n        \n        protected virtual void Update()\n        {\n            \/\/ Modified line for stylus front button\n            bool selected = _stylusHandler.CurrentState.cluster_front_value;\n\n            if (selected)\n            {\n                if (_selected) return;\n                _selected = true;\n                WhenSelected();\n            }\n            else\n            {\n                if (!_selected) return;\n                _selected = false;\n                WhenUnselected();\n            }\n        }\" style=\"color:#d8dee9ff;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki nord\" style=\"background-color: #2e3440ff\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #ECEFF4\">        <\/span><span style=\"color: #616E88\">\/\/ Added this line to get VrStylusHandler.cs script<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        [<\/span><span style=\"color: #D8DEE9\">SerializeField<\/span><span style=\"color: #D8DEE9FF\">]<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #D8DEE9\">private<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">StylusHandler<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">_stylusHandler<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #81A1C1\">...<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #D8DEE9\">protected<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">virtual<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">void<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">Update<\/span><span style=\"color: #D8DEE9FF\">()<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #ECEFF4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ECEFF4\">            <\/span><span style=\"color: #616E88\">\/\/ Modified line for stylus front button<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">            <\/span><span style=\"color: #D8DEE9\">bool<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">selected<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">_stylusHandler<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9\">CurrentState<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9\">cluster_front_value<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">            <\/span><span style=\"color: #81A1C1\">if<\/span><span style=\"color: #D8DEE9FF\"> (<\/span><span style=\"color: #D8DEE9\">selected<\/span><span style=\"color: #D8DEE9FF\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">            <\/span><span style=\"color: #ECEFF4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">                <\/span><span style=\"color: #81A1C1\">if<\/span><span style=\"color: #D8DEE9FF\"> (<\/span><span style=\"color: #D8DEE9\">_selected<\/span><span style=\"color: #D8DEE9FF\">) <\/span><span style=\"color: #81A1C1\">return;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">                <\/span><span style=\"color: #D8DEE9\">_selected<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">true;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">                <\/span><span style=\"color: #88C0D0\">WhenSelected<\/span><span style=\"color: #D8DEE9FF\">()<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">            <\/span><span style=\"color: #ECEFF4\">}<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">            <\/span><span style=\"color: #81A1C1\">else<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">            <\/span><span style=\"color: #ECEFF4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">                <\/span><span style=\"color: #81A1C1\">if<\/span><span style=\"color: #D8DEE9FF\"> (<\/span><span style=\"color: #81A1C1\">!<\/span><span style=\"color: #D8DEE9\">_selected<\/span><span style=\"color: #D8DEE9FF\">) <\/span><span style=\"color: #81A1C1\">return;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">                <\/span><span style=\"color: #D8DEE9\">_selected<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">false;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">                <\/span><span style=\"color: #88C0D0\">WhenUnselected<\/span><span style=\"color: #D8DEE9FF\">()<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">            <\/span><span style=\"color: #ECEFF4\">}<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #ECEFF4\">}<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1531\" height=\"507\" src=\"https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/MXInk_ISDK_Selector2.png\" alt=\"\" class=\"wp-image-5399\" srcset=\"https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/MXInk_ISDK_Selector2.png 1531w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/MXInk_ISDK_Selector2-300x99.png 300w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/MXInk_ISDK_Selector2-1024x339.png 1024w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/MXInk_ISDK_Selector2-768x254.png 768w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/MXInk_ISDK_Selector2-18x6.png 18w\" sizes=\"auto, (max-width: 1531px) 100vw, 1531px\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>Added <strong>StylusSelector<\/strong>.cs and disabled existing <strong>ControllerSelector<\/strong>.cs <\/p>\n\n\n\n<p>Drag and drop selector object again to Ray\/Poke Interactor&#8217;s <strong>Selector <\/strong>field. Make sure to select <strong>StylusSelector <\/strong>script.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1559\" height=\"880\" src=\"https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/MXInk_ISDK_Selector3.png\" alt=\"\" class=\"wp-image-5400\" srcset=\"https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/MXInk_ISDK_Selector3.png 1559w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/MXInk_ISDK_Selector3-300x169.png 300w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/MXInk_ISDK_Selector3-1024x578.png 1024w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/MXInk_ISDK_Selector3-768x434.png 768w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/MXInk_ISDK_Selector3-1536x867.png 1536w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/MXInk_ISDK_Selector3-18x10.png 18w\" sizes=\"auto, (max-width: 1559px) 100vw, 1559px\" \/><\/figure>\n\n\n\n<p>Now you can see the with <strong>Ray Interactor<\/strong>, a ray with cursor coming from the stylus tip. On front button click, the ray performs select action and shows blue cursor visual feedback. Using this, you can interact with UI or manipulate objects. <strong>Poke Interactor<\/strong> allows you to directly poke UI elements with the stylus tip. <strong>Grab Interactor<\/strong> allows you to directly grab objects.<\/p>\n\n\n\n<figure class=\"wp-block-video\"><video height=\"480\" style=\"aspect-ratio: 854 \/ 480;\" width=\"854\" controls src=\"https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/MXInk_ISDK_Interactors.mp4\"><\/video><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Manipulating brush stroke objects with Grab Interactor\/Interactables <\/h3>\n\n\n\n<p>To grab and move brush strokes created with LineRenderer, we need to set LineRenderer&#8217;s <strong>useWorldSpace <\/strong>property false. I have modified <strong>LineDrawing<\/strong>.cs script in Logitech&#8217;s MX Ink Sample app. Then I created an empty container and added Interaction SDK&#8217;s Grab and Ray Grab Interactors. For newly created lines, I simply add them into this container so that they can be grabbed and manipulated.<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" data-code=\"    private void StartNewLine()\n    {\n        var gameObject = new GameObject(&quot;line&quot;);\n        \/\/ Add to a container object with Interaction SDK's Grab Interactors\n        gameObject.transform.SetParent(lineContainer.transform);\n        \n        ...\n\n        LineRenderer lineRenderer = gameObject.AddComponent&lt;LineRenderer&gt;();\n        \n        ...\n        \/\/ Udated line\n        _currentLine.useWorldSpace = false;\n\" style=\"color:#d8dee9ff;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki nord\" style=\"background-color: #2e3440ff\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #D8DEE9\">private<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">void<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">StartNewLine<\/span><span style=\"color: #D8DEE9FF\">()<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #ECEFF4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #81A1C1\">var<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">gameObject<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">new<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">GameObject<\/span><span style=\"color: #D8DEE9FF\">(<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">line<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #D8DEE9FF\">)<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ECEFF4\">        <\/span><span style=\"color: #616E88\">\/\/ Add to a container object with Interaction SDK&#39;s Grab Interactors<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #D8DEE9\">gameObject<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9\">transform<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">SetParent<\/span><span style=\"color: #D8DEE9FF\">(<\/span><span style=\"color: #D8DEE9\">lineContainer<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9\">transform<\/span><span style=\"color: #D8DEE9FF\">)<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #81A1C1\">...<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #D8DEE9\">LineRenderer<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">lineRenderer<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">gameObject<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">AddComponent<\/span><span style=\"color: #ECEFF4\">&lt;<\/span><span style=\"color: #D8DEE9FF\">LineRenderer<\/span><span style=\"color: #ECEFF4\">&gt;<\/span><span style=\"color: #D8DEE9FF\">()<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #81A1C1\">...<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ECEFF4\">        <\/span><span style=\"color: #616E88\">\/\/ Udated line<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #D8DEE9\">_currentLine<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9\">useWorldSpace<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">false;<\/span><\/span>\n<span class=\"line\"><\/span><\/code><\/pre><\/div>\n\n\n\n<p>Now the new brush strokes are added to a container object and it is grabbable and movable. With <strong>GrabFreeTransformer<\/strong>, you can do two-handed scaling and rotating manipulation as well.<\/p>\n\n\n\n<figure class=\"wp-block-video\"><video height=\"480\" style=\"aspect-ratio: 854 \/ 480;\" width=\"854\" controls src=\"https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/MXInk_Manipulation_Twohanded_480p.mp4\"><\/video><\/figure>\n\n\n\n<p>Interaction SDK&#8217;s <strong>GrabFreeTransformer <\/strong>setup for two-handed manipulation. You need to uncheck &#8216;Transfer On Second Selection&#8217; to allow both controllers\/stylus grabbing the object.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1733\" height=\"1692\" src=\"https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/2024-10-14-08_50_34-2G0YC1ZF870V9P-File-Explorer.png\" alt=\"\" class=\"wp-image-5416\" srcset=\"https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/2024-10-14-08_50_34-2G0YC1ZF870V9P-File-Explorer.png 1733w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/2024-10-14-08_50_34-2G0YC1ZF870V9P-File-Explorer-300x293.png 300w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/2024-10-14-08_50_34-2G0YC1ZF870V9P-File-Explorer-1024x1000.png 1024w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/2024-10-14-08_50_34-2G0YC1ZF870V9P-File-Explorer-768x750.png 768w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/2024-10-14-08_50_34-2G0YC1ZF870V9P-File-Explorer-1536x1500.png 1536w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/2024-10-14-08_50_34-2G0YC1ZF870V9P-File-Explorer-12x12.png 12w\" sizes=\"auto, (max-width: 1733px) 100vw, 1733px\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Concurrent Tracking &#8211; Multimodal Input with Controller and Stylus<\/h2>\n\n\n\n<p>Most of the painting or productivity applications require extensive menus for various options and functionalities. Typically they are attached to one of the controllers and interact with it using the controller on the other hand.<\/p>\n\n\n\n<p>The application can support mixed input of controller and stylus allowing the user comfortably interact with controller-attached UI while drawing.<\/p>\n\n\n\n<p>For this example, I modified one of the example menu UI patterns provided in <strong><a href=\"https:\/\/developers.meta.com\/horizon\/documentation\/unity\/unity-isdk-uiset\" target=\"_blank\" rel=\"noopener\" title=\"\">Interaction SDK&#8217;s UI Set<\/a><\/strong> and attached it to the left controller.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1985\" height=\"1152\" src=\"https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/2024-10-13-16_30_25-ISDKv69-UISetPatterns-Android-Unity-2022.3.23f1-_DX11_.png\" alt=\"\" class=\"wp-image-5403\" srcset=\"https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/2024-10-13-16_30_25-ISDKv69-UISetPatterns-Android-Unity-2022.3.23f1-_DX11_.png 1985w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/2024-10-13-16_30_25-ISDKv69-UISetPatterns-Android-Unity-2022.3.23f1-_DX11_-300x174.png 300w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/2024-10-13-16_30_25-ISDKv69-UISetPatterns-Android-Unity-2022.3.23f1-_DX11_-1024x594.png 1024w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/2024-10-13-16_30_25-ISDKv69-UISetPatterns-Android-Unity-2022.3.23f1-_DX11_-768x446.png 768w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/2024-10-13-16_30_25-ISDKv69-UISetPatterns-Android-Unity-2022.3.23f1-_DX11_-1536x891.png 1536w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/2024-10-13-16_30_25-ISDKv69-UISetPatterns-Android-Unity-2022.3.23f1-_DX11_-18x10.png 18w\" sizes=\"auto, (max-width: 1985px) 100vw, 1985px\" \/><\/figure>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"2310\" height=\"1136\" src=\"https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/2024-10-13-22_57_50-ISDKv69-SampleScene-Android-Unity-2022.3.23f1_-_DX11_.png\" alt=\"\" class=\"wp-image-5411\" srcset=\"https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/2024-10-13-22_57_50-ISDKv69-SampleScene-Android-Unity-2022.3.23f1_-_DX11_.png 2310w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/2024-10-13-22_57_50-ISDKv69-SampleScene-Android-Unity-2022.3.23f1_-_DX11_-300x148.png 300w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/2024-10-13-22_57_50-ISDKv69-SampleScene-Android-Unity-2022.3.23f1_-_DX11_-1024x504.png 1024w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/2024-10-13-22_57_50-ISDKv69-SampleScene-Android-Unity-2022.3.23f1_-_DX11_-768x378.png 768w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/2024-10-13-22_57_50-ISDKv69-SampleScene-Android-Unity-2022.3.23f1_-_DX11_-1536x755.png 1536w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/2024-10-13-22_57_50-ISDKv69-SampleScene-Android-Unity-2022.3.23f1_-_DX11_-2048x1007.png 2048w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/2024-10-13-22_57_50-ISDKv69-SampleScene-Android-Unity-2022.3.23f1_-_DX11_-18x9.png 18w\" sizes=\"auto, (max-width: 2310px) 100vw, 2310px\" \/><\/figure>\n\n\n\n<p>Typical menu UI example that is attached to the left controller for quick and easy access. It works well with Ray and Poke Interactors that have been added to the stylus.<\/p>\n\n\n\n<figure class=\"wp-block-video\"><video height=\"480\" style=\"aspect-ratio: 854 \/ 480;\" width=\"854\" controls src=\"https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/MX_Ink_Controller_Menu.mp4\"><\/video><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Stylus attached UI<\/h2>\n\n\n\n<p>Just like controller-attached UI, we can think about UI elements attached to the stylus device that can provide crucial information or functionality that are frequently accessed. In this example, I attached color palette UI which shows currently selected color. Modified rear button functionality to switch color when pressed.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1870\" height=\"1070\" src=\"https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/2024-10-15-22_29_22-ISDKv69-SampleScene2-Android-Unity-2022.3.23f1-_DX11_.png\" alt=\"\" class=\"wp-image-5438\" srcset=\"https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/2024-10-15-22_29_22-ISDKv69-SampleScene2-Android-Unity-2022.3.23f1-_DX11_.png 1870w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/2024-10-15-22_29_22-ISDKv69-SampleScene2-Android-Unity-2022.3.23f1-_DX11_-300x172.png 300w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/2024-10-15-22_29_22-ISDKv69-SampleScene2-Android-Unity-2022.3.23f1-_DX11_-1024x586.png 1024w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/2024-10-15-22_29_22-ISDKv69-SampleScene2-Android-Unity-2022.3.23f1-_DX11_-768x439.png 768w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/2024-10-15-22_29_22-ISDKv69-SampleScene2-Android-Unity-2022.3.23f1-_DX11_-1536x879.png 1536w, https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/2024-10-15-22_29_22-ISDKv69-SampleScene2-Android-Unity-2022.3.23f1-_DX11_-18x10.png 18w\" sizes=\"auto, (max-width: 1870px) 100vw, 1870px\" \/><\/figure>\n\n\n\n<figure class=\"wp-block-video\"><video height=\"720\" style=\"aspect-ratio: 1280 \/ 720;\" width=\"1280\" controls src=\"https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/MXInk_AttachedUI.mp4\"><\/video><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Customizing the pressure curve for tip and middle button<\/h2>\n\n\n\n<p>Users can customize the sensitivity of the stylus tip and middle buttons through Quest&#8217;s <strong>Settings &gt; Devices &gt; Stylus <\/strong>page. By default, when you put your stylus onto a physical surface, it starts creating brush strokes just like a real pen. However, if you want to make it more firm &#8211; meaning, requiring more pressure to start drawing (e.g. to prevent accidental unwanted strokes), you can adjust Initial Activation Force curve.<\/p>\n\n\n\n<figure class=\"wp-block-video\"><video height=\"720\" style=\"aspect-ratio: 1280 \/ 720;\" width=\"1280\" controls src=\"https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/MXInk_Settings1.mp4\"><\/video><\/figure>\n\n\n\n<p>Testing out different pressure sensitivity settings:<\/p>\n\n\n\n<figure class=\"wp-block-video\"><video height=\"720\" style=\"aspect-ratio: 1280 \/ 720;\" width=\"1280\" controls src=\"https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/MXInk_PressureSensitivity.mp4\"><\/video><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Related Articles<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/mixedrealitynow.com\/building-a-simple-app-with-meta-horizon-os-ui-set\" target=\"_blank\" rel=\"noopener\" title=\"\">How to build a simple spatial panel app with Meta Horizon OS UI Set<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/mixedrealitynow.com\/getting-started-with-meta-xr-interaction-sdk-quest-3-how-to-crucial-interactions\" target=\"_blank\" rel=\"noopener\" title=\"\">How To Create Spatial Interactions with Meta XR Interaction SDK<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/mixedrealitynow.com\/building-mr-apps-using-physical-surfaces-has-never-been-easier-how-to-use-meta-mixed-reality-utility-kit-mruk\" target=\"_blank\" rel=\"noopener\" title=\"\">Building MR apps using physical surfaces with Mixed Reality Utility Kit<\/a><\/li>\n<\/ul>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>As a designer, being able to put a brush stroke on a three-dimensional canvas was [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":5408,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"om_disable_all_campaigns":false,"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[31,6,21],"tags":[],"class_list":["post-5265","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-articles","category-featured","category-projects"],"aioseo_notices":[],"jetpack_featured_media_url":"https:\/\/mixedrealitynow.com\/wp-content\/uploads\/2024\/10\/MXInk_Hero.jpg","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/mixedrealitynow.com\/ko\/wp-json\/wp\/v2\/posts\/5265","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/mixedrealitynow.com\/ko\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/mixedrealitynow.com\/ko\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/mixedrealitynow.com\/ko\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/mixedrealitynow.com\/ko\/wp-json\/wp\/v2\/comments?post=5265"}],"version-history":[{"count":37,"href":"https:\/\/mixedrealitynow.com\/ko\/wp-json\/wp\/v2\/posts\/5265\/revisions"}],"predecessor-version":[{"id":5451,"href":"https:\/\/mixedrealitynow.com\/ko\/wp-json\/wp\/v2\/posts\/5265\/revisions\/5451"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/mixedrealitynow.com\/ko\/wp-json\/wp\/v2\/media\/5408"}],"wp:attachment":[{"href":"https:\/\/mixedrealitynow.com\/ko\/wp-json\/wp\/v2\/media?parent=5265"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mixedrealitynow.com\/ko\/wp-json\/wp\/v2\/categories?post=5265"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mixedrealitynow.com\/ko\/wp-json\/wp\/v2\/tags?post=5265"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}