From 6467797c3d07a3323f67de31d3e7798452870090 Mon Sep 17 00:00:00 2001 From: dexy Date: Tue, 26 Nov 2019 01:26:28 +1100 Subject: [PATCH] Cutscene viewer rendering peds --- CodeWalker.Core/GameFiles/GameFileCache.cs | 2 +- .../GameFiles/MetaTypes/MetaTypes.cs | 1 + CodeWalker.Core/GameFiles/Resources/Clip.cs | 2 + CodeWalker.Core/World/Ped.cs | 95 ++++++++++ PedsForm.cs | 65 +------ Rendering/Renderable.cs | 5 + Rendering/Renderer.cs | 17 +- World/CutsceneForm.cs | 173 +++++++++++++++--- 8 files changed, 264 insertions(+), 96 deletions(-) diff --git a/CodeWalker.Core/GameFiles/GameFileCache.cs b/CodeWalker.Core/GameFiles/GameFileCache.cs index 8e713b7..e22a0f4 100644 --- a/CodeWalker.Core/GameFiles/GameFileCache.cs +++ b/CodeWalker.Core/GameFiles/GameFileCache.cs @@ -93,7 +93,7 @@ namespace CodeWalker.GameFiles public bool BuildExtendedJenkIndex = true; public bool LoadArchetypes = true; public bool LoadVehicles = false; - public bool LoadPeds = false; + public bool LoadPeds = true; private bool PreloadedMode = false; private string GTAFolder; diff --git a/CodeWalker.Core/GameFiles/MetaTypes/MetaTypes.cs b/CodeWalker.Core/GameFiles/MetaTypes/MetaTypes.cs index 354b434..b868ea7 100644 --- a/CodeWalker.Core/GameFiles/MetaTypes/MetaTypes.cs +++ b/CodeWalker.Core/GameFiles/MetaTypes/MetaTypes.cs @@ -6259,6 +6259,7 @@ namespace CodeWalker.GameFiles } public string GetTextureSuffix(int texnum) { + if (texnum < 0) texnum = 0; const string alphas = "abcdefghijklmnopqrstuvwxyz"; var tex = TexData[texnum]; var texid = tex.texId; diff --git a/CodeWalker.Core/GameFiles/Resources/Clip.cs b/CodeWalker.Core/GameFiles/Resources/Clip.cs index 2cdab41..402abb0 100644 --- a/CodeWalker.Core/GameFiles/Resources/Clip.cs +++ b/CodeWalker.Core/GameFiles/Resources/Clip.cs @@ -2902,6 +2902,8 @@ namespace CodeWalker.GameFiles public ClipMapEntry Next { get; set; } public bool EnableRootMotion { get; set; } = false; //used by CW to toggle whether or not to include root motion when playing animations + public bool OverridePlayTime { get; set; } = false; //used by CW to manually override the animation playback time + public float PlayTime { get; set; } = 0.0f; public override void Read(ResourceDataReader reader, params object[] parameters) diff --git a/CodeWalker.Core/World/Ped.cs b/CodeWalker.Core/World/Ped.cs index 6d953e2..75afe0b 100644 --- a/CodeWalker.Core/World/Ped.cs +++ b/CodeWalker.Core/World/Ped.cs @@ -117,5 +117,100 @@ namespace CodeWalker.World + + + public void SetComponentDrawable(int index, string name, string tex, GameFileCache gfc) + { + if (string.IsNullOrEmpty(name)) + { + DrawableNames[index] = null; + Drawables[index] = null; + Textures[index] = null; + return; + } + + MetaHash namehash = JenkHash.GenHash(name.ToLowerInvariant()); + Drawable d = null; + if (Ydd?.Dict != null) + { + Ydd.Dict.TryGetValue(namehash, out d); + } + if ((d == null) && (DrawableFilesDict != null)) + { + RpfFileEntry file = null; + if (DrawableFilesDict.TryGetValue(namehash, out file)) + { + var ydd = gfc.GetFileUncached(file); + while ((ydd != null) && (!ydd.Loaded)) + { + Thread.Sleep(20);//kinda hacky + gfc.TryLoadEnqueue(ydd); + } + if (ydd?.Drawables?.Length > 0) + { + d = ydd.Drawables[0];//should only be one in this dict + } + } + } + + MetaHash texhash = JenkHash.GenHash(tex.ToLowerInvariant()); + Texture t = null; + if (Ytd?.TextureDict?.Dict != null) + { + Ytd.TextureDict.Dict.TryGetValue(texhash, out t); + } + if ((t == null) && (TextureFilesDict != null)) + { + RpfFileEntry file = null; + if (TextureFilesDict.TryGetValue(texhash, out file)) + { + var ytd = gfc.GetFileUncached(file); + while ((ytd != null) && (!ytd.Loaded)) + { + Thread.Sleep(20);//kinda hacky + gfc.TryLoadEnqueue(ytd); + } + if (ytd?.TextureDict?.Textures?.data_items.Length > 0) + { + t = ytd.TextureDict.Textures.data_items[0];//should only be one in this dict + } + } + } + + + if (d != null) Drawables[index] = d; + if (t != null) Textures[index] = t; + + DrawableNames[index] = name; + } + + public void SetComponentDrawable(int index, int drawbl, int alt, int tex, GameFileCache gfc) + { + var vi = Ymt?.VariationInfo; + if (vi != null) + { + var compData = vi.GetComponentData(index); + if (compData?.DrawblData3 != null) + { + var item = (drawbl < (compData.DrawblData3?.Length ?? 0)) ? compData.DrawblData3[drawbl] : null; + if (item != null) + { + var name = item?.GetDrawableName(alt); + var texn = item?.GetTextureName(tex); + SetComponentDrawable(index, name, texn, gfc); + } + } + } + } + + public void LoadDefaultComponents(GameFileCache gfc) + { + for (int i = 0; i < 12; i++) + { + SetComponentDrawable(i, 0, 0, 0, gfc); + } + } + + } } diff --git a/PedsForm.cs b/PedsForm.cs index e2e7bb4..a5dd93d 100644 --- a/PedsForm.cs +++ b/PedsForm.cs @@ -807,70 +807,9 @@ namespace CodeWalker.Peds var comboItem = comboObj as ComponentComboItem; var name = comboItem?.DrawableName; - if (string.IsNullOrEmpty(name)) - { - SelectedPed.DrawableNames[index] = null; - SelectedPed.Drawables[index] = null; - SelectedPed.Textures[index] = null; - UpdateModelsUI(); - return; - } + var tex = comboItem?.TextureName; - MetaHash namehash = JenkHash.GenHash(name.ToLowerInvariant()); - Drawable d = null; - if (SelectedPed.Ydd?.Dict != null) - { - SelectedPed.Ydd.Dict.TryGetValue(namehash, out d); - } - if ((d == null) && (SelectedPed.DrawableFilesDict != null)) - { - RpfFileEntry file = null; - if (SelectedPed.DrawableFilesDict.TryGetValue(namehash, out file)) - { - var ydd = GameFileCache.GetFileUncached(file); - while ((ydd != null) && (!ydd.Loaded)) - { - Thread.Sleep(20);//kinda hacky - GameFileCache.TryLoadEnqueue(ydd); - } - if (ydd?.Drawables?.Length > 0) - { - d = ydd.Drawables[0];//should only be one in this dict - } - } - } - - - var tex = comboItem.TextureName; - MetaHash texhash = JenkHash.GenHash(tex.ToLowerInvariant()); - Texture t = null; - if (SelectedPed.Ytd?.TextureDict?.Dict != null) - { - SelectedPed.Ytd.TextureDict.Dict.TryGetValue(texhash, out t); - } - if ((t == null) && (SelectedPed.TextureFilesDict != null)) - { - RpfFileEntry file = null; - if (SelectedPed.TextureFilesDict.TryGetValue(texhash, out file)) - { - var ytd = GameFileCache.GetFileUncached(file); - while ((ytd != null) && (!ytd.Loaded)) - { - Thread.Sleep(20);//kinda hacky - GameFileCache.TryLoadEnqueue(ytd); - } - if (ytd?.TextureDict?.Textures?.data_items.Length > 0) - { - t = ytd.TextureDict.Textures.data_items[0];//should only be one in this dict - } - } - } - - - if (d != null) SelectedPed.Drawables[index] = d; - if (t != null) SelectedPed.Textures[index] = t; - - SelectedPed.DrawableNames[index] = name; + SelectedPed.SetComponentDrawable(index, name, tex, GameFileCache); UpdateModelsUI(); } diff --git a/Rendering/Renderable.cs b/Rendering/Renderable.cs index e983107..f595d4b 100644 --- a/Rendering/Renderable.cs +++ b/Rendering/Renderable.cs @@ -427,6 +427,11 @@ namespace CodeWalker.Rendering public void UpdateAnims(double realTime) { + if (ClipMapEntry?.OverridePlayTime ?? false) + { + realTime = ClipMapEntry.PlayTime; + } + if (CurrentAnimTime == realTime) return;//already updated this! CurrentAnimTime = realTime; diff --git a/Rendering/Renderer.cs b/Rendering/Renderer.cs index d3274f1..eb8af1e 100644 --- a/Rendering/Renderer.cs +++ b/Rendering/Renderer.cs @@ -2496,7 +2496,7 @@ namespace CodeWalker.Rendering return res; } - public bool RenderDrawable(DrawableBase drawable, Archetype arche, YmapEntityDef entity, uint txdHash = 0, TextureDictionary txdExtra = null, Texture diffOverride = null, ClipMapEntry animClip = null) + public bool RenderDrawable(DrawableBase drawable, Archetype arche, YmapEntityDef entity, uint txdHash = 0, TextureDictionary txdExtra = null, Texture diffOverride = null, ClipMapEntry animClip = null, Ped ped = null) { //enqueue a single drawable for rendering. @@ -2521,10 +2521,10 @@ namespace CodeWalker.Rendering rndbl.ResetBoneTransforms(); } - return RenderRenderable(rndbl, arche, entity); + return RenderRenderable(rndbl, arche, entity, ped); } - private bool RenderRenderable(Renderable rndbl, Archetype arche, YmapEntityDef entity) + private bool RenderRenderable(Renderable rndbl, Archetype arche, YmapEntityDef entity, Ped ped = null) { //enqueue a single renderable for rendering. @@ -2564,6 +2564,15 @@ namespace CodeWalker.Rendering camrel += position; distance = entity.Distance; } + else if (ped != null) + { + position = ped.Position; + orientation = ped.Rotation; + bbmin += position; + bbmax += position; + camrel += position; + distance = (camrel + bscen).Length(); + } else { distance = (camrel + bscen).Length(); @@ -2799,7 +2808,7 @@ namespace CodeWalker.Rendering if (drawFlag) { - RenderDrawable(drawable, null, null, 0, td, texture, ac); + RenderDrawable(drawable, null, null, 0, td, texture, ac, ped); } diff --git a/World/CutsceneForm.cs b/World/CutsceneForm.cs index 5f570cb..6ae3358 100644 --- a/World/CutsceneForm.cs +++ b/World/CutsceneForm.cs @@ -1,4 +1,5 @@ using CodeWalker.GameFiles; +using CodeWalker.Rendering; using SharpDX; using System; using System.Collections.Generic; @@ -69,7 +70,13 @@ namespace CodeWalker.World public void GetVisibleYmaps(Camera camera, Dictionary ymaps) { - //use a temporary ymap for entities. + //use a temporary ymap for entities? + + var renderer = WorldForm?.Renderer; + if (renderer == null) return; + + if (Cutscene == null) return; + Cutscene.Render(renderer); } @@ -140,12 +147,12 @@ namespace CodeWalker.World var csnode = CutsceneTreeView.Nodes.Add(cutFile.FileEntry?.Name); csnode.Tag = cs; - if (cf.pCutsceneObjects != null) + if (cs.SceneObjects != null) { var objsnode = csnode.Nodes.Add("Objects"); objsnode.Name = "Objects"; - foreach (var obj in cf.pCutsceneObjects) + foreach (var obj in cs.SceneObjects.Values) { var objnode = objsnode.Nodes.Add(obj.ToString()); objnode.Tag = obj; @@ -400,38 +407,84 @@ namespace CodeWalker.World float cutOffset = newTime - cutStart;//offset into the current cut + + void updateObjectTransform(CutsceneObject obj, ClipMapEntry cme, ushort boneTag, byte posTrack, byte rotTrack) + { + if (cme != null) + { + if (cme.Clip is ClipAnimation canim) + { + if (canim.Animation != null) + { + var t = canim.GetPlaybackTime(cutOffset); + var f = canim.Animation.GetFramePosition(t); + var p = canim.Animation.FindBoneIndex(boneTag, posTrack); + var r = canim.Animation.FindBoneIndex(boneTag, rotTrack); + if (p >= 0) obj.Position = canim.Animation.EvaluateVector4(f, p, true).XYZ(); + if (r >= 0) obj.Rotation = canim.Animation.EvaluateQuaternion(f, r, true); + } + } + else if (cme.Clip is ClipAnimationList alist) + { + if (alist.Animations?.Data != null) + { + foreach (var anim in alist.Animations.Data) + { + var t = anim.GetPlaybackTime(cutOffset); + var f = anim.Animation.GetFramePosition(t); + var p = anim.Animation.FindBoneIndex(boneTag, posTrack); + var r = anim.Animation.FindBoneIndex(boneTag, rotTrack); + if (p >= 0) obj.Position = anim.Animation.EvaluateVector4(f, p, true).XYZ(); + if (r >= 0) obj.Rotation = anim.Animation.EvaluateQuaternion(f, r, true); + } + } + } + } + } + + + + var ycd = (cutIndex < (Ycds?.Length ?? 0)) ? Ycds[cutIndex] : null; if (ycd?.CutsceneMap != null) { + ClipMapEntry cme = null; + if (CameraObject != null) { - ClipMapEntry cme = null; ycd.CutsceneMap.TryGetValue(CameraObject.Name, out cme); - if (cme != null) - { - if (cme.Clip is ClipAnimation canim) - { - if (canim.Animation != null) - { - var t = canim.GetPlaybackTime(PlaybackTime); - var f = canim.Animation.GetFramePosition(t); - var p = canim.Animation.FindBoneIndex(0, 7);//camera position - var r = canim.Animation.FindBoneIndex(0, 8);//camera rotation - if (p >= 0) CameraObject.Position = canim.Animation.EvaluateVector4(f, p, true).XYZ(); - if (r >= 0) CameraObject.Rotation = canim.Animation.EvaluateQuaternion(f, r, true); - } - - } - - } - - //var animname = CameraObject.Name.ToString() + "-" + cutIndex.ToString(); - //MetaHash animhash = JenkHash.GenHash() - - + updateObjectTransform(CameraObject, cme, 0, 7, 8); } + if (SceneObjects != null) + { + foreach (var obj in SceneObjects.Values) + { + if (obj.Ped != null) + { + ycd.CutsceneMap.TryGetValue(obj.Ped.NameHash, out cme); + var pos = Position; + var rot = Rotation; + if (cme != null) + { + cme.OverridePlayTime = true; + cme.PlayTime = cutOffset; + + updateObjectTransform(obj, cme, 0, 5, 6); + + pos = pos + obj.Position; + rot = rot * obj.Rotation; + } + obj.Ped.Position = pos; + obj.Ped.Rotation = rot; + obj.Ped.AnimClip = cme; + } + } + } + + + } @@ -439,6 +492,26 @@ namespace CodeWalker.World } + public void Render(Renderer renderer) + { + + if (SceneObjects != null) + { + foreach (var obj in SceneObjects.Values) + { + if (obj.Ped != null) + { + renderer.RenderPed(obj.Ped); + } + } + } + + } + + + + + private void RaiseEvents(float upToTime) { @@ -747,8 +820,10 @@ namespace CodeWalker.World var dur = args.fSubtitleDuration; txt = txt.Replace("~z~", ""); + txt = txt.Replace("~c~~n~", "\n - "); txt = txt.Replace("~n~", "\n"); txt = txt.Replace("~c~", " - "); + txt = txt.Replace("~t~", " - "); WorldForm.ShowSubtitle(txt, dur); } @@ -858,7 +933,7 @@ namespace CodeWalker.World foreach (var obj in Objects.Values) { var sobj = new CutsceneObject(); - sobj.Init(obj); + sobj.Init(obj, GameFileCache); SceneObjects[sobj.ObjectID] = sobj; } } @@ -873,8 +948,10 @@ namespace CodeWalker.World public Vector3 Position { get; set; } public Quaternion Rotation { get; set; } + public Ped Ped { get; set; } - public void Init(CutObject obj) + + public void Init(CutObject obj, GameFileCache gfc) { CutObject = obj; ObjectID = obj?.iObjectId ?? -1; @@ -895,18 +972,23 @@ namespace CodeWalker.World } else if (obj is CutPedModelObject ped) { + InitPed(ped, gfc); } else if (obj is CutPropModelObject prop) { + InitProp(prop, gfc); } else if (obj is CutVehicleModelObject veh) { + InitVehicle(veh, gfc); } else if (obj is CutWeaponModelObject weap) { + InitWeapon(weap, gfc); } else if (obj is CutHiddenModelObject hid) { + InitHiddenModel(hid, gfc); } else if (obj is CutFixupModelObject fix) { @@ -948,6 +1030,41 @@ namespace CodeWalker.World { } } + private void InitPed(CutPedModelObject ped, GameFileCache gfc) + { + + Ped = new Ped(); + Ped.Init(ped.StreamingName, gfc); + Ped.LoadDefaultComponents(gfc); + + if (ped.StreamingName == JenkHash.GenHash("player_zero")) + { + //for michael, switch his outfit so it's not glitching everywhere (until it's fixed?) + Ped.SetComponentDrawable(3, 27, 0, 0, gfc); + Ped.SetComponentDrawable(4, 19, 0, 0, gfc); + Ped.SetComponentDrawable(6, null, null, gfc); + } + } + + private void InitProp(CutPropModelObject prop, GameFileCache gfc) + { + + } + + private void InitVehicle(CutVehicleModelObject veh, GameFileCache gfc) + { + + } + + private void InitWeapon(CutWeaponModelObject weap, GameFileCache gfc) + { + + } + + private void InitHiddenModel(CutHiddenModelObject hid, GameFileCache gfc) + { + + } public override string ToString()