Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Unity API Returning Vector3s but the Values are Wrong

Sorry for the weird title, not sure what to title this lol

I’m using Replicate API to try and get a point cloud in Unity, I finally go the API returning a JSON file, and I’m pretty certain it’s returning vector3s of the positions of each point. Yet, when i cast it to a vector3 the values get all out of whack, every number will be like, 6.852349e-43, and even some "NaN"s, when on the API they appear to be all values below 0.

here’s what they look like on Replicate’s page,

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

  "coords": [
    [
      -0.039118800312280655,
      -0.25740715861320496,
      0.28050559759140015
    ],
    [
      0.13176295161247253,
      0.23972494900226593,
      -0.06013050675392151
    ],

Yet when i cast them to a vector 3 in Unity, they’re returning like this,
Broken Points
I’m assuming I just shouldn’t be casting them to a vector3? Replicate’s page says ""coords" is an [N x 3] array of (X,Y,Z) point coordinates" and after extensive Googling, i have no idea what NX3 means. I’ve tried float[,], float[,,], and float[][] instead of vector3, but vector3’s the only one that ever gets assigned properly from the JSON file, (using JsonUtility.FromJson)

Here’s my script calling the api,

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
using SimpleJSON;

public class Prompt
{
    public string prompt;
}

public class PostResponse
{

    public string completed_at;
    public string created_at;
    public string error;
    public string id;
    public Prompt input;
    public string[] logs;
    public string metrics;
    public Output output;
    public string started_at;
    public string status;
    public string version;

}

public class ReplicateAPI : MonoBehaviour
{
    private string API_ENDPOINT = "https://api.replicate.com/v1/predictions";
    private string REPLICATE_API_TOKEN = "r8_DhOE6C7LuA7edPvLh7Io0ukwbmXCLEY450vc3";

    [SerializeField] PointEResponse response;

    public string prompt = "dog";

    [SerializeField] Material material;

    [SerializeField] MeshFilter meshFilter;

    private void Start()
    {
        StartCoroutine(Predict());
    }

    IEnumerator Predict()
    {
        string requestData = $@"{{
    ""version"": ""1a4da7adf0bc84cd786c1df41c02db3097d899f5c159f5fd5814a11117bdf02b"",
    ""input"": {{
        ""prompt"": ""{prompt}"",
        ""output_format"": ""json_file""
    }}
}}";

        UnityWebRequest request = UnityWebRequest.Put(API_ENDPOINT, requestData);
        request.method = "POST";
        request.SetRequestHeader("Authorization", "Token " + REPLICATE_API_TOKEN);
        request.SetRequestHeader("Content-Type", "application/json");


        yield return request.SendWebRequest();

        if (request.result == UnityWebRequest.Result.ConnectionError || request.result == UnityWebRequest.Result.ProtocolError)
        {
            Debug.LogError("Error " + request.responseCode + ": " + request.error);
            StartCoroutine(Predict());
        }
        else
        {
            Debug.Log(request.downloadHandler.text);
            PostResponse response = JsonUtility.FromJson<PostResponse>(request.downloadHandler.text);

            StartCoroutine(GetResult(response.id));
        }
    }

    IEnumerator GetResult(string id)
    {
        yield return new WaitForSeconds(5);
        UnityWebRequest request = UnityWebRequest.Get(API_ENDPOINT + "/" + id);
        request.SetRequestHeader("Authorization", "Token " + REPLICATE_API_TOKEN);
        request.SetRequestHeader("Content-Type", "application/json");



        yield return request.SendWebRequest();

        if (request.result == UnityWebRequest.Result.ConnectionError || request.result == UnityWebRequest.Result.ProtocolError)
        {
            Debug.LogError("Error " + request.responseCode + ": " + request.error);
            yield break;
        }
        else
        {
            response = JsonUtility.FromJson<PointEResponse>(request.downloadHandler.text);



            Debug.Log(request.downloadHandler.text);
        }

        if (response.status != "succeeded") StartCoroutine(GetResult(id));
        else
        {

            if (response.output.json_file.colors != null && response.output.json_file.colors.Length > 0)
            {

                for (int i = 0; i < response.output.json_file.colors.Length; i++)
                {

                    GameObject g = GameObject.CreatePrimitive(PrimitiveType.Sphere);
                    g.transform.localScale = Vector3.one / 100;
                    g.transform.position = new Vector3(response.output.json_file.coords[i].x, response.output.json_file.coords[i].y, response.output.json_file.coords[i].z);
                    Material m = new Material(material);
                    m.color = new Color(response.output.json_file.colors[i].x, response.output.json_file.colors[i].y, response.output.json_file.colors[i].z);
                    g.GetComponent<MeshRenderer>().material = m;
                }

            }
            else
            {
                StartCoroutine(GetResult(id));
            }
        }
    }

}

And the "response" classes look like this,

using System;
using UnityEngine;

[Serializable]
public class PointEResponse
{
    public string id;
    public string version;
    public Urls urls;
    public DateTime created_at;
    public DateTime started_at;
    public DateTime completed_at;
    public string source;
    public string status;
    public Prompt input;
    public Output output;
    public object error;
    public string[] logs;
    public Metrics metrics;
}

[Serializable]
public class Output
{
    public JsonFile json_file;
}

[Serializable]
public class JsonFile
{
    public Vector3[] coords;
    public Vector3[] colors;

}

public class Urls
{
    public string get;
    public string cancel;
}

public class Metrics
{
    public float predict_time;
}

Where the output contains the coordinates and the colours of each coordinate. I’m assuming i’m not supposed to be casting these to a Vector3, but I’ve tried everything else I know of any the only one that even returns any form

Thanks in advance if anyone can give me some insight on this.

>Solution :

The JSON you get here would not match a Vector3 directly.

Afaik a Vector3 is not directly JSON (de)serializable.

"coords": [
    [
      -0.039118800312280655,
      -0.25740715861320496,
      0.28050559759140015
    ],
    [
      0.13176295161247253,
      0.23972494900226593,
      -0.06013050675392151
    ],

means you get something rather looking like

[Serializable]
public class JsonFile
{
    public float[][] coords;

    // assuming same layout for those
    public float[][] colors;
}

If and how exactly this would be laid out in memory into a Vector3 structs is dark magic I guess.

This type of complex collection however is also NOT SPPORTED by the built-in JsonUtility/Unity’s Serializer!

You would rather need a third-party library such as e.g. Newtonsoft Json.NET which comes as a package via Package Manager

using that instead you would go e.g.

using UnityEngine.Scripting;
using Newtonsoft.Json;

...

[Serializable]
public class JsonFile
{
    public Vector3[] coords;
    public Color[] colors;
    
    [Preserve]
    [JsonConstructor]
    public JsonFile(float[][] coords, float[][] colors)
    {
        this.coords = new Vector3[coords.GetLength(0)];
        for (var i = 0; i < this.coords.Length; i++)
        {
            this.coords[i] = new Vector3(coords[i][0], coords[i][1], coords[i][2]);
        }

        this.colors = new Color[colors.GetLength(0)];
        for(var i = 0; i < this.colors.Length; i++)
        {
            this.colors[i] = new Color(colors[i][0], colors[i][1], colors[i][2]);
        }
    }
}

and then go

var response = JsonConvert.DeserializeObject<PostResponse>(request.downloadHandler.text);

btw since Vector3 is a struct you an simply do

g.transform.position = response.output.json_file.coords[i];
...
m.color = nresponse.output.json_file.colors[i];
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading