R26_dev8 - First public commit

This commit is contained in:
dexyfex
2017-09-21 20:33:05 +10:00
Unverified
commit a8243c3e0e
391 changed files with 157678 additions and 0 deletions
+364
View File
@@ -0,0 +1,364 @@
using CodeWalker.Properties;
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 = Settings.Default.CameraSmoothing;// 10.0f;//0.15f;
public float Sensitivity = Settings.Default.CameraSensitivity;// 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 = Settings.Default.CameraFieldOfView;// 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.5f;
public float ZFar = 12000.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 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, 1.0f, 3000.0f);
}
else if (IsOrthographic)
{
ProjMatrix = Matrix.OrthoRH(AspectRatio * OrthographicSize, OrthographicSize, ZNear, ZFar);
}
else
{
ProjMatrix = Matrix.PerspectiveFovRH(FieldOfView, AspectRatio, ZNear, ZFar);
}
//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, 0.0f));
MouseRayFar = ViewProjInvMatrix.MultiplyW(new Vector3(mx, my, 1.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)
{
lock (syncRoot)
{
TargetRotation.X += x;
TargetRotation.Y += y;
}
}
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.M13), (vp.M23), (vp.M33), 0.0f));//(vp.M43));
Planes[5] = Plane.Normalize(new Plane((vp.M14 - vp.M13), (vp.M24 - vp.M23), (vp.M34 - vp.M33), (vp.M44 - 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 ContainsSphereNoClip(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 < 5; i++)
{
if (Plane.DotCoordinate(Planes[i], c) < nr)
{
return false;
}
}
return true;
}
public bool ContainsSphereNoFrontClip(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 ((i != 4) && (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;
}
}
}
+459
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", "x", "y", "z");
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", "x", "y", "z");
Rotation = Xml.GetChildVector3Attributes(xml, "mRotation", "x", "y", "z");
Scale = Xml.GetChildVector3Attributes(xml, "mScale", "x", "y", "z");
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", "x", "y", "z");
AnimBlendWeights = Xml.GetChildVector3Attributes(xml, "mAnimBlendWeights", "x", "y", "z");
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;
}
}
+179
View File
@@ -0,0 +1,179 @@
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 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;
}
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);
}
}
}
+166
View File
@@ -0,0 +1,166 @@
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 VertexTypePC[] GetPathVertices()
{
return null;
}
public VertexTypePC[] GetTriangleVertices()
{
return TriangleVerts;
}
public VertexTypePC[] 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()
{
foreach (var group in Groups.Values)
{
}
}
}
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();
}
}
}
+1856
View File
File diff suppressed because it is too large Load Diff
+2078
View File
File diff suppressed because it is too large Load Diff
+124
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");
}
}
}
+148
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.ToLower();
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);
}
}
}
+433
View File
@@ -0,0 +1,433 @@
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;
foreach (var node in Nodes)
{
if ((node.NodeType == 1) || (node.NodeType == 2) || (node.NodeType == 5))
{
sc++;
}
}
return sc;
}
}
public VertexTypePC[] LinkedVerts { get; set; }
public Vector4[] NodePositions { get; set; }
public VertexTypePC[] GetPathVertices()
{
return LinkedVerts;
}
public VertexTypePC[] 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 VertexTypePC[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);
}
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);
}
}
}
+213
View File
@@ -0,0 +1,213 @@
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]);
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]);
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]);
WaveQuads.Add(wavequad);
}
Inited = true;
}
public void GetVisibleQuads(Camera camera, List<WaterQuad> quads)
{
if (!Inited) return;
var vf = camera.ViewFrustum;
for (int i = 0; i < WaterQuads.Count; i++)
{
var quad = WaterQuads[i];
Vector3 camrel = quad.BSCenter - camera.Position;
if (vf.ContainsSphereNoClipNoOpt(ref camrel, quad.BSRadius))
{
quads.Add(quad);
}
}
}
}
public class WaterQuad
{
public float minX { get; set; }
public float maxX { get; set; }
public float minY { get; set; }
public float maxY { get; set; }
public int Type { get; set; }
public bool IsInvisible { get; set; }
public bool HasLimitedDepth { get; set; }
public float z { 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 Vector3 BSCenter;
public float BSRadius;
public void Init(XmlNode node)
{
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" />
*/
BSCenter = new Vector3((minX + maxX) * 0.5f, (minY + maxY) * 0.5f, z);
BSRadius = new Vector2(maxX - minX, maxY - minY).Length() * 0.5f;
}
public override string ToString()
{
return string.Format("{0}, {1}, {2}", FloatUtil.ToString(minX), FloatUtil.ToString(minY), FloatUtil.ToString(z), FloatUtil.ToString(maxX), FloatUtil.ToString(maxY));
}
}
public class WaterCalmingQuad
{
public float minX { get; set; }
public float maxX { get; set; }
public float minY { get; set; }
public float maxY { get; set; }
public float fDampening { get; set; }
public void Init(XmlNode node)
{
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" />
*/
}
}
public class WaterWaveQuad
{
public float minX { get; set; }
public float maxX { get; set; }
public float minY { get; set; }
public float maxY { get; set; }
public float Amplitude { get; set; }
public float XDirection { get; set; }
public float YDirection { get; set; }
public void Init(XmlNode node)
{
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");
/*
<minX value="1664" />
<maxX value="1988" />
<minY value="-120" />
<maxY value="132" />
<Amplitude value="0.1" />
<XDirection value="-0.603208" />
<YDirection value="-0.797584" />
*/
}
}
}
+569
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.ToLower()));
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.ToLower();
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.ToLower();
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");
}
}
}
+941
View File
@@ -0,0 +1,941 @@
using CodeWalker.Rendering;
using SharpDX;
using SharpDX.Direct3D11;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CodeWalker.World
{
public abstract class Widget
{
public bool Visible { get; set; } = true;
public abstract void Update(Camera cam);
public abstract void Render(DeviceContext context, Camera cam, WidgetShader shader);
protected bool GetAxisRayHit(Vector3 ax1, Vector3 ax2, Vector3 camrel, Ray ray, out Vector3 pos)
{
//helper method for double sided ray/plane intersection
Vector3 pn = Vector3.Cross(ax1, ax2);
Plane p = new Plane(camrel, pn);
if (ray.Intersects(ref p, out pos))
{
return true;
}
else
{
p = new Plane(camrel, -pn);
if (ray.Intersects(ref p, out pos))
{
return true;
}
}
pos = Vector3.Zero;
return false;
}
protected int GetAxisIndex(WidgetAxis axis)
{
switch (axis)
{
default:
case WidgetAxis.X: return 0;
case WidgetAxis.Y: return 1;
case WidgetAxis.Z: return 2;
}
}
protected float GetWorldSize(float pxsize, float dist, Camera cam)
{
float sssize = pxsize / cam.Height;
float size = sssize * dist;
if (cam.IsMapView || cam.IsOrthographic)
{
size = sssize * cam.OrthographicSize;
}
return size;
}
}
public class TransformWidget : Widget
{
public DefaultWidget DefaultWidget { get; set; } = new DefaultWidget();
public PositionWidget PositionWidget { get; set; } = new PositionWidget();
public RotationWidget RotationWidget { get; set; } = new RotationWidget();
public ScaleWidget ScaleWidget { get; set; } = new ScaleWidget();
public bool ObjectSpace
{
get { return PositionWidget.ObjectSpace; }
set
{
DefaultWidget.ObjectSpace = value;
PositionWidget.ObjectSpace = value;
RotationWidget.ObjectSpace = value;
}
}
public Vector3 Position
{
get { return PositionWidget.Position; }
set
{
PositionWidget.Position = value;
RotationWidget.Position = value;
ScaleWidget.Position = value;
DefaultWidget.Position = value;
}
}
public Quaternion Rotation
{
get { return RotationWidget.Rotation; }
set
{
PositionWidget.Rotation = value;
RotationWidget.Rotation = value;
ScaleWidget.Rotation = value;
DefaultWidget.Rotation = value;
}
}
public Vector3 Scale
{
get { return ScaleWidget.Scale; }
set { ScaleWidget.Scale = value; }
}
public WidgetMode Mode { get; set; } = WidgetMode.Default;
public event WidgetPositionChangeHandler OnPositionChange;
public event WidgetRotationChangeHandler OnRotationChange;
public event WidgetScaleChangeHandler OnScaleChange;
public bool IsUnderMouse
{
get
{
switch (Mode)
{
default:
case WidgetMode.Default: return false;
case WidgetMode.Position: return (PositionWidget.MousedAxis != WidgetAxis.None);
case WidgetMode.Rotation: return (RotationWidget.MousedAxis != WidgetAxis.None);
case WidgetMode.Scale: return (ScaleWidget.MousedAxis != WidgetAxis.None);
}
}
}
public bool IsDragging
{
get
{
switch (Mode)
{
default:
case WidgetMode.Default: return false;
case WidgetMode.Position: return PositionWidget.IsDragging;
case WidgetMode.Rotation: return RotationWidget.IsDragging;
case WidgetMode.Scale: return ScaleWidget.IsDragging;
}
}
set
{
switch (Mode)
{
case WidgetMode.Position: PositionWidget.IsDragging = value; break;
case WidgetMode.Rotation: RotationWidget.IsDragging = value; break;
case WidgetMode.Scale: ScaleWidget.IsDragging = value; break;
}
}
}
public TransformWidget()
{
PositionWidget.OnPositionChange += PositionWidget_OnPositionChange;
RotationWidget.OnRotationChange += RotationWidget_OnRotationChange;
ScaleWidget.OnScaleChange += ScaleWidget_OnScaleChange;
}
private void PositionWidget_OnPositionChange(Vector3 newpos, Vector3 oldpos)
{
DefaultWidget.Position = newpos;
RotationWidget.Position = newpos;
ScaleWidget.Position = newpos;
OnPositionChange?.Invoke(newpos, oldpos);
}
private void RotationWidget_OnRotationChange(Quaternion newrot, Quaternion oldrot)
{
DefaultWidget.Rotation = newrot;
PositionWidget.Rotation = newrot;
ScaleWidget.Rotation = newrot;
OnRotationChange?.Invoke(newrot, oldrot);
}
private void ScaleWidget_OnScaleChange(Vector3 newscale, Vector3 oldscale)
{
OnScaleChange?.Invoke(newscale, oldscale);
}
public override void Update(Camera cam)
{
if (!Visible) return;
switch (Mode)
{
case WidgetMode.Position:
PositionWidget.Update(cam);
break;
case WidgetMode.Rotation:
RotationWidget.Update(cam);
break;
case WidgetMode.Scale:
ScaleWidget.Update(cam);
break;
case WidgetMode.Default:
DefaultWidget.Update(cam);
break;
}
}
public override void Render(DeviceContext context, Camera cam, WidgetShader shader)
{
if (!Visible) return;
switch (Mode)
{
case WidgetMode.Position:
PositionWidget.Render(context, cam, shader);
break;
case WidgetMode.Rotation:
RotationWidget.Render(context, cam, shader);
break;
case WidgetMode.Scale:
ScaleWidget.Render(context, cam, shader);
break;
case WidgetMode.Default:
DefaultWidget.Render(context, cam, shader);
break;
}
}
}
public class DefaultWidget : Widget
{
public Vector3 Position { get; set; }
public Quaternion Rotation { get; set; } = Quaternion.Identity;
public float Size { get; set; } = 70.0f;
public bool ObjectSpace { get; set; } = true;
public override void Render(DeviceContext context, Camera cam, WidgetShader shader)
{
if (!Visible) return;
Vector3 camrel = Position - cam.Position;
float dist = camrel.Length();
float size = GetWorldSize(Size, dist, cam);
var ori = ObjectSpace ? Rotation : Quaternion.Identity;
shader.DrawDefaultWidget(context, cam, camrel, ori, size);
}
public override void Update(Camera cam)
{
//nothing to update here...
}
}
public class PositionWidget : Widget
{
public Vector3 Position { get; set; }
public Quaternion Rotation { get; set; } = Quaternion.Identity;
public event WidgetPositionChangeHandler OnPositionChange;
public WidgetAxis MousedAxis { get; set; } = WidgetAxis.None;
public WidgetAxis DraggedAxis { get; set; } = WidgetAxis.None;
public Vector3 DraggedAxisDir { get; set; }
public Vector3 DraggedAxisSideDir { get; set; }
public Vector3 DragStartPosition { get; set; }
public bool IsDragging { get; set; }
private bool WasDragging = false;
private Vector3 DragStartVec; //projected onto the plane/axis being dragged
public float Size { get; set; } = 90.0f;
public bool ObjectSpace { get; set; } = true;
public PositionWidget()
{
}
public override void Render(DeviceContext context, Camera cam, WidgetShader shader)
{
if (!Visible) return;
Vector3 camrel = Position - cam.Position;
float dist = camrel.Length();
float size = GetWorldSize(Size, dist, cam);
WidgetAxis ax = IsDragging ? DraggedAxis : MousedAxis;
var ori = ObjectSpace ? Rotation : Quaternion.Identity;
shader.DrawPositionWidget(context, cam, camrel, ori, size, ax);
}
public override void Update(Camera cam)
{
if (!Visible) return;
var ori = ObjectSpace ? Rotation : Quaternion.Identity;
Vector3 xdir = Vector3.UnitX;
Vector3 ydir = Vector3.UnitY;
Vector3 zdir = Vector3.UnitZ;
Vector3[] axes = { xdir, ydir, zdir };
Vector3[] sides1 = { ydir, zdir, xdir };
Vector3[] sides2 = { zdir, xdir, ydir };
WidgetAxis[] sideax1 = { WidgetAxis.Y, WidgetAxis.Z, WidgetAxis.X };
WidgetAxis[] sideax2 = { WidgetAxis.Z, WidgetAxis.X, WidgetAxis.Y };
Quaternion iori = Quaternion.Invert(ori);
Vector3 camrel = iori.Multiply(Position - cam.Position);
Vector3 cdir = Vector3.Normalize(camrel);
Ray ray = cam.MouseRay;
ray.Position = iori.Multiply(ray.Position);
ray.Direction = iori.Multiply(ray.Direction);
float dist = camrel.Length();
float size = GetWorldSize(Size, dist, cam);
float linestart = 0.2f * size;
float lineend = 1.0f * size;
float sideval = 0.4f * size;
float arrowstart = 1.0f * size;
float arrowend = 1.33f * size;
float arrowrad = 0.06f * size;
float axhitrad = 0.07f * size;
float axhitstart = 0.2f * size;
float axhitend = 1.33f * size;
float sidehitend = 0.5f * size;
float sidehitstart = 0.25f * size;
float allhitrad = 0.07f * size;
//test for single and double axes hits
BoundingBox bb = new BoundingBox();
BoundingBox bb2 = new BoundingBox();
float hitd = float.MaxValue;
float d, d2;
WidgetAxis hitax = WidgetAxis.None;
for (int i = 0; i < 3; i++)
{
WidgetAxis ax = (WidgetAxis)(1 << i);
Vector3 s = sides1[i] * axhitrad + sides2[i] * axhitrad;
bb.Minimum = camrel - s + axes[i] * axhitstart;
bb.Maximum = camrel + s + axes[i] * axhitend;
if (ray.Intersects(ref bb, out d)) //single axis
{
if (d < hitd)
{
hitd = d;
hitax = ax;
}
}
for (int n = i + 1; n < 3; n++)
{
//double axis hit test - don't hit if in the central area (L shape hit area)
WidgetAxis ax2 = (WidgetAxis)(1 << n);
bb.Minimum = camrel;
bb.Maximum = camrel + axes[i] * sidehitend + axes[n] * sidehitend;
bb2.Minimum = camrel;
bb2.Maximum = camrel + axes[i] * sidehitstart + axes[n] * sidehitstart;
if (ray.Intersects(ref bb, out d) && !ray.Intersects(ref bb2, out d2))
{
if (d < hitd)
{
hitd = d;
hitax = ax | ax2;
}
}
}
}
//small box at the center for all axes hit.
Vector3 ss = (axes[0] + axes[1] + axes[2]) * allhitrad;
bb.Minimum = camrel - ss;
bb.Maximum = camrel + ss;
if (ray.Intersects(ref bb, out d))
{
if (d < hitd)
{
hitd = d;
hitax = WidgetAxis.XYZ;
}
}
MousedAxis = hitax;
if (IsDragging && !WasDragging)
{
//drag start. mark the start vector and axes
DraggedAxis = MousedAxis;
DragStartPosition = Position;
DraggedAxisDir = axes[0];
DraggedAxisSideDir = axes[1];
switch (DraggedAxis)
{
case WidgetAxis.XZ: DraggedAxisSideDir = axes[2]; break;
case WidgetAxis.YZ: DraggedAxisDir = axes[1]; DraggedAxisSideDir = axes[2]; break;
case WidgetAxis.Y: DraggedAxisDir = axes[1]; break;
case WidgetAxis.Z: DraggedAxisDir = axes[2]; break;
}
switch (DraggedAxis) //find the best second axis to use, for single axis motion only.
{
case WidgetAxis.X:
case WidgetAxis.Y:
case WidgetAxis.Z:
int curax = GetAxisIndex(DraggedAxis);
int minax = 0;
float mindp = float.MaxValue;
for (int i = 0; i < 3; i++)
{
if (i != curax)
{
float dp = Math.Abs(Vector3.Dot(cdir, axes[i]));
if (dp < mindp)
{
mindp = dp;
minax = i;
}
}
}
DraggedAxisSideDir = axes[minax];
break;
}
if (DraggedAxis == WidgetAxis.XYZ)
{
//all axes, move in the screen plane
float ad1 = Math.Abs(Vector3.Dot(cdir, Vector3.UnitY));
float ad2 = Math.Abs(Vector3.Dot(cdir, Vector3.UnitZ));
DraggedAxisDir = Vector3.Normalize(Vector3.Cross(cdir, (ad1 > ad2) ? Vector3.UnitY : Vector3.UnitZ));
DraggedAxisSideDir = Vector3.Normalize(Vector3.Cross(cdir, DraggedAxisDir));
}
bool hit = GetAxisRayHit(DraggedAxisDir, DraggedAxisSideDir, camrel, ray, out DragStartVec);
if ((MousedAxis == WidgetAxis.None) || !hit)
{
IsDragging = false;
}
}
else if (IsDragging)
{
//continue drag.
Vector3 newvec;
bool hit = GetAxisRayHit(DraggedAxisDir, DraggedAxisSideDir, camrel, ray, out newvec);
if (hit)
{
Vector3 diff = newvec - DragStartVec;
switch (DraggedAxis)
{
case WidgetAxis.X: diff.Y = 0; diff.Z = 0; break;
case WidgetAxis.Y: diff.X = 0; diff.Z = 0; break;
case WidgetAxis.Z: diff.X = 0; diff.Y = 0; break;
}
if (ObjectSpace)
{
diff = ori.Multiply(diff);
}
if (diff.Length() < 10000.0f) //limit movement in one go, to avoid losing the widget...
{
Vector3 oldpos = Position;
Position = DragStartPosition + diff;
if (Position != oldpos)
{
OnPositionChange?.Invoke(Position, oldpos);
}
}
}
}
WasDragging = IsDragging;
}
}
public class RotationWidget : Widget
{
public Vector3 Position { get; set; }
public Quaternion Rotation { get; set; } = Quaternion.Identity;
public event WidgetRotationChangeHandler OnRotationChange;
public WidgetAxis MousedAxis { get; set; } = WidgetAxis.None;
public WidgetAxis DraggedAxis { get; set; } = WidgetAxis.None;
public Vector3 DraggedAxisDir { get; set; }
public Vector3 DraggedAxisSideDir1 { get; set; }
public Vector3 DraggedAxisSideDir2 { get; set; }
public Quaternion DragStartRotation { get; set; }
public bool IsDragging { get; set; }
private bool WasDragging = false;
private Vector3 DragStartVec; //projected onto the plane/axis being dragged
public float Size { get; set; } = 100.0f;
public bool ObjectSpace { get; set; } = true;
public WidgetAxis EnableAxes { get; set; } = WidgetAxis.XYZ;
public RotationWidget()
{
}
public override void Render(DeviceContext context, Camera cam, WidgetShader shader)
{
if (!Visible) return;
Vector3 camrel = Position - cam.Position;
float dist = camrel.Length();
float size = GetWorldSize(Size, dist, cam);
WidgetAxis ax = IsDragging ? DraggedAxis : MousedAxis;
var ori = ObjectSpace ? Rotation : Quaternion.Identity;
shader.DrawRotationWidget(context, cam, camrel, ori, size, ax, EnableAxes);
}
public override void Update(Camera cam)
{
if (!Visible) return;
var ori = ObjectSpace ? Rotation : Quaternion.Identity;
Vector3 xdir = ori.Multiply(Vector3.UnitX);
Vector3 ydir = ori.Multiply(Vector3.UnitY);
Vector3 zdir = ori.Multiply(Vector3.UnitZ);
Vector3[] axes = { xdir, ydir, zdir };
Vector3[] sides1 = { ydir, zdir, xdir };
Vector3[] sides2 = { zdir, xdir, ydir };
Ray ray = cam.MouseRay;
Vector3 camrel = Position - cam.Position;
float dist = camrel.Length();
float size = GetWorldSize(Size, dist, cam);
float ocircsize = 1.0f * size; //outer ring radius
float icircsize = 0.75f * size; //inner ring radius
float icircthick = 0.2f * size; //inner ring hit width
float ocircthick = 0.13f * size;//outer ring hit width
float icirchiti = icircsize - icircthick;
float icirchito = icircsize + icircthick;
float ocirchiti = ocircsize - ocircthick;
float ocirchito = ocircsize + ocircthick;
//test for the main axes hits
float cullvalue = -0.18f;
float hitd = float.MaxValue;
Vector3 hitp = camrel;
Vector3 hitrel = Vector3.Zero;
WidgetAxis hitax = WidgetAxis.None;
Vector3 hitaxd = Vector3.UnitX;
Vector3 hitax1 = Vector3.UnitY;
Vector3 hitax2 = Vector3.UnitZ;
for (int i = 0; i < 3; i++)
{
WidgetAxis ax = (WidgetAxis)(1 << i);
if ((ax & EnableAxes) == 0) continue;
Vector3 s1 = sides1[i];
Vector3 s2 = sides2[i];
if (GetAxisRayHit(s1, s2, camrel, ray, out hitp))
{
float hitdist = hitp.Length();
float hitreld = (camrel.Length() - hitdist) / size;
if (hitreld < cullvalue) continue; //this hit was at the backside of the widget; ignore
Vector3 thitrel = hitp - camrel;
float hitrad = thitrel.Length();
if ((hitrad > icirchiti) && (hitrad < icirchito) && (hitdist < hitd))
{
hitd = hitdist;
hitax = ax;
hitax1 = s1;
hitax2 = s2;
hitrel = thitrel;
hitaxd = axes[i];
}
}
}
//test for the outer ring hit
if ((hitax == WidgetAxis.None) && (EnableAxes == WidgetAxis.XYZ))
{
Vector3 sdir = Vector3.Normalize(camrel);
//if (cam.IsMapView || cam.IsOrthographic)
//{
// sdir = cam.ViewDirection;
//}
float ad1 = Math.Abs(Vector3.Dot(sdir, Vector3.UnitY));
float ad2 = Math.Abs(Vector3.Dot(sdir, Vector3.UnitZ));
Vector3 ax1 = Vector3.Normalize(Vector3.Cross(sdir, (ad1 > ad2) ? Vector3.UnitY : Vector3.UnitZ));
Vector3 ax2 = Vector3.Normalize(Vector3.Cross(sdir, ax1));
if (GetAxisRayHit(ax1, ax2, camrel, ray, out hitp))
{
Vector3 thitrel = hitp - camrel;
float hitrad = thitrel.Length();
if ((hitrad > ocirchiti) && (hitrad < ocirchito))
{
hitax = WidgetAxis.XYZ;
hitax1 = ax1;
hitax2 = ax2;
hitrel = thitrel;
hitaxd = sdir;
}
}
}
MousedAxis = hitax;
if (IsDragging && !WasDragging)
{
//drag start. mark the start vector and axes
DraggedAxis = MousedAxis;
DragStartRotation = Rotation;
DraggedAxisDir = hitaxd;
DraggedAxisSideDir1 = hitax1;
DraggedAxisSideDir2 = hitax2;
bool hit = GetAxisRayHit(DraggedAxisSideDir1, DraggedAxisSideDir2, camrel, ray, out DragStartVec);
if ((MousedAxis == WidgetAxis.None) || !hit)
{
IsDragging = false;
}
}
else if (IsDragging)
{
//continue drag.
Vector3 newvec;
bool hit = GetAxisRayHit(DraggedAxisSideDir1, DraggedAxisSideDir2, camrel, ray, out newvec);
if (hit)
{
Vector3 diff = newvec - DragStartVec;
if (diff.Length() < 10000.0f) //put some limit to the plane intersection...
{
Vector3 nv = Vector3.Normalize(newvec - camrel);
Vector3 ov = Vector3.Normalize(DragStartVec - camrel);
float na = AngleOnAxes(nv, DraggedAxisSideDir1, DraggedAxisSideDir2);
float oa = AngleOnAxes(ov, DraggedAxisSideDir1, DraggedAxisSideDir2);
float a = na - oa;
Quaternion rot = Quaternion.RotationAxis(DraggedAxisDir, a);
Quaternion oldrot = Rotation;
Rotation = Quaternion.Normalize(Quaternion.Multiply(rot, DragStartRotation));
if (Rotation != oldrot)
{
OnRotationChange?.Invoke(Rotation, oldrot);
}
}
}
}
WasDragging = IsDragging;
}
private float AngleOnAxes(Vector3 v, Vector3 ax1, Vector3 ax2)
{
float d1 = Vector3.Dot(v, ax1);
float d2 = Vector3.Dot(v, ax2);
return (float)Math.Atan2(d2, d1);
}
}
public class ScaleWidget : Widget
{
public Vector3 Position { get; set; }
public Quaternion Rotation { get; set; } = Quaternion.Identity;
public Vector3 Scale { get; set; } = Vector3.One;
public event WidgetScaleChangeHandler OnScaleChange;
public WidgetAxis MousedAxis { get; set; } = WidgetAxis.None;
public WidgetAxis DraggedAxis { get; set; } = WidgetAxis.None;
public Vector3 DraggedAxisDir { get; set; }
public Vector3 DraggedAxisSideDir { get; set; }
public Vector3 DragStartScale { get; set; }
public bool IsDragging { get; set; }
private bool WasDragging = false;
private Vector3 DragStartVec; //projected onto the plane/axis being dragged
public bool LockXY { get; set; } = true;
public float Size { get; set; } = 90.0f;
public ScaleWidget()
{
}
public override void Render(DeviceContext context, Camera cam, WidgetShader shader)
{
if (!Visible) return;
Vector3 camrel = Position - cam.Position;
float dist = camrel.Length();
float size = GetWorldSize(Size, dist, cam);
WidgetAxis ax = IsDragging ? DraggedAxis : MousedAxis;
var ori = Rotation; //scale is always in object space.
shader.DrawScaleWidget(context, cam, camrel, ori, size, ax);
}
public override void Update(Camera cam)
{
if (!Visible) return;
var ori = Rotation;// : Quaternion.Identity;
Vector3 xdir = Vector3.UnitX;
Vector3 ydir = Vector3.UnitY;
Vector3 zdir = Vector3.UnitZ;
Vector3[] axes = { xdir, ydir, zdir };
Vector3[] sides1 = { ydir, zdir, xdir };
Vector3[] sides2 = { zdir, xdir, ydir };
WidgetAxis[] sideax1 = { WidgetAxis.Y, WidgetAxis.Z, WidgetAxis.X };
WidgetAxis[] sideax2 = { WidgetAxis.Z, WidgetAxis.X, WidgetAxis.Y };
Quaternion iori = Quaternion.Invert(ori);
Vector3 camrel = iori.Multiply(Position - cam.Position);
Vector3 cdir = Vector3.Normalize(camrel);
Ray ray = cam.MouseRay;
ray.Position = iori.Multiply(ray.Position);
ray.Direction = iori.Multiply(ray.Direction);
float dist = camrel.Length();
float size = GetWorldSize(Size, dist, cam);
float axhitrad = 0.09f * size;
float axhitstart = 0.4f * size;
float axhitend = 1.33f * size;
float innertri = 0.7f * size;
float outertri = 1.0f * size;
//test for single and double axes hits
BoundingBox bb = new BoundingBox();
float hitd = float.MaxValue;
float d;
Vector3 hitp;
WidgetAxis hitax = WidgetAxis.None;
for (int i = 0; i < 3; i++)
{
WidgetAxis ax = (WidgetAxis)(1 << i);
Vector3 s = sides1[i] * axhitrad + sides2[i] * axhitrad;
bb.Minimum = camrel - s + axes[i] * axhitstart;
bb.Maximum = camrel + s + axes[i] * axhitend;
if (ray.Intersects(ref bb, out d)) //single axis
{
if (d < hitd)
{
hitd = d;
hitax = ax;
}
}
Vector3 s1 = axes[i];
Vector3 s2 = sides1[i];
if (GetAxisRayHit(s1, s2, camrel, ray, out hitp))
{
//test if hitp is within the inner triangle - uniform scale
//test if hitp is within the outer triangle - 2 axes scale
float hitpl = hitp.Length();
if (hitpl > hitd) continue;
Vector3 hitrel = hitp - camrel;
float d1 = Vector3.Dot(hitrel, s1);
float d2 = Vector3.Dot(hitrel, s2);
if ((d1 > 0) && (d2 > 0))
{
if ((d1 < innertri) && (d2 < innertri) && ((d1 + d2) < innertri))
{
hitd = hitpl;
hitax = WidgetAxis.XYZ;
}
else if ((d1 < outertri) && (d2 < outertri) && ((d1 + d2) < outertri))
{
hitd = hitpl;
hitax = ax | sideax1[i];
}
}
}
}
if (LockXY)
{
switch (hitax)
{
case WidgetAxis.X:
case WidgetAxis.Y:
hitax = WidgetAxis.XY;
break;
case WidgetAxis.XZ:
case WidgetAxis.YZ:
hitax = WidgetAxis.XYZ;
break;
}
}
MousedAxis = hitax;
if (IsDragging && !WasDragging)
{
//drag start. mark the start vector and axes
DraggedAxis = MousedAxis;
DragStartScale = Scale;
DraggedAxisDir = axes[0];
DraggedAxisSideDir = axes[1];
switch (DraggedAxis)
{
case WidgetAxis.XZ: DraggedAxisSideDir = axes[2]; break;
case WidgetAxis.YZ: DraggedAxisDir = axes[1]; DraggedAxisSideDir = axes[2]; break;
case WidgetAxis.Y: DraggedAxisDir = axes[1]; break;
case WidgetAxis.Z: DraggedAxisDir = axes[2]; break;
}
switch (DraggedAxis) //find the best second axis to use, for single axis motion only.
{
case WidgetAxis.X:
case WidgetAxis.Y:
case WidgetAxis.Z:
int curax = GetAxisIndex(DraggedAxis);
int minax = 0;
float mindp = float.MaxValue;
for (int i = 0; i < 3; i++)
{
if (i != curax)
{
float dp = Math.Abs(Vector3.Dot(cdir, axes[i]));
if (dp < mindp)
{
mindp = dp;
minax = i;
}
}
}
DraggedAxisSideDir = axes[minax];
break;
}
if (DraggedAxis == WidgetAxis.XYZ)
{
//all axes, move in the screen plane
float ad1 = Math.Abs(Vector3.Dot(cdir, Vector3.UnitY));
float ad2 = Math.Abs(Vector3.Dot(cdir, Vector3.UnitZ));
DraggedAxisDir = Vector3.Normalize(Vector3.Cross(cdir, (ad1 > ad2) ? Vector3.UnitY : Vector3.UnitZ));
DraggedAxisSideDir = Vector3.Normalize(Vector3.Cross(cdir, DraggedAxisDir));
}
bool hit = GetAxisRayHit(DraggedAxisDir, DraggedAxisSideDir, camrel, ray, out DragStartVec);
if ((MousedAxis == WidgetAxis.None) || !hit)
{
IsDragging = false;
}
}
else if (IsDragging)
{
//continue drag.
Vector3 newvec;
bool hit = GetAxisRayHit(DraggedAxisDir, DraggedAxisSideDir, camrel, ray, out newvec);
if (hit)
{
Vector3 diff = newvec - DragStartVec;
switch (DraggedAxis)
{
case WidgetAxis.X: diff.Y = 0; diff.Z = 0; break;
case WidgetAxis.Y: diff.X = 0; diff.Z = 0; break;
case WidgetAxis.Z: diff.X = 0; diff.Y = 0; break;
}
//diff = ori.Multiply(diff);
Vector3 ods = DragStartVec - camrel;// ori.Multiply(DragStartVec);
float odl = Math.Max(ods.Length(), 0.0001f); //don't divide by 0
float ndl = Math.Max((ods + diff).Length(), 0.001f); //don't scale to 0 size
float dl = ndl / odl;
if (diff.Length() < 10000.0f) //limit movement in one go, to avoid crazy values...
{
Vector3 oldscale = Scale;
Vector3 sv = Vector3.One;
switch (DraggedAxis)
{
case WidgetAxis.X: sv = new Vector3(dl, 1, 1); break;
case WidgetAxis.Y: sv = new Vector3(1, dl, 1); break;
case WidgetAxis.Z: sv = new Vector3(1, 1, dl); break;
case WidgetAxis.XY: sv = new Vector3(dl, dl, 1); break;
case WidgetAxis.YZ: sv = new Vector3(1, dl, dl); break;
case WidgetAxis.XZ: sv = new Vector3(dl, 1, dl); break;
case WidgetAxis.XYZ: sv = new Vector3(dl); break;
}
Scale = DragStartScale * sv;
if (Scale != oldscale)
{
OnScaleChange?.Invoke(Scale, oldscale);
}
}
}
}
WasDragging = IsDragging;
}
}
public delegate void WidgetPositionChangeHandler(Vector3 newpos, Vector3 oldpos);
public delegate void WidgetRotationChangeHandler(Quaternion newrot, Quaternion oldrot);
public delegate void WidgetScaleChangeHandler(Vector3 newscale, Vector3 oldscale);
public enum WidgetMode
{
Default = 0,
Position = 1,
Rotation = 2,
Scale = 3,
}
public enum WidgetAxis
{
None = 0,
X = 1,
Y = 2,
Z = 4,
XY = 3,
XZ = 5,
YZ = 6,
XYZ = 7,
}
}