Archived
Private
Public Access
1
0

Initial commit

This commit is contained in:
2022-09-04 12:45:01 +02:00
commit f4a01d6a69
11601 changed files with 4206660 additions and 0 deletions

View File

@@ -0,0 +1,306 @@
using CodeWalker.GameFiles;
using SharpDX;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CodeWalker.World
{
public class AudioZones
{
public volatile bool Inited = false;
public GameFileCache GameFileCache;
public List<AudioPlacement> Zones = new List<AudioPlacement>();
public List<AudioPlacement> Emitters = new List<AudioPlacement>();
public List<AudioPlacement> AllItems = new List<AudioPlacement>();
public Dictionary<RelFile, AudioPlacement[]> PlacementsDict = new Dictionary<RelFile, AudioPlacement[]>();
public void Init(GameFileCache gameFileCache, Action<string> updateStatus)
{
Inited = false;
GameFileCache = gameFileCache;
Zones.Clear();
Emitters.Clear();
AllItems.Clear();
List<AudioPlacement> placements = new List<AudioPlacement>();
foreach (var relfile in GameFileCache.AudioDatRelFiles)
{
if (relfile == null) continue;
placements.Clear();
CreatePlacements(relfile, placements, true);
PlacementsDict[relfile] = placements.ToArray();
}
AllItems.AddRange(Zones);
AllItems.AddRange(Emitters);
Inited = true;
}
private void CreatePlacements(RelFile relfile, List<AudioPlacement> placements, bool addtoLists = false)
{
foreach (var reldata in relfile.RelDatas)
{
AudioPlacement placement = null;
if (reldata is Dat151AmbientZone)
{
placement = new AudioPlacement(relfile, reldata as Dat151AmbientZone);
if (addtoLists) Zones.Add(placement);
}
else if (reldata is Dat151AmbientRule)
{
placement = new AudioPlacement(relfile, reldata as Dat151AmbientRule);
if (addtoLists) Emitters.Add(placement);
}
if (placement != null)
{
placements.Add(placement);
}
}
}
public void GetPlacements(List<RelFile> relfiles, List<AudioPlacement> placements)
{
foreach (var relfile in relfiles)
{
AudioPlacement[] fileplacements = null;
if (!PlacementsDict.TryGetValue(relfile, out fileplacements))
{
List<AudioPlacement> newplacements = new List<AudioPlacement>();
CreatePlacements(relfile, newplacements);
fileplacements = newplacements.ToArray();
PlacementsDict[relfile] = fileplacements;
}
if (fileplacements != null)
{
placements.AddRange(fileplacements);
}
}
}
}
public class AudioPlacement
{
public string Name { get; set; }
public MetaHash NameHash { get; set; }
public RelFile RelFile { get; set; }
public Dat151AmbientZone AudioZone { get; set; }
public Dat151AmbientRule AudioEmitter { get; set; }
public Dat151ZoneShape Shape { get; set; }
public string ShortTypeName { get; set; }
public string FullTypeName { get; set; }
public Vector3 InnerPos { get; set; }
public Vector3 InnerMin { get; set; }
public Vector3 InnerMax { get; set; }
public float InnerRad { get; set; }
public Quaternion InnerOri { get; set; }
public Vector3 OuterPos { get; set; }
public Vector3 OuterMin { get; set; }
public Vector3 OuterMax { get; set; }
public float OuterRad { get; set; }
public Quaternion OuterOri { get; set; }
public Vector3 Position { get; set; }
public Vector3 HitboxMin { get; set; }
public Vector3 HitboxMax { get; set; }
public Quaternion Orientation { get; set; }
public Quaternion OrientationInv { get; set; }
public float HitSphereRad { get; set; }
public AudioPlacement(RelFile rel, Dat151AmbientZone zone)
{
RelFile = rel;
AudioZone = zone;
ShortTypeName = "AudioZone";
FullTypeName = "Audio Zone";
UpdateFromZone();
}
public AudioPlacement(RelFile rel, Dat151AmbientRule emitter)
{
RelFile = rel;
AudioEmitter = emitter;
ShortTypeName = "AudioEmitter";
FullTypeName = "Audio Emitter";
UpdateFromEmitter();
}
public void UpdateFromZone()
{
if (AudioZone == null) return;
var zone = AudioZone;
Name = zone.Name;
NameHash = zone.NameHash;
Shape = zone.Shape;
float deg2rad = (float)(Math.PI / 180.0);
switch (zone.Shape)
{
case Dat151ZoneShape.Box:
InnerPos = zone.PlaybackZonePosition;
InnerMax = zone.PlaybackZoneSize * 0.5f;
InnerMin = -InnerMax;
InnerOri = Quaternion.RotationAxis(Vector3.UnitZ, zone.PlaybackZoneAngle * deg2rad);
break;
case Dat151ZoneShape.Sphere:
InnerPos = zone.PlaybackZonePosition;
InnerOri = Quaternion.Identity;
InnerRad = zone.PlaybackZoneSize.X;
OuterRad = zone.ActivationZoneSize.X;
break;
case Dat151ZoneShape.Line:
InnerPos = zone.PlaybackZonePosition;
InnerMin = new Vector3(-1.0f, -1.0f, 0.0f);
InnerMax = new Vector3(1.0f, 1.0f, (zone.PlaybackZoneSize - zone.PlaybackZonePosition).Length());
InnerOri = Quaternion.Invert(Quaternion.LookAtLH(zone.PlaybackZonePosition, zone.PlaybackZoneSize, Vector3.UnitZ));
break;
}
OuterPos = zone.ActivationZonePosition;
OuterMax = zone.ActivationZoneSize * 0.5f;
OuterMin = -OuterMax;
OuterOri = Quaternion.RotationAxis(Vector3.UnitZ, zone.ActivationZoneAngle * deg2rad);
bool useouter = ((InnerMax.X == 0) || (InnerMax.Y == 0) || (InnerMax.Z == 0));
if (useouter && (zone.Shape != Dat151ZoneShape.Sphere))
{ } //not sure what these are yet!
Position = useouter ? OuterPos : InnerPos;
HitboxMax = useouter ? OuterMax : InnerMax;
HitboxMin = useouter ? OuterMin : InnerMin;
Orientation = useouter ? OuterOri : InnerOri;
OrientationInv = Quaternion.Invert(Orientation);
HitSphereRad = InnerRad;
if (zone.Shape == Dat151ZoneShape.Sphere)
{
Position = InnerPos;
}
}
public void UpdateFromEmitter()
{
if (AudioEmitter == null) return;
var emitter = AudioEmitter;
Name = emitter.Name;
NameHash = emitter.NameHash;
Shape = Dat151ZoneShape.Sphere;
Orientation = Quaternion.Identity;
OrientationInv = Quaternion.Identity;
InnerPos = emitter.Position;
OuterPos = InnerPos;
InnerRad = emitter.InnerRad;
OuterRad = emitter.OuterRad;
bool useouter = (InnerRad == 0);
if (useouter)
{
InnerRad = 1;
}
Position = InnerPos;
HitSphereRad = InnerRad;// useouter ? OuterRad : InnerRad;
}
public void SetPosition(Vector3 pos)
{
bool useouter = ((InnerMax.X == 0) || (InnerMax.Y == 0) || (InnerMax.Z == 0));
Vector3 delta = pos - InnerPos;
InnerPos = pos;
OuterPos += delta;
Position = useouter ? OuterPos : InnerPos;
if (AudioZone != null)
{
AudioZone.PlaybackZonePosition = InnerPos;
AudioZone.ActivationZonePosition = OuterPos;
}
if (AudioEmitter != null)
{
AudioEmitter.Position = InnerPos;
}
}
public void SetOrientation(Quaternion ori)
{
Orientation = ori;
OrientationInv = Quaternion.Invert(ori);
Vector3 t = ori.Multiply(Vector3.UnitX);
float angl = (float)Math.Atan2(t.Y, t.X);
while (angl < 0) angl += ((float)Math.PI * 2.0f);
float rad2deg = (float)(180.0 / Math.PI);
float dangl = angl * rad2deg;
uint uangl = (uint)dangl;
if (InnerOri == OuterOri)
{
InnerOri = Orientation;
OuterOri = Orientation;
if (AudioZone != null)
{
AudioZone.PlaybackZoneAngle = uangl;
AudioZone.ActivationZoneAngle = uangl;
}
}
else
{
//not sure yet how to allow independent rotation of inner & outer boxes...
//maybe only in project window?
bool useouter = ((InnerMax.X == 0) || (InnerMax.Y == 0) || (InnerMax.Z == 0));
if (useouter)
{
OuterOri = Orientation;
if (AudioZone != null)
{
AudioZone.ActivationZoneAngle = uangl;
}
}
else
{
InnerOri = Orientation;
if (AudioZone != null)
{
AudioZone.PlaybackZoneAngle = uangl;
}
}
}
}
public string GetNameString()
{
if (!string.IsNullOrEmpty(Name)) return Name;
return NameHash.ToString();
}
}
}

View File

@@ -0,0 +1,381 @@
using SharpDX;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CodeWalker.World
{
public class Camera
{
public Vector3 TargetRotation = Vector3.Zero;
public Vector3 CurrentRotation = Vector3.Zero;
public float Smoothness;// 10.0f;//0.15f;
public float Sensitivity;// 0.005f;
public float TargetDistance = 1.0f;
public float CurrentDistance = 1.0f;
public float ZoomCurrentTime = 0.0f;
public float ZoomTargetTime = 2.0f;
public float ZoomVelocity = 0.0f;
public float ZoomSpeed = 0.1f;
public float Width = 1920.0f;
public float Height = 1080.0f;
public float FieldOfView;// 1.0f;
public float FieldOfViewFactor = 0.5f / (float)Math.Tan(/*FieldOfView*/ 1.0f * 0.5f);
public float AspectRatio = 1920.0f / 1080.0f;
public float ZNear = 0.01f;
public float ZFar = 100000.0f;
public Entity FollowEntity = null;
public Vector3 LocalLookAt = Vector3.ForwardLH;
public float VOffset = 0.0f;
public bool UpdateProj = true;
public bool IsMapView = false;
public bool IsOrthographic = false;
public float OrthographicSize = 20.0f;
public float OrthographicTargetSize = 20.0f;
public Matrix ProjMatrix = Matrix.Identity;
public Vector3 Position = Vector3.Zero;
public Vector3 UpDirection = Vector3.Up;
public Vector3 ViewDirection = Vector3.ForwardLH;
public Quaternion ViewQuaternion = Quaternion.Identity;
public Quaternion ViewInvQuaternion = Quaternion.Identity;
public Matrix ViewMatrix = Matrix.Identity;
public Matrix ViewInvMatrix = Matrix.Identity;
public Matrix ViewProjMatrix = Matrix.Identity;
public Matrix ViewProjInvMatrix = Matrix.Identity;
public Frustum ViewFrustum = new Frustum();
public Vector3 MouseRayNear = Vector3.Zero;
public Vector3 MouseRayFar = Vector3.Zero;
public Ray MouseRay;
private float MouseX = 0;
private float MouseY = 0;
private object syncRoot = new object();
public Camera(float smoothness, float sensitivity, float fov)
{
Smoothness = smoothness;
Sensitivity = sensitivity;
FieldOfView = fov;
FieldOfViewFactor = 0.5f / (float)Math.Tan(FieldOfView * 0.5f);
}
public void SetMousePosition(int x, int y)
{
MouseX = (x / Width) * 2.0f - 1.0f;
MouseY = (y / Height) * -2.0f + 1.0f;
}
public void SetFollowEntity(Entity e)
{
FollowEntity = e;
}
public void Update(float elapsed)
{
lock (syncRoot)
{
UpdateFollow(elapsed);
if (UpdateProj) UpdateProjMatrix();
//float mx = (LastMouseX / Width) * 2.0f;
//float my = (LastMouseY / Height) * -2.0f;
////MousedItem = nullptr;
////MousedThing = nullptr;
////MousedItemSpace = nullptr;
UpdateProjection();//, mx, my);
}
}
private void UpdateFollow(float elapsed)
{
const float ythresh = 1.55f;
const float nythresh = -1.55f;
Vector3 up = Vector3.Up;// new Vector3(0.0f, 1.0f, 0.0f);
if (TargetRotation.Y > ythresh) TargetRotation.Y = ythresh;
if (TargetRotation.Y < nythresh) TargetRotation.Y = nythresh;
float sv = Math.Min(Smoothness * elapsed, 1.0f);
CurrentRotation = CurrentRotation + ((TargetRotation - CurrentRotation) * sv);
if (TargetDistance > 11000.0f) TargetDistance = 11000.0f; //11km max zoom dist
if (TargetDistance < 0.0001f) TargetDistance = 0.0001f; //0.1mm min zoom dist...
ZoomCurrentTime += elapsed;
if (ZoomCurrentTime > ZoomTargetTime) ZoomCurrentTime = ZoomTargetTime;
float currentTime = ZoomCurrentTime / ZoomTargetTime;
float deltaDist = TargetDistance - CurrentDistance;
if (currentTime < 1.0f && deltaDist > 0.0f)
{
//TODO: when to properly reset ZoomCurrentTime?
float y = currentTime*currentTime*currentTime; //powf(currentTime, 3.0f);
deltaDist *= y;
}
CurrentDistance = CurrentDistance + deltaDist * ZoomSpeed;
if (IsOrthographic || IsMapView)
{
if (OrthographicTargetSize > 20000.0f) OrthographicTargetSize = 20000.0f;
if (OrthographicTargetSize < 1.0f) OrthographicTargetSize = 1.0f;
OrthographicSize = OrthographicSize + ((OrthographicTargetSize - OrthographicSize) * sv);
UpdateProj = true;
}
if (IsMapView)
{
//in map view, need a constant view matrix aligned to XY.
Vector3 cpos = new Vector3();
if (FollowEntity != null)
{
cpos = FollowEntity.Position;
}
LocalLookAt = Vector3.Zero;
Position = cpos;
//Position.Z = 1000.0f;
ViewDirection = -Vector3.UnitZ;
UpDirection = Vector3.UnitY;
}
else
{
//normal view mode
Vector3 rdir = new Vector3();
float cryd = (float)Math.Cos(CurrentRotation.Y);
rdir.X = -(float)Math.Sin(-CurrentRotation.X) * cryd;
rdir.Z = -(float)Math.Cos(-CurrentRotation.X) * cryd;
rdir.Y = (float)Math.Sin(CurrentRotation.Y);
Vector3 lookat = new Vector3(0.0f, VOffset, 0.0f);
Vector3 cpos = new Vector3();
if (FollowEntity != null)
{
up = FollowEntity.Orientation.Multiply(up);
lookat = FollowEntity.Orientation.Multiply(lookat);
rdir = FollowEntity.Orientation.Multiply(rdir);
cpos = FollowEntity.Position;
}
LocalLookAt = (rdir * CurrentDistance) + lookat;
Position = cpos + LocalLookAt;
ViewDirection = Vector3.Normalize(-rdir);
UpDirection = up;
}
//M16FLookAt(LocalProjection.ViewMatrix, V3F(0.0f, 0.0f, 0.0f), LocalProjection.ViewDirection, LocalProjection.UpDirection);
ViewQuaternion = Quaternion.LookAtRH(Vector3.Zero, ViewDirection, UpDirection);
ViewInvQuaternion = Quaternion.Invert(ViewQuaternion);
ViewMatrix = ViewQuaternion.ToMatrix();
ViewInvMatrix = Matrix.Invert(ViewMatrix);
}
private void UpdateProjMatrix()
{
if (IsMapView)
{
ProjMatrix = Matrix.OrthoRH(AspectRatio * OrthographicSize, OrthographicSize, 3000.0f, 1.0f);
}
else if (IsOrthographic)
{
ProjMatrix = Matrix.OrthoRH(AspectRatio * OrthographicSize, OrthographicSize, ZFar, ZNear);
}
else
{
ProjMatrix = Matrix.PerspectiveFovRH(FieldOfView, AspectRatio, ZFar, ZNear);
}
//ProjMatrix._33/=ZFar;
//ProjMatrix._43/=ZFar;
UpdateProj = false;
}
private void UpdateProjection() //CameraSpaceProjection& p, float mx, float my)
{
float mx = MouseX;
float my = MouseY;
ViewProjMatrix = Matrix.Multiply(ViewMatrix, ProjMatrix);
ViewProjInvMatrix = Matrix.Invert(ViewProjMatrix);
MouseRayNear = ViewProjInvMatrix.MultiplyW(new Vector3(mx, my, 1.0f));
MouseRayFar = ViewProjInvMatrix.MultiplyW(new Vector3(mx, my, 0.0f));
MouseRay.Position = Vector3.Zero;
MouseRay.Direction = Vector3.Normalize(MouseRayFar - MouseRayNear);
if (IsMapView || IsOrthographic)
{
MouseRay.Position = MouseRayNear;
}
ViewFrustum.Update(ref ViewProjMatrix);
ViewFrustum.Position = Position;
}
private void UpdateMousedItem()
{
//////MousedItem = nullptr;
//////MousedThing = nullptr;
//////MousedItemSpace = nullptr;
////int i = 0;
//////var cp = &SpaceProjections[i++];
//////auto item = cp->MousedItem;
//////auto thing = cp->MousedThing;
////auto count = cp->MouseTestedItems;
//////while((item==nullptr) && (thing==nullptr) && (i<SpaceProjections.size()))
////Moused.Clear();
////if (cp->Moused.HasValue) Moused.Set(cp->Moused);
////while (!Moused.HasValue && (i < SpaceProjections.size()))
////{
//// cp = &SpaceProjections[i++];
//// //item = cp->MousedItem;
//// //thing = cp->MousedThing;
//// if (cp->Moused.HasValue)
//// {
//// Moused.Set(cp->Moused);
//// }
//// count += cp->MouseTestedItems;
////}
//////if((item!=nullptr) || (thing!=nullptr))
//////{
////// MousedItem = item;
////// MousedThing = thing;
////// MousedItemSpace = cp->MousedItemSpace;
//////}
}
public void OnWindowResize(int w, int h)
{
lock (syncRoot)
{
Width = (float)w;
Height = (float)h;
AspectRatio = Width / Height;
UpdateProj = true;
}
}
public void ControllerRotate(float x, float y, float elapsed)
{
lock (syncRoot)
{
TargetRotation.X += x*elapsed;
TargetRotation.Y += y*elapsed;
}
}
public void ControllerZoom(float z)
{
lock (syncRoot)
{
float v = (z < 0) ? (1.0f - z) : (z > 0) ? (1.0f / (1.0f + z)) : 1.0f;
TargetDistance *= v;
OrthographicTargetSize *= v;
}
}
public void MouseRotate(int x, int y)
{
lock (syncRoot)
{
TargetRotation.X += x * Sensitivity;
TargetRotation.Y += y * Sensitivity;
}
}
public void MouseZoom(int z)
{
lock (syncRoot)
{
float v = (z < 0) ? 1.1f : (z > 0) ? 1.0f / 1.1f : 1.0f;
TargetDistance *= v;
OrthographicTargetSize *= v;
}
}
}
public class Frustum
{
public Plane[] Planes = new Plane[6];
public Vector3 Position;
public void Update(ref Matrix vp)
{
//left, right, top, bottom, near, far
Planes[0] = Plane.Normalize(new Plane((vp.M14 + vp.M11), (vp.M24 + vp.M21), (vp.M34 + vp.M31), (vp.M44 + vp.M41)));
Planes[1] = Plane.Normalize(new Plane((vp.M14 - vp.M11), (vp.M24 - vp.M21), (vp.M34 - vp.M31), (vp.M44 - vp.M41)));
Planes[2] = Plane.Normalize(new Plane((vp.M14 - vp.M12), (vp.M24 - vp.M22), (vp.M34 - vp.M32), (vp.M44 - vp.M42)));
Planes[3] = Plane.Normalize(new Plane((vp.M14 + vp.M12), (vp.M24 + vp.M22), (vp.M34 + vp.M32), (vp.M44 + vp.M42)));
Planes[4] = Plane.Normalize(new Plane((vp.M14 - vp.M13), (vp.M24 - vp.M23), (vp.M34 - vp.M33), (vp.M44 - vp.M43)));
Planes[5] = Plane.Normalize(new Plane((vp.M13), (vp.M23), (vp.M33), 0.0f));//(vp.M43));
}
//public bool ContainsSphere(ref Vector3 c, float cls, float r)
//{
// //cls = c length squared, for optimization
// if (cls < (r * r))
// {
// return true; //frustrum center is in the sphere
// }
// float nr = -r;
// for (int i = 0; i < 6; i++)
// {
// if (Plane.DotCoordinate(Planes[i], c) < nr)
// {
// return false;
// }
// }
// return true;
//}
public bool ContainsSphereNoClipNoOpt(ref Vector3 c, float r)
{
float nr = -r;
for (int i = 0; i < 5; i++)
{
if (Plane.DotCoordinate(Planes[i], c) < nr)
{
return false;
}
}
return true;
}
public bool ContainsAABBNoClip(ref Vector3 cen, ref Vector3 e)
{
var c = cen - Position;
for (int i = 0; i < 5; i++)
{
var pn = Planes[i].Normal;
var d = (c.X * pn.X) + (c.Y * pn.Y) + (c.Z * pn.Z); //Vector3.Dot(c, pn);//
var r = (e.X * (pn.X > 0 ? pn.X : -pn.X)) + (e.Y * (pn.Y > 0 ? pn.Y : -pn.Y)) + (e.Z * (pn.Z > 0 ? pn.Z : -pn.Z)); //Vector3.Dot(e, pn.Abs()); //
if ((d + r) < 0) return false;
}
return true;
}
public bool ContainsAABBNoClipNoOpt(ref Vector3 bmin, ref Vector3 bmax)
{
var c = (bmax + bmin) * 0.5f - Position;
var e = (bmax - bmin) * 0.5f;
for (int i = 0; i < 5; i++)
{
var pd = Planes[i].D;
var pn = Planes[i].Normal;
var d = (c.X * pn.X) + (c.Y * pn.Y) + (c.Z * pn.Z);
var r = (e.X * (pn.X > 0 ? pn.X : -pn.X)) + (e.Y * (pn.Y > 0 ? pn.Y : -pn.Y)) + (e.Z * (pn.Z > 0 ? pn.Z : -pn.Z));
if ((d + r) < -pd) return false;
//if ((d - r) < -pd) ; //intersecting
}
return true;
}
public bool ContainsAABBNoFrontClipNoOpt(ref Vector3 bmin, ref Vector3 bmax)
{
var c = (bmax + bmin) * 0.5f - Position;
var e = (bmax - bmin) * 0.5f;
for (int i = 0; i < 4; i++)
{
var pd = Planes[i].D;
var pn = Planes[i].Normal;
var d = (c.X * pn.X) + (c.Y * pn.Y) + (c.Z * pn.Z);
var r = (e.X * (pn.X > 0 ? pn.X : -pn.X)) + (e.Y * (pn.Y > 0 ? pn.Y : -pn.Y)) + (e.Z * (pn.Z > 0 ? pn.Z : -pn.Z));
if ((d + r) < -pd) return false;
//if ((d - r) < -pd) ; //intersecting
}
return true;
}
}
}

View File

@@ -0,0 +1,459 @@
using CodeWalker.GameFiles;
using SharpDX;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
namespace CodeWalker.World
{
public class Clouds
{
public volatile bool Inited = false;
public Weather Weather;
public Timecycle Timecycle;
public Dictionary<string, CloudAnimSetting> AnimSettings { get; set; }
public CloudAnimOverrides AnimOverrides = new CloudAnimOverrides();
public CloudHatManager HatManager;
public CloudSettingsMap SettingsMap;
public Clouds()
{
AnimSettings = new Dictionary<string, CloudAnimSetting>();
AddAnimSetting(new CloudAnimSetting("UVOffset1.X", "UV Offset 1 X", -1.0f, 1.0f, 0.0f));
AddAnimSetting(new CloudAnimSetting("UVOffset1.Y", "UV Offset 1 Y", -1.0f, 1.0f, 0.0f));
AddAnimSetting(new CloudAnimSetting("UVOffset2.X", "UV Offset 2 X", -1.0f, 1.0f, 0.0f));
AddAnimSetting(new CloudAnimSetting("UVOffset2.Y", "UV Offset 2 Y", -1.0f, 1.0f, 0.0f));
AddAnimSetting(new CloudAnimSetting("UVOffset3.X", "UV Offset 3 X", -1.0f, 1.0f, 0.0f));
AddAnimSetting(new CloudAnimSetting("UVOffset3.Y", "UV Offset 3 Y", -1.0f, 1.0f, 0.0f));
//AddAnimSetting(new CloudAnimSetting("RescaleUV1.X", "Rescale UV 1 X", 0.0f, 50.0f, 1.0f));
//AddAnimSetting(new CloudAnimSetting("RescaleUV1.Y", "Rescale UV 1 Y", 0.0f, 50.0f, 1.0f));
//AddAnimSetting(new CloudAnimSetting("RescaleUV2.X", "Rescale UV 2 X", 0.0f, 50.0f, 1.0f));
//AddAnimSetting(new CloudAnimSetting("RescaleUV2.Y", "Rescale UV 2 Y", 0.0f, 50.0f, 1.0f));
//AddAnimSetting(new CloudAnimSetting("RescaleUV3.X", "Rescale UV 3 X", 0.0f, 50.0f, 1.0f));
//AddAnimSetting(new CloudAnimSetting("RescaleUV3.Y", "Rescale UV 3 Y", 0.0f, 50.0f, 1.0f));
//AddAnimSetting(new CloudAnimSetting("cloudLayerAnimScale1.X", "Anim Scale 1 X", 0.0f, 8.0f, 1.0f));
//AddAnimSetting(new CloudAnimSetting("cloudLayerAnimScale1.Y", "Anim Scale 1 Y", 0.0f, 8.0f, 1.0f));
//AddAnimSetting(new CloudAnimSetting("cloudLayerAnimScale2.X", "Anim Scale 2 X", 0.0f, 8.0f, 1.0f));
//AddAnimSetting(new CloudAnimSetting("cloudLayerAnimScale2.Y", "Anim Scale 2 Y", 0.0f, 8.0f, 1.0f));
//AddAnimSetting(new CloudAnimSetting("cloudLayerAnimScale3.X", "Anim Scale 3 X", 0.0f, 8.0f, 1.0f));
//AddAnimSetting(new CloudAnimSetting("cloudLayerAnimScale3.Y", "Anim Scale 3 Y", 0.0f, 8.0f, 1.0f));
}
private void AddAnimSetting(CloudAnimSetting setting)
{
AnimSettings[setting.Name] = setting;
}
private void UpdateAnimOverrides()
{
AnimOverrides.UVOffset1.X = AnimSettings["UVOffset1.X"].CurrentValue;
AnimOverrides.UVOffset1.Y = AnimSettings["UVOffset1.Y"].CurrentValue;
AnimOverrides.UVOffset2.X = AnimSettings["UVOffset2.X"].CurrentValue;
AnimOverrides.UVOffset2.Y = AnimSettings["UVOffset2.Y"].CurrentValue;
AnimOverrides.UVOffset3.X = AnimSettings["UVOffset3.X"].CurrentValue;
AnimOverrides.UVOffset3.Y = AnimSettings["UVOffset3.Y"].CurrentValue;
//AnimOverrides.RescaleUV1.X = AnimSettings["RescaleUV1.X"].CurrentValue;
//AnimOverrides.RescaleUV1.Y = AnimSettings["RescaleUV1.Y"].CurrentValue;
//AnimOverrides.RescaleUV2.X = AnimSettings["RescaleUV2.X"].CurrentValue;
//AnimOverrides.RescaleUV2.Y = AnimSettings["RescaleUV2.Y"].CurrentValue;
//AnimOverrides.RescaleUV3.X = AnimSettings["RescaleUV3.X"].CurrentValue;
//AnimOverrides.RescaleUV3.Y = AnimSettings["RescaleUV3.Y"].CurrentValue;
//AnimOverrides.cloudLayerAnimScale1.X = AnimSettings["cloudLayerAnimScale1.X"].CurrentValue;
//AnimOverrides.cloudLayerAnimScale1.Y = AnimSettings["cloudLayerAnimScale1.Y"].CurrentValue;
//AnimOverrides.cloudLayerAnimScale2.X = AnimSettings["cloudLayerAnimScale2.X"].CurrentValue;
//AnimOverrides.cloudLayerAnimScale2.Y = AnimSettings["cloudLayerAnimScale2.Y"].CurrentValue;
//AnimOverrides.cloudLayerAnimScale3.X = AnimSettings["cloudLayerAnimScale3.X"].CurrentValue;
//AnimOverrides.cloudLayerAnimScale3.Y = AnimSettings["cloudLayerAnimScale3.Y"].CurrentValue;
}
public void Init(GameFileCache gameFileCache, Action<string> updateStatus, Weather weather)
{
Weather = weather;
Timecycle = weather.Timecycle;
var rpfman = gameFileCache.RpfMan;
string filename = "common.rpf\\data\\clouds.xml";
//TODO: RpfMan should be able to get the right version? or maybe let gameFileCache do it!
string kffilename = "common.rpf\\data\\cloudkeyframes.xml";
if (gameFileCache.EnableDlc)
{
kffilename = "update\\update.rpf\\common\\data\\cloudkeyframes.xml";
}
XmlDocument cloudsxml = rpfman.GetFileXml(filename);
XmlDocument cloudskfxml = rpfman.GetFileXml(kffilename);
HatManager = new CloudHatManager();
HatManager.Init(cloudsxml.DocumentElement); //CloudHatManager
SettingsMap = new CloudSettingsMap();
SettingsMap.Init(cloudskfxml.DocumentElement); //CloudSettingsMap
Inited = true;
}
public void Update(float elapsed)
{
UpdateAnimOverrides();
}
}
public class CloudHatManager
{
public CloudHatFrag[] CloudHatFrags { get; set; }
public float DesiredTransitionTimeSec { get; set; }
public Vector3 CamPositionScaler { get; set; }
public float AltitudeScrollScaler { get; set; }
public void Init(XmlElement xml)
{
List<CloudHatFrag> fraglist = new List<CloudHatFrag>();
XmlNodeList frags = xml.SelectNodes("mCloudHatFrags/Item");
foreach (XmlNode node in frags)
{
XmlElement fragel = node as XmlElement;
if (fragel != null)
{
CloudHatFrag frag = new CloudHatFrag();
frag.Init(fragel);
fraglist.Add(frag);
}
}
CloudHatFrags = fraglist.ToArray();
DesiredTransitionTimeSec = Xml.GetChildFloatAttribute(xml, "mDesiredTransitionTimeSec", "value");
CamPositionScaler = Xml.GetChildVector3Attributes(xml, "mCamPositionScaler");
AltitudeScrollScaler = Xml.GetChildFloatAttribute(xml, "mAltitudeScrollScaler", "value");
}
public CloudHatFrag FindFrag(string name)
{
for (int i = 0; i < CloudHatFrags.Length; i++)
{
CloudHatFrag f = CloudHatFrags[i];
if (f.Name == name)
{
return f;
}
}
return null;
}
}
public class CloudHatFrag
{
public Vector3 Position { get; set; }
public Vector3 Rotation { get; set; }
public Vector3 Scale { get; set; }
public string Name { get; set; }
public CloudHatFragLayer[] Layers { get; set; }
public float TransitionAlphaRange { get; set; }
public float TransitionMidPoint { get; set; }
public bool Enabled { get; set; }
public Vector3 AngularVelocity { get; set; }
public Vector3 AnimBlendWeights { get; set; }
public Vector2[] UVVelocity { get; set; }
public byte[] AnimMode { get; set; }
public bool[] ShowLayer { get; set; }
public bool EnableAnimations { get; set; }
public void Init(XmlElement xml)
{
Position = Xml.GetChildVector3Attributes(xml, "mPosition");
Rotation = Xml.GetChildVector3Attributes(xml, "mRotation");
Scale = Xml.GetChildVector3Attributes(xml, "mScale");
Name = Xml.GetChildInnerText(xml, "mName");
List<CloudHatFragLayer> layerlist = new List<CloudHatFragLayer>();
XmlNodeList layersxml = xml.SelectNodes("mLayers/Item");
foreach (XmlNode node in layersxml)
{
XmlElement layerel = node as XmlElement;
if (layerel != null)
{
CloudHatFragLayer layer = new CloudHatFragLayer();
layer.Init(layerel);
layerlist.Add(layer);
}
}
Layers = layerlist.ToArray();
TransitionAlphaRange = Xml.GetChildFloatAttribute(xml, "mTransitionAlphaRange", "value");
TransitionMidPoint = Xml.GetChildFloatAttribute(xml, "mTransitionMidPoint", "value");
Enabled = Xml.GetChildBoolAttribute(xml, "mEnabled", "value");
AngularVelocity = Xml.GetChildVector3Attributes(xml, "mAngularVelocity");
AnimBlendWeights = Xml.GetChildVector3Attributes(xml, "mAnimBlendWeights");
string uvvelocitystr = Xml.GetChildInnerText(xml, "mUVVelocity").Trim();
string[] uvvelocities = uvvelocitystr.Split('\n');
UVVelocity = new Vector2[uvvelocities.Length];
for (int i = 0; i < uvvelocities.Length; i++)
{
Vector2 vel = Vector2.Zero;
string uvvel = uvvelocities[i].Trim();
string[] uvvelc = uvvel.Split('\t');
if (uvvelc.Length == 2)
{
FloatUtil.TryParse(uvvelc[0].Trim(), out vel.X);
FloatUtil.TryParse(uvvelc[1].Trim(), out vel.Y);
}
UVVelocity[i] = vel;
}
string animmodestr = Xml.GetChildInnerText(xml, "mAnimMode").Trim();
string[] animmodes = animmodestr.Split('\n');
AnimMode = new byte[animmodes.Length];
for (int i = 0; i < animmodes.Length; i++)
{
byte.TryParse(animmodes[i].Trim(), out AnimMode[i]);
}
//string showlayerstr = Xml.GetChildInnerText(xml, "mShowLayer").Trim();
XmlNodeList showlayersxml = xml.SelectNodes("mShowLayer/Item");
ShowLayer = new bool[showlayersxml.Count];
for (int i = 0; i < showlayersxml.Count; i++)
{
XmlNode slnode = showlayersxml[i];
if (slnode is XmlElement)
{
ShowLayer[i] = Xml.GetBoolAttribute(slnode, "value");
}
}
EnableAnimations = Xml.GetChildBoolAttribute(xml, "mEnableAnimations", "value");
}
public override string ToString()
{
return Name;
}
}
public class CloudHatFragLayer
{
public string Filename { get; set; }
public float CostFactor { get; set; }
public float RotationScale { get; set; }
public float CamPositionScalerAdjust { get; set; }
public float TransitionInTimePercent { get; set; }
public float TransitionOutTimePercent { get; set; }
public float TransitionInDelayPercent { get; set; }
public float TransitionOutDelayPercent { get; set; }
public float HeightTigger { get; set; }
public float HeightFadeRange { get; set; }
public void Init(XmlElement xml)
{
Filename = Xml.GetChildInnerText(xml, "mFilename");
CostFactor = Xml.GetChildFloatAttribute(xml, "mCostFactor", "value");
RotationScale = Xml.GetChildFloatAttribute(xml, "mRotationScale", "value");
CamPositionScalerAdjust = Xml.GetChildFloatAttribute(xml, "mCamPositionScalerAdjust", "value");
TransitionInTimePercent = Xml.GetChildFloatAttribute(xml, "mTransitionInTimePercent", "value");
TransitionOutTimePercent = Xml.GetChildFloatAttribute(xml, "mTransitionOutTimePercent", "value");
TransitionInDelayPercent = Xml.GetChildFloatAttribute(xml, "mTransitionInDelayPercent", "value");
TransitionOutDelayPercent = Xml.GetChildFloatAttribute(xml, "mTransitionOutDelayPercent", "value");
HeightTigger = Xml.GetChildFloatAttribute(xml, "mHeightTigger", "value");
HeightFadeRange = Xml.GetChildFloatAttribute(xml, "mHeightFadeRange", "value");
}
public override string ToString()
{
return Filename;
}
}
public class CloudSettingsMap
{
public float[] KeyframeTimes { get; set; }
public Dictionary<string, CloudSettingsMapItem> SettingsMap { get; set; }
public void Init(XmlElement xml)
{
string kftstr = Xml.GetChildInnerText(xml, "KeyframeTimes").Trim();
string[] kftarr = kftstr.Split('\n');
KeyframeTimes = new float[kftarr.Length];
for (int i = 0; i < kftarr.Length; i++)
{
FloatUtil.TryParse(kftarr[i].Trim(), out KeyframeTimes[i]);
}
SettingsMap = new Dictionary<string, CloudSettingsMapItem>();
XmlNodeList mapxml = xml.SelectNodes("SettingsMap/Item");
foreach (XmlNode node in mapxml)
{
XmlElement itemel = node as XmlElement;
if (itemel != null)
{
CloudSettingsMapItem item = new CloudSettingsMapItem();
item.Init(itemel);
SettingsMap[item.Name] = item;
}
}
}
}
public class CloudSettingsMapItem
{
public string Name { get; set; }
public CloudSettingsMapCloudList CloudList { get; set; } = new CloudSettingsMapCloudList();
public CloudSettingsMapKeyData CloudColor { get; set; } = new CloudSettingsMapKeyData();
public CloudSettingsMapKeyData CloudLightColor { get; set; } = new CloudSettingsMapKeyData();
public CloudSettingsMapKeyData CloudAmbientColor { get; set; } = new CloudSettingsMapKeyData();
public CloudSettingsMapKeyData CloudSkyColor { get; set; } = new CloudSettingsMapKeyData();
public CloudSettingsMapKeyData CloudBounceColor { get; set; } = new CloudSettingsMapKeyData();
public CloudSettingsMapKeyData CloudEastColor { get; set; } = new CloudSettingsMapKeyData();
public CloudSettingsMapKeyData CloudWestColor { get; set; } = new CloudSettingsMapKeyData();
public CloudSettingsMapKeyData CloudScaleFillColors { get; set; } = new CloudSettingsMapKeyData();
public CloudSettingsMapKeyData CloudDensityShift_Scale_ScatteringConst_Scale { get; set; } = new CloudSettingsMapKeyData();
public CloudSettingsMapKeyData CloudPiercingLightPower_Strength_NormalStrength_Thickness { get; set; } = new CloudSettingsMapKeyData();
public CloudSettingsMapKeyData CloudScaleDiffuseFillAmbient_WrapAmount { get; set; } = new CloudSettingsMapKeyData();
public void Init(XmlNode xml)
{
Name = Xml.GetChildInnerText(xml, "Name");
var snode = xml.SelectSingleNode("Settings");
CloudList.Init(snode.SelectSingleNode("CloudList"));
CloudColor.Init(snode.SelectSingleNode("CloudColor"));
CloudLightColor.Init(snode.SelectSingleNode("CloudLightColor"));
CloudAmbientColor.Init(snode.SelectSingleNode("CloudAmbientColor"));
CloudSkyColor.Init(snode.SelectSingleNode("CloudSkyColor"));
CloudBounceColor.Init(snode.SelectSingleNode("CloudBounceColor"));
CloudEastColor.Init(snode.SelectSingleNode("CloudEastColor"));
CloudWestColor.Init(snode.SelectSingleNode("CloudWestColor"));
CloudScaleFillColors.Init(snode.SelectSingleNode("CloudScaleFillColors"));
CloudDensityShift_Scale_ScatteringConst_Scale.Init(snode.SelectSingleNode("CloudDensityShift_Scale_ScatteringConst_Scale"));
CloudPiercingLightPower_Strength_NormalStrength_Thickness.Init(snode.SelectSingleNode("CloudPiercingLightPower_Strength_NormalStrength_Thickness"));
CloudScaleDiffuseFillAmbient_WrapAmount.Init(snode.SelectSingleNode("CloudScaleDiffuseFillAmbient_WrapAmount"));
}
public override string ToString()
{
return Name;
}
}
public class CloudSettingsMapCloudList
{
public int[] Probability { get; set; }
public int[] Bits { get; set; } //one bit for each cloud hat frag
public void Init(XmlNode xml)
{
string pstr = Xml.GetChildInnerText(xml, "mProbability").Trim();
string bstr = Xml.GetChildInnerText(xml, "mBits").Trim();
string[] parr = pstr.Split('\n');
string[] barr = bstr.Split('\n');
Probability = new int[parr.Length];
Bits = new int[barr.Length];
for (int i = 0; i < parr.Length; i++)
{
int.TryParse(parr[i].Trim(), out Probability[i]);
}
for (int i = 0; i < barr.Length; i++)
{
Bits[i] = Convert.ToInt32(barr[i].Trim(), 16);
}
}
}
public class CloudSettingsMapKeyData
{
public int numKeyEntries { get; set; }
public Dictionary<float, Vector4> keyEntryData { get; set; }
public void Init(XmlNode xml)
{
var kdxml = xml.SelectSingleNode("keyData");
numKeyEntries = Xml.GetChildIntAttribute(kdxml, "numKeyEntries", "value");
string kestr = Xml.GetChildInnerText(kdxml, "keyEntryData").Trim();
string[] kearr = kestr.Split('\n');
keyEntryData = new Dictionary<float, Vector4>();
for (int i = 0; i < kearr.Length; i++)
{
string kvstr = kearr[i].Trim();
string[] kvarr = kvstr.Split('\t');
float key = 0.0f;
Vector4 val = Vector4.Zero;
if (kvarr.Length >= 5)
{
FloatUtil.TryParse(kvarr[0].Trim(), out key);
FloatUtil.TryParse(kvarr[1].Trim(), out val.X);
FloatUtil.TryParse(kvarr[2].Trim(), out val.Y);
FloatUtil.TryParse(kvarr[3].Trim(), out val.Z);
FloatUtil.TryParse(kvarr[4].Trim(), out val.W);
}
else
{ }
keyEntryData[key] = val;
}
}
}
public class CloudAnimSetting
{
public string Name { get; set; }
public string DisplayName { get; set; }
public float MinValue { get; set; }
public float MaxValue { get; set; }
public float DefaultValue { get; set; }
public float CurrentValue { get; set; }
public CloudAnimSetting(string name, string displayname, float minval, float maxval, float defaultval)
{
Name = name;
DisplayName = displayname;
MinValue = minval;
MaxValue = maxval;
DefaultValue = defaultval;
CurrentValue = defaultval;
}
public override string ToString()
{
return DisplayName;
}
}
public class CloudAnimOverrides
{
public Vector2 UVOffset1 = Vector2.Zero;
public Vector2 UVOffset2 = Vector2.Zero;
public Vector2 UVOffset3 = Vector2.Zero;
public Vector2 RescaleUV1 = Vector2.One;
public Vector2 RescaleUV2 = Vector2.One;
public Vector2 RescaleUV3 = Vector2.One;
public Vector2 cloudLayerAnimScale1 = Vector2.One;
public Vector2 cloudLayerAnimScale2 = Vector2.One;
public Vector2 cloudLayerAnimScale3 = Vector2.One;
}
}

View File

@@ -0,0 +1,198 @@
using CodeWalker.GameFiles;
using SharpDX;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CodeWalker.World
{
public class Entity
{
public Space Space;
public YmapEntityDef EntityDef;
public float Radius;
public Vector3 Center;
public Vector3 Position;
public Quaternion Orientation = Quaternion.Identity;
public Quaternion OrientationInv = Quaternion.Identity;
public float Mass;
public Matrix MomentOfInertia;
public Vector3 Momentum;
public Vector3 Velocity;
public Vector3 AngularMomentum;
public Vector3 AngularVelocity;
public bool WasColliding;
public bool EnableCollisions;
public bool Enabled;
public float Lifetime;
public float Age;
//public CollisionShape ..
public virtual void PreUpdate(float elapsed)
{ }
}
public class PedEntity : Entity
{
public Vector2 ControlMovement;
public bool ControlJump;
public bool ControlBoost;
public Vector3 ForwardVec;
public Quaternion CameraOrientation = Quaternion.LookAtLH(Vector3.Zero, Vector3.Up, Vector3.ForwardLH);
public Entity CameraEntity = new Entity();
public bool OnGround = false;
public PedEntity()
{
Radius = 0.5f;
Center = new Vector3(0.0f, 0.0f, -1.2f); //base collision point is 1.7m below center... for camera offset
Mass = 80.0f;
ForwardVec = Vector3.UnitY;
CameraEntity.Orientation = CameraOrientation;
CameraEntity.OrientationInv = Quaternion.Invert(Orientation);
}
public override void PreUpdate(float elapsed)
{
//float rotspd = 0.5f;
float movspd = 10.0f;
float velspd = 10.0f;
float jmpvel = 3.0f;
float boostmult = 10.0f;
if (ControlBoost) movspd *= boostmult;
Quaternion rot = Quaternion.Identity;// .RotationAxis(Vector3.UnitZ, -ControlMovement.X * rotspd * elapsed);
Quaternion ori = Quaternion.Multiply(Orientation, rot);
SetOrientation(ori);
float jmpamt = (ControlJump ? jmpvel : 0);
Vector3 curvel = Velocity;
Vector3 controlvel = new Vector3(ControlMovement * movspd, jmpamt);
Vector3 targetvel = controlvel + new Vector3(0, 0, curvel.Z);
Vector3 newvel = curvel + (targetvel - curvel) * velspd * elapsed;
Velocity = newvel;
var coll = Space.FindFirstCollision(this, elapsed);
if (coll.Hit)
{
Vector3 collpos = coll.PrePos; //last known ok position
Vector3 disp = Velocity * elapsed;
Vector3 oldpos = Position;
Vector3 targetpos = Position + disp;
float displ = disp.Length();
//////BoundingSphere sph = new BoundingSphere(targetpos + Center, Radius);
//////r.SphereHit = SphereIntersect(sph);
if ((disp.Z > -0.25f))/* && (displ < Radius * 2.0f)*/
{
Vector3 raydir = new Vector3(0.0f, 0.0f, -1.0f);
Vector3 rayoff = new Vector3(0.0f, 0.0f, 0.0f);
Ray ray = new Ray(targetpos + Center + rayoff, raydir);
var rayhit = Space.RayIntersect(ray, 1.0f);
if (rayhit.Hit)
{
if (rayhit.HitDist > 0)
{
Position = rayhit.Position - Center + new Vector3(0, 0, Radius);
//collpos = Position;//targetpos;//
}
else
{
//the start of the ray was a collision... can't move here
Position = collpos;
}
}
else //might happen when about to go off a big drop?
{
Position = targetpos;// collpos;
//collpos = targetpos;
}
}
else //moving fast...
{
Position = collpos; //last known ok position
}
//Position = collpos; //last known ok position
bool wasOnGround = OnGround;
OnGround = (Vector3.Dot(coll.SphereHit.Normal, Vector3.UnitZ) > 0.8f);
if (OnGround)
{
}
Vector3 findisp = Position - oldpos;
float findispl = findisp.Length();
float fdisp = Math.Min(displ, findispl);
Vector3 dispdir = findisp / Math.Max(findispl, 0.0001f);
float absvel = fdisp / Math.Max(elapsed, 0.0001f);
Velocity = dispdir * absvel;
//Vector3 veldir = Vector3.Normalize(Position - oldpos);
//float vellen = (collpos - oldpos).Length() / Math.Max(elapsed, 0.0001f);
//Velocity = veldir * vellen;
//Velocity = (Position - oldpos) / Math.Max(elapsed, 0.0001f);
}
else
{
Position = coll.HitPos; //hitpos is the end pos if not hit
OnGround = false;
var raydir = new Vector3(0.0f, 0.0f, -1.0f);
var ray = new Ray(Position, raydir);
var rayhit = Space.RayIntersect(ray, float.MaxValue);
if (!rayhit.Hit && rayhit.TestComplete)
{
//must be under the map? try to find the ground...
ray.Position = Position + new Vector3(0.0f, 0.0f, 1000.0f);
rayhit = Space.RayIntersect(ray, float.MaxValue);
if (rayhit.Hit)
{
Position = rayhit.Position + new Vector3(0.0f, 0.0f, Radius) - Center;
OnGround = true;
}
else
{ }//didn't find the ground, what to do now?
}
}
CameraEntity.Position = Position;
}
private void SetOrientation(Quaternion ori)
{
Orientation = ori;
OrientationInv = Quaternion.Invert(Orientation);
CameraEntity.Orientation = Quaternion.Multiply(Orientation, CameraOrientation);
CameraEntity.OrientationInv = Quaternion.Invert(Orientation);
}
}
}

View File

@@ -0,0 +1,170 @@
using CodeWalker.GameFiles;
using SharpDX;
using System;
using System.Collections.Generic;
using System.Text;
namespace CodeWalker.World
{
public class Heightmaps : BasePathData
{
public volatile bool Inited = false;
public GameFileCache GameFileCache;
public List<HeightmapFile> HeightmapFiles = new List<HeightmapFile>();
public Vector4[] GetNodePositions()
{
return NodePositions;
}
public EditorVertex[] GetPathVertices()
{
return null;
}
public EditorVertex[] GetTriangleVertices()
{
return TriangleVerts;
}
public Vector4[] NodePositions;
public EditorVertex[] TriangleVerts;
public void Init(GameFileCache gameFileCache, Action<string> updateStatus)
{
Inited = false;
GameFileCache = gameFileCache;
HeightmapFiles.Clear();
if (gameFileCache.EnableDlc)
{
LoadHeightmap("update\\update.rpf\\common\\data\\levels\\gta5\\heightmap.dat");
LoadHeightmap("update\\update.rpf\\common\\data\\levels\\gta5\\heightmapheistisland.dat");
}
else
{
LoadHeightmap("common.rpf\\data\\levels\\gta5\\heightmap.dat");
}
BuildVertices();
Inited = true;
}
private void LoadHeightmap(string filename)
{
var hmf = GameFileCache.RpfMan.GetFile<HeightmapFile>(filename);
HeightmapFiles.Add(hmf);
}
public void BuildVertices()
{
var vlist = new List<EditorVertex>();
var nlist = new List<Vector4>();
foreach (var hmf in HeightmapFiles)
{
BuildHeightmapVertices(hmf, vlist, nlist);
}
if (vlist.Count > 0)
{
TriangleVerts = vlist.ToArray();
}
else
{
TriangleVerts = null;
}
if (nlist.Count > 0)
{
NodePositions = nlist.ToArray();
}
else
{
NodePositions = null;
}
}
private void BuildHeightmapVertices(HeightmapFile hmf, List<EditorVertex> vl, List<Vector4> nl)
{
var v1 = new EditorVertex();
var v2 = new EditorVertex();
var v3 = new EditorVertex();
var v4 = new EditorVertex();
uint cgrn = (uint)new Color(0, 128, 0, 60).ToRgba();
uint cyel = (uint)new Color(128, 128, 0, 200).ToRgba();
var w = hmf.Width;
var h = hmf.Height;
var hmin = hmf.MinHeights;
var hmax = hmf.MaxHeights;
var min = hmf.BBMin;
var max = hmf.BBMax;
var siz = max - min;
var step = siz / new Vector3(w - 1, h - 1, 255);
v1.Colour = v2.Colour = v3.Colour = v4.Colour = cyel;
for (int yi = 1; yi < h; yi++)
{
var yo = yi - 1;
for (int xi = 1; xi < w; xi++)
{
var xo = xi - 1;
var o1 = yo * w + xo;
var o2 = yo * w + xi;
var o3 = yi * w + xo;
var o4 = yi * w + xi;
v1.Position = min + step * new Vector3(xo, yo, hmin[o1]);
v2.Position = min + step * new Vector3(xi, yo, hmin[o2]);
v3.Position = min + step * new Vector3(xo, yi, hmin[o3]);
v4.Position = min + step * new Vector3(xi, yi, hmin[o4]);
vl.Add(v1); vl.Add(v2); vl.Add(v3);
vl.Add(v3); vl.Add(v2); vl.Add(v4);
}
}
v1.Colour = v2.Colour = v3.Colour = v4.Colour = cgrn;
for (int yi = 1; yi < h; yi++)
{
var yo = yi - 1;
for (int xi = 1; xi < w; xi++)
{
var xo = xi - 1;
var o1 = yo * w + xo;
var o2 = yo * w + xi;
var o3 = yi * w + xo;
var o4 = yi * w + xi;
v1.Position = min + step * new Vector3(xo, yo, hmax[o1]);
v2.Position = min + step * new Vector3(xi, yo, hmax[o2]);
v3.Position = min + step * new Vector3(xo, yi, hmax[o3]);
v4.Position = min + step * new Vector3(xi, yi, hmax[o4]);
vl.Add(v1); vl.Add(v2); vl.Add(v3);
vl.Add(v3); vl.Add(v2); vl.Add(v4);
}
}
for (int y = 0; y < h; y++)
{
for (int x = 0; x < w; x++)
{
var o = y * w + x;
nl.Add(new Vector4(min + step * new Vector3(x, y, hmin[o]), 10));
nl.Add(new Vector4(min + step * new Vector3(x, y, hmax[o]), 10));
}
}
}
}
}

View File

@@ -0,0 +1,306 @@
using CodeWalker.GameFiles;
using SharpDX;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace CodeWalker.World
{
[TypeConverter(typeof(ExpandableObjectConverter))] public class Ped
{
public string Name { get; set; } = string.Empty;
public MetaHash NameHash { get; set; } = 0;//ped name hash
public CPedModelInfo__InitData InitData { get; set; } = null; //ped init data
public YddFile Ydd { get; set; } = null; //ped drawables
public YtdFile Ytd { get; set; } = null; //ped textures
public YldFile Yld { get; set; } = null; //ped clothes
public YcdFile Ycd { get; set; } = null; //ped animations
public YedFile Yed { get; set; } = null; //ped expressions
public YftFile Yft { get; set; } = null; //ped skeleton YFT
public PedFile Ymt { get; set; } = null; //ped variation info
public Dictionary<MetaHash, RpfFileEntry> DrawableFilesDict { get; set; } = null;
public Dictionary<MetaHash, RpfFileEntry> TextureFilesDict { get; set; } = null;
public Dictionary<MetaHash, RpfFileEntry> ClothFilesDict { get; set; } = null;
public RpfFileEntry[] DrawableFiles { get; set; } = null;
public RpfFileEntry[] TextureFiles { get; set; } = null;
public RpfFileEntry[] ClothFiles { get; set; } = null;
public ClipMapEntry AnimClip { get; set; } = null;
public Expression Expression { get; set; } = null;
public string[] DrawableNames { get; set; } = new string[12];
public Drawable[] Drawables { get; set; } = new Drawable[12];
public Texture[] Textures { get; set; } = new Texture[12];
public Expression[] Expressions { get; set; } = new Expression[12];
public ClothInstance[] Clothes { get; set; } = new ClothInstance[12];
public bool EnableRootMotion { get; set; } = false; //used to toggle whether or not to include root motion when playing animations
public Skeleton Skeleton { get; set; } = null;
public Vector3 Position { get; set; } = Vector3.Zero;
public Quaternion Rotation { get; set; } = Quaternion.Identity;
public YmapEntityDef RenderEntity = new YmapEntityDef(); //placeholder entity object for rendering
public void Init(string name, GameFileCache gfc)
{
var hash = JenkHash.GenHash(name.ToLowerInvariant());
Init(hash, gfc);
Name = name;
}
public void Init(MetaHash pedhash, GameFileCache gfc)
{
Name = string.Empty;
NameHash = 0;
InitData = null;
Ydd = null;
Ytd = null;
Yld = null;
Ycd = null;
Yed = null;
Yft = null;
Ymt = null;
AnimClip = null;
for (int i = 0; i < 12; i++)
{
Drawables[i] = null;
Textures[i] = null;
Expressions[i] = null;
}
CPedModelInfo__InitData initdata = null;
if (!gfc.PedsInitDict.TryGetValue(pedhash, out initdata)) return;
var ycdhash = JenkHash.GenHash(initdata.ClipDictionaryName.ToLowerInvariant());
var yedhash = JenkHash.GenHash(initdata.ExpressionDictionaryName.ToLowerInvariant());
//bool pedchange = NameHash != pedhash;
//Name = pedname;
NameHash = pedhash;
InitData = initdata;
Ydd = gfc.GetYdd(pedhash);
Ytd = gfc.GetYtd(pedhash);
Ycd = gfc.GetYcd(ycdhash);
Yed = gfc.GetYed(yedhash);
Yft = gfc.GetYft(pedhash);
PedFile pedFile = null;
gfc.PedVariationsDict?.TryGetValue(pedhash, out pedFile);
Ymt = pedFile;
Dictionary<MetaHash, RpfFileEntry> peddict = null;
gfc.PedDrawableDicts.TryGetValue(NameHash, out peddict);
DrawableFilesDict = peddict;
DrawableFiles = DrawableFilesDict?.Values.ToArray();
gfc.PedTextureDicts.TryGetValue(NameHash, out peddict);
TextureFilesDict = peddict;
TextureFiles = TextureFilesDict?.Values.ToArray();
gfc.PedClothDicts.TryGetValue(NameHash, out peddict);
ClothFilesDict = peddict;
ClothFiles = ClothFilesDict?.Values.ToArray();
RpfFileEntry clothFile = null;
if (ClothFilesDict?.TryGetValue(pedhash, out clothFile) ?? false)
{
Yld = gfc.GetFileUncached<YldFile>(clothFile);
while ((Yld != null) && (!Yld.Loaded))
{
Thread.Sleep(1);//kinda hacky
gfc.TryLoadEnqueue(Yld);
}
}
while ((Ydd != null) && (!Ydd.Loaded))
{
Thread.Sleep(1);//kinda hacky
Ydd = gfc.GetYdd(pedhash);
}
while ((Ytd != null) && (!Ytd.Loaded))
{
Thread.Sleep(1);//kinda hacky
Ytd = gfc.GetYtd(pedhash);
}
while ((Ycd != null) && (!Ycd.Loaded))
{
Thread.Sleep(1);//kinda hacky
Ycd = gfc.GetYcd(ycdhash);
}
while ((Yed != null) && (!Yed.Loaded))
{
Thread.Sleep(1);//kinda hacky
Yed = gfc.GetYed(yedhash);
}
while ((Yft != null) && (!Yft.Loaded))
{
Thread.Sleep(1);//kinda hacky
Yft = gfc.GetYft(pedhash);
}
Skeleton = Yft?.Fragment?.Drawable?.Skeleton?.Clone();
MetaHash cliphash = JenkHash.GenHash("idle");
ClipMapEntry cme = null;
Ycd?.ClipMap?.TryGetValue(cliphash, out cme);
AnimClip = cme;
var exprhash = JenkHash.GenHash(initdata.ExpressionName.ToLowerInvariant());
Expression expr = null;
Yed?.ExprMap?.TryGetValue(exprhash, out expr);
Expression = expr;
UpdateEntity();
}
public void SetComponentDrawable(int index, string name, string tex, GameFileCache gfc)
{
if (string.IsNullOrEmpty(name))
{
DrawableNames[index] = null;
Drawables[index] = null;
Textures[index] = null;
Expressions[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<YddFile>(file);
while ((ydd != null) && (!ydd.Loaded))
{
Thread.Sleep(1);//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<YtdFile>(file);
while ((ytd != null) && (!ytd.Loaded))
{
Thread.Sleep(1);//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
}
}
}
CharacterCloth cc = null;
if (Yld?.Dict != null)
{
Yld.Dict.TryGetValue(namehash, out cc);
}
if ((cc == null) && (ClothFilesDict != null))
{
RpfFileEntry file = null;
if (ClothFilesDict.TryGetValue(namehash, out file))
{
var yld = gfc.GetFileUncached<YldFile>(file);
while ((yld != null) && (!yld.Loaded))
{
Thread.Sleep(1);//kinda hacky
gfc.TryLoadEnqueue(yld);
}
if (yld?.ClothDictionary?.Clothes?.data_items?.Length > 0)
{
cc = yld.ClothDictionary.Clothes.data_items[0];//should only be one in this dict
}
}
}
ClothInstance c = null;
if (cc != null)
{
c = new ClothInstance();
c.Init(cc, Skeleton);
}
Expression e = null;
if (Yed?.ExprMap != null)
{
Yed.ExprMap.TryGetValue(namehash, out e);
}
if (d != null) Drawables[index] = d.ShallowCopy() as Drawable;
if (t != null) Textures[index] = t;
if (c != null) Clothes[index] = c;
if (e != null) Expressions[index] = e;
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);
}
}
public void UpdateEntity()
{
RenderEntity.SetPosition(Position);
RenderEntity.SetOrientation(Rotation);
}
}
}

View File

@@ -0,0 +1,206 @@
using CodeWalker.GameFiles;
using SharpDX;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CodeWalker.World
{
public class PopZones : BasePathData
{
public volatile bool Inited = false;
public GameFileCache GameFileCache;
public Dictionary<string, PopZone> Groups = new Dictionary<string, PopZone>();
public Vector4[] GetNodePositions()
{
return null;
}
public EditorVertex[] GetPathVertices()
{
return null;
}
public EditorVertex[] GetTriangleVertices()
{
return TriangleVerts;
}
public EditorVertex[] TriangleVerts;
public void Init(GameFileCache gameFileCache, Action<string> updateStatus)
{
Inited = false;
GameFileCache = gameFileCache;
var rpfman = gameFileCache.RpfMan;
string filename = "common.rpf\\data\\levels\\gta5\\popzone.ipl";
if (gameFileCache.EnableDlc)
{
filename = "update\\update.rpf\\common\\data\\levels\\gta5\\popzone.ipl";
}
string ipltext = rpfman.GetFileUTF8Text(filename);
if (string.IsNullOrEmpty(ipltext))
{
ipltext = "";
}
Groups.Clear();
var ipllines = ipltext.Split('\n');
bool inzone = false;
foreach (var iplline in ipllines)
{
var linet = iplline.Trim();
if (linet == "zone")
{
inzone = true;
}
else if (linet == "end")
{
inzone = false;
}
else if (inzone)
{
PopZoneBox box = new PopZoneBox();
box.Init(linet);
PopZone group;
if (!Groups.TryGetValue(box.NameLabel, out group))
{
group = new PopZone();
group.NameLabel = box.NameLabel;
Groups[box.NameLabel] = group;
}
group.Boxes.Add(box);
}
}
foreach (var group in Groups.Values)
{
var hash = JenkHash.GenHash(group.NameLabel.ToLowerInvariant());
group.Name = GlobalText.TryGetString(hash);
}
BuildVertices();
Inited = true;
}
public void BuildVertices()
{
var vlist = new List<EditorVertex>();
var v1 = new EditorVertex();
var v2 = new EditorVertex();
var v3 = new EditorVertex();
var v4 = new EditorVertex();
foreach (var group in Groups.Values)
{
var hash = JenkHash.GenHash(group.NameLabel.ToLowerInvariant());
byte cr = (byte)((hash >> 8) & 0xFF);
byte cg = (byte)((hash >> 16) & 0xFF);
byte cb = (byte)((hash >> 24) & 0xFF);
byte ca = 60;
uint cv = (uint)new Color(cr, cg, cb, ca).ToRgba();
v1.Colour = cv;
v2.Colour = cv;
v3.Colour = cv;
v4.Colour = cv;
foreach (var box in group.Boxes)
{
var min = box.Box.Minimum;
var max = box.Box.Maximum;
v1.Position = new Vector3(min.X, min.Y, 0);
v2.Position = new Vector3(max.X, min.Y, 0);
v3.Position = new Vector3(min.X, max.Y, 0);
v4.Position = new Vector3(max.X, max.Y, 0);
vlist.Add(v1);
vlist.Add(v2);
vlist.Add(v3);
vlist.Add(v3);
vlist.Add(v2);
vlist.Add(v4);
}
}
if (vlist.Count > 0)
{
TriangleVerts = vlist.ToArray();
}
else
{
TriangleVerts = null;
}
}
}
public class PopZone
{
public string NameLabel { get; set; }
public string Name { get; set; } //lookup from gxt2 with label..?
public List<PopZoneBox> Boxes { get; set; } = new List<PopZoneBox>();
public override string ToString()
{
return NameLabel + ": " + Name;
}
}
public class PopZoneBox
{
public string ID { get; set; }
public BoundingBox Box { get; set; }
public string NameLabel { get; set; }
public float UnkVal { get; set; }
public void Init(string iplline)
{
var parts = iplline.Split(',');
if (parts.Length >= 9)
{
ID = parts[0].Trim();
BoundingBox b = new BoundingBox();
b.Minimum.X = FloatUtil.Parse(parts[1].Trim());
b.Minimum.Y = FloatUtil.Parse(parts[2].Trim());
b.Minimum.Z = FloatUtil.Parse(parts[3].Trim());
b.Maximum.X = FloatUtil.Parse(parts[4].Trim());
b.Maximum.Y = FloatUtil.Parse(parts[5].Trim());
b.Maximum.Z = FloatUtil.Parse(parts[6].Trim());
Box = b;
NameLabel = parts[7].Trim();
UnkVal = FloatUtil.Parse(parts[8].Trim());
}
}
public override string ToString()
{
return ID + ": " + NameLabel + ": " + Box.ToString();
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,124 @@
using CodeWalker.GameFiles;
using SharpDX;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
namespace CodeWalker.World
{
public class Timecycle
{
public volatile bool Inited = false;
public float sun_roll { get; set; }
public float sun_yaw { get; set; }
public float moon_roll { get; set; }
public float moon_wobble_freq { get; set; }
public float moon_wobble_amp { get; set; }
public float moon_wobble_offset { get; set; }
public List<TimecycleSample> Samples { get; set; } = new List<TimecycleSample>();
public List<string> Regions { get; set; } = new List<string>();
public float CurrentHour { get; set; } = 0;
public int CurrentSampleIndex { get; set; } = 0;
public float CurrentSampleBlend { get; set; } = 1.0f;
public float NextSampleBlend { get; set; } = 0.0f;
public Vector3 CurrentSunDirection { get; set; } = new Vector3(0, 0, 1);
public Vector3 CurrentMoonDirection { get; set; } = new Vector3(0, 0, -1);
public void Init(GameFileCache gameFileCache, Action<string> updateStatus)
{
var rpfman = gameFileCache.RpfMan;
string filename = "common.rpf\\data\\levels\\gta5\\time.xml";
XmlDocument timexml = rpfman.GetFileXml(filename);
XmlElement time = timexml.DocumentElement;
XmlNode suninfo = time.SelectSingleNode("suninfo");
XmlNode mooninfo = time.SelectSingleNode("mooninfo");
XmlNodeList samples = time.SelectNodes("sample");
XmlNodeList regions = time.SelectNodes("region");
sun_roll = Xml.GetFloatAttribute(suninfo, "sun_roll");
sun_yaw = Xml.GetFloatAttribute(suninfo, "sun_yaw");
moon_roll = Xml.GetFloatAttribute(mooninfo, "moon_roll");
moon_wobble_freq = Xml.GetFloatAttribute(mooninfo, "moon_wobble_freq");
moon_wobble_amp = Xml.GetFloatAttribute(mooninfo, "moon_wobble_amp");
moon_wobble_offset = Xml.GetFloatAttribute(mooninfo, "moon_wobble_offset");
Samples.Clear();
for (int i = 0; i < samples.Count; i++)
{
TimecycleSample tcs = new TimecycleSample();
tcs.Init(samples[i]);
Samples.Add(tcs);
}
Regions.Clear();
for (int i = 0; i < regions.Count; i++)
{
Regions.Add(Xml.GetStringAttribute(regions[i], "name"));
}
Inited = true;
}
public void SetTime(float hour)
{
float day = Math.Max(hour / 24.0f, 0.0f);
float h = hour - ((float)Math.Floor(day) * 24.0f);
CurrentHour = h;
for (int i = 0; i < Samples.Count; i++)
{
bool lasti = (i >= Samples.Count - 1);
var cur = Samples[i];
var nxt = Samples[lasti ? 0 : i+1];
var nxth = lasti ? nxt.hour + 24.0f : nxt.hour;
if (((h >= cur.hour) && (h < nxth)) || lasti)
{
float blendrange = (nxth - cur.hour) - cur.duration;
float blendstart = cur.hour + cur.duration;
float blendrel = h - blendstart;
float blendval = blendrel / blendrange;
float blend = Math.Min(Math.Max(blendval, 0.0f), 1.0f);
NextSampleBlend = blend;
CurrentSampleBlend = 1.0f - blend;
CurrentSampleIndex = i;
break;
}
}
}
public bool IsNightTime
{
get
{
return (CurrentHour < 6.0f) || (CurrentHour > 20.0f);
}
}
}
public class TimecycleSample
{
public string name { get; set; }
public float hour { get; set; }
public float duration { get; set; }
public string uw_tc_mod { get; set; }
public void Init(XmlNode node)
{
name = Xml.GetStringAttribute(node, "name");
hour = Xml.GetFloatAttribute(node, "hour");
duration = Xml.GetFloatAttribute(node, "duration");
uw_tc_mod = Xml.GetStringAttribute(node, "uw_tc_mod");
}
}
}

View File

@@ -0,0 +1,148 @@
using CodeWalker.GameFiles;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
namespace CodeWalker.World
{
public class TimecycleMods
{
public Dictionary<uint, TimecycleMod> Dict = new Dictionary<uint, TimecycleMod>();
public void Init(GameFileCache gameFileCache, Action<string> updateStatus)
{
Dict.Clear();
var rpfman = gameFileCache.RpfMan;
LoadXml(rpfman.GetFileXml("common.rpf\\data\\timecycle\\timecycle_mods_1.xml"));
LoadXml(rpfman.GetFileXml("common.rpf\\data\\timecycle\\timecycle_mods_2.xml"));
LoadXml(rpfman.GetFileXml("common.rpf\\data\\timecycle\\timecycle_mods_3.xml"));
LoadXml(rpfman.GetFileXml("common.rpf\\data\\timecycle\\timecycle_mods_4.xml"));
LoadXml(rpfman.GetFileXml("update\\update.rpf\\common\\data\\timecycle\\timecycle_mods_1.xml"));
LoadXml(rpfman.GetFileXml("update\\update.rpf\\common\\data\\timecycle\\timecycle_mods_2.xml"));//doesn't exist, but try anyway
LoadXml(rpfman.GetFileXml("update\\update.rpf\\common\\data\\timecycle\\timecycle_mods_3.xml"));
LoadXml(rpfman.GetFileXml("update\\update.rpf\\common\\data\\timecycle\\timecycle_mods_4.xml"));
if (gameFileCache.EnableDlc)
{
foreach (var dlcrpf in gameFileCache.DlcActiveRpfs)
{
foreach (var file in dlcrpf.AllEntries)
{
if (file.NameLower.EndsWith(".xml") && file.NameLower.StartsWith("timecycle_mods_"))
{
LoadXml(rpfman.GetFileXml(file.Path));
}
}
}
}
gameFileCache.TimeCycleModsDict = Dict;
}
private void LoadXml(XmlDocument doc)
{
var root = doc.DocumentElement;
if (root == null)
{ return; }
float version = Xml.GetFloatAttribute(root, "version");
var modnodes = root.SelectNodes("modifier");
foreach (XmlNode modnode in modnodes)
{
if (!(modnode is XmlElement)) continue;
TimecycleMod mod = new TimecycleMod();
mod.Init(modnode);
Dict[mod.nameHash] = mod;
}
}
}
[TypeConverter(typeof(ExpandableObjectConverter))]
public class TimecycleMod
{
public string name { get; set; }
public uint nameHash { get; set; }
public int numMods { get; set; }
public int userFlags { get; set; }
public TimecycleModValue[] Values { get; set; }
public Dictionary<string, TimecycleModValue> Dict { get; set; }
public void Init(XmlNode node)
{
Dict = new Dictionary<string, TimecycleModValue>();
name = Xml.GetStringAttribute(node, "name");
numMods = Xml.GetIntAttribute(node, "numMods");
userFlags = Xml.GetIntAttribute(node, "userFlags");
string namel = name.ToLowerInvariant();
JenkIndex.Ensure(namel);
nameHash = JenkHash.GenHash(namel);
List<TimecycleModValue> vals = new List<TimecycleModValue>();
foreach (XmlNode valnode in node.ChildNodes)
{
if (!(valnode is XmlElement)) continue;
TimecycleModValue val = new TimecycleModValue();
val.Init(valnode);
vals.Add(val);
Dict[val.name] = val;
}
Values = vals.ToArray();
}
public override string ToString()
{
return name + " (" + numMods.ToString() + " mods, userFlags: " + userFlags.ToString() + ")";
}
}
[TypeConverter(typeof(ExpandableObjectConverter))]
public class TimecycleModValue
{
public string name { get; set; }
public float value1 { get; set; }
public float value2 { get; set; }
public void Init(XmlNode node)
{
name = node.Name;
string valstr = node.InnerText;
string[] valstrs = valstr.Split(' ');
if (valstrs.Length == 2)
{
value1 = FloatUtil.Parse(valstrs[0]);
value2 = FloatUtil.Parse(valstrs[1]);
}
else
{ }
}
public override string ToString()
{
return name + ": " + FloatUtil.ToString(value1) + ", " + FloatUtil.ToString(value2);
}
}
}

View File

@@ -0,0 +1,436 @@
using CodeWalker.GameFiles;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using SharpDX;
namespace CodeWalker.World
{
public class Trains
{
public volatile bool Inited = false;
public GameFileCache GameFileCache;
public List<TrainTrack> TrainTracks { get; set; } = new List<TrainTrack>();
public void Init(GameFileCache gameFileCache, Action<string> updateStatus)
{
GameFileCache = gameFileCache;
var rpfman = gameFileCache.RpfMan;
string trainsfilename = "common.rpf\\data\\levels\\gta5\\trains.xml";
XmlDocument trainsxml = rpfman.GetFileXml(trainsfilename);
XmlElement trainsdata = trainsxml.DocumentElement;
//TODO: parse train_configs
string tracksfilename = "common.rpf\\data\\levels\\gta5\\traintracks.xml";
XmlDocument tracksxml = rpfman.GetFileXml(tracksfilename);
XmlElement tracksdata = tracksxml.DocumentElement;
XmlNodeList tracks = tracksdata.SelectNodes("train_track");
TrainTracks.Clear();
for (int i = 0; i < tracks.Count; i++)
{
var trackxml = tracks[i];
TrainTrack tt = new TrainTrack();
tt.Load(gameFileCache, trackxml);
TrainTracks.Add(tt);
}
Inited = true;
}
}
public class TrainTrack : BasePathData
{
public string filename { get; set; }
public string trainConfigName { get; set; }
public bool isPingPongTrack { get; set; }
public bool stopsAtStations { get; set; }
public bool MPstopsAtStations { get; set; }
public float speed { get; set; }
public float brakingDist { get; set; }
public List<TrainTrackNode> Nodes { get; set; }
public int NodeCount { get; set; }
public int StationCount
{
get
{
int sc = 0;
if (Nodes != null)
{
foreach (var node in Nodes)
{
if ((node.NodeType == 1) || (node.NodeType == 2) || (node.NodeType == 5))
{
sc++;
}
}
}
return sc;
}
}
public EditorVertex[] LinkedVerts { get; set; }
public Vector4[] NodePositions { get; set; }
public EditorVertex[] GetPathVertices()
{
return LinkedVerts;
}
public EditorVertex[] GetTriangleVertices()
{
return null;
}
public Vector4[] GetNodePositions()
{
return NodePositions;
}
public PathBVH BVH { get; set; }
public string NodesString { get; set; }
public RpfFileEntry RpfFileEntry { get; set; }
public string Name { get; set; }
public string FilePath { get; set; }
public bool HasChanged { get; set; }
public bool Loaded { get; set; }
public void Load(GameFileCache gameFileCache, XmlNode node)
{
//load from game file cache
filename = Xml.GetStringAttribute(node, "filename");
trainConfigName = Xml.GetStringAttribute(node, "trainConfigName");
isPingPongTrack = Xml.GetBoolAttribute(node, "isPingPongTrack");
stopsAtStations = Xml.GetBoolAttribute(node, "stopsAtStations");
MPstopsAtStations = Xml.GetBoolAttribute(node, "MPstopsAtStations");
speed = Xml.GetFloatAttribute(node, "speed");
brakingDist = Xml.GetFloatAttribute(node, "brakingDist");
RpfFileEntry = gameFileCache.RpfMan.GetEntry(filename) as RpfFileEntry;
NodesString = gameFileCache.RpfMan.GetFileUTF8Text(filename);
SetNameFromFilename();
FilePath = Name;
Load(NodesString);
BuildVertices();
BuildBVH();
Loaded = true;
}
public void Load(byte[] data)
{
filename = string.Empty;
trainConfigName = string.Empty;
RpfFileEntry = new RpfBinaryFileEntry();
string str = Encoding.UTF8.GetString(data);
Load(str);
BuildVertices();
BuildBVH();
Loaded = true;
}
public byte[] Save()
{
NodeCount = Nodes.Count;
StringBuilder sb = new StringBuilder();
sb.AppendLine(Nodes.Count.ToString());
foreach (var node in Nodes)
{
var nstr = FloatUtil.GetVector3String(node.Position).Replace(",","") + " " + node.NodeType.ToString();
sb.AppendLine(nstr);
}
string str = sb.ToString();
return Encoding.UTF8.GetBytes(str);
}
public void SetNameFromFilename()
{
string[] fparts = filename.Replace('\\', '/').Split('/');
if ((fparts == null) || (fparts.Length == 0))
{
Name = filename;
}
else
{
Name = fparts[fparts.Length - 1];
}
}
public void Load(string trackstr)
{
//load nodes from a text string...
NodesString = trackstr;
if (!string.IsNullOrEmpty(trackstr))
{
string[] trackstrs = trackstr.Split(new[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
if (trackstrs.Length > 1)
{
int nodecount;
int.TryParse(trackstrs[0], out nodecount);
NodeCount = nodecount;
List<TrainTrackNode> nodes = new List<TrainTrackNode>();
for (int i = 1; i < trackstrs.Length; i++)
{
var nodestr = trackstrs[i].Trim();
var nodevals = nodestr.Split(' ');
if (nodevals.Length == 4)
{
TrainTrackNode ttnode = new TrainTrackNode();
var x = FloatUtil.Parse(nodevals[0]);
var y = FloatUtil.Parse(nodevals[1]);
var z = FloatUtil.Parse(nodevals[2]);
int nodetype;
int.TryParse(nodevals[3], out nodetype);
ttnode.Position = new Vector3(x, y, z);
ttnode.NodeType = nodetype;
ttnode.Track = this;
ttnode.Index = nodes.Count;
ttnode.Links[0] = (nodes.Count > 0) ? nodes[nodes.Count - 1] : null;
if (ttnode.Links[0] != null)
{
ttnode.Links[0].Links[1] = ttnode;
}
nodes.Add(ttnode);
}
else
{ }
}
Nodes = nodes;
}
else
{ }
}
else
{ }
if (Nodes == null)
{
Nodes = new List<TrainTrackNode>();
}
}
public void BuildVertices()
{
if ((Nodes != null) && (Nodes.Count > 0))
{
var nc = Nodes.Count;
var lc = nc - 1;
var lvc = lc * 2;
var np = new Vector4[nc];
var lv = new EditorVertex[lvc];
for (int i = 0; i < nc; i++)
{
np[i] = new Vector4(Nodes[i].Position, 1.0f);
if (i > 0)
{
var l = i - 1;
var li = l * 2;
var ni = li + 1;
lv[li].Position = Nodes[l].Position;
lv[ni].Position = Nodes[i].Position;
lv[li].Colour = (uint)Nodes[l].GetColour();
lv[ni].Colour = (uint)Nodes[i].GetColour();
}
}
NodePositions = np;
LinkedVerts = lv;
}
}
public void UpdateBvhForNode(TrainTrackNode node)
{
//this needs to be called when a node's position changes...
//need to recalc the BVH for mouse intersection optimisation purposes.
//if (BVH == null) return;
//BVH.UpdateForNode(node);
BuildBVH();
//also updates the NodePositions for the visible vertex
if (Nodes != null)
{
for (int i = 0; i < Nodes.Count; i++)
{
if (Nodes[i] == node)
{
NodePositions[i] = new Vector4(node.Position, 1.0f);
break;
}
}
}
}
public void BuildBVH()
{
BVH = new PathBVH(Nodes, 10, 10);
}
public TrainTrackNode AddNode(TrainTrackNode afternode = null)
{
int cnt = Nodes?.Count ?? 0;
TrainTrackNode tn = new TrainTrackNode();
tn.Track = this;
tn.Index = (afternode != null) ? afternode.Index + 1 : cnt;
if (Nodes == null)
{
Nodes = new List<TrainTrackNode>();
}
if (afternode != null)
{
TrainTrackNode aln = afternode.Links[1];
if (aln != null) aln.Links[0] = tn;
afternode.Links[1] = tn;
tn.Links[0] = afternode;
tn.Links[1] = aln;
int idx = tn.Index;
Nodes.Insert(idx, tn);
for (int i = 0; i < Nodes.Count; i++)
{
Nodes[i].Index = i;
}
}
else
{
if (cnt > 0)
{
TrainTrackNode ln = Nodes[cnt - 1];
tn.Links[0] = ln;
ln.Links[1] = tn;
}
Nodes.Add(tn);
}
NodeCount = Nodes.Count;
return tn;
}
public bool RemoveNode(TrainTrackNode node)
{
bool r = false;
r = Nodes.Remove(node);
NodeCount = Nodes.Count;
if (r)
{
var l0 = node.Links[0];
var l1 = node.Links[1];
if (l0 != null)
{
l0.Links[1] = l1;
}
if (l1 != null)
{
l1.Links[0] = l0;
}
for (int i = 0; i < Nodes.Count; i++)
{
Nodes[i].Index = i;
}
BuildVertices();
}
return r;
}
public override string ToString()
{
return Name + ": " + filename + " (" + NodeCount.ToString() + " nodes)";
}
}
public class TrainTrackNode : BasePathNode
{
public Vector3 Position { get; set; }
public int NodeType { get; set; }
public TrainTrack Track { get; set; }
public int Index { get; set; }
public TrainTrackNode[] Links { get; set; } = new TrainTrackNode[2];
public int GetColour()
{
switch (NodeType)
{
case 0: return new Color4(1.0f, 0.0f, 0.0f, 1.0f).ToRgba();
case 1: return new Color4(1.0f, 1.0f, 0.0f, 1.0f).ToRgba();
case 2: return new Color4(0.0f, 1.0f, 0.0f, 1.0f).ToRgba();
case 3: return new Color4(0.0f, 1.0f, 1.0f, 1.0f).ToRgba();
case 4: return new Color4(0.0f, 0.0f, 1.0f, 1.0f).ToRgba();
case 5: return new Color4(1.0f, 0.0f, 1.0f, 1.0f).ToRgba();
default: return new Color4(1.0f, 1.0f, 1.0f, 1.0f).ToRgba();
}
}
public void SetPosition(Vector3 pos)
{
Position = pos;
}
public override string ToString()
{
return Index.ToString() + ": " + NodeType.ToString();// + ": " + FloatUtil.GetVector3String(Position);
}
}
}

View File

@@ -0,0 +1,102 @@
using CodeWalker.GameFiles;
using SharpDX;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace CodeWalker.World
{
[TypeConverter(typeof(ExpandableObjectConverter))] public class Vehicle
{
public string Name { get; set; } = string.Empty;
public MetaHash NameHash { get; set; } = 0;//base vehicle name hash
public MetaHash ModelHash { get; set; } = 0;//vehicle model name hash, can be _hi
public VehicleInitData InitData { get; set; } = null;
public YftFile Yft { get; set; } = null;
public YcdFile ConvRoofDict { get; set; } = null;
public ClipMapEntry ConvRoofClip { get; set; } = null;
public string DisplayMake { get; set; } = string.Empty;//make display name
public string DisplayName { get; set; } = string.Empty;//model display name
public YmapEntityDef RenderEntity = new YmapEntityDef(); //placeholder entity object for rendering
public Vector3 Position { get; set; } = Vector3.Zero;
public Quaternion Rotation { get; set; } = Quaternion.Identity;
public void Init(string name, GameFileCache gfc, bool hidef = true)
{
Name = name;
var modelnamel = name.ToLowerInvariant();
MetaHash modelhash = JenkHash.GenHash(modelnamel);
MetaHash modelhashhi = JenkHash.GenHash(modelnamel + "_hi");
var yfthash = hidef ? modelhashhi : modelhash;
VehicleInitData vid = null;
if (gfc.VehiclesInitDict.TryGetValue(modelhash, out vid))
{
bool vehiclechange = NameHash != modelhash;
ConvRoofDict = null;
ConvRoofClip = null;
ModelHash = yfthash;
NameHash = modelhash;
InitData = vid;
Yft = gfc.GetYft(ModelHash);
while ((Yft != null) && (!Yft.Loaded))
{
Thread.Sleep(1);//kinda hacky
Yft = gfc.GetYft(ModelHash);
}
DisplayMake = GlobalText.TryGetString(JenkHash.GenHash(vid.vehicleMakeName.ToLowerInvariant()));
DisplayName = GlobalText.TryGetString(JenkHash.GenHash(vid.gameName.ToLowerInvariant()));
if (!string.IsNullOrEmpty(vid.animConvRoofDictName) && (vid.animConvRoofDictName.ToLowerInvariant() != "null"))
{
var ycdhash = JenkHash.GenHash(vid.animConvRoofDictName.ToLowerInvariant());
var cliphash = JenkHash.GenHash(vid.animConvRoofName?.ToLowerInvariant());
ConvRoofDict = gfc.GetYcd(ycdhash);
while ((ConvRoofDict != null) && (!ConvRoofDict.Loaded))
{
Thread.Sleep(1);//kinda hacky
ConvRoofDict = gfc.GetYcd(ycdhash);
}
ClipMapEntry cme = null;
ConvRoofDict?.ClipMap?.TryGetValue(cliphash, out cme);
ConvRoofClip = cme;
}
}
else
{
ModelHash = 0;
NameHash = 0;
InitData = null;
Yft = null;
DisplayMake = "-";
DisplayName = "-";
ConvRoofDict = null;
ConvRoofClip = null;
}
UpdateEntity();
}
public void UpdateEntity()
{
RenderEntity.SetPosition(Position);
RenderEntity.SetOrientation(Rotation);
}
}
}

View File

@@ -0,0 +1,225 @@
using CodeWalker.GameFiles;
using SharpDX;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
namespace CodeWalker.World
{
public class Water
{
public volatile bool Inited = false;
public GameFileCache GameFileCache;
public List<WaterQuad> WaterQuads = new List<WaterQuad>();
public List<WaterCalmingQuad> CalmingQuads = new List<WaterCalmingQuad>();
public List<WaterWaveQuad> WaveQuads = new List<WaterWaveQuad>();
public void Init(GameFileCache gameFileCache, Action<string> updateStatus)
{
GameFileCache = gameFileCache;
var rpfman = gameFileCache.RpfMan;
string filename = "common.rpf\\data\\levels\\gta5\\water.xml";
XmlDocument waterxml = rpfman.GetFileXml(filename);
XmlElement waterdata = waterxml.DocumentElement;
XmlNodeList waterquads = waterdata.SelectNodes("WaterQuads/Item");
WaterQuads.Clear();
for (int i = 0; i < waterquads.Count; i++)
{
var waterquad = new WaterQuad();
waterquad.Init(waterquads[i], i);
WaterQuads.Add(waterquad);
}
XmlNodeList calmingquads = waterdata.SelectNodes("CalmingQuads/Item");
CalmingQuads.Clear();
for (int i = 0; i < calmingquads.Count; i++)
{
var calmingquad = new WaterCalmingQuad();
calmingquad.Init(calmingquads[i], i);
CalmingQuads.Add(calmingquad);
}
XmlNodeList wavequads = waterdata.SelectNodes("WaveQuads/Item");
WaveQuads.Clear();
for (int i = 0; i < wavequads.Count; i++)
{
var wavequad = new WaterWaveQuad();
wavequad.Init(wavequads[i], i);
WaveQuads.Add(wavequad);
}
Inited = true;
}
public List<T> GetVisibleQuads<T>(Camera camera, IEnumerable<T> allQuads) where T : BaseWaterQuad
{
List<T> quads = new List<T>();
if (!Inited) return quads;
var vf = camera.ViewFrustum;
foreach (var quad in allQuads)
{
Vector3 camrel = quad.BSCenter - camera.Position;
if (vf.ContainsSphereNoClipNoOpt(ref camrel, quad.BSRadius))
{
quads.Add(quad);
}
}
return quads;
}
}
public abstract class BaseWaterQuad
{
public int xmlNodeIndex { get; set; }
public float minX { get; set; }
public float maxX { get; set; }
public float minY { get; set; }
public float maxY { get; set; }
public float? z { get; set; } = null;
public abstract void Init(XmlNode node, int index);
public void CalcBS()
{
BSCenter = new Vector3((minX + maxX) * 0.5f, (minY + maxY) * 0.5f, z ?? 0);
BSRadius = new Vector2(maxX - minX, maxY - minY).Length() * 0.5f;
}
public Vector3 BSCenter { get; private set; }
public float BSRadius { get; private set; }
public override string ToString()
{
return string.Format("[{0}] X=({1} : {2}), Y=({3} : {4})", xmlNodeIndex, minX, maxX, minY, maxY);
}
}
public class WaterQuad : BaseWaterQuad
{
public int Type { get; set; }
public bool IsInvisible { get; set; }
public bool HasLimitedDepth { get; set; }
public float a1 { get; set; }
public float a2 { get; set; }
public float a3 { get; set; }
public float a4 { get; set; }
public bool NoStencil { get; set; }
public override void Init(XmlNode node, int index)
{
xmlNodeIndex = index;
minX = Xml.GetChildFloatAttribute(node, "minX", "value");
maxX = Xml.GetChildFloatAttribute(node, "maxX", "value");
minY = Xml.GetChildFloatAttribute(node, "minY", "value");
maxY = Xml.GetChildFloatAttribute(node, "maxY", "value");
Type = Xml.GetChildIntAttribute(node, "Type", "value");
IsInvisible = Xml.GetChildBoolAttribute(node, "IsInvisible", "value");
HasLimitedDepth = Xml.GetChildBoolAttribute(node, "HasLimitedDepth", "value");
z = Xml.GetChildFloatAttribute(node, "z", "value");
a1 = Xml.GetChildFloatAttribute(node, "a1", "value");
a2 = Xml.GetChildFloatAttribute(node, "a2", "value");
a3 = Xml.GetChildFloatAttribute(node, "a3", "value");
a4 = Xml.GetChildFloatAttribute(node, "a4", "value");
NoStencil = Xml.GetChildBoolAttribute(node, "NoStencil", "value");
/*
<minX value="-1592" />
<maxX value="-1304" />
<minY value="-1744" />
<maxY value="-1624" />
<Type value="0" />
<IsInvisible value="false" />
<HasLimitedDepth value="false" />
<z value="0.0" />
<a1 value="26" />
<a2 value="26" />
<a3 value="26" />
<a4 value="26" />
<NoStencil value="false" />
*/
CalcBS();
}
}
public class WaterCalmingQuad : BaseWaterQuad
{
public float fDampening { get; set; }
public override void Init(XmlNode node, int index)
{
xmlNodeIndex = index;
minX = Xml.GetChildFloatAttribute(node, "minX", "value");
maxX = Xml.GetChildFloatAttribute(node, "maxX", "value");
minY = Xml.GetChildFloatAttribute(node, "minY", "value");
maxY = Xml.GetChildFloatAttribute(node, "maxY", "value");
fDampening = Xml.GetChildFloatAttribute(node, "fDampening", "value");
/*
<minX value="1752" />
<maxX value="2076" />
<minY value="216" />
<maxY value="800" />
<fDampening value="0.05" />
*/
CalcBS();
}
}
public class WaterWaveQuad : BaseWaterQuad
{
public float Amplitude { get; set; }
public float XDirection { get; set; }
public float YDirection { get; set; }
public Quaternion WaveOrientation { get; set; }
public override void Init(XmlNode node, int index)
{
xmlNodeIndex = index;
minX = Xml.GetChildFloatAttribute(node, "minX", "value");
maxX = Xml.GetChildFloatAttribute(node, "maxX", "value");
minY = Xml.GetChildFloatAttribute(node, "minY", "value");
maxY = Xml.GetChildFloatAttribute(node, "maxY", "value");
Amplitude = Xml.GetChildFloatAttribute(node, "Amplitude", "value");
XDirection = Xml.GetChildFloatAttribute(node, "XDirection", "value");
YDirection = Xml.GetChildFloatAttribute(node, "YDirection", "value");
float angl = (float)Math.Atan2(YDirection, XDirection);
WaveOrientation = Quaternion.RotationYawPitchRoll(0.0f, 0.0f, angl);
/*
<minX value="1664" />
<maxX value="1988" />
<minY value="-120" />
<maxY value="132" />
<Amplitude value="0.1" />
<XDirection value="-0.603208" />
<YDirection value="-0.797584" />
*/
CalcBS();
}
}
}

View File

@@ -0,0 +1,290 @@
using CodeWalker.GameFiles;
using SharpDX;
using System;
using System.Collections.Generic;
using System.Text;
namespace CodeWalker.World
{
public class Watermaps : BasePathData
{
public volatile bool Inited = false;
public GameFileCache GameFileCache;
public List<WatermapFile> WatermapFiles = new List<WatermapFile>();
public Vector4[] GetNodePositions()
{
return NodePositions;
}
public EditorVertex[] GetPathVertices()
{
return null;
}
public EditorVertex[] GetTriangleVertices()
{
return TriangleVerts;
}
public Vector4[] NodePositions;
public EditorVertex[] TriangleVerts;
public void Init(GameFileCache gameFileCache, Action<string> updateStatus)
{
Inited = false;
GameFileCache = gameFileCache;
WatermapFiles.Clear();
LoadWatermap("common.rpf\\data\\levels\\gta5\\waterheight.dat");
BuildVertices();
Inited = true;
}
private void LoadWatermap(string filename)
{
var wmf = GameFileCache.RpfMan.GetFile<WatermapFile>(filename);
WatermapFiles.Add(wmf);
}
public void BuildVertices()
{
var vlist = new List<EditorVertex>();
var nlist = new List<Vector4>();
foreach (var wmf in WatermapFiles)
{
BuildWatermapVertices(wmf, vlist, nlist);
}
if (vlist.Count > 0)
{
TriangleVerts = vlist.ToArray();
}
else
{
TriangleVerts = null;
}
if (nlist.Count > 0)
{
NodePositions = nlist.ToArray();
}
else
{
NodePositions = null;
}
}
private void BuildWatermapVertices(WatermapFile wmf, List<EditorVertex> vl, List<Vector4> nl)
{
var v1 = new EditorVertex();
var v2 = new EditorVertex();
var v3 = new EditorVertex();
var v4 = new EditorVertex();
uint cblu = (uint)new Color(0, 0, 128, 60).ToRgba();
float getHeight(int o)
{
var harr = wmf.GridWatermapRefs[o];
if (harr == null) return 0;
if (harr.Length == 0) return 0;
var h0 = harr[0];
var i0 = h0.Item;
if (h0.Type == WatermapFile.WaterItemType.River)
{
return h0.Vector.Z;
}
if (h0.Type == WatermapFile.WaterItemType.Lake)
{
if (i0 != null) return i0.Position.Z;
}
if (h0.Type == WatermapFile.WaterItemType.Pool)
{
if (i0 != null) return i0.Position.Z;
}
return h0.Vector.Z;
}
uint getColour(int o)
{
var harr = wmf.GridWatermapRefs[o];
if (harr == null) return cblu;
if (harr.Length == 0) return cblu;
var i0 = harr[0].Item;
if (i0 == null) return cblu;
var c = i0.Colour;
c.A = 128;
return (uint)c.ToRgba();
}
var w = wmf.Width;
var h = wmf.Height;
var min = new Vector3(wmf.CornerX, wmf.CornerY, 0.0f);
var step = new Vector3(wmf.TileX, -wmf.TileY, 1.0f);
//var siz = new Vector3(w, h, 1) * step;
for (int yi = 1; yi < h; yi++)
{
var yo = yi - 1;
for (int xi = 1; xi < w; xi++)
{
var xo = xi - 1;
var o1 = yi * w + xo;
var o2 = yi * w + xi;
var o3 = yo * w + xo;
var o4 = yo * w + xi;
v1.Position = min + step * new Vector3(xo, yi, getHeight(o1));
v2.Position = min + step * new Vector3(xi, yi, getHeight(o2));
v3.Position = min + step * new Vector3(xo, yo, getHeight(o3));
v4.Position = min + step * new Vector3(xi, yo, getHeight(o4));
v1.Colour = getColour(o1);
v2.Colour = getColour(o2);
v3.Colour = getColour(o3);
v4.Colour = getColour(o4);
//vl.Add(v1); vl.Add(v2); vl.Add(v3);
//vl.Add(v3); vl.Add(v2); vl.Add(v4);
}
}
//for (int y = 0; y < h; y++)
//{
// for (int x = 0; x < w; x++)
// {
// var o = y * w + x;
// nl.Add(new Vector4(min + step * new Vector3(x, y, getHeight(o)), 10));
// }
//}
void addQuad(Quad q)
{
v1.Position = q.P1;
v2.Position = q.P2;
v3.Position = q.P3;
v4.Position = q.P4;
vl.Add(v1); vl.Add(v2); vl.Add(v3);
vl.Add(v3); vl.Add(v2); vl.Add(v4);
}
void addRivEnd(Vector3 p, Vector3 s, Vector3 d, float r)
{
v1.Position = p;
v2.Position = p + s * r;
v3.Position = p + d * r;
v4.Position = p - s * r;
vl.Add(v1); vl.Add(v2); vl.Add(v3);
vl.Add(v1); vl.Add(v3); vl.Add(v4);
}
var rivers = wmf.Rivers;
if (rivers != null)
{
foreach (var river in rivers)
{
if ((river.Vectors == null) || (river.VectorCount <= 1))
{ continue; }
var rwid = 20.0f;
var rc = river.Colour;
rc.A = 128;
v1.Colour = v2.Colour = v3.Colour = v4.Colour = (uint)rc.ToRgba();
var quads = new Quad[river.Vectors.Length - 1];
var li = river.Vectors.Length - 1;
for (int i = 1; i < river.Vectors.Length; i++)
{
var o = i - 1;
var vo = river.Vectors[o];
var vi = river.Vectors[i];
var dif = vi.XYZ() - vo.XYZ();
var dir = Vector3.Normalize(dif);
var sid = Vector3.Normalize(Vector3.Cross(dir, Vector3.UnitZ));
if (Math.Abs(dir.Z) > 0.95f)
{
dir = Vector3.UnitY;
sid = Vector3.UnitX;
}
quads[o].P1 = vo.XYZ() - sid*rwid;
quads[o].P2 = vo.XYZ() + sid*rwid;
quads[o].P3 = vi.XYZ() - sid*rwid;
quads[o].P4 = vi.XYZ() + sid*rwid;
if (i == 1) addRivEnd(vo.XYZ(), -sid, -dir, rwid);
if (i == li) addRivEnd(vi.XYZ(), sid, dir, rwid);
}
for (int i = 1; i < quads.Length; i++)
{
var o = i - 1;
quads[o].P3 = quads[i].P1 = (quads[o].P3 + quads[i].P1) * 0.5f;
quads[o].P4 = quads[i].P2 = (quads[o].P4 + quads[i].P2) * 0.5f;
}
for (int i = 0; i < quads.Length; i++)
{
addQuad(quads[i]);
}
}
}
var lakes = wmf.Lakes;
if (lakes != null)
{
foreach (var lake in lakes)
{
if ((lake.Vectors == null) || (lake.VectorCount == 0))
{ continue; }
var lp = lake.Position;
var lc = lake.Colour;
lc.A = 128;
v1.Colour = v2.Colour = v3.Colour = v4.Colour = (uint)lc.ToRgba();
for (int i = 0; i < lake.Vectors.Length; i++)
{
var vi = lake.Vectors[i];
var vp = new Vector3(vi.X, vi.Y, lp.Z);
var q = new Quad();
q.P1 = vp + new Vector3(vi.Z, -vi.W, 0);
q.P2 = vp + new Vector3(vi.Z, vi.W, 0);
q.P3 = vp + new Vector3(-vi.Z, -vi.W, 0);
q.P4 = vp + new Vector3(-vi.Z, vi.W, 0);
addQuad(q);
}
}
}
var pools = wmf.Pools;
if (pools != null)
{
foreach (var pool in pools)
{
var pp = pool.Position;
var ps = pool.Size;
var pc = pool.Colour;
pc.A = 128;
v1.Colour = v2.Colour = v3.Colour = v4.Colour = (uint)pc.ToRgba();
var q = new Quad();
q.P1 = pp + new Vector3(ps.X, -ps.Y, 0);
q.P2 = pp + new Vector3(ps.X, ps.Y, 0);
q.P3 = pp + new Vector3(-ps.X, -ps.Y, 0);
q.P4 = pp + new Vector3(-ps.X, ps.Y, 0);
addQuad(q);
}
}
}
struct Quad
{
public Vector3 P1;
public Vector3 P2;
public Vector3 P3;
public Vector3 P4;
}
}
}

View File

@@ -0,0 +1,70 @@
using CodeWalker.GameFiles;
using SharpDX;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace CodeWalker.World
{
[TypeConverter(typeof(ExpandableObjectConverter))] public class Weapon
{
public string Name { get; set; } = string.Empty;
public MetaHash NameHash { get; set; } = 0;//base weapon name hash
public MetaHash ModelHash { get; set; } = 0;//weapon model name hash, can be _hi
public YdrFile Ydr { get; set; } = null;
public Drawable Drawable { get; set; } = null;
public YmapEntityDef RenderEntity = new YmapEntityDef(); //placeholder entity object for rendering
public Vector3 Position { get; set; } = Vector3.Zero;
public Quaternion Rotation { get; set; } = Quaternion.Identity;
public void Init(string name, GameFileCache gfc, bool hidef = true)
{
Name = name;
var modelnamel = name.ToLowerInvariant();
MetaHash modelhash = JenkHash.GenHash(modelnamel);
MetaHash modelhashhi = JenkHash.GenHash(modelnamel + "_hi");
var ydrhash = hidef ? modelhashhi : modelhash;
NameHash = modelhash;
ModelHash = ydrhash;
var useHash = ModelHash;
Ydr = gfc.GetYdr(ModelHash);
if (Ydr == null)
{
useHash = NameHash;
Ydr = gfc.GetYdr(NameHash);
}
while ((Ydr != null) && (!Ydr.Loaded))
{
Thread.Sleep(1);//kinda hacky
Ydr = gfc.GetYdr(useHash);
}
if (Ydr != null)
{
Drawable = Ydr.Drawable?.ShallowCopy() as Drawable;
}
UpdateEntity();
}
public void UpdateEntity()
{
RenderEntity.SetPosition(Position);
RenderEntity.SetOrientation(Rotation);
}
}
}

View File

@@ -0,0 +1,569 @@
using CodeWalker.GameFiles;
using SharpDX;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
namespace CodeWalker.World
{
public class Weather
{
public Dictionary<string, WeatherGpuFx> WeatherGpuFx { get; set; } = new Dictionary<string, WeatherGpuFx>();
public Dictionary<string, WeatherType> WeatherTypes { get; set; } = new Dictionary<string, WeatherType>();
public List<WeatherCycle> WeatherCycles { get; set; } = new List<WeatherCycle>();
public WeatherValues CurrentValues;
public volatile bool Inited = false;
public WeatherType CurrentWeatherType;
public WeatherType NextWeatherType;
public float WeatherChangeTime = 0.33f;
public float CurrentWeatherChangeTime = 0.0f;
public float CurrentWeatherChangeBlend = 0.0f;
public string Region = "GLOBAL"; //URBAN or GLOBAL..
public WeatherCycleKeyframeRegion CurrentWeatherRegion;
public WeatherCycleKeyframeRegion NextWeatherRegion;
public Timecycle Timecycle;
public TimecycleMods TimecycleMods;
public void Init(GameFileCache gameFileCache, Action<string> updateStatus, Timecycle timecycle)
{
Timecycle = timecycle;
var rpfman = gameFileCache.RpfMan;
//TODO: RpfMan should be able to get the right version? or maybe let gameFileCache do it!
string filename = "common.rpf\\data\\levels\\gta5\\weather.xml";
if (gameFileCache.EnableDlc)
{
filename = "update\\update.rpf\\common\\data\\levels\\gta5\\weather.xml";
}
XmlDocument weatherxml = rpfman.GetFileXml(filename);
XmlElement weather = weatherxml.DocumentElement;
XmlNodeList weathergpufx = weather.SelectNodes("WeatherGpuFx/Item");
WeatherGpuFx.Clear();
for (int i = 0; i < weathergpufx.Count; i++)
{
var weathergpufxi = new WeatherGpuFx();
weathergpufxi.Init(weathergpufx[i]);
WeatherGpuFx[weathergpufxi.Name] = weathergpufxi;
}
XmlNodeList weathertypes = weather.SelectNodes("WeatherTypes/Item");
WeatherTypes.Clear();
for (int i = 0; i < weathertypes.Count; i++)
{
var weathertype = new WeatherType();
weathertype.Init(gameFileCache, weathertypes[i]);
WeatherTypes[weathertype.Name] = weathertype;
}
XmlNodeList weathercycles = weather.SelectNodes("WeatherCycles/Item");
WeatherCycles.Clear();
for (int i = 0; i < weathercycles.Count; i++)
{
var weathercycle = new WeatherCycle();
weathercycle.Init(weathercycles[i]);
WeatherCycles.Add(weathercycle);
}
if (WeatherTypes.Count > 0)
{
CurrentWeatherType = WeatherTypes.Values.First();
CurrentWeatherRegion = CurrentWeatherType.GetRegion(Region);
NextWeatherType = CurrentWeatherType;
NextWeatherRegion = NextWeatherType.GetRegion(Region);
}
TimecycleMods = new TimecycleMods();
TimecycleMods.Init(gameFileCache, updateStatus);
Inited = true;
}
public void Update(float elapsed)
{
if (!Inited) return;
if (CurrentWeatherType != NextWeatherType)
{
CurrentWeatherChangeTime += elapsed;
if (CurrentWeatherChangeTime >= WeatherChangeTime)
{
CurrentWeatherType = NextWeatherType;
CurrentWeatherChangeTime = 0.0f;
}
CurrentWeatherChangeBlend = Math.Min(CurrentWeatherChangeTime / WeatherChangeTime, 1.0f);
}
if (CurrentWeatherType != null)
{
CurrentWeatherRegion = CurrentWeatherType.GetRegion(Region);
}
if (NextWeatherType != null)
{
NextWeatherRegion = NextWeatherType.GetRegion(Region);
}
CurrentValues.Update(this);
}
public void SetNextWeather(string name)
{
WeatherTypes.TryGetValue(name, out NextWeatherType);
if (NextWeatherType == null)
{
NextWeatherType = CurrentWeatherType;
}
else
{
CurrentWeatherChangeTime = 0.0f;
}
}
public float GetDynamicValue(string name)
{
int csi = Timecycle.CurrentSampleIndex;
float csb = Timecycle.CurrentSampleBlend;
if ((CurrentWeatherRegion != null) && (NextWeatherRegion != null))
{
float cv = CurrentWeatherRegion.GetCurrentValue(name, csi, csb);
float nv = NextWeatherRegion.GetCurrentValue(name, csi, csb);
return cv * (1.0f - CurrentWeatherChangeBlend) + nv * CurrentWeatherChangeBlend;
}
else if (CurrentWeatherRegion != null)
{
return CurrentWeatherRegion.GetCurrentValue(name, csi, csb);
}
//throw new Exception("CurrentWeatherRegion was null.");
return 0.0f;
}
public Vector3 GetDynamicRGB(string rname, string gname, string bname)
{
int csi = Timecycle.CurrentSampleIndex;
float csb = Timecycle.CurrentSampleBlend;
if ((CurrentWeatherRegion != null) && (NextWeatherRegion != null))
{
float cvr = CurrentWeatherRegion.GetCurrentValue(rname, csi, csb);
float cvg = CurrentWeatherRegion.GetCurrentValue(gname, csi, csb);
float cvb = CurrentWeatherRegion.GetCurrentValue(bname, csi, csb);
float nvr = NextWeatherRegion.GetCurrentValue(rname, csi, csb);
float nvg = NextWeatherRegion.GetCurrentValue(gname, csi, csb);
float nvb = NextWeatherRegion.GetCurrentValue(bname, csi, csb);
Vector3 cv = new Vector3(cvr, cvg, cvb);
Vector3 nv = new Vector3(nvr, nvg, nvb);
return cv * (1.0f - CurrentWeatherChangeBlend) + nv * CurrentWeatherChangeBlend;
}
else if (CurrentWeatherRegion != null)
{
float cvr = CurrentWeatherRegion.GetCurrentValue(rname, csi, csb);
float cvg = CurrentWeatherRegion.GetCurrentValue(gname, csi, csb);
float cvb = CurrentWeatherRegion.GetCurrentValue(bname, csi, csb);
return new Vector3(cvr, cvg, cvb);
}
//throw new Exception("CurrentWeatherRegion was null.");
return Vector3.Zero;
}
public Vector4 GetDynamicRGBA(string rname, string gname, string bname, string aname)
{
int csi = Timecycle.CurrentSampleIndex;
float csb = Timecycle.CurrentSampleBlend;
if ((CurrentWeatherRegion != null) && (NextWeatherRegion != null))
{
float cvr = CurrentWeatherRegion.GetCurrentValue(rname, csi, csb);
float cvg = CurrentWeatherRegion.GetCurrentValue(gname, csi, csb);
float cvb = CurrentWeatherRegion.GetCurrentValue(bname, csi, csb);
float cva = CurrentWeatherRegion.GetCurrentValue(aname, csi, csb);
float nvr = NextWeatherRegion.GetCurrentValue(rname, csi, csb);
float nvg = NextWeatherRegion.GetCurrentValue(gname, csi, csb);
float nvb = NextWeatherRegion.GetCurrentValue(bname, csi, csb);
float nva = NextWeatherRegion.GetCurrentValue(aname, csi, csb);
Vector4 cv = new Vector4(cvr, cvg, cvb, cva);
Vector4 nv = new Vector4(nvr, nvg, nvb, nva);
return cv * (1.0f - CurrentWeatherChangeBlend) + nv * CurrentWeatherChangeBlend;
}
else if (CurrentWeatherRegion != null)
{
float cvr = CurrentWeatherRegion.GetCurrentValue(rname, csi, csb);
float cvg = CurrentWeatherRegion.GetCurrentValue(gname, csi, csb);
float cvb = CurrentWeatherRegion.GetCurrentValue(bname, csi, csb);
float cva = CurrentWeatherRegion.GetCurrentValue(aname, csi, csb);
return new Vector4(cvr, cvg, cvb, cva);
}
//throw new Exception("CurrentWeatherRegion was null.");
return Vector4.Zero;
}
}
public class WeatherGpuFx
{
public string Name { get; set; }
public string SystemType { get; set; }
public string diffuseName { get; set; }
public string distortionTexture { get; set; }
public string diffuseSplashName { get; set; }
public string driveType { get; set; }
public float windInfluence { get; set; }
public float gravity { get; set; }
public string emitterSettingsName { get; set; }
public string renderSettingsName { get; set; }
public void Init(XmlNode node)
{
Name = Xml.GetChildInnerText(node, "Name");
SystemType = Xml.GetChildInnerText(node, "SystemType");
diffuseName = Xml.GetChildInnerText(node, "diffuseName");
distortionTexture = Xml.GetChildInnerText(node, "distortionTexture");
diffuseSplashName = Xml.GetChildInnerText(node, "diffuseSplashName");
driveType = Xml.GetChildInnerText(node, "driveType");
windInfluence = Xml.GetChildFloatAttribute(node, "windInfluence", "value");
gravity = Xml.GetChildFloatAttribute(node, "gravity", "value");
emitterSettingsName = Xml.GetChildInnerText(node, "emitterSettingsName");
renderSettingsName = Xml.GetChildInnerText(node, "renderSettingsName");
}
public override string ToString()
{
return Name;
}
}
public class WeatherType
{
public MetaHash NameHash { get; set; }
public string Name { get; set; }
public float Sun { get; set; }
public float Cloud { get; set; }
public float WindMin { get; set; }
public float WindMax { get; set; }
public float Rain { get; set; }
public float Snow { get; set; }
public float SnowMist { get; set; }
public float Fog { get; set; }
public float RippleBumpiness { get; set; }
public float RippleMinBumpiness { get; set; }
public float RippleMaxBumpiness { get; set; }
public float RippleBumpinessWindScale { get; set; }
public float RippleScale { get; set; }
public float RippleSpeed { get; set; }
public float RippleVelocityTransfer { get; set; }
public float OceanBumpiness { get; set; }
public float DeepOceanScale { get; set; }
public float OceanNoiseMinAmplitude { get; set; }
public float OceanWaveAmplitude { get; set; }
public float ShoreWaveAmplitude { get; set; }
public float OceanWaveWindScale { get; set; }
public float ShoreWaveWindScale { get; set; }
public float OceanWaveMinAmplitude { get; set; }
public float ShoreWaveMinAmplitude { get; set; }
public float OceanWaveMaxAmplitude { get; set; }
public float ShoreWaveMaxAmplitude { get; set; }
public float OceanFoamIntensity { get; set; }
public float OceanFoamScale { get; set; }
public float RippleDisturb { get; set; }
public float Lightning { get; set; }
public float Sandstorm { get; set; }
public string OldSettingName { get; set; }
public string DropSettingName { get; set; }
public string MistSettingName { get; set; }
public string GroundSettingName { get; set; }
public string TimeCycleFilename { get; set; }
public string CloudSettingsName { get; set; }
public WeatherCycleKeyframeData TimeCycleData;
public void Init(GameFileCache gameFileCache, XmlNode node)
{
Name = Xml.GetChildInnerText(node, "Name");
NameHash = new MetaHash(JenkHash.GenHash(Name.ToLowerInvariant()));
Sun = Xml.GetChildFloatAttribute(node, "Sun", "value");
Cloud = Xml.GetChildFloatAttribute(node, "Cloud", "value");
WindMin = Xml.GetChildFloatAttribute(node, "WindMin", "value");
WindMax = Xml.GetChildFloatAttribute(node, "WindMax", "value");
Rain = Xml.GetChildFloatAttribute(node, "Rain", "value");
Snow = Xml.GetChildFloatAttribute(node, "Snow", "value");
SnowMist = Xml.GetChildFloatAttribute(node, "SnowMist", "value");
Fog = Xml.GetChildFloatAttribute(node, "Fog", "value");
RippleBumpiness = Xml.GetChildFloatAttribute(node, "RippleBumpiness", "value");
RippleMinBumpiness = Xml.GetChildFloatAttribute(node, "RippleMinBumpiness", "value");
RippleMaxBumpiness = Xml.GetChildFloatAttribute(node, "RippleMaxBumpiness", "value");
RippleBumpinessWindScale = Xml.GetChildFloatAttribute(node, "RippleBumpinessWindScale", "value");
RippleScale = Xml.GetChildFloatAttribute(node, "RippleScale", "value");
RippleSpeed = Xml.GetChildFloatAttribute(node, "RippleSpeed", "value");
RippleVelocityTransfer = Xml.GetChildFloatAttribute(node, "RippleVelocityTransfer", "value");
OceanBumpiness = Xml.GetChildFloatAttribute(node, "OceanBumpiness", "value");
DeepOceanScale = Xml.GetChildFloatAttribute(node, "DeepOceanScale", "value");
OceanNoiseMinAmplitude = Xml.GetChildFloatAttribute(node, "OceanNoiseMinAmplitude", "value");
OceanWaveAmplitude = Xml.GetChildFloatAttribute(node, "OceanWaveAmplitude", "value");
ShoreWaveAmplitude = Xml.GetChildFloatAttribute(node, "ShoreWaveAmplitude", "value");
OceanWaveWindScale = Xml.GetChildFloatAttribute(node, "OceanWaveWindScale", "value");
ShoreWaveWindScale = Xml.GetChildFloatAttribute(node, "ShoreWaveWindScale", "value");
OceanWaveMinAmplitude = Xml.GetChildFloatAttribute(node, "OceanWaveMinAmplitude", "value");
ShoreWaveMinAmplitude = Xml.GetChildFloatAttribute(node, "ShoreWaveMinAmplitude", "value");
OceanWaveMaxAmplitude = Xml.GetChildFloatAttribute(node, "OceanWaveMaxAmplitude", "value");
ShoreWaveMaxAmplitude = Xml.GetChildFloatAttribute(node, "ShoreWaveMaxAmplitude", "value");
OceanFoamIntensity = Xml.GetChildFloatAttribute(node, "OceanFoamIntensity", "value");
OceanFoamScale = Xml.GetChildFloatAttribute(node, "OceanFoamScale", "value");
RippleDisturb = Xml.GetChildFloatAttribute(node, "RippleDisturb", "value");
Lightning = Xml.GetChildFloatAttribute(node, "Lightning", "value");
Sandstorm = Xml.GetChildFloatAttribute(node, "Sandstorm", "value");
OldSettingName = Xml.GetChildInnerText(node, "OldSettingName");
DropSettingName = Xml.GetChildInnerText(node, "DropSettingName");
MistSettingName = Xml.GetChildInnerText(node, "MistSettingName");
GroundSettingName = Xml.GetChildInnerText(node, "GroundSettingName");
TimeCycleFilename = Xml.GetChildInnerText(node, "TimeCycleFilename");
CloudSettingsName = Xml.GetChildInnerText(node, "CloudSettingsName");
if (!string.IsNullOrEmpty(TimeCycleFilename))
{
//TODO: RpfMan should be able to get the right version? or maybe let gameFileCache do it!
string fname = TimeCycleFilename.ToLowerInvariant();
bool useupd = gameFileCache.EnableDlc;
if (useupd)
{
fname = fname.Replace("common:", "update/update.rpf/common");
}
XmlDocument tcxml = gameFileCache.RpfMan.GetFileXml(fname);
if (useupd && !tcxml.HasChildNodes)
{
fname = TimeCycleFilename.ToLowerInvariant();
tcxml = gameFileCache.RpfMan.GetFileXml(fname);
}
foreach (XmlNode cycle in tcxml.DocumentElement.ChildNodes)
{
TimeCycleData = new WeatherCycleKeyframeData();
TimeCycleData.Init(cycle);
}
}
}
public WeatherCycleKeyframeRegion GetRegion(string name)
{
if ((TimeCycleData != null) && (TimeCycleData.Regions != null))
{
WeatherCycleKeyframeRegion r;
if (TimeCycleData.Regions.TryGetValue(name, out r))
{
return r;
}
return TimeCycleData.Regions.Values.FirstOrDefault();
}
return null;
}
public override string ToString()
{
return Name;
}
}
public class WeatherCycle
{
public string CycleName { get; set; }
public float TimeMult { get; set; }
public void Init(XmlNode node)
{
CycleName = Xml.GetChildInnerText(node, "CycleName");
TimeMult = Xml.GetChildFloatAttribute(node, "TimeMult", "value");
}
public override string ToString()
{
return CycleName + ", " + TimeMult.ToString();
}
}
public class WeatherCycleKeyframeData
{
public string Name { get; set; }
public int RegionCount { get; set; }
public Dictionary<string, WeatherCycleKeyframeRegion> Regions { get; set; }
public void Init(XmlNode node)
{
//read cycle node
Name = Xml.GetStringAttribute(node, "name");
RegionCount = Xml.GetIntAttribute(node, "regions");
Regions = new Dictionary<string, WeatherCycleKeyframeRegion>();
foreach (XmlNode child in node.ChildNodes)
{
WeatherCycleKeyframeRegion r = new WeatherCycleKeyframeRegion();
r.Init(child);
Regions[r.Name] = r;
}
}
public override string ToString()
{
return Name;
}
}
public class WeatherCycleKeyframeRegion
{
public string Name { get; set; }
public Dictionary<string, WeatherCycleKeyframeDataEntry> Data { get; set; }
public void Init(XmlNode node)
{
//read region node
Name = Xml.GetStringAttribute(node, "name");
Data = new Dictionary<string, WeatherCycleKeyframeDataEntry>();
foreach (XmlNode child in node.ChildNodes)
{
if (child != null)
{
WeatherCycleKeyframeDataEntry d = new WeatherCycleKeyframeDataEntry();
d.Init(child);
Data[d.Name] = d;
}
}
}
public float GetCurrentValue(string name, int sample, float curblend)
{
WeatherCycleKeyframeDataEntry e;
if (Data.TryGetValue(name, out e))
{
if (sample >= e.Values.Length)
{
//System.Windows.Forms.MessageBox.Show("Sample index was out of range: " + sample.ToString());
sample = e.Values.Length - 1;
}
int nxtsample = (sample < e.Values.Length - 1) ? sample + 1 : 0;
float cv = e.Values[sample];
float nv = e.Values[nxtsample];
return cv * curblend + nv * (1.0f - curblend);
}
//throw new Exception("WeatherCycleKeyframeDataEntry " + name + " not found in region " + Name + ".");
return 0.0f;
}
public override string ToString()
{
return Name;
}
}
public class WeatherCycleKeyframeDataEntry
{
public string Name { get; set; }
public float[] Values { get; set; }
public void Init(XmlNode node)
{
//read data node
Name = node.Name;
string[] strvals = node.InnerText.Trim().Split(' ');
Values = new float[strvals.Length];
for (int i = 0; i < strvals.Length; i++)
{
if (!FloatUtil.TryParse(strvals[i], out Values[i]))
{
//System.Windows.Forms.MessageBox.Show("Error parsing float value: " + strvals[i] + "\n" +
// "Node: " + node.OuterXml.ToString());
//throw new Exception();
}
}
}
public override string ToString()
{
return Name;
}
}
public struct WeatherValues
{
public Vector3 sunDirection;
public Vector3 moonDirection;
public Vector4 skyZenithCol;
public Vector4 skyZenithTransitionCol;
public Vector4 skyZenithTransition;
public Vector4 skyAzimuthEastCol;
public Vector4 skyAzimuthWestCol;
public Vector4 skyAzimuthTransitionCol;
public float skyAzimuthTransition;
public float skyHdr;
public Vector4 skyPlane;
public Vector3 skySunCol;
public Vector3 skySunDiscCol;
public float skySunDiscSize;
public float skySunHdr;
public Vector3 skySunMie;
public float skySunInfluenceRadius;
public float skySunScatterInten;
public Vector3 skyMoonCol;
public float skyMoonDiscSize;
public float skyMoonIten;
public float skyMoonInfluenceRadius;
public float skyMoonScatterInten;
public float skyStarsIten;
public Vector4 lightDirCol;
public Vector4 lightDirAmbCol;
public float lightDirAmbIntensityMult;
public float lightDirAmbBounce;
public Vector4 lightNaturalAmbDown;
public Vector4 lightNaturalAmbUp;
public float lightNaturalAmbUpIntensityMult;
public Vector4 lightArtificialIntDown;
public Vector4 lightArtificialIntUp;
public Vector4 lightArtificialExtDown;
public Vector4 lightArtificialExtUp;
public void Update(Weather w)
{
sunDirection = w.GetDynamicRGB("sun_direction_x", "sun_direction_y", "sun_direction_z");
moonDirection = w.GetDynamicRGB("moon_direction_x", "moon_direction_y", "moon_direction_z");
skyZenithCol = w.GetDynamicRGBA("sky_zenith_col_r", "sky_zenith_col_g", "sky_zenith_col_b", "sky_zenith_col_inten");
skyZenithTransitionCol = w.GetDynamicRGBA("sky_zenith_transition_col_r", "sky_zenith_transition_col_g", "sky_zenith_transition_col_b", "sky_zenith_transition_col_inten");
//skyZenithTransition = w.GetDynamicRGBA("sky_zenith_transition_position", "sky_zenith_transition_east_blend", "sky_zenith_transition_west_blend", "sky_zenith_blend_start");
skyZenithTransition = w.GetDynamicRGBA("sky_zenith_blend_start", "sky_zenith_transition_east_blend", "sky_zenith_transition_west_blend", "sky_zenith_transition_position");
skyAzimuthEastCol = w.GetDynamicRGBA("sky_azimuth_east_col_r", "sky_azimuth_east_col_g", "sky_azimuth_east_col_b", "sky_azimuth_east_col_inten");
skyAzimuthWestCol = w.GetDynamicRGBA("sky_azimuth_west_col_r", "sky_azimuth_west_col_g", "sky_azimuth_west_col_b", "sky_azimuth_west_col_inten");
skyAzimuthTransitionCol = w.GetDynamicRGBA("sky_azimuth_transition_col_r", "sky_azimuth_transition_col_g", "sky_azimuth_transition_col_b", "sky_azimuth_transition_col_inten");
skyAzimuthTransition = w.GetDynamicValue("sky_azimuth_transition_position");
skyHdr = w.GetDynamicValue("sky_hdr");
skyPlane = w.GetDynamicRGBA("sky_plane_r", "sky_plane_g", "sky_plane_b", "sky_plane_inten");
skySunCol = w.GetDynamicRGB("sky_sun_col_r", "sky_sun_col_g", "sky_sun_col_b");
skySunDiscCol = w.GetDynamicRGB("sky_sun_disc_col_r", "sky_sun_disc_col_g", "sky_sun_disc_col_b");
skySunDiscSize = w.GetDynamicValue("sky_sun_disc_size");
skySunHdr = w.GetDynamicValue("sky_sun_hdr");
skySunMie = w.GetDynamicRGB("sky_sun_miephase", "sky_sun_miescatter", "sky_sun_mie_intensity_mult");
skySunInfluenceRadius = w.GetDynamicValue("sky_sun_influence_radius");
skySunScatterInten = w.GetDynamicValue("sky_sun_scatter_inten");
skyMoonCol = w.GetDynamicRGB("sky_moon_col_r", "sky_moon_col_g", "sky_moon_col_b");
skyMoonDiscSize = w.GetDynamicValue("sky_moon_disc_size");
skyMoonIten = w.GetDynamicValue("sky_moon_iten");
skyMoonInfluenceRadius = w.GetDynamicValue("sky_moon_influence_radius");
skyMoonScatterInten = w.GetDynamicValue("sky_moon_scatter_inten");
skyStarsIten = w.GetDynamicValue("sky_stars_iten");
lightDirCol = w.GetDynamicRGBA("light_dir_col_r", "light_dir_col_g", "light_dir_col_b", "light_dir_mult");
lightDirAmbCol = w.GetDynamicRGBA("light_directional_amb_col_r", "light_directional_amb_col_g", "light_directional_amb_col_b", "light_directional_amb_intensity");
lightDirAmbIntensityMult = w.GetDynamicValue("light_directional_amb_intensity_mult");
lightDirAmbBounce = w.GetDynamicValue("light_directional_amb_bounce_enabled");
lightNaturalAmbDown = w.GetDynamicRGBA("light_natural_amb_down_col_r", "light_natural_amb_down_col_g", "light_natural_amb_down_col_b", "light_natural_amb_down_intensity");
lightNaturalAmbUp = w.GetDynamicRGBA("light_natural_amb_up_col_r", "light_natural_amb_up_col_g", "light_natural_amb_up_col_b", "light_natural_amb_up_intensity");
lightNaturalAmbUpIntensityMult = w.GetDynamicValue("light_natural_amb_up_intensity_mult");
lightArtificialIntDown = w.GetDynamicRGBA("light_artificial_int_down_col_r", "light_artificial_int_down_col_g", "light_artificial_int_down_col_b", "light_artificial_int_down_intensity");
lightArtificialIntUp = w.GetDynamicRGBA("light_artificial_int_up_col_r", "light_artificial_int_up_col_g", "light_artificial_int_up_col_b", "light_artificial_int_up_intensity");
lightArtificialExtDown = w.GetDynamicRGBA("light_artificial_ext_down_col_r", "light_artificial_ext_down_col_g", "light_artificial_ext_down_col_b", "light_artificial_ext_down_intensity");
lightArtificialExtUp = w.GetDynamicRGBA("light_artificial_ext_up_col_r", "light_artificial_ext_up_col_g", "light_artificial_ext_up_col_b", "light_artificial_ext_up_intensity");
}
}
}