• Hi Guest!

    We have posted a new VaM2 dev log on Patreon, starting a monthly cadence of written progress updates between Beta releases. Highlights include the new Gizmos System, Selection Carousel, and Modes System with Context-Specific Editing. Beta1.2 is 15 of 21 items complete.

    Read the full post on Patreon, or follow progress on the public Trello roadmap.
Resource icon

Guides The way to read Appearance Presets(*.vap) Directly

"Appearance Presets" file (*.vap) is JSON formatted.

target vap file example.
JSON:
{
   "setUnlistedParamsToDefault" : "true",
   "storables" : [
      {
         "id" : "geometry",
         "useAdvancedColliders" : "true",
         "useAuxBreastColliders" : "true",
         "disableAnatomy" : "false",
         "useMaleMorphsOnFemale" : "false",
         "useFemaleMorphsOnMale" : "false",
         "character" : "Female Custom",
         "clothing" : [
            {
               "id" : "...",
               "internalId" : "...",
               "enabled" : "true"
            }, ...
         ],
         "hair" : [
            {
               "id" : "...",
               "internalId" : "...",
               "enabled" : "true"
            }, ...
         ],
         "morphs" : [
            {
               "uid" : "...",
               "name" : "...",
               "value" : "0.5"
            }, ...
        ],...
        {
            "id" : "rescaleObject",
            "scale" : "0.95"
         },

But it is not a strict format and cannot be parsed.
i.e. this not works.

C#:
string jsonStr = SuperController.singleton.ReadFileIntoString(@"Custom\Atom\Person\Appearance\Preset_presetname.vap");  // OK
JsonNode jn = JSON.Parse(jsonStr);  // partially fails silently...
// jn["storables"]["morphs"]  is null

Therefore, let's get a part of JSON that can be interpreted by low-level processing, using this code.

C#:
        /// <summary>
        /// Read values from ".vap" file.
        /// Although vap is JSON, it is not a strict format and cannot be parsed, so it is converted to JSON that can be interpreted by low-level processing and then parsed.
        /// </summary>
        /// <param name="filepath"></param>
        /// <param name="tag">lowest array key.  ex. hair, morphs, ...</param>
        /// <returns></returns>
        public static JSONNode parseVap(string filepath, string tag)
        {
            string json = "";
            try { json = SuperController.singleton.ReadFileIntoString(filepath); }
            catch(Exception e) { dUtil.error($"Error while reading`{filepath}`.\n{e.Message}"); }  // not work b/c error squeeze&clashed.
            if(json == "") return new JSONNode();

            int start = json.IndexOf(tag) - 1;
            int end = json.IndexOf("]\n", start);
            json = json.Substring(start, end - start + 1);
            var ret = JSON.Parse(json);
            if (ret == null)
            {
                string mes = $"'${tag}' not found in '${filepath}'.";
                dUtil.error(mes);
                throw new Exception(mes);
            }
            return ret;
        }

        /// <summary>
        /// Read values from ".vap" file.
        /// Although vap is JSON, it is not a strict format and cannot be parsed, so it is converted to JSON that can be interpreted by low-level processing and then parsed.
        /// </summary>
        /// <param name="filepath"></param>
        /// <param name="tag">lowest dictionary id.  ex. rescaleObject, ...</param>
        /// <returns></returns>
        public static JSONNode parseVapDictionary(string filepath, string tag)
        {
            var json = SuperController.singleton.ReadFileIntoString(filepath);
            int start = json.IndexOf($"\"id\" : \"{tag}") - 1;
            int end = json.IndexOf("}", start);
            json = "{" + json.Substring(start, end - start + 1);
            var ret = JSON.Parse(json);
            if (ret == null)
            {
                string mes = $"'${tag}' not found in '${filepath}'.";
                dUtil.error(mes);
                throw new Exception(mes);
            }
            return ret;
        }

Usage

C#:
appearancePath = @"Custom\Atom\Person\Appearance\Preset_presetname.vap";
// morph
var morphs = new Dictionary<string, float>();  // k,v : name,value
foreach (var j in CharacterFusioner.parseVap(appearancePath, "morphs").Childs)
    morphs[j["name"]] = j["value"].AsFloat;

// rescaleObject
float scale = CharacterFusioner.parseVapDictionary(appearancePath, "rescaleObject")["scale"].AsFloat;
React to this content...

Share this resource

Latest reviews

Positive
Posted:
I just felt my brain get bigger. Thanks!
Upvote 0
Back
Top Bottom