diff --git a/Simulacion/BEPU.cs b/Simulacion/BEPU.cs index f211389..4dc76d9 100644 --- a/Simulacion/BEPU.cs +++ b/Simulacion/BEPU.cs @@ -87,12 +87,12 @@ namespace CtrEditor.Simulacion public bool ConfigureContactManifold(int workerIndex, CollidablePair pair, ref TManifold manifold, out PairMaterialProperties pairMaterial) where TManifold : unmanaged, IContactManifold { - // ✅ CONFIGURACIÓN BÁSICA de materiales físicos + // ✅ CONFIGURACIÓN BÁSICA de materiales físicos con valores rígidos pero estables pairMaterial = new PairMaterialProperties { FrictionCoefficient = 0.3f, - MaximumRecoveryVelocity = 1f, - SpringSettings = new SpringSettings(80, 6) + MaximumRecoveryVelocity = 0.8f, // ✅ AJUSTADO: Velocidad de separación moderada + SpringSettings = new SpringSettings(120, 8) // ✅ AJUSTADO: Más rígido pero bien amortiguado }; if (_simulationManager != null) @@ -165,16 +165,16 @@ namespace CtrEditor.Simulacion botella.isOnBrakeTransport = true; pairMaterial.FrictionCoefficient = 2f; - pairMaterial.MaximumRecoveryVelocity = 0.1f; - pairMaterial.SpringSettings = new SpringSettings(80, 1f); + pairMaterial.MaximumRecoveryVelocity = 0.5f; // ✅ AJUSTADO: Velocidad de separación controlada + pairMaterial.SpringSettings = new SpringSettings(80, 6f); // ✅ AJUSTADO: Rígido para frenado } else { botella.isOnBrakeTransport = false; // ✅ NUEVO: Usar fricción dinámica basada en deslizamiento - pairMaterial.FrictionCoefficient = 0.3f; // CalculateTransportFriction(botella, transport, botellaCollidable); - pairMaterial.MaximumRecoveryVelocity = 0.1f; - pairMaterial.SpringSettings = new SpringSettings(80, 1f); + pairMaterial.FrictionCoefficient = CalculateTransportFriction(botella, transport, botellaCollidable); + pairMaterial.MaximumRecoveryVelocity = 0.6f; // ✅ AJUSTADO: Velocidad de separación más natural + pairMaterial.SpringSettings = new SpringSettings(100, 8f); // ✅ AJUSTADO: Rígido para transportes } } @@ -193,26 +193,40 @@ namespace CtrEditor.Simulacion // ✅ NUEVO: Usar fricción dinámica basada en deslizamiento var curve = curveA ?? curveB; - pairMaterial.FrictionCoefficient = 0.3f; // CalculateCurveFriction(botella, curve, botellaCollidable); - pairMaterial.MaximumRecoveryVelocity = 0.1f; - pairMaterial.SpringSettings = new SpringSettings(80, 1f); // Colision con la cinta por gravedad + pairMaterial.FrictionCoefficient = CalculateCurveFriction(botella, curve, botellaCollidable); + pairMaterial.MaximumRecoveryVelocity = 0.6f; // ✅ AJUSTADO: Velocidad de separación más natural + pairMaterial.SpringSettings = new SpringSettings(100, 8f); // ✅ AJUSTADO: Rígido para curvas } // ✅ CONTACTO BOTELLA-GUÍA: Configuración específica de fricción else if (botella != null && (GetGuiaFromCollidable(pair.A) != null || GetGuiaFromCollidable(pair.B) != null)) { // Fricción baja para las guías, deben deslizar, no arrastrar. - pairMaterial.FrictionCoefficient = 0.1f; - pairMaterial.MaximumRecoveryVelocity = 0.8f; - pairMaterial.SpringSettings = new SpringSettings(50, 1f); // Rebote lateral moderado + pairMaterial.FrictionCoefficient = 0.01f; + pairMaterial.MaximumRecoveryVelocity = 0.5f; // ✅ AJUSTADO: Velocidad de separación normal + pairMaterial.SpringSettings = new SpringSettings(120, 10f); // ✅ AJUSTADO: Rígido para guías } // ✅ NUEVO: CONTACTO BOTELLA-BOTELLA: Lógica de presión proactiva basada en la penetración. else if (botellaA != null && botellaB != null) { // ✅ NUEVO: Usar método especializado para contactos botella-botella CalculateBottlePression(ref manifold, botellaA, botellaB); - pairMaterial.FrictionCoefficient = 0.1f; - pairMaterial.MaximumRecoveryVelocity = 0.03f; - pairMaterial.SpringSettings = new SpringSettings(80, 1f); // Rebote lateral moderado + + // ✅ NUEVO: Limitar fuerzas basadas en profundidad de penetración + var maxPenetrationDepth = GetMaxPenetrationDepth(ref manifold); + if (maxPenetrationDepth > 0.005f) // Penetración profunda (reducido umbral) + { + // Fuerzas controladas para penetraciones profundas - evitar explosiones pero mantener rigidez + pairMaterial.FrictionCoefficient = 0.01f; + pairMaterial.MaximumRecoveryVelocity = 0.3f; // ✅ AUMENTADO: Más velocidad de separación + pairMaterial.SpringSettings = new SpringSettings(60, 12f); // ✅ AUMENTADO: Más rígido pero bien amortiguado + } + else + { + // Fuerzas normales para contactos superficiales - botellas rígidas + pairMaterial.FrictionCoefficient = 0.02f; + pairMaterial.MaximumRecoveryVelocity = 0.5f; // ✅ AUMENTADO: Velocidad normal de separación + pairMaterial.SpringSettings = new SpringSettings(150, 10f); // ✅ AUMENTADO: Muy rígido para contactos normales + } } // Ajustes básicos para otras botellas else if (botella != null) @@ -220,8 +234,8 @@ namespace CtrEditor.Simulacion System.Diagnostics.Debug.WriteLine($"[ConfigureContactManifold] ERROR: contacto no resuelto!"); // Fricción moderada para colisiones entre botellas. pairMaterial.FrictionCoefficient = 0.3f; - pairMaterial.MaximumRecoveryVelocity = 1f; - pairMaterial.SpringSettings = new SpringSettings(30, 1); + pairMaterial.MaximumRecoveryVelocity = 0.6f; // ✅ AJUSTADO: Velocidad de separación normal + pairMaterial.SpringSettings = new SpringSettings(120, 8); // ✅ AJUSTADO: Rígido pero controlado } } @@ -331,8 +345,8 @@ namespace CtrEditor.Simulacion float slipSpeedThreshold = transportVelocity.Length()*0.85f; // m/s - umbral para cambiar de fricción estática a dinámica const float heatupRate = 1f; // Qué tan rápido sube HighSlippery - const float cooldownRate = 0.5f; // Qué tan rápido baja HighSlippery - const float staticFriction = 0.25f; + const float cooldownRate = 0.2f; // Qué tan rápido baja HighSlippery + const float staticFriction = 0.30f; const float dynamicFriction = 0.10f; // Sistema de heatup/cooldown para HighSlippery @@ -382,8 +396,8 @@ namespace CtrEditor.Simulacion float slipSpeedThreshold = curveVelocityAtPoint.Length() * 0.85f; // m/s const float heatupRate = 1f; // Qué tan rápido sube HighSlippery en curvas - const float cooldownRate = 0.5f; // Qué tan rápido baja HighSlippery en curvas - const float staticFriction = 0.20f; + const float cooldownRate = 0.2f; // Qué tan rápido baja HighSlippery en curvas + const float staticFriction = 0.30f; const float dynamicFriction = 0.10f; // Sistema de heatup/cooldown para HighSlippery @@ -440,6 +454,31 @@ namespace CtrEditor.Simulacion } } + /// + /// ✅ NUEVA FUNCIÓN: Obtiene la profundidad máxima de penetración en un manifold + /// + private float GetMaxPenetrationDepth(ref TManifold manifold) where TManifold : unmanaged, IContactManifold + { + try + { + float maxDepth = 0f; + for (int i = 0; i < manifold.Count; ++i) + { + var depth = manifold.GetDepth(i); + if (depth > maxDepth) + { + maxDepth = depth; + } + } + return maxDepth; + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"[GetMaxPenetrationDepth] Error: {ex.Message}"); + return 0f; + } + } + public bool ConfigureContactManifold(int workerIndex, CollidablePair pair, int childIndexA, int childIndexB, ref ConvexContactManifold manifold) { return true; @@ -938,6 +977,9 @@ namespace CtrEditor.Simulacion botella.ContactPressure = 0f; } + // ✅ NUEVO: APLICAR LIMITACIÓN DE FUERZAS Z ANTES DEL TIMESTEP + ApplyZForceLimitation(); + // ✅ CONSERVAR - validación de deltaTime var currentTime = stopwatch.Elapsed.TotalMilliseconds; var deltaTime = (float)((currentTime - stopwatch_last) / 1000.0); @@ -1498,6 +1540,70 @@ namespace CtrEditor.Simulacion return lineStart + lineDirection * projectionLength; } + /// + /// ✅ NUEVA FUNCIÓN: Limita las fuerzas Z para evitar que las botellas se eleven + /// Esta función se ejecuta ANTES del timestep para controlar fuerzas de separación excesivas + /// + private void ApplyZForceLimitation() + { + try + { + const float maxZVelocity = 0.3f; // ✅ AJUSTADO: Velocidad Z máxima permitida más realista (m/s) + const float zVelocityDamping = 0.8f; // ✅ AJUSTADO: Amortiguamiento menos agresivo para Z + + foreach (var botella in Cuerpos.OfType()) + { + try + { + // Verificar que la botella aún existe en la simulación + if (botella?.BodyHandle.Value >= 0 && simulation?.Bodies?.BodyExists(botella.BodyHandle) == true) + { + var body = simulation.Bodies.GetBodyReference(botella.BodyHandle); + var velocity = body.Velocity; + + // ✅ LIMITACIÓN DIRECTA DE VELOCIDAD Z + if (velocity.Linear.Z > maxZVelocity) + { + velocity.Linear.Z = maxZVelocity; + body.Velocity = velocity; + } + + // ✅ AMORTIGUAMIENTO ADICIONAL EN Z + if (velocity.Linear.Z > 0) + { + velocity.Linear.Z *= zVelocityDamping; + body.Velocity = velocity; + } + + // ✅ LIMITACIÓN DE FUERZAS EXTREMAS POR PENETRACIÓN + // Si la botella está muy por encima del nivel normal, aplicar fuerza hacia abajo + var position = body.Pose.Position; + var maxAllowedZ = simBase.zPos_Transporte + simBase.zAltura_Transporte + botella.Radius * 2; + + if (position.Z > maxAllowedZ) + { + // Aplicar fuerza hacia abajo proporcional a la elevación excesiva + var excessHeight = position.Z - maxAllowedZ; + var downwardForce = new Vector3(0, 0, -excessHeight * 20f); // ✅ AJUSTADO: Fuerza menos agresiva + + // Aplicar impulso para corregir la posición + velocity.Linear += downwardForce * (1f / 60f); // Asumir 60 FPS + body.Velocity = velocity; + } + } + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"[ApplyZForceLimitation] Error procesando botella {botella?.BodyHandle}: {ex.Message}"); + } + } + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"[ApplyZForceLimitation] Error crítico: {ex.Message}"); + } + } + public void Dispose() { Clear();