Search Unity

Question Place Object from Trilib relative to ARMarker

Discussion in 'AR' started by Johetan, May 11, 2022.

  1. Johetan

    Johetan

    Joined:
    Mar 10, 2022
    Posts:
    9
    Iam currently working on a project where i need to place an object relative to a marker.

    Basically the Marker will be placed outside of a box and the app should show me whats inside.
    Therefore the Object should be placed relative to the marker itself.

    Another solution would be to place an object relative to another object.
    (or a possible anchor on the tracked image?)

    This is the code i have thus far - the spawned object gets populated by trilib.

    Code (CSharp):
    1. public class MarkerRecognition : MonoBehaviour  //new Class
    2. {
    3.     [SerializeField]
    4.     public GameObject placable;
    5.     private ARTrackedImageManager image_Manager;
    6.     private GameObject spawned_Object;
    7.     public ModelLoaderOBJ model_LoaderOBJ;
    8.    
    9.     private void Awake()
    10.     {
    11.         image_Manager = FindObjectOfType<ARTrackedImageManager>();
    12.         if(placable)
    13.         {
    14.             spawned_Object = Instantiate(placable, Vector3.zero, Quaternion.identity);
    15.         }
    16.     }
    17.     private void OnEnable()
    18.     {
    19.         image_Manager.trackedImagesChanged += OnImageChange;
    20.     }
    21.  
    22.     private void OnDisable()
    23.     {
    24.         image_Manager.trackedImagesChanged -= OnImageChange;
    25.     }
    26.  
    27.     private void OnImageChange(ARTrackedImagesChangedEventArgs eventArgs)
    28.     {
    29.         foreach (var trackedImage in eventArgs.added)
    30.         {
    31.             Debug.Log(trackedImage.name);
    32.             UpdateImage(trackedImage);
    33.          }
    34.         foreach (var trackedImage in eventArgs.updated)
    35.         {
    36.             Debug.Log(trackedImage.name);
    37.             UpdateImage(trackedImage);
    38.          }
    39.         foreach (var trackedImage in eventArgs.removed)
    40.         {
    41.             Debug.Log(trackedImage.name);
    42.             //spawned_Object.SetActive(false);
    43.          }
    44.     }
    45.  
    46.     private void UpdateImage(ARTrackedImage trackedImage)
    47.     {
    48.         if(spawned_Object)
    49.         {
    50.             Vector3 position = trackedImage.transform.position;
    51.             Quaternion rotation = trackedImage.transform.rotation;
    52.             spawned_Object.transform.position = position;
    53.             spawned_Object.transform.rotation = rotation;
    54.             spawned_Object.SetActive(true);
    55.             AnchorContent(position, spawned_Object);
    56.         }
    57.     }
    58.     private void AnchorContent(Vector3 position, GameObject content)
    59.         {
    60.             if (content.GetComponent<ARAnchor>() == null)//If Anchored no more anchor!
    61.             {
    62.                 content.AddComponent<ARAnchor>();
    63.             }
    64.         }
    65.     public void ChangeTheObject()
    66.     {
    67.         if (!model_LoaderOBJ.loaded_Object == true)
    68.         {
    69.             Debug.Log($"There is no Model to Load");
    70.             return;
    71.         }
    72.         else
    73.         {
    74.             spawned_Object = model_LoaderOBJ.loaded_Object;
    75.             Debug.Log($"Model is Changed");
    76.         }
    77.     }
    78. }
     
  2. andyb-unity

    andyb-unity

    Unity Technologies

    Joined:
    Feb 10, 2022
    Posts:
    1,051
  3. Johetan

    Johetan

    Joined:
    Mar 10, 2022
    Posts:
    9
    Thank you,
    I did quite a lot to make it work.
    Basically what I did:
    Make the loaded object a child of some parent object. (Must not be tracked image)
    If you want an image on a marker you have to rotate it 90.
    The instantiate a copy of the parent on the tracked image. Now you can move the child in local coordinates freely.
    I'll be posting my code later on.
    EDIT:
    Here is the code which will place a parent to downloaded Objects on an tracked image.
    Code:
    Code (CSharp):
    1. [RequireComponent(typeof(ARTrackedImageManager))]
    2.  
    3. public class MarkerRecognition : MonoBehaviour  //new Class
    4. {
    5.     [Header("Debugging")]
    6.     public bool use_Debug;
    7.     public bool use_Debug_measure;
    8.     [Header("Core Functionality")]
    9.     [SerializeField]
    10.     public GameObject placable;
    11.     private ARTrackedImageManager image_Manager;
    12.     public GameObject spawned_Object;
    13.     public ModelLoaderOBJ model_LoaderOBJ;
    14.     public GameObject download_Model;
    15.     public bool model_Is_Changed = false;
    16.     public bool tracking_Started;
    17.     [Header("Slider for Manipulating Translation")]
    18.     public Slider manipulationSlider;
    19.     public Slider manipulationSliderX;
    20.     public Slider manipulationSliderZ;
    21.     public GetSliderValue getSliderValue;
    22.     public GetSliderValue getSliderValueX;
    23.     public GetSliderValue getSliderValueZ;
    24.     [Header("Value of Manipulation")]
    25.     public Vector3 added_translation;
    26.     public bool show_Object = true;
    27.     [Header("Empty Game Object to prevent Null Reference")]
    28.     public GameObject dummy;
    29.     public GameObject parent_Object;
    30.    
    31.    
    32.  
    33.  
    34.     private void Awake() //Executed on Application Awake
    35.     {
    36.         spawned_Object = dummy; //needed, otherwise null reference object would be instantiated
    37.         image_Manager = FindObjectOfType<ARTrackedImageManager>(); //searches for an image Manager component
    38.     }
    39.     public void Start()
    40.     {
    41.         manipulationSlider.onValueChanged.AddListener(delegate{ManipulateChild();}); //Checks for an on value changed event, needs to be at start
    42.         manipulationSliderX.onValueChanged.AddListener(delegate{ManipulateChild();});
    43.         manipulationSliderZ.onValueChanged.AddListener(delegate{ManipulateChild();});
    44.     }
    45.     private void OnEnable() //when script becomes active, appended to XR-Origin, therefore on awake
    46.     {
    47.         image_Manager.trackedImagesChanged += OnImageChange; //listens to the imagechange event from trackedimagemanager
    48.     }
    49.     private void OnDisable()
    50.     {
    51.         image_Manager.trackedImagesChanged -= OnImageChange;
    52.     }
    53.     private void OnImageChange(ARTrackedImagesChangedEventArgs eventArgs) //Method that will be called if the image changed, moved or is removed
    54.     {
    55.         foreach (var trackedImage in eventArgs.added)
    56.         {
    57.             spawned_Object = placable; //changes the Object to a Gameobject containing a tooltip
    58.             spawned_Object.transform.SetPositionAndRotation(trackedImage.transform.localPosition, trackedImage.transform.localRotation); //puts object on marker, with local Rotation of the Marker
    59.             AnchorContent(trackedImage.transform.position, spawned_Object); //Puts an Anchor on the spawned object, as an image can not be used to be an anchor
    60.             spawned_Object.SetActive(true); //sets object activity and thus visibility to active
    61.             tracking_Started = true; //bool used for tracking information
    62.         }
    63.         foreach (var trackedImage in eventArgs.updated)
    64.         {
    65.             UpdateImage(trackedImage); //method for what should happen on image update, eg. image move
    66.             tracking_Started = true;
    67.         }
    68.         foreach (var trackedImage in eventArgs.removed) //not used, otherwise Augmentation would disappear when the image is removed
    69.         {
    70.             //spawned_Object.SetActive(false); //Not needed, as only one marker will be initializied and used, if lost, SLAM takes over
    71.         }
    72.     }
    73.     private void UpdateImage(ARTrackedImage trackedImage) //Method for updating the image
    74.     {
    75.         spawned_Object.transform.SetPositionAndRotation(trackedImage.transform.localPosition, trackedImage.transform.localRotation); //transforms the local position of the Object to the marker
    76.         spawned_Object.SetActive(true);
    77.         if(use_Debug) //used to give information on position of the tracked image and the augmentation
    78.         {
    79.             Debug.Log("Rotation of the Spawned Object" + spawned_Object.transform.rotation);
    80.             Debug.Log("Rotation of the Image Object" + trackedImage.transform.rotation);
    81.         }
    82.     }
    83.     private void AnchorContent(Vector3 position, GameObject content) //anchors content, if not yet anchored
    84.         {
    85.             if (content.GetComponent<ARAnchor>() == null)
    86.             {
    87.                 content.AddComponent<ARAnchor>();
    88.             }
    89.         }
    90.     public void ChangeTheObject() //called on Buttonpress, only works if old and new object are not the same
    91.     {
    92.         int number_of_Children = parent_Object.transform.childCount;
    93.         if(spawned_Object==null) //if there is no object, none can be changed, function returns, but not null
    94.         {
    95.             if(use_Debug)
    96.             {
    97.                 Debug.Log($"There is no Scene");
    98.             }
    99.             return;
    100.         }
    101.         else
    102.         {
    103.             if (number_of_Children == 0) //if the the model loader script has no object loaded, returns, not null
    104.             {
    105.                 if(use_Debug)
    106.                 {
    107.                     Debug.Log($"There is no Model to Load");
    108.                 }
    109.                 return;
    110.             }
    111.             else //every other case, there is a model, there is a model from the loader script, thus the model can be changed
    112.             {
    113.                 spawned_Object.SetActive(false);
    114.                 spawned_Object = download_Model;
    115.                 spawned_Object.SetActive(true);
    116.                 if(use_Debug)
    117.                 {
    118.                     Debug.Log($"Model is Changed");
    119.                 }
    120.                 model_Is_Changed = true;
    121.             }
    122.         }
    123.     }
    124.     public void EnableObject() //called to change visibility of the model, crawls through all renderers of the model, changes them all to disabled, or, enabled, also changes a bool to let the button know if true or false
    125.     {
    126.         if(spawned_Object)
    127.         {
    128.             if(show_Object)
    129.             {
    130.                 show_Object = false;
    131.                 Renderer[] rs = spawned_Object.GetComponentsInChildren<Renderer>();
    132.                 foreach(Renderer r in rs)
    133.                 r.enabled = false;
    134.             }
    135.             else
    136.             {
    137.                 show_Object = true;
    138.                 Renderer[] rs = spawned_Object.GetComponentsInChildren<Renderer>();
    139.                 foreach(Renderer r in rs)
    140.                 r.enabled = true;
    141.             }      
    142.         }
    143.         else
    144.         {
    145.             return;
    146.         }
    147.     }
    148.     void ManipulateChild() //because the gameobject inherits world coordinates, but we want to move it relative to the marker, one has to translate the child object. Values change via events.
    149.     {
    150.         added_translation = new Vector3 (getSliderValueX.slider_value,getSliderValue.slider_value,getSliderValueZ.slider_value); //new vector, for translation
    151.         spawned_Object.transform.GetChild(0).gameObject.transform.localPosition = added_translation; //sets the child position to new values, was 0,0,0 is now whatever sliders contain, default is 0,0,0
    152.         if (use_Debug_measure)
    153.         {
    154.             Debug.Log("Added Translation: " + added_translation);
    155.         }
    156.     }
    157. }
    Structure of Objects:
    Parent Object 0 = dummy -> empty
    --- --- Dummy_Child = the child of the dummy -> empty

    Dummy gives the Sliders "somthing to do" (Was the easiest way for me)

    Parent_Object1 = placable -> this becomes "spawned Object" at recognition of a marker.
    --- Plane_Object = the child to be manipulated via sliders ((Y-ROTATION MUST BE 180, otherwise marker wont rotationally align)
    --- --- Child_Object = is child of the plane, this is the 3D Object, or Text, or else.

    Parent_Object2 = parent_Object -> will be spawned after ChangeTheObject();
    --- Plane_Object = the child to be manipulated via sliders (Y-ROTATION MUST BE 180, otherwise marker wont rotationally align)
    --- --- Child_Object = is child of the plane, this is the 3D Object, or Text, or else.
     
    Last edited: Jul 7, 2022