/// <summary> /// Establece que la primitiva actual ha sido contactada por otra primitiva de colisión /// </summary> /// <param name="primitive">Primitiva de colisión que ha contactado con la actual</param> public virtual void PrimitiveContacted(CollisionPrimitive primitive) { if (OnPrimitiveContacted != null) { OnPrimitiveContacted(primitive); } }
/// <summary> /// Aplica la fuerza del amortiguador al objeto especificado /// </summary> /// <param name="obj">Objeto</param> /// <param name="duration">Duración</param> public override void UpdateForce(ref IPhysicObject obj, float duration) { // Obtener el cuerpo del objeto CollisionPrimitive primitive = obj.Primitive; if (primitive != null) { // Calculate the two ends in world space Vector3 lws = primitive.GetPointInWorldSpace(m_ConnectionPoint); Vector3 ows = m_Other.GetPointInWorldSpace(m_OtherConnectionPoint); // Calculate the vector of the spring Vector3 force = lws - ows; // Calculate the magnitude of the force float magnitude = force.Length(); magnitude = Math.Abs(magnitude - m_RestLength); magnitude *= m_SpringConstant; // Calculate the final force and apply it force.Normalize(); force *= -magnitude; primitive.AddForceAtPoint(force, lws); } }
/// <summary> /// Genera los contactos requeridos para restaurar la unión si ha sido violada /// </summary> /// <param name="contactData">Información de contactos</param> /// <param name="limit">Límite de contactos a generar</param> /// <returns>Devuelve el número de contactos generados</returns> /// <remarks>Tan solo generará un contacto o ninguno</remarks> public override int AddContact(ref CollisionData contactData, int limit) { if (contactData.HasFreeContacts()) { CollisionPrimitive objectOne = null; if (this.m_BodyOne != null) { objectOne = this.m_BodyOne.Primitive; } CollisionPrimitive objectTwo = null; if (this.m_BodyTwo != null) { objectTwo = this.m_BodyTwo.Primitive; } Vector3 positionOne = this.m_RelativePointOne; Vector3 positionOneWorld = this.m_RelativePointOne; if (objectOne != null) { positionOne = objectOne.Position; positionOneWorld = objectOne.GetPointInWorldSpace(this.m_RelativePointOne); } Vector3 positionTwo = this.m_RelativePointTwo; Vector3 positionTwoWorld = this.m_RelativePointTwo; if (objectTwo != null) { positionTwo = objectTwo.Position; positionTwoWorld = objectTwo.GetPointInWorldSpace(this.m_RelativePointTwo); } float currentLen = Vector3.Distance(positionOneWorld, positionTwoWorld); if (Math.Abs(currentLen) > this.m_Length) { Contact contact = contactData.CurrentContact; contact.Bodies[0] = objectOne; contact.Bodies[1] = objectTwo; contact.ContactNormal = Vector3.Normalize(positionTwoWorld - positionOneWorld); contact.ContactPoint = (positionOneWorld + positionTwoWorld) * 0.5f; contact.Penetration = currentLen - this.m_Length; contact.Friction = 1.0f; contact.Restitution = 0; contactData.AddContact(); return(1); } } return(0); }
/// <summary> /// Detecta la colisión entre dos objetos /// </summary> /// <param name="obj1">Objeto primero</param> /// <param name="obj2">Objeto segundo</param> /// <param name="data">Información de colisión</param> /// <returns>Devuelve verdadero si ha habido colisión</returns> public static bool BetweenObjects(ref CollisionPrimitive primitive1, ref CollisionPrimitive primitive2, ref CollisionData data) { if (primitive1 != null && primitive2 != null) { if (primitive1 is CollisionBox) { if (primitive2 is CollisionBox) { return(BoxAndBox((CollisionBox)primitive1, (CollisionBox)primitive2, ref data)); } else if (primitive2 is CollisionSphere) { return(BoxAndSphere((CollisionBox)primitive1, (CollisionSphere)primitive2, ref data)); } else if (primitive2 is CollisionPlane) { return(BoxAndHalfSpace((CollisionBox)primitive1, (CollisionPlane)primitive2, ref data)); } else if (primitive2 is CollisionTriangleSoup) { return(BoxAndTriangleSoup((CollisionBox)primitive1, (CollisionTriangleSoup)primitive2, ref data)); } } else if (primitive1 is CollisionSphere) { if (primitive2 is CollisionBox) { return(BoxAndSphere((CollisionBox)primitive2, (CollisionSphere)primitive1, ref data)); } else if (primitive2 is CollisionSphere) { return(SphereAndSphere((CollisionSphere)primitive1, (CollisionSphere)primitive2, ref data)); } else if (primitive2 is CollisionPlane) { return(SphereAndHalfSpace((CollisionSphere)primitive1, (CollisionPlane)primitive2, ref data)); } else if (primitive2 is CollisionTriangleSoup) { return(SphereAndTriangleSoup((CollisionSphere)primitive1, (CollisionTriangleSoup)primitive2, ref data)); } } throw new Exception("Tipo de colisión no controlada"); } return(false); }
/// <summary> /// Añade los contactos necesarios para mantener unidos mediante la barra a los cuerpos /// </summary> /// <param name="contactData">Datos de colisión</param> /// <param name="limit">Límite de contactos a añadir</param> /// <returns>Devuelve el número de contacos añadidos</returns> /// <remarks>Sólo añade un contacto o ninguno</remarks> public override int AddContact(ref CollisionData contactData, int limit) { if (contactData.HasFreeContacts()) { CollisionPrimitive objectOne = null; if (this.m_BodyOne != null) { objectOne = this.m_BodyOne.Primitive; } CollisionPrimitive objectTwo = null; if (this.m_BodyTwo != null) { objectTwo = this.m_BodyTwo.Primitive; } // Encontrar la longitud actual Vector3 positionOne = this.m_RelativePointOne; Vector3 positionOneWorld = this.m_RelativePointOne; if (objectOne != null) { positionOne = objectOne.Position; positionOneWorld = objectOne.GetPointInWorldSpace(this.m_RelativePointOne); } Vector3 positionTwo = this.m_RelativePointTwo; Vector3 positionTwoWorld = this.m_RelativePointTwo; if (objectTwo != null) { positionTwo = objectTwo.Position; positionTwoWorld = objectTwo.GetPointInWorldSpace(this.m_RelativePointTwo); } float currentLen = Vector3.Distance(positionOneWorld, positionTwoWorld); // Comprobar si estamos en extensión correcta if (currentLen != m_Length) { // Rellenar el contacto Contact contact = contactData.CurrentContact; contact.Bodies[0] = objectOne; contact.Bodies[1] = objectTwo; contact.ContactPoint = (positionOneWorld + positionTwoWorld) * 0.5f; // Calcular la normal Vector3 normal = Vector3.Normalize(positionTwo - positionOne); // La normal de contacto depende de si hay que extender o contraer para conservar la longitud if (currentLen > m_Length) { contact.ContactNormal = normal; contact.Penetration = currentLen - m_Length; } else { contact.ContactNormal = Vector3.Negate(normal); contact.Penetration = this.m_Length - currentLen; } // Siempre restitución 0 contact.Restitution = 0f; contactData.AddContact(); return(1); } } return(0); }
/// <summary> /// Calcula la fuerza y la aplica al objeto especificado /// </summary> /// <param name="obj">Objeto</param> /// <param name="duration">Duración</param> public override void UpdateForce(ref IPhysicObject obj, float duration) { // Obtener el cuerpo del objeto CollisionPrimitive primitive = obj.Primitive; if (primitive != null) { // Detectar la fase de la explosión en la que estamos if (this.m_TimePassed <= this.ImplosionDuration) { // Fase de implosión float distance = Vector3.Distance(primitive.Position, this.DetonationCenter); if (distance > this.ImplosionMinRadius && distance <= this.ImplosionMaxRadius) { // El cuerpo está en el área de implosión. Aplicar las fuerzas float max = this.ImplosionMaxRadius - this.ImplosionMinRadius; float curr = distance - this.ImplosionMinRadius; float forceMagnitude = curr / max; Vector3 force = Vector3.Normalize(this.DetonationCenter - primitive.Position) * this.ImplosionForce * forceMagnitude; primitive.AddForce(force); } } else if (this.m_TimePassed <= (this.ImplosionDuration + this.ConcussionDuration)) { // Honda expansiva // Intervalo actual de máxima acción de la honda float min = this.ShockwaveSpeed * this.m_TimePassed; float max = this.ShockwaveSpeed * (this.m_TimePassed + duration); // Distancia al centro del objeto float distance = Vector3.Distance(primitive.Position, this.DetonationCenter); float totalDuration = this.ConcussionDuration + this.ImplosionDuration; float maxDuration = this.ConcussionDuration; float maxDistance = (this.ShockwaveSpeed * maxDuration) + this.ShockwaveThickness; float forceMagnitude = 0f; if (distance >= min && distance <= max) { // En plena honda expansiva. Se aplican las fuerzas atenuadas sólo por la duración float relativeTime = 0f; if (m_TimePassed < totalDuration) { relativeTime = 1f - (this.m_TimePassed / totalDuration); } forceMagnitude = this.PeakConcussionForce * relativeTime; } else if (distance < min) { // El objeto ha sido sobrepasado por la honda expansiva. Fuerza mínimamente atenuada float relativeTime = 0f; if (this.m_TimePassed < totalDuration) { relativeTime = 1f - (this.m_TimePassed / totalDuration); } forceMagnitude = this.PeakConcussionForce * relativeTime; } else if (distance > max && distance <= maxDistance) { // El objeto no ha sido alcanzado por la honda expansiva. Fuerza atenuada por el tiempo y la distancia float relativeDistance = 0f; if (distance < maxDistance) { relativeDistance = 1f - (distance / maxDistance); } float relativeTime = 0f; if (this.m_TimePassed < totalDuration) { relativeTime = 1f - (this.m_TimePassed / totalDuration); } forceMagnitude = this.PeakConcussionForce * relativeDistance * relativeTime; } if (forceMagnitude > 0f) { Vector3 force = Vector3.Normalize(primitive.Position - this.DetonationCenter) * forceMagnitude; primitive.AddForce(force); } } else { // Fin de la explosión this.m_ExplosionActive = false; } } this.m_TimePassed += duration; }