{"id":55,"date":"2018-08-25T00:30:14","date_gmt":"2018-08-25T00:30:14","guid":{"rendered":"https:\/\/www.xlsoft.com\/doc\/kudan\/?page_id=55"},"modified":"2018-10-18T20:39:50","modified_gmt":"2018-10-18T20:39:50","slug":"marker-to-markerless","status":"publish","type":"page","link":"https:\/\/www.xlsoft.com\/doc\/kudan\/marker-to-markerless\/","title":{"rendered":"iOS Marker to Markerless"},"content":{"rendered":"<header id=\"content-head\">\n<div class=\"row clearfix\">\nThis tutorial takes you through to transfer a node that has been added to an image trackable to ArbiTrack.<br \/>\n<\/header>\n<p><\/p>\n<div id=\"content-container\" class=\"grid-container-fluid ng-scope\">\n<section class=\"content-body\">\n<div class=\"su-note\"  style=\"border-color:#c9d3d8;border-radius:3px;-moz-border-radius:3px;-webkit-border-radius:3px;\"><div class=\"su-note-inner su-u-clearfix su-u-trim\" style=\"background-color:#e3edf2;border-color:#ffffff;color:#333333;border-radius:3px;-moz-border-radius:3px;-webkit-border-radius:3px;\">\n<h3 style=\"color:#5bc0de;\"><i class=\"fa fa-info-circle\" title=\"Info\"><\/i>Advanced Tutorial<\/h3>\n<p style=\"color: gray;\">This tutorial assumes you have gone through both <a href=\"https:\/\/www.xlsoft.com\/doc\/kudan\/marker-basics\">Marker Basics <\/a>and <a href=\"https:\/\/www.xlsoft.com\/doc\/kudan\/arbitrack-basics\">ArbiTrack Basics<\/a>. If you have not, it is strongly recommended that you do so before you head any further.<\/p>\n<\/div><\/div>\n<div class=\"magic-block-textarea\">\n<p>This tutorial uses the Lego Marker and Kudan Cow assets from the\u00a0<a class=\"doc-link\" href=\"https:\/\/www.xlsoft.com\/doc\/kudan\/marker-basics\/\" data-sidebar=\"marker-basics\">Marker Basics<\/a>\u00a0tutorial. If you don&#8217;t already have them, head over to\u00a0<a class=\"doc-link\" href=\"https:\/\/www.xlsoft.com\/doc\/kudan\/marker-basics\/\" data-sidebar=\"marker-basics\">Marker Basics<\/a>\u00a0and click the download link.<\/p>\n<\/div>\n<div class=\"magic-block-api-header\">\n<div id=\"section-create-an-image-trackable\" class=\"anchor waypoint\"><\/div>\n<h2 class=\"header-scroll is-api-header\">Create an image trackable<\/h2>\n<\/div>\n<div class=\"magic-block-textarea\">\n<p>First things first, you&#8217;ll need to create an image trackable using the Lego Marker image, then create an image node with the Kudan Cow image and add it to the image trackable.<\/p>\n<p>You&#8217;ll also need to add an extra line of code here to make sure our image node has a unique name. We&#8217;ll use this name to find it in the tracker later.\n<\/p><\/div>\n<div class=\"su-tabs su-tabs-style-default su-tabs-mobile-stack\" data-active=\"1\" data-scroll-offset=\"0\" data-anchor-in-url=\"no\"><div class=\"su-tabs-nav\"><span class=\"\" data-url=\"\" data-target=\"blank\" tabindex=\"0\" role=\"button\">ViewController.m<\/span><\/div><div class=\"su-tabs-panes\"><div class=\"su-tabs-pane su-u-clearfix su-u-trim\" data-title=\"ViewController.m\">[code lang=&#8221;Java&#8221;]- (void)touchesBegan:(NSSet&lt;UITouch *&gt; *)\/\/ Initialise the image node with our image.<br \/>\n\/\/ We don&#8217;t include the file extension for this image because it&#8217;s a PNG.<br \/>\nARImageNode *imageNode = [[ARImageNode alloc] initWithBundledFile:@&quot;Kudan Cow&quot;];<\/p>\n<p>imageNode.name = @&quot;Cow&quot;;[\/code]<\/div><\/div><\/div>\n<div class=\"magic-block-api-header\">\n<div id=\"section-register-the-view-controller-as-an-arbitrack-delegate\" class=\"anchor waypoint\"><\/div>\n<h2 class=\"header-scroll is-api-header\">Register the View Controller as an ArbiTrack delegate<\/h2>\n<\/div>\n<div class=\"magic-block-textarea\">\n<p>One of the things we need to know is when ArbiTrack starts. Fortunately, we have a delegate for that. Add the following code at the end of your\u00a0<code>setupContent<\/code>\u00a0method:<\/p>\n<\/div>\n<div class=\"su-tabs su-tabs-style-default su-tabs-mobile-stack\" data-active=\"1\" data-scroll-offset=\"0\" data-anchor-in-url=\"no\"><div class=\"su-tabs-nav\"><span class=\"\" data-url=\"\" data-target=\"blank\" tabindex=\"0\" role=\"button\">ViewController.m<\/span><\/div><div class=\"su-tabs-panes\"><div class=\"su-tabs-pane su-u-clearfix su-u-trim\" data-title=\"ViewController.m\">[code lang=&#8221;Java&#8221;]- (void)touchesBegan:(NSSet&lt;UITouch *&gt; *)\/\/ Initialise ArbiTrack<br \/>\nARArbiTrackerManager *arbiTrack = [ARArbiTrackerManager getInstance];<br \/>\n[arbiTrack initialise];<\/p>\n<p>\/\/Add view controller as an ArbiTrack delegate<br \/>\n[arbiTrack addDelegate:self];[\/code]<\/div><\/div><\/div>\n<div class=\"magic-block-textarea\">\n<p>This will call a delegate method &#8211;\u00a0<code>arbiTrackStarted<\/code>\u00a0&#8211; when ArbiTrack has started and positioned its world.<\/p>\n<\/div>\n<div class=\"magic-block-api-header\">\n<div id=\"section-setup-the-target-node\" class=\"anchor waypoint\"><\/div>\n<h2 class=\"header-scroll is-api-header\">Setup the target node<\/h2>\n<\/div>\n<div class=\"magic-block-textarea\">\n<p>We&#8217;ll be using the target node a little differently here. In the\u00a0<a class=\"doc-link\" href=\"https:\/\/www.xlsoft.com\/doc\/kudan\/arbitrack-basics\/\">ArbiTrack Basics<\/a>\u00a0tutorial, we created a node and used it as a target. In this tutorial, we want ArbiTrack to start tracking at the same position as the image trackable. Therefore, we must use the trackable&#8217;s world as ArbiTrack&#8217;s target node. Add the following code to your\u00a0<code>setupContent<\/code>\u00a0method:<\/p>\n<\/div>\n<div class=\"su-tabs su-tabs-style-default su-tabs-mobile-stack\" data-active=\"1\" data-scroll-offset=\"0\" data-anchor-in-url=\"no\"><div class=\"su-tabs-nav\"><span class=\"\" data-url=\"\" data-target=\"blank\" tabindex=\"0\" role=\"button\">ViewController.m<\/span><\/div><div class=\"su-tabs-panes\"><div class=\"su-tabs-pane su-u-clearfix su-u-trim\" data-title=\"ViewController.m\">[code lang=&#8221;Java&#8221;]\/\/ Use the image trackable&#8217;s world as the target node.<br \/>\n\/\/ This causes ArbiTrack to start tracking at the trackable&#8217;s position.<br \/>\narbiTrack.targetNode = imageTrackable.world;[\/code]<\/div><\/div><\/div>\n<div class=\"magic-block-api-header\">\n<div id=\"section-implement-arbitracks-delegate-method\" class=\"anchor waypoint\"><\/div>\n<h2 class=\"header-scroll is-api-header\">Implement ArbiTrack&#8217;s delegate method<\/h2>\n<\/div>\n<div class=\"magic-block-textarea\">\n<p>The next thing we need to do is tell ArbiTrack what it needs to do when it starts. To do this, we will implement the\u00a0<code>arbiTrackStarted<\/code>\u00a0delegate method in our View Controller. It looks like this:<\/p>\n<\/div>\n<div class=\"su-tabs su-tabs-style-default su-tabs-mobile-stack\" data-active=\"1\" data-scroll-offset=\"0\" data-anchor-in-url=\"no\"><div class=\"su-tabs-nav\"><span class=\"\" data-url=\"\" data-target=\"blank\" tabindex=\"0\" role=\"button\">ViewController.m<\/span><\/div><div class=\"su-tabs-panes\"><div class=\"su-tabs-pane su-u-clearfix su-u-trim\" data-title=\"ViewController.m\">[code lang=&#8221;Java&#8221;]\/\/ Delegate method called once ArbiTrack has started<br \/>\n&#8211; (void)arbiTrackStarted<br \/>\n{<\/p>\n<p>}[\/code]<\/div><\/div><\/div>\n<div class=\"magic-block-textarea\">\n<p>To switch between marker and markerless tracking, we&#8217;ll need to get the image node that appears when the marker is detected and transfer it to ArbiTrack&#8217;s world. We also need to make sure that the image is in the right position and orientation, otherwise it will look like it jumps around when it changes between tracking types, and we don&#8217;t want that.<\/p>\n<p>Add the following code to your\u00a0<code>arbiTrackStarted<\/code>\u00a0method:<\/p>\n<\/div>\n<div class=\"su-tabs su-tabs-style-default su-tabs-mobile-stack\" data-active=\"1\" data-scroll-offset=\"0\" data-anchor-in-url=\"no\"><div class=\"su-tabs-nav\"><span class=\"\" data-url=\"\" data-target=\"blank\" tabindex=\"0\" role=\"button\">ViewController.m<\/span><\/div><div class=\"su-tabs-panes\"><div class=\"su-tabs-pane su-u-clearfix su-u-trim\" data-title=\"ViewController.m\">[code lang=&#8221;Java&#8221;]\/\/ Delegate method called once ArbiTrack has started<br \/>\n&#8211; (void)arbiTrackStarted<br \/>\n{<br \/>\n  \/\/ Find the lego trackable in the tracker using it&#8217;s name.<br \/>\n  ARImageTrackable *imageTrackable = [[ARImageTrackerManager getInstance] findTrackableByName:@&quot;Lego Marker&quot;];<\/p>\n<p>  \/\/ Find the cow node in the lego trackable&#8217;s world using it&#8217;s name.<br \/>\n  ARImageNode *cowNode = (ARImageNode *)[imageTrackable.world findChildWithName:@&quot;Cow&quot;];<\/p>\n<p>  ARArbiTrackerManager *arbiTrack = [ARArbiTrackerManager getInstance];<\/p>\n<p>  \/\/ Alter the orientation of the cow node to preserve it&#8217;s full orientation as it&#8217;s reparented.<br \/>\n    cowNode.orientation = [arbiTrack.world.orientation.inverse multiplyByQuaternion:cowNode.fullOrientation];<\/p>\n<p>  \/\/ Remove the cow node as a child of the trackable world and add it to ArbiTrack&#8217;s world.<br \/>\n  [imageTrackable.world removeChild:cowNode];<br \/>\n  [arbiTrack.world addChild:cowNode];<br \/>\n}[\/code]<\/div><\/div><\/div>\n<div class=\"magic-block-textarea\">\n<p>Now, when ArbiTrack starts, it will reparent the image node so that it is tracked markerlessly. We still need to tell ArbiTrack when to start, though.<\/p>\n<\/div>\n<div class=\"magic-block-api-header\">\n<div id=\"section-start-arbitrack\" class=\"anchor waypoint\"><\/div>\n<h2 class=\"header-scroll is-api-header\">Start ArbiTrack<\/h2>\n<\/div>\n<div class=\"magic-block-textarea\">\n<p>To tell ArbiTrack when to start, we&#8217;ll use the same method as the one in the\u00a0<a class=\"doc-link\" href=\"https:\/\/www.xlsoft.com\/doc\/kudan\/arbitrack-basics\/\">ArbiTrack Basics<\/a>\u00a0tutorial to respond to touch input. It looks like this:<\/p>\n<\/div>\n<div class=\"su-tabs su-tabs-style-default su-tabs-mobile-stack\" data-active=\"1\" data-scroll-offset=\"0\" data-anchor-in-url=\"no\"><div class=\"su-tabs-nav\"><span class=\"\" data-url=\"\" data-target=\"blank\" tabindex=\"0\" role=\"button\">ViewController.m<\/span><\/div><div class=\"su-tabs-panes\"><div class=\"su-tabs-pane su-u-clearfix su-u-trim\" data-title=\"ViewController.m\">[code lang=&#8221;Java&#8221;]- (void)touchesBegan:(NSSet&lt;UITouch *&gt; *)touches withEvent:(UIEvent *)event<br \/>\n{<\/p>\n<p>}[\/code]<\/div><\/div><\/div>\n<div class=\"magic-block-textarea\">\n<p>Now, we need to know whether or not ArbiTrack is running when we tap, because we only want ArbiTrack to start if it hasn&#8217;t already started. In addition, we want to know whether or not our trackable is being detected, because we can&#8217;t go from marker tracking to markerless tracking if we aren&#8217;t tracking a marker.<\/p>\n<p>Implementing these steps, our\u00a0<code>touchesBegan<\/code>\u00a0method now looks like this:<\/p>\n<\/div>\n<div class=\"su-tabs su-tabs-style-default su-tabs-mobile-stack\" data-active=\"1\" data-scroll-offset=\"0\" data-anchor-in-url=\"no\"><div class=\"su-tabs-nav\"><span class=\"\" data-url=\"\" data-target=\"blank\" tabindex=\"0\" role=\"button\">Objective-C<\/span><\/div><div class=\"su-tabs-panes\"><div class=\"su-tabs-pane su-u-clearfix su-u-trim\" data-title=\"Objective-C\">[code lang=&#8221;Java&#8221;]- (void)touchesBegan:(NSSet&lt;UITouch *&gt; *)touches withEvent:(UIEvent *)event<br \/>\n{<br \/>\n  \/\/ Get references to the ArbiTracker and Image Trackable.<br \/>\n  ARArbiTrackerManager *arbiTrack = [ARArbiTrackerManager getInstance];<br \/>\n  ARImageTrackable *imageTrackable = [[ARImageTrackerManager getInstance] findTrackableByName:@&quot;Lego Marker&quot;];<\/p>\n<p>  \/\/ Only start ArbiTrack if it isn&#8217;t currently running.<br \/>\n  if (!arbiTrack.isTracking)<br \/>\n  {<br \/>\n      \/\/ If the marker is not currently detected, exit the method and don&#8217;t switch to arbitrack.<br \/>\n      if (!imageTrackable.isDetected)<br \/>\n      {<br \/>\n          return;<br \/>\n      }<\/p>\n<p>      [arbiTrack start];<br \/>\n  }<br \/>\n}[\/code]<\/div><\/div><\/div>\n<div class=\"magic-block-textarea\">\n<p>This means that when we tap the screen, if ArbiTrack isn&#8217;t already running and the marker is being detected, it will start markerless tracking, which in turn will trigger the\u00a0<code>arbiTrackStarted<\/code>\u00a0method and transfer over our image node.<\/p>\n<p>You might be wondering why we implement the\u00a0<code>arbiTrackStarted<\/code>\u00a0method at all, since we could just run that same code when we tap the screen. The reason for this is that when ArbiTrack first starts, it doesn&#8217;t actually get data from the Gyroscope right away. Because of this, its world&#8217;s transform does not get updated for a couple of frames. If we tried to transfer the image node at this point, it would rotate and jump across the screen.<\/p>\n<p>The\u00a0<code>arbiTrackStarted<\/code>\u00a0method is only called when ArbiTrack has started\u00a0<strong>and<\/strong>\u00a0has values, so we can be sure that when this method is called, it&#8217;s world has been updated it is safe to transfer our content across.<\/p>\n<p>If you build and run your app now, you&#8217;ll find that you are able to detect a marker and, when you tap the screen, it will switch to markerless tracking. You can add some form of text or implement the touch control as a button press using an IBOutlet if you want more visual feedback when the tracking method changes.<\/p>\n<p>So far so good, but right now we can only tap the screen once, and we can&#8217;t stop ArbiTrack and go back to marker tracking once we&#8217;ve made the switch. We&#8217;ll need to implement a way to toggle between tracking methods, transferring the image node as necessary.<\/p>\n<\/div>\n<div class=\"magic-block-api-header\">\n<div id=\"section-toggle-tracking-methods\" class=\"anchor waypoint\"><\/div>\n<h2 class=\"header-scroll is-api-header\">Toggle tracking methods<\/h2>\n<\/div>\n<div class=\"magic-block-textarea\">\n<p>This step is fairly simple. To go back to marker tracking, all we need to do is what we did to go to ArbiTrack, but in reverse. Plus, because the image tracker doesn&#8217;t have a world it needs to update, we can perform the transfer right in the\u00a0<code>touchesBegan<\/code>\u00a0method.<\/p>\n<p>Since we already have an\u00a0<em>if<\/em>\u00a0statement checking whether ArbiTrack is running, all we need is an\u00a0<em>else<\/em>:<\/p>\n<\/div>\n<div class=\"su-tabs su-tabs-style-default su-tabs-mobile-stack\" data-active=\"1\" data-scroll-offset=\"0\" data-anchor-in-url=\"no\"><div class=\"su-tabs-nav\"><span class=\"\" data-url=\"\" data-target=\"blank\" tabindex=\"0\" role=\"button\">ViewController.m<\/span><\/div><div class=\"su-tabs-panes\"><div class=\"su-tabs-pane su-u-clearfix su-u-trim\" data-title=\"ViewController.m\">[code lang=&#8221;Java&#8221;]- (void)touchesBegan:(NSSet&lt;UITouch *&gt; *)touches withEvent:(UIEvent *)event<br \/>\n{<br \/>\n  \/\/ Get references to the ArbiTracker and Image Trackable.<br \/>\n  ARArbiTrackerManager *arbiTrack = [ARArbiTrackerManager getInstance];<br \/>\n  ARImageTrackable *imageTrackable = [[ARImageTrackerManager getInstance] findTrackableByName:@&quot;Lego Marker&quot;];<\/p>\n<p>  \/\/ Only start ArbiTrack if it isn&#8217;t currently running.<br \/>\n  if (!arbiTrack.isTracking)<br \/>\n  {<br \/>\n      \/\/ If the marker is not currently detected, exit the method and don&#8217;t switch to arbitrack.<br \/>\n      if (!imageTrackable.isDetected)<br \/>\n      {<br \/>\n          return;<br \/>\n      }<\/p>\n<p>      [arbiTrack start];<br \/>\n  }<\/p>\n<p>  \/\/ If ArbiTrack is running<br \/>\n  else<br \/>\n  {<br \/>\n    \/\/ Find the Cow node in ArbiTrack&#8217;s world.<br \/>\n    ARImageNode *cowNode = (ARImageNode *)[arbiTrack.world findChildWithName:@&quot;Cow&quot;];<\/p>\n<p>    \/\/ Adjust the node&#8217;s orientation to lie flat on the marker<br \/>\n    cowNode.orientation = [ARQuaternion quaternionWithIdentity];<\/p>\n<p>    \/\/ Remove the Cow node from ArbiTrack&#8217;s world and add it to the image trackable&#8217;s world again.<br \/>\n    [arbiTrack.world removeChild:cowNode];<br \/>\n    [imageTrackable.world addChild:cowNode];<\/p>\n<p>    \/\/ Stop ArbiTrack<br \/>\n    [arbiTrack stop];<br \/>\n  }[\/code]<\/div><\/div><\/div>\n<div class=\"magic-block-textarea\">\n<p>Now, if you build and run the app once more, you should see that you can tap the screen while tracking a marker to switch to ArbiTrack,\u00a0<em>and<\/em>\u00a0you can tap again to go back to marker tracking.<\/p>\n<\/div>\n<\/section>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>This tutorial takes you through to transfer a node that has been added to an image trackable to ArbiTrack. This tutorial uses the Lego Marker and Kudan Cow assets from the\u00a0Marker Basics\u00a0tutorial. If you don&#8217;t already have them, head over to\u00a0Marker Basics\u00a0and click the download link. Create an image trackable First things first, you&#8217;ll need &#8230; <a title=\"iOS Marker to Markerless\" class=\"read-more\" href=\"https:\/\/www.xlsoft.com\/doc\/kudan\/marker-to-markerless\/\">Read more<span class=\"screen-reader-text\">iOS Marker to Markerless<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"bekko_page_header":""},"_links":{"self":[{"href":"https:\/\/www.xlsoft.com\/doc\/kudan\/wp-json\/wp\/v2\/pages\/55"}],"collection":[{"href":"https:\/\/www.xlsoft.com\/doc\/kudan\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/www.xlsoft.com\/doc\/kudan\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/www.xlsoft.com\/doc\/kudan\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.xlsoft.com\/doc\/kudan\/wp-json\/wp\/v2\/comments?post=55"}],"version-history":[{"count":37,"href":"https:\/\/www.xlsoft.com\/doc\/kudan\/wp-json\/wp\/v2\/pages\/55\/revisions"}],"predecessor-version":[{"id":1410,"href":"https:\/\/www.xlsoft.com\/doc\/kudan\/wp-json\/wp\/v2\/pages\/55\/revisions\/1410"}],"wp:attachment":[{"href":"https:\/\/www.xlsoft.com\/doc\/kudan\/wp-json\/wp\/v2\/media?parent=55"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}