This commit is contained in:
dexy 2022-03-20 11:09:45 +11:00
commit 77d18307fa
8 changed files with 669 additions and 109 deletions

View File

@ -2673,11 +2673,12 @@ namespace CodeWalker.GameFiles
MinRepeatTime = br.ReadInt16(); MinRepeatTime = br.ReadInt16();
VariableCount = br.ReadByte(); VariableCount = br.ReadByte();
VariableNames = new MetaHash[VariableCount]; VariableNames = new MetaHash[VariableCount];
VariableValues = new byte[VariableCount];
for (int i = 0; i < VariableCount; i++) for (int i = 0; i < VariableCount; i++)
{ {
VariableNames[i] = br.ReadUInt32(); VariableNames[i] = br.ReadUInt32();
VariableValues[i] = br.ReadByte();
} }
VariableValues = br.ReadBytes(VariableCount);
ChildSoundsHashes = new[] { ChildSound, FallBackSound }; ChildSoundsHashes = new[] { ChildSound, FallBackSound };
} }
@ -2743,13 +2744,8 @@ namespace CodeWalker.GameFiles
for (int i = 0; i < VariableCount; i++) for (int i = 0; i < VariableCount; i++)
{ {
bw.Write(VariableNames[i]); bw.Write(VariableNames[i]);
bw.Write(VariableValues[i]);
} }
if (VariableValues != null)
{
bw.Write(VariableValues);
}
else
{ }
} }
public override uint[] GetHashTableOffsets() public override uint[] GetHashTableOffsets()
{ {

View File

@ -0,0 +1,270 @@
using SharpDX;
using System.IO;
using TC = System.ComponentModel.TypeConverterAttribute;
using EXP = System.ComponentModel.ExpandableObjectConverter;
namespace CodeWalker.GameFiles
{
public class YpdbFile : GameFile, PackedFile
{
public int SerializerVersion { get; set; } // 2
public int PoseMatcherVersion { get; set; } // 0
public uint Signature { get; set; }
public int SamplesCount { get; set; }
public PoseMatcherMatchSample[] Samples { get; set; }
public int BoneTagsCount { get; set; }
public ushort[] BoneTags { get; set; }
public PoseMatcherWeightSet WeightSet { get; set; }
public float Unk7 { get; set; } // 0.033333f
public int Unk8 { get; set; } // 1
public YpdbFile() : base(null, GameFileType.Ypdb)
{
}
public YpdbFile(RpfFileEntry entry) : base(entry, GameFileType.Ypdb)
{
}
public void Load(byte[] data, RpfFileEntry entry)
{
if (entry != null)
{
RpfFileEntry = entry;
Name = entry.Name;
}
using (MemoryStream ms = new MemoryStream(data))
{
DataReader r = new DataReader(ms, Endianess.LittleEndian);
Read(r);
}
Loaded = true;
}
public byte[] Save()
{
MemoryStream s = new MemoryStream();
DataWriter w = new DataWriter(s);
Write(w);
var buf = new byte[s.Length];
s.Position = 0;
s.Read(buf, 0, buf.Length);
return buf;
}
private void Read(DataReader r)
{
byte magic = r.ReadByte();
if (magic != 0x1A) // 0x1A indicates to the game deserializer that it's binary instead of text
{ } // no hit
SerializerVersion = r.ReadInt32();
PoseMatcherVersion = r.ReadInt32();
Signature = r.ReadUInt32();
SamplesCount = r.ReadInt32();
if (SamplesCount > 0)
{
Samples = new PoseMatcherMatchSample[SamplesCount];
for (int i = 0; i < SamplesCount; i++)
Samples[i] = new PoseMatcherMatchSample(r);
}
BoneTagsCount = r.ReadInt32();
if (BoneTagsCount > 0)
{
BoneTags = new ushort[BoneTagsCount];
for (int i = 0; i < BoneTagsCount; i++)
BoneTags[i] = r.ReadUInt16();
}
WeightSet = new PoseMatcherWeightSet(r);
Unk7 = r.ReadSingle();
Unk8 = r.ReadInt32();
uint signature2 = r.ReadUInt32();
if (SerializerVersion != 2)
{ } // no hit
if (PoseMatcherVersion != 0)
{ } // no hit
if (BoneTagsCount != WeightSet.WeightsCount)
{ } // no hit
if (Unk7 != 0.033333f)
{ } // no hit
if (Unk8 != 1)
{ } // no hit
if (Signature != signature2)
{ } // no hit
if (r.Position != r.Length)
{ }
}
private void Write(DataWriter w)
{
w.Write((byte)0x1A);
w.Write(SerializerVersion);
w.Write(PoseMatcherVersion);
w.Write(Signature);
w.Write(SamplesCount);
if (SamplesCount > 0)
{
foreach (var entry in Samples)
entry.Write(w);
}
w.Write(BoneTagsCount);
if (BoneTagsCount > 0)
{
foreach (var boneTag in BoneTags)
w.Write(boneTag);
}
WeightSet.Write(w);
w.Write(Unk7);
w.Write(Unk8);
w.Write(Signature);
}
}
[TC(typeof(EXP))]
public class PoseMatcherMatchSample
{
// rage::crPoseMatcherData::MatchSample
public MetaHash ClipSet { get; set; } // from clip_sets.ymt/xml
public MetaHash Clip { get; set; }
public float Unk3 { get; set; }
public PoseMatcherPointCloud PointCloud { get; set; }
public PoseMatcherMatchSample(DataReader r)
{
ClipSet = r.ReadUInt32();
Clip = r.ReadUInt32();
Unk3 = r.ReadSingle();
PointCloud = new PoseMatcherPointCloud(r);
}
public void Write(DataWriter w)
{
w.Write(ClipSet);
w.Write(Clip);
w.Write(Unk3);
PointCloud.Write(w);
}
public override string ToString()
{
return $"{ClipSet}, {Clip}";
}
}
[TC(typeof(EXP))]
public class PoseMatcherPointCloud
{
// rage::crpmPointCloud
public int PointsCount { get; set; }
public Vector3[] Points { get; set; }
public int Unk2_Count { get; set; } // == PointsCount
public float[] Unk2_Items { get; set; }
public Vector3 BoundsMin { get; set; }
public Vector3 BoundsMax { get; set; }
public float Unk5 { get; set; }
public PoseMatcherPointCloud(DataReader r)
{
PointsCount = r.ReadInt32();
if (PointsCount > 0)
{
Points = new Vector3[PointsCount];
for (int i = 0; i < PointsCount; i++)
Points[i] = new Vector3(r.ReadSingle(), r.ReadSingle(), r.ReadSingle());
}
Unk2_Count = r.ReadInt32();
if (Unk2_Count > 0)
{
Unk2_Items = new float[Unk2_Count];
for (int i = 0; i < Unk2_Count; i++)
Unk2_Items[i] = r.ReadSingle();
}
BoundsMin = new Vector3(r.ReadSingle(), r.ReadSingle(), r.ReadSingle());
BoundsMax = new Vector3(r.ReadSingle(), r.ReadSingle(), r.ReadSingle());
Unk5 = r.ReadSingle();
if (PointsCount != Unk2_Count)
{ } // no hit
}
public void Write(DataWriter w)
{
w.Write(PointsCount);
if (PointsCount > 0)
{
foreach (var point in Points)
w.Write(point);
}
w.Write(Unk2_Count);
if (Unk2_Count > 0)
{
foreach (var entry in Unk2_Items)
w.Write(entry);
}
w.Write(BoundsMin);
w.Write(BoundsMax);
w.Write(Unk5);
}
}
[TC(typeof(EXP))]
public class PoseMatcherWeightSet
{
// rage::crWeightSet
public int WeightsCount { get; set; }
public float[] Weights { get; set; }
public PoseMatcherWeightSet(DataReader r)
{
WeightsCount = r.ReadInt32();
if (WeightsCount > 0)
{
Weights = new float[WeightsCount];
for (int i = 0; i < WeightsCount; i++)
Weights[i] = r.ReadSingle();
}
}
public void Write(DataWriter w)
{
w.Write(WeightsCount);
if (WeightsCount > 0)
{
foreach (var weight in Weights)
w.Write(weight);
}
}
}
}

View File

@ -84,6 +84,7 @@ namespace CodeWalker.GameFiles
Watermap = 28, Watermap = 28,
Mrf = 29, Mrf = 29,
DistantLights = 30, DistantLights = 30,
Ypdb = 31,
} }

View File

@ -215,6 +215,7 @@ namespace CodeWalker.GameFiles
//TestYvrs(); //TestYvrs();
//TestYwrs(); //TestYwrs();
//TestYmaps(); //TestYmaps();
//TestYpdbs();
//TestMrfs(); //TestMrfs();
//TestPlacements(); //TestPlacements();
//TestDrawables(); //TestDrawables();
@ -4526,6 +4527,48 @@ namespace CodeWalker.GameFiles
} }
} }
} }
public void TestYpdbs()
{
foreach (RpfFile file in AllRpfs)
{
foreach (RpfEntry entry in file.AllEntries)
{
var rfe = entry as RpfFileEntry;
if (rfe == null) continue;
try
{
if (rfe.NameLower.EndsWith(".ypdb"))
{
UpdateStatus(string.Format(entry.Path));
YpdbFile ypdb = RpfMan.GetFile<YpdbFile>(entry);
if (ypdb != null)
{
var odata = entry.File.ExtractFile(entry as RpfFileEntry);
var ndata = ypdb.Save();
if (ndata.Length == odata.Length)
{
for (int i = 0; i < ndata.Length; i++)
{
if (ndata[i] != odata[i])
{ break; }
}
}
else
{ }
}
else
{ }
}
}
catch (Exception ex)
{
UpdateStatus("Error! " + ex.ToString());
}
}
}
}
public void TestMrfs() public void TestMrfs()
{ {
foreach (RpfFile file in AllRpfs) foreach (RpfFile file in AllRpfs)

View File

@ -309,6 +309,7 @@ namespace CodeWalker
InitFileType(".awc", "Audio Wave Container", 22, FileTypeAction.ViewAwc, true); InitFileType(".awc", "Audio Wave Container", 22, FileTypeAction.ViewAwc, true);
InitFileType(".rel", "Audio Data (REL)", 23, FileTypeAction.ViewRel, true); InitFileType(".rel", "Audio Data (REL)", 23, FileTypeAction.ViewRel, true);
InitFileType(".nametable", "Name Table", 5, FileTypeAction.ViewNametable); InitFileType(".nametable", "Name Table", 5, FileTypeAction.ViewNametable);
InitFileType(".ypdb", "Pose Matcher Database", 9, FileTypeAction.ViewYpdb);
InitSubFileType(".dat", "cache_y.dat", "Cache File", 6, FileTypeAction.ViewCacheDat, true); InitSubFileType(".dat", "cache_y.dat", "Cache File", 6, FileTypeAction.ViewCacheDat, true);
InitSubFileType(".dat", "heightmap.dat", "Heightmap", 6, FileTypeAction.ViewHeightmap, true); InitSubFileType(".dat", "heightmap.dat", "Heightmap", 6, FileTypeAction.ViewHeightmap, true);
@ -1545,6 +1546,9 @@ namespace CodeWalker
case FileTypeAction.ViewDistantLights: case FileTypeAction.ViewDistantLights:
ViewDistantLights(name, path, data, fe); ViewDistantLights(name, path, data, fe);
break; break;
case FileTypeAction.ViewYpdb:
ViewYpdb(name, path, data, fe);
break;
case FileTypeAction.ViewHex: case FileTypeAction.ViewHex:
default: default:
ViewHex(name, path, data); ViewHex(name, path, data);
@ -1806,6 +1810,13 @@ namespace CodeWalker
f.Show(); f.Show();
f.LoadFile(dlf, dlf.RpfFileEntry); f.LoadFile(dlf, dlf.RpfFileEntry);
} }
private void ViewYpdb(string name, string path, byte[] data, RpfFileEntry e)
{
var ypdb = RpfFile.GetFile<YpdbFile>(e, data);
GenericForm f = new GenericForm(this);
f.Show();
f.LoadFile(ypdb, ypdb.RpfFileEntry);
}
private RpfFileEntry CreateFileEntry(string name, string path, ref byte[] data) private RpfFileEntry CreateFileEntry(string name, string path, ref byte[] data)
{ {
@ -4878,6 +4889,7 @@ namespace CodeWalker
ViewMrf = 24, ViewMrf = 24,
ViewNametable = 25, ViewNametable = 25,
ViewDistantLights = 26, ViewDistantLights = 26,
ViewYpdb = 27,
} }

View File

@ -30,6 +30,7 @@
{ {
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ModelLightForm)); System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ModelLightForm));
this.MainSplitContainer = new System.Windows.Forms.SplitContainer(); this.MainSplitContainer = new System.Windows.Forms.SplitContainer();
this.DuplicateLightButton = new System.Windows.Forms.Button();
this.DeleteLightButton = new System.Windows.Forms.Button(); this.DeleteLightButton = new System.Windows.Forms.Button();
this.NewLightButton = new System.Windows.Forms.Button(); this.NewLightButton = new System.Windows.Forms.Button();
this.LightsTreeView = new CodeWalker.WinForms.TreeViewFix(); this.LightsTreeView = new CodeWalker.WinForms.TreeViewFix();
@ -113,6 +114,7 @@
this.EditMenu = new System.Windows.Forms.ToolStripMenuItem(); this.EditMenu = new System.Windows.Forms.ToolStripMenuItem();
this.EditNewLightMenu = new System.Windows.Forms.ToolStripMenuItem(); this.EditNewLightMenu = new System.Windows.Forms.ToolStripMenuItem();
this.EditDeleteLightMenu = new System.Windows.Forms.ToolStripMenuItem(); this.EditDeleteLightMenu = new System.Windows.Forms.ToolStripMenuItem();
this.EditDuplicateLightMenu = new System.Windows.Forms.ToolStripMenuItem();
this.OptionsMenu = new System.Windows.Forms.ToolStripMenuItem(); this.OptionsMenu = new System.Windows.Forms.ToolStripMenuItem();
this.OptionsShowOutlinesMenu = new System.Windows.Forms.ToolStripMenuItem(); this.OptionsShowOutlinesMenu = new System.Windows.Forms.ToolStripMenuItem();
this.MoveMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.MoveMenuItem = new System.Windows.Forms.ToolStripMenuItem();
@ -148,6 +150,7 @@
// //
// MainSplitContainer.Panel1 // MainSplitContainer.Panel1
// //
this.MainSplitContainer.Panel1.Controls.Add(this.DuplicateLightButton);
this.MainSplitContainer.Panel1.Controls.Add(this.DeleteLightButton); this.MainSplitContainer.Panel1.Controls.Add(this.DeleteLightButton);
this.MainSplitContainer.Panel1.Controls.Add(this.NewLightButton); this.MainSplitContainer.Panel1.Controls.Add(this.NewLightButton);
this.MainSplitContainer.Panel1.Controls.Add(this.LightsTreeView); this.MainSplitContainer.Panel1.Controls.Add(this.LightsTreeView);
@ -155,10 +158,22 @@
// MainSplitContainer.Panel2 // MainSplitContainer.Panel2
// //
this.MainSplitContainer.Panel2.Controls.Add(this.LightPropertiesPanel); this.MainSplitContainer.Panel2.Controls.Add(this.LightPropertiesPanel);
this.MainSplitContainer.Size = new System.Drawing.Size(733, 523); this.MainSplitContainer.Panel2.Paint += new System.Windows.Forms.PaintEventHandler(this.MainSplitContainer_Panel2_Paint);
this.MainSplitContainer.Size = new System.Drawing.Size(733, 561);
this.MainSplitContainer.SplitterDistance = 184; this.MainSplitContainer.SplitterDistance = 184;
this.MainSplitContainer.TabIndex = 0; this.MainSplitContainer.TabIndex = 0;
// //
// DuplicateLightButton
//
this.DuplicateLightButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.DuplicateLightButton.Location = new System.Drawing.Point(7, 526);
this.DuplicateLightButton.Name = "DuplicateLightButton";
this.DuplicateLightButton.Size = new System.Drawing.Size(169, 23);
this.DuplicateLightButton.TabIndex = 8;
this.DuplicateLightButton.Text = "Duplicate Light";
this.DuplicateLightButton.UseVisualStyleBackColor = true;
this.DuplicateLightButton.Click += new System.EventHandler(this.DuplicateLightButton_Click);
//
// DeleteLightButton // DeleteLightButton
// //
this.DeleteLightButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.DeleteLightButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
@ -279,7 +294,7 @@
this.LightPropertiesPanel.Controls.Add(this.label31); this.LightPropertiesPanel.Controls.Add(this.label31);
this.LightPropertiesPanel.Location = new System.Drawing.Point(4, 3); this.LightPropertiesPanel.Location = new System.Drawing.Point(4, 3);
this.LightPropertiesPanel.Name = "LightPropertiesPanel"; this.LightPropertiesPanel.Name = "LightPropertiesPanel";
this.LightPropertiesPanel.Size = new System.Drawing.Size(538, 517); this.LightPropertiesPanel.Size = new System.Drawing.Size(529, 517);
this.LightPropertiesPanel.TabIndex = 1; this.LightPropertiesPanel.TabIndex = 1;
// //
// VolumetricFadeDistanceUpDown // VolumetricFadeDistanceUpDown
@ -1127,7 +1142,8 @@
// //
this.EditMenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.EditMenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.EditNewLightMenu, this.EditNewLightMenu,
this.EditDeleteLightMenu}); this.EditDeleteLightMenu,
this.EditDuplicateLightMenu});
this.EditMenu.Name = "EditMenu"; this.EditMenu.Name = "EditMenu";
this.EditMenu.Size = new System.Drawing.Size(39, 20); this.EditMenu.Size = new System.Drawing.Size(39, 20);
this.EditMenu.Text = "Edit"; this.EditMenu.Text = "Edit";
@ -1135,7 +1151,7 @@
// EditNewLightMenu // EditNewLightMenu
// //
this.EditNewLightMenu.Name = "EditNewLightMenu"; this.EditNewLightMenu.Name = "EditNewLightMenu";
this.EditNewLightMenu.Size = new System.Drawing.Size(180, 22); this.EditNewLightMenu.Size = new System.Drawing.Size(154, 22);
this.EditNewLightMenu.Text = "New Light"; this.EditNewLightMenu.Text = "New Light";
this.EditNewLightMenu.Click += new System.EventHandler(this.EditNewLightMenu_Click); this.EditNewLightMenu.Click += new System.EventHandler(this.EditNewLightMenu_Click);
// //
@ -1143,10 +1159,18 @@
// //
this.EditDeleteLightMenu.Enabled = false; this.EditDeleteLightMenu.Enabled = false;
this.EditDeleteLightMenu.Name = "EditDeleteLightMenu"; this.EditDeleteLightMenu.Name = "EditDeleteLightMenu";
this.EditDeleteLightMenu.Size = new System.Drawing.Size(180, 22); this.EditDeleteLightMenu.Size = new System.Drawing.Size(154, 22);
this.EditDeleteLightMenu.Text = "Delete Light"; this.EditDeleteLightMenu.Text = "Delete Light";
this.EditDeleteLightMenu.Click += new System.EventHandler(this.EditDeleteLightMenu_Click); this.EditDeleteLightMenu.Click += new System.EventHandler(this.EditDeleteLightMenu_Click);
// //
// EditDuplicateLightMenu
//
this.EditDuplicateLightMenu.Enabled = false;
this.EditDuplicateLightMenu.Name = "EditDuplicateLightMenu";
this.EditDuplicateLightMenu.Size = new System.Drawing.Size(154, 22);
this.EditDuplicateLightMenu.Text = "Duplicate Light";
this.EditDuplicateLightMenu.Click += new System.EventHandler(this.EditDuplicateLightMenu_Click);
//
// OptionsMenu // OptionsMenu
// //
this.OptionsMenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.OptionsMenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
@ -1193,7 +1217,7 @@
// //
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(733, 547); this.ClientSize = new System.Drawing.Size(733, 585);
this.Controls.Add(this.MainSplitContainer); this.Controls.Add(this.MainSplitContainer);
this.Controls.Add(this.MainMenu); this.Controls.Add(this.MainMenu);
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
@ -1291,6 +1315,7 @@
private System.Windows.Forms.ToolStripMenuItem EditMenu; private System.Windows.Forms.ToolStripMenuItem EditMenu;
private System.Windows.Forms.ToolStripMenuItem EditNewLightMenu; private System.Windows.Forms.ToolStripMenuItem EditNewLightMenu;
private System.Windows.Forms.ToolStripMenuItem EditDeleteLightMenu; private System.Windows.Forms.ToolStripMenuItem EditDeleteLightMenu;
private System.Windows.Forms.ToolStripMenuItem EditDuplicateLightMenu;
private System.Windows.Forms.ToolStripMenuItem OptionsMenu; private System.Windows.Forms.ToolStripMenuItem OptionsMenu;
private System.Windows.Forms.ToolStripMenuItem OptionsShowOutlinesMenu; private System.Windows.Forms.ToolStripMenuItem OptionsShowOutlinesMenu;
private System.Windows.Forms.ToolStripMenuItem MoveMenuItem; private System.Windows.Forms.ToolStripMenuItem MoveMenuItem;
@ -1319,5 +1344,6 @@
private System.Windows.Forms.NumericUpDown FlashinessUpDown; private System.Windows.Forms.NumericUpDown FlashinessUpDown;
private System.Windows.Forms.Button DeleteLightButton; private System.Windows.Forms.Button DeleteLightButton;
private System.Windows.Forms.Button NewLightButton; private System.Windows.Forms.Button NewLightButton;
private System.Windows.Forms.Button DuplicateLightButton;
} }
} }

View File

@ -111,6 +111,7 @@ namespace CodeWalker.Forms
{ {
DeleteLightButton.Enabled = false; DeleteLightButton.Enabled = false;
EditDeleteLightMenu.Enabled = false; EditDeleteLightMenu.Enabled = false;
EditDuplicateLightMenu.Enabled = false;
populatingui = true; populatingui = true;
PositionTextBox.Text = ""; PositionTextBox.Text = "";
DirectionTextBox.Text = ""; DirectionTextBox.Text = "";
@ -154,6 +155,7 @@ namespace CodeWalker.Forms
{ {
DeleteLightButton.Enabled = true; DeleteLightButton.Enabled = true;
EditDeleteLightMenu.Enabled = true; EditDeleteLightMenu.Enabled = true;
EditDuplicateLightMenu.Enabled = true;
populatingui = true; populatingui = true;
PositionTextBox.Text = FloatUtil.GetVector3String(light.Position); PositionTextBox.Text = FloatUtil.GetVector3String(light.Position);
DirectionTextBox.Text = FloatUtil.GetVector3String(light.Direction); DirectionTextBox.Text = FloatUtil.GetVector3String(light.Direction);
@ -222,6 +224,57 @@ namespace CodeWalker.Forms
light.TimeFlags = 14680191; light.TimeFlags = 14680191;
return light; return light;
} }
private LightAttributes DuplicateLightAttribute()
{
LightAttributes light = new LightAttributes();
light.Unknown_0h = selectedLight.Unknown_0h;
light.Unknown_4h = selectedLight.Unknown_4h;
light.Position = selectedLight.Position;
light.Unknown_14h = selectedLight.Unknown_14h;
light.ColorR = selectedLight.ColorR;
light.ColorG = selectedLight.ColorG;
light.ColorB = selectedLight.ColorB;
light.Flashiness = selectedLight.Flashiness;
light.Intensity = selectedLight.Intensity;
light.Flags = selectedLight.Flags;
light.BoneId = selectedLight.BoneId;
light.Type = selectedLight.Type;
light.GroupId = selectedLight.GroupId;
light.TimeFlags = selectedLight.TimeFlags;
light.Falloff = selectedLight.Falloff;
light.FalloffExponent = selectedLight.FalloffExponent;
light.CullingPlaneNormal = selectedLight.CullingPlaneNormal;
light.CullingPlaneOffset = selectedLight.CullingPlaneOffset;
light.ShadowBlur = selectedLight.ShadowBlur;
light.Unknown_45h = selectedLight.Unknown_45h;
light.Unknown_46h = selectedLight.Unknown_46h;
light.VolumeIntensity = selectedLight.VolumeIntensity;
light.VolumeSizeScale = selectedLight.VolumeSizeScale;
light.VolumeOuterColorR = selectedLight.VolumeOuterColorR;
light.VolumeOuterColorG = selectedLight.VolumeOuterColorG;
light.VolumeOuterColorB = selectedLight.VolumeOuterColorB;
light.LightHash = selectedLight.LightHash;
light.VolumeOuterIntensity = selectedLight.VolumeOuterIntensity;
light.CoronaSize = selectedLight.CoronaSize;
light.VolumeOuterExponent = selectedLight.VolumeOuterExponent;
light.LightFadeDistance = selectedLight.LightFadeDistance;
light.ShadowFadeDistance = selectedLight.ShadowFadeDistance;
light.SpecularFadeDistance = selectedLight.SpecularFadeDistance;
light.VolumetricFadeDistance = selectedLight.VolumetricFadeDistance;
light.ShadowNearClip = selectedLight.ShadowNearClip;
light.CoronaIntensity = selectedLight.CoronaIntensity;
light.CoronaZBias = selectedLight.CoronaZBias;
light.Direction = selectedLight.Direction;
light.Tangent = selectedLight.Tangent;
light.ConeInnerAngle = selectedLight.ConeInnerAngle;
light.ConeOuterAngle = selectedLight.ConeOuterAngle;
light.Extent = selectedLight.Extent;
light.ProjectedTextureHash = selectedLight.ProjectedTextureHash;
light.Unknown_A4h = selectedLight.Unknown_A4h;
return light;
}
private void SelectLight(LightAttributes light) private void SelectLight(LightAttributes light)
{ {
if (light == null) if (light == null)
@ -318,7 +371,7 @@ namespace CodeWalker.Forms
private void DeleteLight() private void DeleteLight()
{ {
if (selectedLight == null) return; if (selectedLight == null) return;
if(Drawable != null) if (Drawable != null)
{ {
List<LightAttributes> lights = Drawable.LightAttributes.data_items.ToList(); List<LightAttributes> lights = Drawable.LightAttributes.data_items.ToList();
lights.Remove(selectedLight); lights.Remove(selectedLight);
@ -346,6 +399,50 @@ namespace CodeWalker.Forms
LoadModels(DrawableDict); LoadModels(DrawableDict);
} }
} }
}
private void DuplicateLight()
{
if (selectedLight == null) return;
selectedLight = DuplicateLightAttribute();
if (Drawable != null)
{
if (Drawable.LightAttributes == null) Drawable.LightAttributes = new ResourceSimpleList64<LightAttributes>();
List<LightAttributes> lights = Drawable.LightAttributes.data_items?.ToList() ?? new List<LightAttributes>();
lights.Add(selectedLight);
Drawable.LightAttributes.data_items = lights.ToArray();
UpdateLightParams();
LoadModel(Drawable);
}
else if (FragDrawable != null)
{
if (FragDrawable.OwnerFragment.LightAttributes == null) FragDrawable.OwnerFragment.LightAttributes = new ResourceSimpleList64<LightAttributes>();
List<LightAttributes> lights = FragDrawable.OwnerFragment.LightAttributes.data_items?.ToList() ?? new List<LightAttributes>();
lights.Add(selectedLight);
FragDrawable.OwnerFragment.LightAttributes.data_items = lights.ToArray();
UpdateLightParams();
LoadModel(FragDrawable);
}
else
{
var n = LightsTreeView.SelectedNode;
if (n != null)
{
var dr = n.Tag as Drawable;
if (dr == null) { dr = n.Parent.Tag as Drawable; } //try parent node tag also
if (dr != null)
{
if (dr.LightAttributes == null) dr.LightAttributes = new ResourceSimpleList64<LightAttributes>();
List<LightAttributes> lights = dr.LightAttributes.data_items?.ToList() ?? new List<LightAttributes>();
lights.Add(selectedLight);
dr.LightAttributes.data_items = lights.ToArray();
UpdateLightParams();
LoadModels(DrawableDict);
}
}
}
SelectLightTreeNode(selectedLight);
} }
private void UpdateFlagsCheckBoxes() private void UpdateFlagsCheckBoxes()
@ -933,6 +1030,11 @@ namespace CodeWalker.Forms
DeleteLight(); DeleteLight();
} }
private void EditDuplicateLightMenu_Click(object sender, EventArgs e)
{
DuplicateLight();
}
private void OptionsShowOutlinesMenu_Click(object sender, EventArgs e) private void OptionsShowOutlinesMenu_Click(object sender, EventArgs e)
{ {
OptionsShowOutlinesMenu.Checked = !OptionsShowOutlinesMenu.Checked; OptionsShowOutlinesMenu.Checked = !OptionsShowOutlinesMenu.Checked;
@ -969,5 +1071,15 @@ namespace CodeWalker.Forms
{ {
DeleteLight(); DeleteLight();
} }
private void DuplicateLightButton_Click(object sender, EventArgs e)
{
DuplicateLight();
}
private void MainSplitContainer_Panel2_Paint(object sender, PaintEventArgs e)
{
}
} }
} }

View File

@ -432,8 +432,25 @@ namespace CodeWalker.Utils
a[i] = Lerp(a[i], min, max); a[i] = Lerp(a[i], min, max);
} }
break; break;
//case Dat10Synth.Opcode.LERP_BUFFER_2: case Dat10Synth.Opcode.LERP_BUFFER_2: // TODO: some better name for LERP_BUFFER_2
// break; {
var t = GetScalar(param[1]);
float[] min, max;
if ((param[0].Value & 0xFF) == (param[2].Value & 0xFF))
{
min = GetBuffer(param[2]);
max = GetBuffer(param[3]);
}
else
{
min = GetBuffer(param[3]);
max = GetBuffer(param[2]);
}
for (int i = 0; i < BufferSize; i++)
min[i] = Lerp(t, min[i], max[i]);
}
break;
case Dat10Synth.Opcode.LERP_SCALAR: case Dat10Synth.Opcode.LERP_SCALAR:
{ {
var t = GetScalar(param[1]); var t = GetScalar(param[1]);
@ -567,18 +584,24 @@ namespace CodeWalker.Utils
GetScalar(param[5]), GetScalar(param[5]),
ref StateBlocks[param[6].Value]); ref StateBlocks[param[6].Value]);
break; break;
//case Dat10Synth.Opcode.OnePole_LPF_BUFFER_BUFFER: case Dat10Synth.Opcode.OnePole_LPF_BUFFER_BUFFER:
// break; OnePoleLPF(GetBuffer(param[0]), GetBuffer(param[1]), ref StateBlocks[param[2].Value]);
//case Dat10Synth.Opcode.OnePole_LPF_BUFFER_SCALAR: break;
// break; case Dat10Synth.Opcode.OnePole_LPF_BUFFER_SCALAR:
//case Dat10Synth.Opcode.OnePole_LPF_SCALAR_SCALAR: OnePoleLPF(GetBuffer(param[0]), GetScalar(param[1]), ref StateBlocks[param[2].Value]);
// break; break;
//case Dat10Synth.Opcode.OnePole_HPF_BUFFER_BUFFER: case Dat10Synth.Opcode.OnePole_LPF_SCALAR_SCALAR:
// break; SetRegister(param[0], OnePoleLPF(GetScalar(param[1]), GetScalar(param[2]), ref StateBlocks[param[3].Value]));
//case Dat10Synth.Opcode.OnePole_HPF_BUFFER_SCALAR: break;
// break; case Dat10Synth.Opcode.OnePole_HPF_BUFFER_BUFFER:
//case Dat10Synth.Opcode.OnePole_HPF_SCALAR_SCALAR: OnePoleHPF(GetBuffer(param[0]), GetBuffer(param[1]), ref StateBlocks[param[2].Value]);
// break; break;
case Dat10Synth.Opcode.OnePole_HPF_BUFFER_SCALAR:
OnePoleHPF(GetBuffer(param[0]), GetScalar(param[1]), ref StateBlocks[param[2].Value]);
break;
case Dat10Synth.Opcode.OnePole_HPF_SCALAR_SCALAR:
SetRegister(param[0], OnePoleHPF(GetScalar(param[1]), GetScalar(param[2]), ref StateBlocks[param[3].Value]));
break;
case Dat10Synth.Opcode.OSC_RAMP_BUFFER_BUFFER: case Dat10Synth.Opcode.OSC_RAMP_BUFFER_BUFFER:
OscillatorRamp(GetBuffer(param[0]), ref StateBlocks[param[1].Value]); OscillatorRamp(GetBuffer(param[0]), ref StateBlocks[param[1].Value]);
break; break;
@ -816,8 +839,9 @@ namespace CodeWalker.Utils
for (int i = 0; i < BufferSize; i++) for (int i = 0; i < BufferSize; i++)
a[i] = scalar; a[i] = scalar;
break; break;
//case Dat10Synth.Opcode.AWProcess: case Dat10Synth.Opcode.AWProcess:
// break; AWFilter(GetBuffer(param[0]), GetBuffer(param[1]), GetBuffer(param[2]), ref StateBlocks[param[3].Value]);
break;
case Dat10Synth.Opcode.LERP_BUFFER_BUFFER: case Dat10Synth.Opcode.LERP_BUFFER_BUFFER:
{ {
var t = GetBuffer(param[0]); var t = GetBuffer(param[0]);
@ -910,10 +934,12 @@ namespace CodeWalker.Utils
SetRegister(param[0], result); SetRegister(param[0], result);
} }
break; break;
//case Dat10Synth.Opcode.AllpassProcess_BUFFER_SCALAR: case Dat10Synth.Opcode.AllpassProcess_BUFFER_SCALAR:
// break; AllpassFilter(GetBuffer(param[0]), GetScalar(param[1]), ref StateBlocks[param[2].Value]);
//case Dat10Synth.Opcode.AllpassProcess_BUFFER_BUFFER: break;
// break; case Dat10Synth.Opcode.AllpassProcess_BUFFER_BUFFER:
AllpassFilter(GetBuffer(param[0]), GetBuffer(param[1]), ref StateBlocks[param[2].Value]);
break;
case Dat10Synth.Opcode.FINISH: case Dat10Synth.Opcode.FINISH:
frameFinished = true; frameFinished = true;
break; break;
@ -1007,32 +1033,19 @@ namespace CodeWalker.Utils
return (max - min) * t + min; return (max - min) * t + min;
} }
private float HardKnee(float y, float threshold) private float HardKnee(float sample, float threshold)
{ {
/*
ENVELOPE_GEN__R_LINEAR_T_ONE_SHOT 0, 0.025, 0, 1, 0, 0, 1 => B0 [0]
COPY_BUFFER B0 => B1
HARD_KNEE_BUFFER B1, 0.25 => B1
COPY_BUFFER B0 => B2
HARD_KNEE_BUFFER B2, 0.85 => B2
COPY_BUFFER B0 => B4
HARD_KNEE_BUFFER B4, 0.75 => B4
FILL_BUFFER 0 => B3
FINISH =>
*/
// TODO(alexguirre): better names for HardKnee and maybe some comments
float result; float result;
if (y < 0.0f) if (sample < 0.0f)
result = 0.0f; result = 0.0f;
else else
result = (y / threshold) * 0.5f; result = (sample / threshold) * 0.5f;
if ((y - threshold) >= 0.0f) if (sample >= threshold)
result = (((y - threshold) / (1.0f - threshold)) + 1.0f) * 0.5f; result = (((sample - threshold) / (1.0f - threshold)) + 1.0f) * 0.5f;
if (y >= 1.0f) if (sample >= 1.0f)
result = 1.0f; result = 1.0f;
return result; return result;
@ -1048,10 +1061,11 @@ FINISH =>
return (float)Math.Pow(Math.Max(0, a), b); return (float)Math.Pow(Math.Max(0, a), b);
} }
private float NoteToFrequency(float note) // https://newt.phys.unsw.edu.au/jw/notes.html
private float NoteToFrequency(float midiNote)
{ {
// TODO(alexguirre): figure out the meaning of this formula and constants in NoteToFrequency // A4 (note #69) = 440Hz
return (float)Math.Pow(2.0f, (note + 36.376301f) / 12.0f); return 440.0f * (float)Math.Pow(2.0f, (midiNote - 69) / 12.0f);
} }
private void Decimate(float[] buffer, float scalar1, float deltaPerSample, ref StateBlock stateBlock) private void Decimate(float[] buffer, float scalar1, float deltaPerSample, ref StateBlock stateBlock)
@ -1220,7 +1234,7 @@ FINISH =>
float w = (float)(2.0 * Math.PI * centerFrequency / SampleRate); float w = (float)(2.0 * Math.PI * centerFrequency / SampleRate);
float cosW = (float)Math.Cos(w); float cosW = (float)Math.Cos(w);
float v14 = 1.0f / (float)Math.Tan((freq2 * 0.000020833333f) * Math.PI); float v14 = 1.0f / (float)Math.Tan(Math.PI * freq2 / SampleRate);
b0 = 1.0f; b0 = 1.0f;
b1 = 0.0f; b1 = 0.0f;
@ -1243,7 +1257,7 @@ FINISH =>
float w = (float)(2.0f * Math.PI * centerFrequency / SampleRate); float w = (float)(2.0f * Math.PI * centerFrequency / SampleRate);
float cosW = (float)Math.Cos(w); float cosW = (float)Math.Cos(w);
float v15 = (float)Math.Tan((freq2 * 0.000020833333f) * Math.PI); float v15 = (float)Math.Tan(Math.PI * freq2 / SampleRate);
b0 = 1.0f; b0 = 1.0f;
b1 = -2.0f * cosW; b1 = -2.0f * cosW;
@ -1268,7 +1282,7 @@ FINISH =>
float q = centerFrequency / freq2; float q = centerFrequency / freq2;
float alpha = (float)Math.Sin(w) / (2.0f * q); float alpha = (float)Math.Sin(w) / (2.0f * q);
float cosW = (float)Math.Cos(w); float cosW = (float)Math.Cos(w);
float A = Math.Max(0.000001f, gain * 1.4141999f); // TODO(alexguirre): this should be amp = 10.0 ** (gain_db/40.0), where does 1.41 come from? maybe gain not in dB? float A = Math.Max(0.000001f, gain * (float)Math.Sqrt(2)); // TODO(alexguirre): this should be amp = 10.0 ** (gain_db/40.0), where does sqrt(2) come from? maybe gain not in dB?
b0 = 1.0f + alpha * A; b0 = 1.0f + alpha * A;
b1 = -2.0f * cosW; b1 = -2.0f * cosW;
@ -1338,53 +1352,96 @@ FINISH =>
private void BiquadFilter2Pole(float[] buffer, float b0, float b1, float b2, float a1, float a2, ref StateBlock stateBlock) private void BiquadFilter2Pole(float[] buffer, float b0, float b1, float b2, float a1, float a2, ref StateBlock stateBlock)
{ {
// State block: float pole1 = stateBlock.X;
// X -> ... float pole2 = stateBlock.Y;
// Y -> ...
float stateX = stateBlock.X;
float stateY = stateBlock.Y;
for (int i = 0; i < BufferSize; i++) for (int i = 0; i < BufferSize; i++)
{ {
float x = (buffer[i] * b0) + stateX; float s = buffer[i];
stateX = (buffer[i] * b1) - (x * a1) + stateY; float x = (s * b0) + pole1;
stateY = (buffer[i] * b2) - (x * a2); pole1 = (s * b1) - (x * a1) + pole2;
pole2 = (s * b2) - (x * a2);
buffer[i] = x; buffer[i] = x;
} }
stateBlock.X = stateX; stateBlock.X = pole1;
stateBlock.Y = stateY; stateBlock.Y = pole2;
} }
private void BiquadFilter4Pole(float[] buffer, float b0, float b1, float b2, float a1, float a2, ref StateBlock stateBlock) private void BiquadFilter4Pole(float[] buffer, float b0, float b1, float b2, float a1, float a2, ref StateBlock stateBlock)
{ {
// State block: float pole1 = stateBlock.X;
// X -> ... float pole2 = stateBlock.Y;
// Y -> ... float pole3 = stateBlock.Z;
// Z -> ... float pole4 = stateBlock.W;
// W -> ...
float stateX = stateBlock.X;
float stateY = stateBlock.Y;
float stateZ = stateBlock.Z;
float stateW = stateBlock.W;
for (int i = 0; i < BufferSize; i++) for (int i = 0; i < BufferSize; i++)
{ {
float x = (buffer[i] * b0) + stateX; float s = buffer[i];
float y = (x * b0) + stateZ; float x = (s * b0) + pole1;
stateX = (buffer[i] * b1) - (x * a1) + stateY; pole1 = (s * b1) - (x * a1) + pole2;
stateY = (buffer[i] * b2) - (x * a2); pole2 = (s * b2) - (x * a2);
stateZ = (x * b1) - (y * a1) + stateW; float y = (x * b0) + pole3;
stateW = (x * b2) - (y * a2); pole3 = (x * b1) - (y * a1) + pole4;
pole4 = (x * b2) - (y * a2);
buffer[i] = y; buffer[i] = y;
} }
stateBlock.X = stateX; stateBlock.X = pole1;
stateBlock.Y = stateY; stateBlock.Y = pole2;
stateBlock.Z = stateZ; stateBlock.Z = pole3;
stateBlock.W = stateW; stateBlock.W = pole4;
}
// https://www.earlevel.com/main/2012/12/15/a-one-pole-filter/
// TODO: verify OnePoleLPF/HPF results
private void OnePoleLPF(float[] buffer, float[] frequencies, ref StateBlock stateBlock)
{
for (int i = 0; i < BufferSize; i++)
{
buffer[i] = OnePoleLPF(buffer[i], frequencies[i], ref stateBlock);
}
}
private void OnePoleLPF(float[] buffer, float frequency, ref StateBlock stateBlock)
{
for (int i = 0; i < BufferSize; i++)
{
buffer[i] = OnePoleLPF(buffer[i], frequency, ref stateBlock);
}
}
private float OnePoleLPF(float sample, float frequency, ref StateBlock stateBlock)
{
float previousSample = stateBlock.X;
float b1 = (float)Math.Exp(-2.0f * Math.PI * frequency * 256.0f / SampleRate);
float a0 = 1.0f - b1;
float s = a0 * sample + (b1 * previousSample);
stateBlock.X = s;
return s;
}
private void OnePoleHPF(float[] buffer, float[] frequencies, ref StateBlock stateBlock)
{
for (int i = 0; i < BufferSize; i++)
{
buffer[i] = OnePoleHPF(buffer[i], frequencies[i], ref stateBlock);
}
}
private void OnePoleHPF(float[] buffer, float frequency, ref StateBlock stateBlock)
{
for (int i = 0; i < BufferSize; i++)
{
buffer[i] = OnePoleHPF(buffer[i], frequency, ref stateBlock);
}
}
private float OnePoleHPF(float sample, float frequency, ref StateBlock stateBlock)
{
return sample - OnePoleLPF(sample, frequency, ref stateBlock);
} }
private void EnvelopeFollower(float[] buffer, float a2, float a3, ref StateBlock stateBlock) private void EnvelopeFollower(float[] buffer, float a2, float a3, ref StateBlock stateBlock)
@ -1469,13 +1526,12 @@ FINISH =>
// State block: // State block:
// X -> latch SET // X -> latch SET
// TODO(alexguirre): understand logic of TriggerLatch bool set = stateBlock.X != 0.0f;
float stateSet = stateBlock.X;
float latch = 0.0f; float latch = 0.0f;
if (triggered && stateSet == 0.0f) if (triggered && !set)
latch = 1.0f; latch = 1.0f;
if (triggered != (stateSet != 0.0f)) if (triggered != set)
{ {
stateBlock.X = triggered ? 1.0f : 0.0f; stateBlock.X = triggered ? 1.0f : 0.0f;
} }
@ -1811,21 +1867,6 @@ FINISH =>
// TODO(alexguirre): TimedTrigger may not be equivalent, game code has like 10 states, here I'm using the same states as the envelope gen // TODO(alexguirre): TimedTrigger may not be equivalent, game code has like 10 states, here I'm using the same states as the envelope gen
// TODO(alexguirre): verify how TimedTrigger works in-game // TODO(alexguirre): verify how TimedTrigger works in-game
/*
ENVELOPE_GEN__R_LINEAR_T_ONE_SHOT 0.1, 0.2, 0.3, 0.5, 0.4, 0.35, 1 => B6, R0 [1]
TIMED_TRIGGER__T_ONE_SHOT 1, 0.1, 0.2, 0.3, 0.4, 0.35 => R1, R2, R3, R4, R5 [2]
;FILL_BUFFER R0 => B0 ; envelope finished
FILL_BUFFER R1 => B1 ; timed trigger finished
;FILL_BUFFER R2 => B2 ; attack
;FILL_BUFFER R3 => B3 ; decay
;FILL_BUFFER R4 => B4 ; hold
;FILL_BUFFER R5 => B5 ; release
FILL_BUFFER 0 => B7
FINISH =>
*/
private TimedTriggerResult TimedTrigger(EnvelopeTriggerMode triggerMode, ref StateBlock stateBlock, float trigger, float predelay, float attack, float decay, float hold, float release) private TimedTriggerResult TimedTrigger(EnvelopeTriggerMode triggerMode, ref StateBlock stateBlock, float trigger, float predelay, float attack, float decay, float hold, float release)
{ {
// State block: // State block:
@ -2070,6 +2111,65 @@ FINISH =>
} }
} }
private void AWFilter(float[] buffer, float[] buffer2, float[] buffer3, ref StateBlock stateBlock)
{
var s = stateBlock.X;
for (int i = 0; i < BufferSize; i++)
{
var v10 = buffer3[i] * 64.0f;
var v12 = (float)rnd.NextDouble() * v10 + buffer2[i] * 127.0f + 1.0f;
var v13 = 1.0f / v12;
var v14 = (1.0f - (v13 * v12)) * v13 + v13;
s = s - ((s - buffer[i]) * v14);
buffer[i] = s;
}
stateBlock.X = s;
}
// https://thewolfsound.com/allpass-filter/#first-order-iir-allpass
private void AllpassFilter(float[] buffer, float breakFrequency, ref StateBlock stateBlock)
{
float previousUnfilteredSample = stateBlock.X;
float previousSample = stateBlock.Y;
float a1 = (float)Math.Tan(Math.PI * breakFrequency / SampleRate);
a1 = (a1 - 1.0f) / (a1 + 1.0f);
for (int i = 0; i < BufferSize; i++)
{
float sample = a1 * buffer[i] + previousUnfilteredSample - a1 * previousSample;
sample = Math.Max(Math.Min(sample, 1.0f), -1.0f);
previousUnfilteredSample = buffer[i];
previousSample = sample;
buffer[i] = sample;
}
stateBlock.X = previousUnfilteredSample;
stateBlock.Y = previousSample;
}
private void AllpassFilter(float[] buffer, float[] breakFrequencies, ref StateBlock stateBlock)
{
float previousUnfilteredSample = stateBlock.X;
float previousSample = stateBlock.Y;
for (int i = 0; i < BufferSize; i++)
{
float a1 = (float)Math.Tan(Math.PI * breakFrequencies[i] / SampleRate);
a1 = (a1 - 1.0f) / (a1 + 1.0f);
float sample = a1 * buffer[i] + previousUnfilteredSample - a1 * previousSample;
sample = Math.Max(Math.Min(sample, 1.0f), -1.0f);
previousUnfilteredSample = buffer[i];
previousSample = sample;
buffer[i] = sample;
}
stateBlock.X = previousUnfilteredSample;
stateBlock.Y = previousSample;
}