///// <summary> ///// Calculation help constant: (approx.) 1 / √(3). ///// </summary> //private const float invSqrt3 = 0.57735026919f; ///// <summary> ///// Apply krash to all meshes in this part. ///// </summary> ///// <param name="krash">Krash to apply.</param> ///// <param name="fireEvent">Fire "DamageReceived" event.</param> //public void ApplyKrash(Krash krash, bool fireEvent = true) //{ // Vector3 relativeVelocity = part.transform.TransformDirection(krash.RelativeVelocity); //Transform the direction of the collision to the world reference frame. // //Remove previous log entry. // FlightLogger.eventLog.Remove($"{part.name} (ID: {part.flightID}) was damaged {(Damage * 100).ToString("0.00")}%"); // Damage += (relativeVelocity.magnitude / part.crashTolerance) / _damageDivider; // //Add new log entry. // FlightLogger.eventLog.Add($"{part.name} (ID: {part.flightID}) was damaged {(Damage * 100).ToString("0.00")}%"); // //Fire "DamageReceived" event. // if (fireEvent && DamageReceived != null) // DamageReceived(this, Damage); // if (_exclude) // return; // //Dent transformation is a maximum of 75% of the part size. // Vector3 transform = (relativeVelocity / (0.75f * part.partInfo.partSize) / (part.crashTolerance / Malleability)); // Vector3 worldPosition = part.transform.TransformPoint(krash.ContactPoint); // DeformMesh(transform, worldPosition); // DeformCollider(transform, worldPosition); //} ///// <summary> ///// Updates the visual components of the part. ///// Thanks Ryan Bray (https://github.com/rbray89). ///// </summary> ///// <param name="transform">Vector indicating the amount of deformation.</param> ///// <param name="worldPosition">Position in the world to apply the deformation from.</param> //private void DeformMesh(Vector3 transform, Vector3 worldPosition) //{ // foreach (MeshFilter meshFilter in meshFilters) // { // Mesh mesh = meshFilter.mesh; // if (meshFilter.sharedMesh == null) // continue; // if (mesh == null) // mesh = meshFilter.sharedMesh; // if (_subdivide && !subdivided && part.partInfo.partSize >= MeshSubdivisionThreshold) // { // subdivided = true; // MeshHelper.Subdivide(mesh, worldPosition, DentDistance); // } // Vector3 transformT = meshFilter.transform.InverseTransformVector(transform); // Vector3 contactPointLocal = meshFilter.transform.InverseTransformPoint(worldPosition); // Vector3 dentDistanceLocal = meshFilter.transform.TransformDirection(Vector3.one).normalized; // dentDistanceLocal = meshFilter.transform.InverseTransformVector(DentDistance * dentDistanceLocal); // dentDistanceLocal = Vector3.Max(-dentDistanceLocal, dentDistanceLocal); // Vector3 dentDistanceInv; // dentDistanceInv.x = invSqrt3 / dentDistanceLocal.x; // dentDistanceInv.y = invSqrt3 / dentDistanceLocal.y; // dentDistanceInv.z = invSqrt3 / dentDistanceLocal.z; // Vector3[] vertices = mesh.vertices; // Color32[] Colors = new Color32[vertices.Length]; // for (int i = 0; i < vertices.Length; i++) // { // Vector3 distance = vertices[i] - contactPointLocal; // distance = Vector3.Max(-distance, distance); // distance = dentDistanceLocal - distance; // distance.Scale(dentDistanceInv); // if (distance.x < 0 || distance.y < 0 || distance.z < 0) // continue; // Colors[i] = Color32.Lerp(new Color32(255, 255, 255, 255), new Color(0, 0, 0, 255), Damage); // vertices[i] += distance.sqrMagnitude * transformT; // } // mesh.vertices = vertices; // //TODO: Make this a KKS-mod // //mesh.colors32 = Colors; // } //} #endregion #region Experimental /// <summary> /// Apply krash to all meshes in this part. /// </summary> /// <param name="krash">Krash to apply.</param> /// <param name="fireEvent">Fire "DamageReceived" event.</param> public void ApplyKrash(Krash krash, bool fireEvent = true) { Vector3 relativeVelocity = part.transform.TransformDirection(krash.RelativeVelocity); //Transform the direction of the collision to the world reference frame. //Remove previous log entry. FlightLogger.eventLog.Remove($"{part.name} (ID: {part.flightID}) was damaged {(Damage * 100).ToString("0.00")}%"); Damage += (relativeVelocity.magnitude / part.crashTolerance) / _damageDivider; //Add new log entry. FlightLogger.eventLog.Add($"{part.name} (ID: {part.flightID}) was damaged {(Damage * 100).ToString("0.00")}%"); //Fire "DamageReceived" event. if (fireEvent && DamageReceived != null) { DamageReceived(this, Damage); } if (_exclude) { return; } //Dent transformation. Vector3 transform = (relativeVelocity / (part.crashTolerance / Malleability)); Vector3 worldPosition = part.transform.TransformPoint(krash.ContactPoint); DeformMesh(transform, worldPosition); DeformCollider(transform, worldPosition); }
/// <summary> /// Called when this part enters a collision with another object. /// </summary> /// <param name="collision">Collision object containing information about the collision.</param> protected virtual void OnCollisionEnter(Collision collision) { if (!HighLogic.LoadedSceneIsFlight) { return; } //Transform the velocity of the collision into the reference frame of the part. Vector3 relativeVelocity = part.transform.InverseTransformDirection(collision.relativeVelocity); float angle = Vector3.Angle(relativeVelocity, part.transform.InverseTransformDirection(collision.contacts[0].normal)); //If collision occurs under a large angle, damage is ignored for now. //TODO: angle: [70, 90>: SCRAPING. ADD TEXTURES? if (angle > 70) { return; } //Convert angle to [0, 1]. angle = Mathf.Cos(Mathf.Deg2Rad * angle); //Scale the impact velocity by the angle. relativeVelocity *= angle; ////Limit collisions to one per collision delay. //if (_collisionDelayCounter < _collisionDelay) // return; ////Reset the physics counter. //_collisionDelayCounter = 0; //Only receive damage if part exists and relative velocity is greater than the original tolerance divided malleability of the part. if (part == null || relativeVelocity.magnitude <= OriginalCrashTolerance) { return; } //No need to do anything if the damage is neglible. if (relativeVelocity.magnitude / part.crashTolerance <= 0) { return; } Krash krash = new Krash { RelativeVelocity = relativeVelocity, //Transform the direction of the collision to the reference frame of the part. ContactPoint = part.transform.InverseTransformPoint(collision.contacts[0].point), }; Krashes.Add(krash); ApplyKrash(krash); }
protected virtual void FixedUpdate() { if (!HighLogic.LoadedSceneIsFlight || part == null) { _splashed = false; return; } //Shield highlight decay shieldVisible -= (float)0.006; shieldVisible = Math.Min(1, Math.Max(0, shieldVisible)); SetHighlighting(); //Get the altitude of the part, instead of the altitude of the vessel. double partAltitude = FlightGlobals.getAltitudeAtPos(part.transform.position, FlightGlobals.currentMainBody); if (partAltitude > 0) { return; } //Only receive damage if part's velocity is greater than the original tolerance divided malleability of the part. double scaledHorizontalSpeed = part.vessel.horizontalSrfSpeed / HorizontalSplashdownScaling; if (part.vessel.verticalSpeed + scaledHorizontalSpeed <= (OriginalCrashTolerance / Malleability)) { return; } //Already splashed down. if (_splashed) { return; } _splashed = true; //Closest part to the core (lowest point on the collider). This should be faster than checking all vertices. Vector3 contactPoint = part.collider.ClosestPointOnBounds(FlightGlobals.currentMainBody.position); //Transform the direction of the collision to the reference frame of the part and scale it down a bit to match the actual mesh a bit better. contactPoint = part.transform.InverseTransformPoint(contactPoint) / part.collider.bounds.size.magnitude; Krash krash = new Krash { ContactPoint = contactPoint, RelativeVelocity = new Vector3((float)scaledHorizontalSpeed, (float)part.vessel.verticalSpeed), }; //Fire "Splashdown" event. if (Splashdown != null) { Splashdown(this, krash); } }
/// <summary> /// Called when this part gets loaded. /// </summary> /// <param name="node"></param> public override void OnLoad(ConfigNode node) { base.OnLoad(node); //No need to load krashes when not in Flight Scene or non-existent parts/vessels. if (!HighLogic.LoadedSceneIsFlight || part == null || part.vessel == null) { return; } #if DEBUG Debug.Log($"OnLoad: {part.name}"); #endif //Clear damage and krashes. Damage = 0; Krashes = new List <Krash>(); foreach (ConfigNode cn in node.nodes) { if (cn.name != "Krash") { continue; //No krash to apply. } Vector3 relativeVelocity = new Vector3(float.Parse(cn.GetValue("RelativeVelocity.x")), float.Parse(cn.GetValue("RelativeVelocity.y")), float.Parse(cn.GetValue("RelativeVelocity.z"))); Vector3 contactPoint = new Vector3(float.Parse(cn.GetValue("ContactPoint.x")), float.Parse(cn.GetValue("ContactPoint.y")), float.Parse(cn.GetValue("ContactPoint.z"))); Krash krash = new Krash { //Load the relative velocity of the saved krash. RelativeVelocity = relativeVelocity, //Load the position of the saved krash. ContactPoint = contactPoint, }; Krashes.Add(krash); ApplyKrash(krash); } #if DEBUG if (Krashes.Count > 0) { Debug.Log($"[KerbalKrashSystem] Loaded {Krashes.Count} krashes for {part.name} (part ID: {part.flightID})"); } #endif }
//private bool subdivided = false; /// <summary> /// Apply krash to all meshes in this part. /// </summary> /// <param name="krash">Krash to apply.</param> /// <param name="fireEvent">Fire "DamageReceived" event.</param> public void ApplyKrash(Krash krash, bool fireEvent = true) { Vector3 relativeVelocity = part.transform.TransformDirection(krash.RelativeVelocity); //Transform the direction of the collision to the world reference frame. Damage += (relativeVelocity.magnitude / part.crashTolerance) / _damageDivider; //Fire "DamageReceived" event. if (fireEvent && DamageReceived != null) { DamageReceived(this, Damage); } if (_exclude) { return; } Vector3 transform = (relativeVelocity / (1f * part.partInfo.partSize) / (part.crashTolerance / Malleability)); Vector3 worldPosition = part.transform.TransformPoint(krash.ContactPoint); DeformMesh(transform, worldPosition); DeformCollider(transform, worldPosition); }
/// <summary> /// Called when this part enters a collision with another object. /// </summary> /// <param name="collision">Collision object containing information about the collision.</param> protected virtual void OnCollisionEnter(Collision collision) { //Only receive damage if part exists and relative velocity is greater than the original tolerance divided malleability of the part. if (part == null || collision.relativeVelocity.magnitude <= (OriginalCrashTolerance / Malleability)) { return; } //TODO: Temporary fix. foreach (ContactPoint contactPoint in collision.contacts) { if (contactPoint.thisCollider is WheelCollider || contactPoint.otherCollider is WheelCollider) { return; } } //No need to do anything if the damage is neglible. if (collision.relativeVelocity.magnitude / part.crashTolerance <= 0) { return; } Krash krash = new Krash { //Transform the velocity of the collision into the reference frame of the part. RelativeVelocity = part.transform.InverseTransformDirection(collision.relativeVelocity), //Transform the direction of the collision to the reference frame of the part. ContactPoint = part.transform.InverseTransformPoint(collision.contacts[0].point), }; Krashes.Add(krash); ApplyKrash(krash); }
private void OnSplashdown(KerbalKrashSystem sender, Krash krash) { Krashes.Add(krash); ApplyKrash(krash); }
/// <summary> /// Called when this part enters a collision with another object. /// </summary> /// <param name="collision">Collision object containing information about the collision.</param> protected virtual void OnCollisionEnter(Collision collision) { if (!HighLogic.LoadedSceneIsFlight || (vessel != null && vessel.isEVA)) { return; } //Transform the velocity of the collision into the reference frame of the part. Vector3 relativeVelocity = part.transform.InverseTransformDirection(collision.relativeVelocity); float angle = Vector3.Angle(relativeVelocity, part.transform.InverseTransformDirection(collision.contacts[0].normal)); //If collision occurs under a large angle, damage is ignored for now. // if (angle > 70) // { // //TODO: angle: [70, 90>: SCRAPING. ADD TEXTURES? //#if DEBUG // Debug.Log("Angle too steep, no damage."); //#endif // return; // } //Convert angle to [0, 1]. angle = Mathf.Cos(Mathf.Deg2Rad * angle); //Scale the impact velocity by the angle. relativeVelocity *= angle; //Only receive damage if part exists and relative velocity is greater than the original tolerance divided malleability of the part. if (part == null || relativeVelocity.magnitude <= OriginalCrashTolerance) { return; } //No need to do anything if the damage is neglible. if (relativeVelocity.magnitude / part.crashTolerance <= 0) { return; } //Clamp the local position to the bounds of the part. Vector3 local_position = part.transform.InverseTransformPoint(collision.contacts[0].point); local_position = new Vector3 ( Mathf.Clamp(local_position.x, -part.collider.bounds.extents.x, part.collider.bounds.extents.x), Mathf.Clamp(local_position.y, -part.collider.bounds.extents.y, part.collider.bounds.extents.y), Mathf.Clamp(local_position.z, -part.collider.bounds.extents.z, part.collider.bounds.extents.z) ); Krash krash = new Krash { RelativeVelocity = relativeVelocity, //Transform the direction of the collision to the reference frame of the part. ContactPoint = local_position, }; Krashes.Add(krash); ApplyKrash(krash); }
/// <summary> /// Apply krash to all meshes in this part. /// </summary> /// <param name="krash">Krash to apply.</param> /// <param name="inverse">Apply or undo krash.</param> protected void ApplyKrash(Krash krash) { Vector3 relativeVelocity = part.transform.TransformDirection(krash.RelativeVelocity); //Transform the direction of the collision to the world reference frame. Damage += (relativeVelocity.magnitude / part.crashTolerance); Vector3 worldPosContact = part.transform.TransformPoint(krash.ContactPoint); MeshFilter[] meshList = part.FindModelComponents <MeshFilter>(); Vector3 transform = (relativeVelocity / (1f * part.partInfo.partSize) / (part.crashTolerance / Malleability)); float invSqrt3 = 1f / Mathf.Sqrt(3); foreach (MeshFilter meshFilter in meshList) { Mesh mesh = meshFilter.mesh; if (meshFilter.sharedMesh == null) { continue; } if (mesh == null) { mesh = meshFilter.sharedMesh; } Vector3 transformT = meshFilter.transform.InverseTransformVector(transform); Vector3 contactPointLocal = meshFilter.transform.InverseTransformPoint(worldPosContact); Vector3 dentDistanceLocal = meshFilter.transform.TransformDirection(Vector3.one).normalized; dentDistanceLocal = meshFilter.transform.InverseTransformVector(DentDistance * dentDistanceLocal); dentDistanceLocal = Vector3.Max(-dentDistanceLocal, dentDistanceLocal); Vector3 dentDistanceInv; dentDistanceInv.x = invSqrt3 / dentDistanceLocal.x; dentDistanceInv.y = invSqrt3 / dentDistanceLocal.y; dentDistanceInv.z = invSqrt3 / dentDistanceLocal.z; Vector3[] vertices = mesh.vertices; for (int i = 0; i < vertices.Length; i++) { Vector3 distance = vertices[i] - contactPointLocal; distance = Vector3.Max(-distance, distance); distance = dentDistanceLocal - distance; distance.Scale(dentDistanceInv); if (distance.x < 0 || distance.y < 0 || distance.z < 0) { continue; } vertices[i] += distance.sqrMagnitude * transformT; } mesh.vertices = vertices; meshFilter.mesh = mesh; } //Fire "DamageReceived" event. if (DamageReceived != null) { DamageReceived(this, Damage); } }
/// <summary> /// Called when this part gets loaded. /// </summary> /// <param name="node"></param> public override void OnLoad(ConfigNode node) { base.OnLoad(node); //No need to load krashes when not in Flight Scene or non-existent parts/vessels. if (!HighLogic.LoadedSceneIsFlight || part == null || part.vessel == null) return; //Clear damage and krashes. Damage = 0; Krashes = new List<Krash>(); foreach (ConfigNode cn in node.nodes) { if (cn.name != "Krash") continue; //No krash to apply. Vector3 relativeVelocity = new Vector3(float.Parse(cn.GetValue("RelativeVelocity.x")), float.Parse(cn.GetValue("RelativeVelocity.y")), float.Parse(cn.GetValue("RelativeVelocity.z"))); Vector3 contactPoint = new Vector3(float.Parse(cn.GetValue("ContactPoint.x")), float.Parse(cn.GetValue("ContactPoint.y")), float.Parse(cn.GetValue("ContactPoint.z"))); Krash krash = new Krash { //Load the relative velocity of the saved krash. RelativeVelocity = relativeVelocity, //Load the position of the saved krash. ContactPoint = contactPoint, }; Krashes.Add(krash); ApplyKrash(krash); } #if DEBUG if (Krashes.Count > 0) Debug.Log("[KerbalKrashSystem] Applied " + Krashes.Count + " krashes for part ID: " + part.flightID); #endif }
/// <summary> /// Called when this part enters a collision with another object. /// </summary> /// <param name="collision">Collision object containing information about the collision.</param> protected virtual void OnCollisionEnter(Collision collision) { //Only receive damage if part exists and relative velocity is greater than the original tolerance divided malleability of the part. if (part == null || collision.relativeVelocity.magnitude <= (OriginalCrashTolerance / Malleability)) return; //TODO: Temporary fix. foreach (ContactPoint contactPoint in collision.contacts) { if (contactPoint.thisCollider is WheelCollider || contactPoint.otherCollider is WheelCollider) return; } //No need to do anything if the damage is neglible. if (collision.relativeVelocity.magnitude / part.crashTolerance <= 0) return; Krash krash = new Krash { //Transform the velocity of the collision into the reference frame of the part. RelativeVelocity = part.transform.InverseTransformDirection(collision.relativeVelocity), //Transform the direction of the collision to the reference frame of the part. ContactPoint = part.transform.InverseTransformPoint(collision.contacts[0].point), }; Krashes.Add(krash); ApplyKrash(krash); }
/// <summary> /// Apply krash to all meshes in this part. /// </summary> /// <param name="krash">Krash to apply.</param> /// <param name="inverse">Apply or undo krash.</param> protected void ApplyKrash(Krash krash) { Vector3 relativeVelocity = part.transform.TransformDirection(krash.RelativeVelocity); //Transform the direction of the collision to the world reference frame. Damage += (relativeVelocity.magnitude / part.crashTolerance); Vector3 worldPosContact = part.transform.TransformPoint(krash.ContactPoint); MeshFilter[] meshList = part.FindModelComponents<MeshFilter>(); Vector3 transform = (relativeVelocity / (1f * part.partInfo.partSize) / (part.crashTolerance / Malleability)); float invSqrt3 = 1f/Mathf.Sqrt(3); foreach (MeshFilter meshFilter in meshList) { Mesh mesh = meshFilter.mesh; if (meshFilter.sharedMesh == null) continue; if (mesh == null) mesh = meshFilter.sharedMesh; Vector3 transformT = meshFilter.transform.InverseTransformVector(transform); Vector3 contactPointLocal = meshFilter.transform.InverseTransformPoint(worldPosContact); Vector3 dentDistanceLocal = meshFilter.transform.TransformDirection(Vector3.one).normalized; dentDistanceLocal = meshFilter.transform.InverseTransformVector(DentDistance * dentDistanceLocal); dentDistanceLocal = Vector3.Max(-dentDistanceLocal, dentDistanceLocal); Vector3 dentDistanceInv; dentDistanceInv.x = invSqrt3 / dentDistanceLocal.x; dentDistanceInv.y = invSqrt3 / dentDistanceLocal.y; dentDistanceInv.z = invSqrt3 / dentDistanceLocal.z; Vector3[] vertices = mesh.vertices; for (int i = 0; i < vertices.Length; i++) { Vector3 distance = vertices[i] - contactPointLocal; distance = Vector3.Max(-distance, distance); distance = dentDistanceLocal - distance; distance.Scale(dentDistanceInv); if (distance.x < 0 || distance.y < 0 || distance.z < 0) continue; vertices[i] += distance.sqrMagnitude * transformT; } mesh.vertices = vertices; meshFilter.mesh = mesh; } //Fire "DamageReceived" event. if (DamageReceived != null) DamageReceived(this, Damage); }
/// <summary> /// Called when this part enters a collision with another object. /// </summary> /// <param name="collision">Collision object containing information about the collision.</param> protected virtual void OnCollisionEnter(Collision collision) { if (!HighLogic.LoadedSceneIsFlight || vessel.isEVA) { return; } //Transform the velocity of the collision into the reference frame of the part. Vector3 relativeVelocity = part.transform.InverseTransformDirection(collision.relativeVelocity); float angle = Vector3.Angle(relativeVelocity, part.transform.InverseTransformDirection(collision.contacts[0].normal)); //If collision occurs under a large angle, damage is ignored for now. // if (angle > 70) // { // //TODO: angle: [70, 90>: SCRAPING. ADD TEXTURES? //#if DEBUG // Debug.Log("Angle too steep, no damage."); //#endif // return; // } //Convert angle to [0, 1]. angle = Mathf.Cos(Mathf.Deg2Rad * angle); //Scale the impact velocity by the angle. relativeVelocity *= angle; //Impact damage drains shield charge until zero, then causes residual damage to part if (part.Resources.Contains("ShieldCharge") && relativeVelocity.magnitude > 5) { double charge = part.Resources.Get("ShieldCharge").amount; double maxCharge = part.Resources.Get("ShieldCharge").maxAmount; double impact = relativeVelocity.magnitude * part.mass; impact *= 0.5; //scale down impact force if (charge >= impact) { part.RequestResource(ShieldCharge.id, impact); relativeVelocity *= 0; shieldVisible += (float)(impact / maxCharge * 2.0); } else { part.RequestResource(ShieldCharge.id, charge); relativeVelocity *= (float)(1 - charge / impact); shieldVisible += (float)(charge / maxCharge * 2.0); } shieldVisible = Math.Min(1, Math.Max(0, shieldVisible)); } //Only receive damage if part exists and relative velocity is greater than the original tolerance divided malleability of the part. if (part == null || relativeVelocity.magnitude <= OriginalCrashTolerance) { return; } //No need to do anything if the damage is neglible. if (relativeVelocity.magnitude / part.crashTolerance <= 0) { return; } Krash krash = new Krash { RelativeVelocity = relativeVelocity, //Transform the direction of the collision to the reference frame of the part. ContactPoint = part.transform.InverseTransformPoint(collision.contacts[0].point), }; Krashes.Add(krash); ApplyKrash(krash); }