Closed autimator closed 3 years ago
Hi @autimator , you are right, there's a very easy way to do this. I think you didn't see it because it is so easy.
A little background. In the spatial anchor pattern you are used to, you create a spatial anchor to mark a position in the physical world. The coordinates of the spatial anchor will change over time, but whatever the current coordinates of the anchor are (assuming you are close enough to the anchor for tracking to work) are the coordinates corresponding to that point in physical space.
WLT anchors every coordinate in Unity's global space to its corresponding point in physical space. If you measure the coordinates of a physical feature in your environment to be (a,b,c), then later on the coordinates there will still be (a,b,c). If you have persistence enabled, then next session that point in physical space will still have coordinates (a,b,c).
Okay, on to your problem. To persist the locations in the world, you previously had to create and save and load a spatial anchor for each sphere. But WLT guarantees that the coordinates of that location in the world will always be the same. So you only need to save and load the position of the sphere. No individual anchors are needed. No SpacePins are needed. (SpacePins have a different job, but it doesn't sound like you need them.)
A couple of tips:
Let me know if you run into trouble with that, or still have questions. I'm not sure I explained it very well, or I might not have understood what you are trying to do.
Thank you for the elaborate answer @fast-slow-still, You right. Storing the locations and loading them again sounds way too simple, but it works. For others who want to do something similar:
private List<GameObject> spheresList;
public string MemoryFileName = "Markers.txt";
public void markLocation()
{
Vector3 hololensPosition = Camera.main.transform.position;
GameObject sphere = createSphere(System.DateTime.Now.ToString(), Color.red);
sphere.transform.position = hololensPosition;
lock (spheresList)
{
spheresList.Add(sphere);
saveLocations();
}
}
public GameObject createSphere(string name, Color? col)
{
float scale = 0.3F; //scale of the sphere
if (!col.HasValue)
{ col = Color.red; }
GameObject sphere = GameObject.CreatePrimitive(PrimitiveType.Sphere);
sphere.transform.localScale = new Vector3(scale, scale, scale);
sphere.GetComponent<MeshRenderer>().material.color = (Color)col;
sphere.name = name;
return sphere;
}
//Read a file with locations from disk
private void readLocations()
{
spheresList = new List<GameObject>();
if (System.IO.File.Exists(MemoryFileName))
{
using (StreamReader reader = new StreamReader(MemoryFileName))
{
string location = reader.ReadLine();
GameObject sphere = createSphere("marker", Color.red);
sphere.transform.position = StringToVector3(location);
spheresList.Add(sphere);
}
}
}
private void saveLocations()
{
using (StreamWriter writer = new StreamWriter(MemoryFileName))
{
foreach (GameObject go in spheresList)
{
string location = go.transform.position.ToString();
writer.WriteLine(location);
}
}
}
private static Vector3 StringToVector3(string sVector)
{
// Remove the parentheses
if (sVector.StartsWith("(") && sVector.EndsWith(")"))
{
sVector = sVector.Substring(1, sVector.Length - 2);
}
// split the items
string[] sArray = sVector.Split(',');
// store as a Vector3
Vector3 result = new Vector3(
float.Parse(sArray[0]),
float.Parse(sArray[1]),
float.Parse(sArray[2]));
return result;
}
Awesome! Thanks for sharing that code!!!
@autimator I have so comments on the code that you posted.
In saveLocations
I would not recommend string location = go.transform.position.ToString();
. This will only give you one decimal e.g. "1.0f". I used ToString("F4")
, what gives 4 decimals.
Further, for me float.Parse(sArray[0])
in StringToVector3
did not work as expected for me. In my case, the decimal point in the string is simply ignored. Therefore, the example from above would give me 10f
after loading.
In my case I just divide that by 10000 now, like float.Parse(sArray[0]) / 10000
or rather float.Parse(sArray[0]) / 10^4
with 4 being from "F4".
Hello,
After a couple of hours off reading the documentation and trying various sample application on my HoloLens 2 I still don't know how I can best upgrade my code.
The idea is quite simple: mark x locations in the world with a sphere and compute which one is closest to the hololens. Storing and reloading the list when the program is restarted. Previously I just created one anchor/object and cloud than retrieve the list of anchors to find the closest one.
Now I am moving my application to unity 2020.3.17f1 and trying to add the recommended worldlocking tools. I think spacepins are best suited for my application?
But I can't figure out how to add them from code and persist their position, the code below doesn't work.
sphere.AddComponent();
SpacePin test = sphere.GetComponent();
test.SetFrozenPose(test.ExtractModelPose());
I suspect there must be an easy way to do this but I cannot find it in the samples.