Skip to content

Commit 9e68be8

Browse files
committed
initial ability to read bbmodel files
1 parent 0f96fde commit 9e68be8

File tree

8 files changed

+475
-1
lines changed

8 files changed

+475
-1
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
bin/
22
obj/
3+
.vs/
34
*.suo

DenizenModelsConverter/BBModel.cs

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
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 class BBModel
14+
{
15+
public JObject JsonRoot;
16+
17+
public string Name;
18+
19+
public string FormatVersion;
20+
21+
public DateTimeOffset CreationTime;
22+
23+
public List<Element> Elements = new();
24+
25+
public List<Texture> Textures = new();
26+
27+
public List<Animation> Animations = new();
28+
29+
public class Element
30+
{
31+
public string Name;
32+
33+
public IntegerVector From, To, Origin;
34+
35+
public string Type;
36+
37+
public Guid UUID;
38+
39+
public bool Locked, Rescale;
40+
41+
public int AutoUV, Color;
42+
43+
public Face North, South, East, West, Up, Down;
44+
45+
public class Face
46+
{
47+
public int TextureID;
48+
49+
public UV TexCoord;
50+
51+
public class UV
52+
{
53+
public int ULow, UHigh, VLow, VHigh;
54+
}
55+
}
56+
}
57+
58+
public class Texture
59+
{
60+
public string Path, Name, Folder, Namespace, ID, RenderMode, Mode;
61+
62+
public bool Particle, Visible = true;
63+
64+
public Guid UUID;
65+
66+
public byte[] RawImageBytes;
67+
}
68+
69+
public class Animation
70+
{
71+
72+
public Guid UUID;
73+
74+
public string Name;
75+
76+
public enum LoopType
77+
{
78+
LOOP, ONCE
79+
}
80+
81+
public LoopType Loop;
82+
83+
public bool Override;
84+
85+
public string AnimTimeUpdate, BlendWeight;
86+
87+
public double Length;
88+
89+
public int Snapping;
90+
91+
public List<Animator> Animators = new();
92+
93+
public class Animator
94+
{
95+
public Guid UUID;
96+
97+
public string Name;
98+
99+
public List<Keyframe> Keyframes = new();
100+
}
101+
102+
public class Keyframe
103+
{
104+
public enum ChannelType
105+
{
106+
POSITION,
107+
ROTATION
108+
}
109+
110+
public ChannelType Channel;
111+
112+
public DoubleVector DataPoint;
113+
114+
public Guid UUID;
115+
116+
public double Time;
117+
118+
public int Color;
119+
120+
public enum InterpolationType
121+
{
122+
LINEAR,
123+
CATMULLROM
124+
}
125+
126+
public InterpolationType Interpolation;
127+
}
128+
}
129+
}
130+
}
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
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+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
7+
namespace DenizenModelsConverter
8+
{
9+
public struct DoubleVector
10+
{
11+
public double X, Y, Z;
12+
13+
public DoubleVector()
14+
{
15+
X = 0;
16+
Y = 0;
17+
Z = 0;
18+
}
19+
20+
public DoubleVector(double x, double y, double z)
21+
{
22+
X = x;
23+
Y = y;
24+
Z = z;
25+
}
26+
}
27+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// This file is used by Code Analysis to maintain SuppressMessage
2+
// attributes that are applied to this project.
3+
// Project-level suppressions either have no target or are given
4+
// a specific target and scoped to a namespace, type, member, etc.
5+
6+
using System.Diagnostics.CodeAnalysis;
7+
8+
[assembly: SuppressMessage("Usage", "CA2211:Non-constant fields should not be visible", Justification = "Counter-productive to quality code")]
9+
[assembly: SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "Endless false marking of methods whose parameters are defined by delegate/Func/Action usage")]
10+
[assembly: SuppressMessage("CodeQuality", "IDE0079:Remove unnecessary suppression", Justification = "WTF MICROSOFT???")]
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
7+
namespace DenizenModelsConverter
8+
{
9+
public struct IntegerVector
10+
{
11+
public int X, Y, Z;
12+
13+
public IntegerVector()
14+
{
15+
X = 0;
16+
Y = 0;
17+
Z = 0;
18+
}
19+
20+
public IntegerVector(int x, int y, int z)
21+
{
22+
X = x;
23+
Y = y;
24+
Z = z;
25+
}
26+
}
27+
}

0 commit comments

Comments
 (0)