Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

zzre: Add waypoints section to scene editor #392

Merged
merged 6 commits into from
Nov 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading