|
| 1 | +using System; |
| 2 | +using System.Collections.Generic; |
| 3 | +using System.Linq; |
| 4 | +using System.Text; |
| 5 | +using System.Threading.Tasks; |
| 6 | +using FreneticUtilities.FreneticExtensions; |
| 7 | +using FreneticUtilities.FreneticToolkit; |
| 8 | +using Newtonsoft.Json; |
| 9 | +using Newtonsoft.Json.Linq; |
| 10 | + |
| 11 | +namespace DenizenModelsConverter |
| 12 | +{ |
| 13 | + public static class BBModelReader |
| 14 | + { |
| 15 | + public static bool Verbose = false; |
| 16 | + |
| 17 | + public static void Debug(string text) |
| 18 | + { |
| 19 | + if (Verbose) |
| 20 | + { |
| 21 | + Console.WriteLine($"[Debug] {text}"); |
| 22 | + } |
| 23 | + } |
| 24 | + |
| 25 | + public static BBModel Interpret(string fileContent) |
| 26 | + { |
| 27 | + Debug("Start read..."); |
| 28 | + JObject data = JObject.Parse(fileContent); |
| 29 | + Debug("Parsed! Start proc..."); |
| 30 | + JObject meta = data["meta"] as JObject; |
| 31 | + BBModel result = new() |
| 32 | + { |
| 33 | + JsonRoot = data, |
| 34 | + Name = (string)data.GetRequired("name"), |
| 35 | + FormatVersion = (string)meta.GetRequired("format_version"), |
| 36 | + CreationTime = DateTimeOffset.FromUnixTimeSeconds((long)meta.GetRequired("creation_time")).ToLocalTime() |
| 37 | + }; |
| 38 | + Debug("Core read, start body..."); |
| 39 | + JArray elements = (JArray)data["elements"]; |
| 40 | + JArray textures = (JArray)data["textures"]; |
| 41 | + JArray animations = (JArray)data["animations"]; |
| 42 | + if (elements is not null) |
| 43 | + { |
| 44 | + Debug("Contains elements"); |
| 45 | + foreach (JObject jElement in elements) |
| 46 | + { |
| 47 | + JObject jFaces = (JObject)jElement["faces"]; |
| 48 | + string name = (string)jElement.GetRequired("name"); |
| 49 | + string type = jElement.GetString("type", "cube"); |
| 50 | + if (type != "cube") |
| 51 | + { |
| 52 | + Debug($"Skip element of type '{type}' with name '{name}'"); |
| 53 | + continue; |
| 54 | + } |
| 55 | + BBModel.Element element = new() |
| 56 | + { |
| 57 | + Name = name, |
| 58 | + Rescale = jElement.GetBool("rescale", false), |
| 59 | + Locked = jElement.GetBool("locked", false), |
| 60 | + From = ParseIVecFromJson(jElement.GetRequired("from")), |
| 61 | + To = ParseIVecFromJson(jElement.GetRequired("to")), |
| 62 | + AutoUV = (int)jElement.GetRequired("autouv"), |
| 63 | + Color = (int)jElement.GetRequired("color"), |
| 64 | + Origin = ParseIVecFromJson(jElement.GetRequired("origin")), |
| 65 | + North = ParseFaceFromJson(jFaces.GetRequired("north")), |
| 66 | + South = ParseFaceFromJson(jFaces.GetRequired("south")), |
| 67 | + East = ParseFaceFromJson(jFaces.GetRequired("east")), |
| 68 | + West = ParseFaceFromJson(jFaces.GetRequired("west")), |
| 69 | + Up = ParseFaceFromJson(jFaces.GetRequired("up")), |
| 70 | + Down = ParseFaceFromJson(jFaces.GetRequired("down")), |
| 71 | + Type = type, |
| 72 | + UUID = Guid.Parse((string)jElement.GetRequired("uuid")) |
| 73 | + }; |
| 74 | + Debug($"Read element {element.Name}"); |
| 75 | + result.Elements.Add(element); |
| 76 | + } |
| 77 | + } |
| 78 | + if (textures is not null) |
| 79 | + { |
| 80 | + Debug("Contains textures"); |
| 81 | + foreach (JObject jTexture in textures) |
| 82 | + { |
| 83 | + BBModel.Texture texture = new() |
| 84 | + { |
| 85 | + Path = (string)jTexture.GetRequired("path"), |
| 86 | + Name = (string)jTexture.GetRequired("name"), |
| 87 | + Folder = (string)jTexture.GetRequired("folder"), |
| 88 | + Namespace = (string)jTexture.GetRequired("namespace"), |
| 89 | + ID = (string)jTexture.GetRequired("id"), |
| 90 | + Mode = (string)jTexture.GetRequired("mode"), |
| 91 | + Particle = jTexture.GetBool("particle", false), |
| 92 | + Visible = jTexture.GetBool("visible", true), |
| 93 | + // Ignore 'saved' |
| 94 | + UUID = Guid.Parse((string)jTexture.GetRequired("uuid")) |
| 95 | + }; |
| 96 | + string sourceTex = (string)jTexture.GetRequired("source"); |
| 97 | + if (!sourceTex.StartsWith("data:image/png;base64,")) |
| 98 | + { |
| 99 | + throw new Exception($"Cannot read model - texture {texture.Name} contains source data that isn't the expected base64 png."); |
| 100 | + } |
| 101 | + texture.RawImageBytes = Convert.FromBase64String(sourceTex.After("data:image/png;base64,")); |
| 102 | + Debug($"Read texture {texture.Name}"); |
| 103 | + result.Textures.Add(texture); |
| 104 | + } |
| 105 | + } |
| 106 | + if (animations is not null) |
| 107 | + { |
| 108 | + Debug("Contains animations"); |
| 109 | + foreach (JObject jAnimation in animations) |
| 110 | + { |
| 111 | + BBModel.Animation animation = new() |
| 112 | + { |
| 113 | + UUID = Guid.Parse((string)jAnimation.GetRequired("uuid")), |
| 114 | + Name = (string)jAnimation.GetRequired("name"), |
| 115 | + Loop = jAnimation.GetRequiredEnum<BBModel.Animation.LoopType>("loop"), |
| 116 | + Override = jAnimation.GetBool("override", false), |
| 117 | + AnimTimeUpdate = (string)jAnimation.GetRequired("anim_time_update"), |
| 118 | + BlendWeight = (string)jAnimation.GetRequired("blend_weight"), |
| 119 | + Length = (double)jAnimation.GetRequired("length"), |
| 120 | + Snapping = (int)jAnimation.GetRequired("snapping") |
| 121 | + // Ignored 'selected', 'saved', 'path' |
| 122 | + }; |
| 123 | + foreach (KeyValuePair<string, JToken> jAnimatorPair in (JObject)jAnimation.GetRequired("animators")) |
| 124 | + { |
| 125 | + JObject jAnimator = (JObject)jAnimatorPair.Value; |
| 126 | + BBModel.Animation.Animator animator = new() |
| 127 | + { |
| 128 | + UUID = Guid.Parse(jAnimatorPair.Key), |
| 129 | + Name = (string)jAnimator.GetRequired("name") |
| 130 | + }; |
| 131 | + foreach (JObject jFrame in jAnimator.GetRequired("keyframes")) |
| 132 | + { |
| 133 | + BBModel.Animation.Keyframe keyframe = new() |
| 134 | + { |
| 135 | + Channel = jFrame.GetRequiredEnum<BBModel.Animation.Keyframe.ChannelType>("channel"), |
| 136 | + DataPoint = ParseDVecFromJson(jFrame.GetRequired("data_points").First), |
| 137 | + UUID = Guid.Parse((string)jFrame.GetRequired("uuid")), |
| 138 | + Time = (double)jFrame.GetRequired("time"), |
| 139 | + Color = (int)jFrame.GetRequired("color"), |
| 140 | + Interpolation = jFrame.GetRequiredEnum<BBModel.Animation.Keyframe.InterpolationType>("interpolation") |
| 141 | + }; |
| 142 | + animator.Keyframes.Add(keyframe); |
| 143 | + } |
| 144 | + animation.Animators.Add(animator); |
| 145 | + } |
| 146 | + Debug($"Read Animation {animation.Name} with {animation.Animators.Count} animators"); |
| 147 | + result.Animations.Add(animation); |
| 148 | + } |
| 149 | + } |
| 150 | + return result; |
| 151 | + } |
| 152 | + |
| 153 | + public static DoubleVector ParseDVecFromJson(JToken jVal) |
| 154 | + { |
| 155 | + JObject jObj = (JObject)jVal; |
| 156 | + return new DoubleVector((double)jObj.GetRequired("x"), (double)jObj.GetRequired("y"), (double)jObj.GetRequired("z")); |
| 157 | + } |
| 158 | + |
| 159 | + public static IntegerVector ParseIVecFromJson(JToken jVal) |
| 160 | + { |
| 161 | + JArray jArr = (JArray)jVal; |
| 162 | + return new IntegerVector((int)jArr[0], (int)jArr[1], (int)jArr[2]); |
| 163 | + } |
| 164 | + |
| 165 | + public static BBModel.Element.Face ParseFaceFromJson(JToken jVal) |
| 166 | + { |
| 167 | + JObject jObj = (JObject)jVal; |
| 168 | + JArray uv = (JArray)jObj.GetRequired("uv"); |
| 169 | + return new() |
| 170 | + { |
| 171 | + TextureID = (int)jObj.GetRequired("texture"), |
| 172 | + TexCoord = new BBModel.Element.Face.UV() |
| 173 | + { |
| 174 | + ULow = (int)uv[0], // TODO: Validate this ordering |
| 175 | + VLow = (int)uv[1], |
| 176 | + UHigh = (int)uv[2], |
| 177 | + VHigh = (int)uv[3] |
| 178 | + } |
| 179 | + }; |
| 180 | + } |
| 181 | + } |
| 182 | +} |
0 commit comments