Skip to content

Commit

Permalink
zzre: Add waypoints section to scene editor (#392)
Browse files Browse the repository at this point in the history
  • Loading branch information
Helco authored Nov 2, 2024
1 parent 8c57fa0 commit 1ba89c8
Show file tree
Hide file tree
Showing 13 changed files with 497 additions and 88 deletions.
5 changes: 5 additions & 0 deletions zzio/EnumerableExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,11 @@ public static Range Sub(this Range full, Range sub, int maxValue = int.MaxValue)
return newOffset..(newOffset + subLength);
}

public static Range Suffix(this Range range, int suffixLength) =>
range.End.IsFromEnd
? range.End..(range.End.Value - suffixLength)
: range.End..(range.End.Value + suffixLength);

public static IEnumerable<TOutput> PrefixSums<TInput, TOutput>(
this IEnumerable<TInput> set, TOutput first, Func<TOutput, TInput, TOutput> next)
{
Expand Down
3 changes: 2 additions & 1 deletion zzio/primitives/IColor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ public struct IColor
{
public byte r, g, b, a;

public IColor(byte r, byte g, byte b, byte a)
public IColor(byte r, byte g, byte b, byte a = 255)
{
this.r = r;
this.g = g;
Expand Down Expand Up @@ -53,6 +53,7 @@ public void Write(BinaryWriter w)
public static implicit operator FColor(IColor c) => new(c.r / 255f, c.g / 255f, c.b / 255f, c.a / 255f);

public static readonly IColor White = new(0xFFFFFFFF);
public static readonly IColor Grey = new(0xFF808080);
public static readonly IColor Black = new(0xFF000000);
public static readonly IColor Clear = new(0x00000000);
public static readonly IColor Red = new(0xFF0000FF);
Expand Down
142 changes: 59 additions & 83 deletions zzio/scn/WaypointSystem.cs
Original file line number Diff line number Diff line change
@@ -1,139 +1,115 @@
using System;
using System.IO;
using System.Diagnostics;
using System.Numerics;
using System.Collections.Generic;

// This surely needs some reverse engineering work being done...
namespace zzio.scn;

[Serializable]
public struct WaypointInnerData
public struct Waypoint
{
public uint iiv2;
public uint[] data;
}

[Serializable]
public struct WaypointData
{
public uint ii1, ii1ext, iiv2;
public Vector3 v1;
public uint[] innerdata1, innerdata2;
public uint[] inner3data1;
public uint Id, Group;
public Vector3 Position;
public uint[] WalkableIds, JumpableIds;
public uint[]? VisibleIds;
}

[Serializable]
public class WaypointSystem : ISceneSection
{
public uint version;
public byte[] data = new byte[0x18];
public WaypointData[] waypointData = [];
public WaypointInnerData[] inner2data1 = [];
public uint Version;
public byte[] Data = new byte[0x18]; // most likely just for generation? AFAIK not used in game
public Waypoint[] Waypoints = [];
public Dictionary<uint, uint[]> CompatibleGroups = [];

public void Read(Stream stream)
{
using BinaryReader reader = new(stream);
version = reader.ReadUInt32();
uint mustBeZero = reader.ReadUInt32();
Debug.Assert(mustBeZero == 0);
Version = reader.ReadUInt32();
if (reader.ReadUInt32() != 0)
throw new InvalidDataException("Waypoint system start magic is not correct");

if (version >= 5)
data = reader.ReadBytes(0x18);
if (Version >= 5)
Data = reader.ReadBytes(0x18);
uint count1 = reader.ReadUInt32();
WaypointData[] d = new WaypointData[count1];
Waypoint[] d = new Waypoint[count1];
for (uint i = 0; i < count1; i++)
{
d[i].ii1 = reader.ReadUInt32();
if (version >= 4)
d[i].ii1ext = reader.ReadUInt32();
d[i].v1 = reader.ReadVector3();

uint ci1 = reader.ReadUInt32();
d[i].innerdata1 = new uint[ci1];
for (uint j = 0; j < ci1; j++)
d[i].innerdata1[j] = reader.ReadUInt32();
d[i].Id = reader.ReadUInt32();
if (Version >= 4)
d[i].Group = reader.ReadUInt32();
d[i].Position = reader.ReadVector3();

uint ci2 = reader.ReadUInt32();
d[i].innerdata2 = new uint[ci2];
for (uint j = 0; j < ci2; j++)
d[i].innerdata2[j] = reader.ReadUInt32();
d[i].WalkableIds = reader.ReadStructureArray<uint>(reader.ReadInt32(), 4);
d[i].JumpableIds = reader.ReadStructureArray<uint>(reader.ReadInt32(), 4);
}

if (version >= 2)
if (Version >= 2)
{
uint count2 = reader.ReadUInt32();
inner2data1 = new WaypointInnerData[count2];
for (uint j = 0; j < count2; j++)
int groupCount = reader.ReadInt32();
CompatibleGroups = new(groupCount);
for (int j = 0; j < groupCount; j++)
{
inner2data1[j].iiv2 = reader.ReadUInt32();
uint ci3 = reader.ReadUInt32();
inner2data1[j].data = new uint[ci3];
for (uint k = 0; k < ci3; k++)
inner2data1[j].data[k] = reader.ReadUInt32();
CompatibleGroups.Add(
reader.ReadUInt32(),
reader.ReadStructureArray<uint>(reader.ReadInt32(), 4));
}
}

if (version >= 3)
if (Version >= 3)
{
for (uint j = 0; j < count1; j++)
{
uint ci4 = reader.ReadUInt32();
d[j].inner3data1 = new uint[ci4];
for (uint k = 0; k < ci4; k++)
d[j].inner3data1[k] = reader.ReadUInt32();
}
d[j].VisibleIds = reader.ReadStructureArray<uint>(reader.ReadInt32(), 4);
}
waypointData = d;
Waypoints = d;

uint mustBeFFFF = reader.ReadUInt32();
Debug.Assert(mustBeFFFF == 0xffff);
if (reader.ReadUInt32() != 0xffff)
throw new InvalidDataException("Waypoint system end magic is not correct");
}

public void Write(Stream stream)
{
using BinaryWriter writer = new(stream);
writer.Write(version);
writer.Write(Version);
writer.Write(0);
if (version >= 5)
writer.Write(data, 0, 0x18);
WaypointData[] d = waypointData;
if (Version >= 5)
writer.Write(Data, 0, 0x18);
Waypoint[] d = Waypoints;
writer.Write(d.Length);
for (int i = 0; i < d.Length; i++)
{
writer.Write(d[i].ii1);
if (version >= 4)
writer.Write(d[i].ii1ext);
writer.Write(d[i].v1);

writer.Write(d[i].innerdata1.Length);
for (int j = 0; j < d[i].innerdata1.Length; j++)
writer.Write(d[i].innerdata1[j]);

writer.Write(d[i].innerdata2.Length);
for (int j = 0; j < d[i].innerdata2.Length; j++)
writer.Write(d[i].innerdata2[j]);
writer.Write(d[i].Id);
if (Version >= 4)
writer.Write(d[i].Group);
writer.Write(d[i].Position);

writer.Write(d[i].WalkableIds.Length);
for (int j = 0; j < d[i].WalkableIds.Length; j++)
writer.Write(d[i].WalkableIds[j]);

writer.Write(d[i].JumpableIds.Length);
for (int j = 0; j < d[i].JumpableIds.Length; j++)
writer.Write(d[i].JumpableIds[j]);
}

if (version >= 2)
if (Version >= 2)
{
WaypointInnerData[] d2 = inner2data1;
writer.Write(d2.Length);
for (int i = 0; i < d2.Length; i++)
writer.Write(CompatibleGroups.Count);
foreach (var (groupId, wpIds) in CompatibleGroups)
{
writer.Write(d2[i].iiv2);
writer.Write(d2[i].data.Length);
for (int j = 0; j < d2[i].data.Length; j++)
writer.Write(d2[i].data[j]);
writer.Write(groupId);
writer.WriteStructureArray(wpIds, 4);
}
}

if (version >= 3)
if (Version >= 3)
{
for (int i = 0; i < d.Length; i++)
{
writer.Write(d[i].inner3data1.Length);
for (int j = 0; j < d[i].inner3data1.Length; j++)
writer.Write(d[i].inner3data1[j]);
var links = d[i].VisibleIds ?? [];
writer.Write(links.Length);
writer.WriteStructureArray(links, 4);
}
}

Expand Down
1 change: 1 addition & 0 deletions zzio/utils/BinaryIOExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ public static unsafe void ReadStructureArray<T>(this BinaryReader reader, T[] ar

public static T[] ReadStructureArray<T>(this BinaryReader reader, int count, int expectedSizeOfElement) where T : unmanaged
{
ArgumentOutOfRangeException.ThrowIfNegative(count);
var array = new T[count];
reader.ReadStructureArray(array, expectedSizeOfElement);
return array;
Expand Down
18 changes: 18 additions & 0 deletions zzre.core/imgui/MenuBar.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,24 @@ public void AddRadio(string path, IReadOnlyList<string> labels, GetRefValueFunc<
EndMenu();
});

public void AddRadio<TEnum>(string path, GetRefValueFunc<TEnum> getValue, Action? onChanged = null)
where TEnum : struct, Enum => AddItem(path, name =>
{
if (!BeginMenu(name))
return;
ref var curValue = ref getValue();
var values = Enum.GetValues<TEnum>();
foreach (var value in values)
{
if (MenuItem(value.ToString(), "", curValue.GetHashCode() == value.GetHashCode()))
{
curValue = value;
onChanged?.Invoke();
}
}
EndMenu();
});

public void AddSlider(string path, float minVal, float maxVal, GetRefValueFunc<float> getValue, Action? onChanged = null) => AddItem(path, name =>
{
if (SliderFloat(name, ref getValue(), minVal, maxVal))
Expand Down
1 change: 0 additions & 1 deletion zzre.core/math/Frustum.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;

namespace zzre;
Expand Down
21 changes: 21 additions & 0 deletions zzre.core/math/MathEx.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Linq;
using System.Numerics;
using System.Runtime.CompilerServices;
using zzio;

namespace zzre;

Expand Down Expand Up @@ -131,4 +132,24 @@ public static Vector3 HorizontalSlerp(Vector3 from, Vector3 to, float curvature,
from.Y,
MathF.Cos(newAngle));
}

public const float GoldenRatioFract = 0.61803398875f;
public const float GoldenRatio = 1.61803398875f;

public static IEnumerable<float> GoldenRatioSequence(float acc = 0f)
{
while (true)
{
yield return acc;
acc = (acc + GoldenRatioFract) % 1f;
}
}

public static IEnumerable<FColor> GoldenRatioColors(
float hueStart = 0f,
float saturation = 1f,
float luminosity = 0.5f,
float alpha = 1f) =>
GoldenRatioSequence(hueStart)
.Select(hue => new FColor(hue, saturation, luminosity, alpha).HSLToRGB());
}
67 changes: 67 additions & 0 deletions zzre.core/math/ZZIOExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
using Veldrid;
using zzio;
using zzio.rwbs;
using static System.MathF;
using static zzre.MathEx;

namespace zzre;

Expand Down Expand Up @@ -41,4 +43,69 @@ public static Vector3 ToZZRotationVector(this Quaternion rotation) =>
CollisionSectorType.Z => Vector3.UnitZ,
_ => throw new ArgumentOutOfRangeException($"Unknown collision sector type {sectorType}")
};

// adapted from https://gist.github.com/ciembor/1494530 - "It's the do what you want license :)"

public static FColor RGBToHSL(this FColor c)
{
float max = Max(Max(c.r, c.g), c.b);
float min = Min(Min(c.r, c.g), c.b);
float mid = (max + min) / 2;

FColor result = new(mid, mid, mid, c.a);
if (Cmp(max, min))
result.r = result.g = 0f; // achromatic
else
{
float d = max - min;
result.g = (result.b > 0.5) ? d / (2 - max - min) : d / (max + min);

if (Cmp(max, c.r))
{
result.r = (c.g - c.b) / d + (c.g < c.b ? 6 : 0);
}
else if (Cmp(max, c.g))
{
result.r = (c.b - c.r) / d + 2;
}
else if (Cmp(max, c.b))
{
result.r = (c.r - c.g) / d + 4;
}

result.r /= 6;
}
return result;
}

private static float HueToRGB(float p, float q, float t)
{
if (t < 0)
t += 1;
if (t > 1)
t -= 1;
return t switch
{
< 1f / 6 => p + (q - p) * 6 * t,
< 1f / 2 => q,
< 2f / 3 => p + (q - p) * (2f / 3 - t) * 6,
_ => p
};
}

public static FColor HSLToRGB(this FColor c)
{
if (CmpZero(c.g))
return new(c.b, c.b, c.b, c.a);

float q = c.b < 0.5f
? c.b * (1 + c.g)
: c.b + c.g - c.b * c.g;
float p = 2 * c.b - q;
return new(
HueToRGB(p, q, c.r + 1f / 3),
HueToRGB(p, q, c.r),
HueToRGB(p, q, c.r - 1f / 3),
c.a);
}
}
Loading

0 comments on commit 1ba89c8

Please sign in to comment.