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,

  "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];

Leave a Reply