// packFileName is the file name relative to the steamingAssets folder
    public void loadPrecompute(string packFileName, int packID,
                               ref List <StepAction> gtStepAction,
                               bool rotBeforeMov)
    {
        string dirName  = "";
        string fileName = "";

        DirFileName(packFileName, ref dirName, ref fileName);

        // Read the data
        string path = Application.streamingAssetsPath
                      + "/" + dirName + "_precompute_unity"
                      + "/" + fileName + "_" + packID;
        string           jsonString      = File.ReadAllText(path);
        PrecomputeUnity2 precomputeUnity = JsonUtility.FromJson <PrecomputeUnity2>(jsonString);

        if (rotBeforeMov)
        {
            gtStepAction = precomputeUnity.gtRotBeforeMov;
        }
        else
        {
            gtStepAction = precomputeUnity.gtNotRotBeforeMov;
        }
    }
    // packFileName is the file name relative to the steamingAssets folder
    // outNameUnity is the file name for the precomputed unity file relative to the steamingAssets folder
    // outNamePython is the file name for the precomputed python file relative to the steamingAssets folder
    public void PrecomptePack(string packFileName, int packID, string outNameUnity, string outNamePython)
    {
        // Step 0: not recomputing if files already exist
        if (System.IO.File.Exists(Application.streamingAssetsPath + "/" + outNameUnity) &&
            System.IO.File.Exists(Application.streamingAssetsPath + "/" + outNamePython))
        {
            return;
        }

        // Step 1: Read the pack
        Pack pack = packEvol.ReadPack(packFileName, packID);


        // Step 2a: Get gt action
        List <StepAction> gtRotBeforeMov    = gtPack.GetGtStepAction(ref pack, rotBeforeMov: true);
        List <StepAction> gtNotRotBeforeMov = gtPack.GetGtStepAction(ref pack, rotBeforeMov: false);

        // Step 2b: Get gt2 action
        List <StepAction2> gtRotBeforeMov2    = gtPack.GetGtStepAction2(ref pack, gtRotBeforeMov, rotBeforeMov: true);
        List <StepAction2> gtNotRotBeforeMov2 = gtPack.GetGtStepAction2(ref pack, gtNotRotBeforeMov, rotBeforeMov: false);

        // Step 3: Get shapes
        GameObject[] shapes = null;
        string[]     _null  = null;
        packEvol.GetShapes(ref _null, ref pack.sources, ref shapes, loadFromResources: false, keepRenderer: false);
        for (int i = 0; i < shapes.Length; i++)
        {
            shapes[i].transform.localScale = pack.scales[i];
        }

        // Step 4: Get shape voxels
        List <float> vox = new List <float> (Mathf.CeilToInt(pack.efficiency
                                                             * packingAgent.VOX_RES
                                                             * packingAgent.VOX_RES
                                                             * packingAgent.VOX_RES));

        for (int i = 0; i < shapes.Length; i++)
        {
            shapes[i].transform.position = Vector3.zero;
            packingAgent.boxVoxel(ref vox, i);
            shapes[i].transform.position = 100 * Vector3.one;
        }
        vox.TrimExcess();

        // Destroy the shapes
        for (int i = 0; i < shapes.Length; i++)
        {
            Object.DestroyImmediate(shapes[i]);
        }

        // Step 5: Calculate and Write PrecomputeUnity
        string           path            = Application.streamingAssetsPath + "/" + outNameUnity;
        PrecomputeUnity2 precomputeUnity = new PrecomputeUnity2(gtRotBeforeMov, gtNotRotBeforeMov,
                                                                gtRotBeforeMov2, gtNotRotBeforeMov2);

        File.WriteAllText(path, JsonUtility.ToJson(precomputeUnity));

        // Step 6: Calculate and Write PrecomputePython
        path = Application.streamingAssetsPath + "/" + outNamePython;
        PrecomputePython precomputePython = new PrecomputePython(vox);

        File.WriteAllText(path, JsonUtility.ToJson(precomputePython));
    }