Se implementó un nuevo sistema de limitación de fuerzas para contactos múltiples simultáneos en la simulación de botellas, mejorando la estabilidad y realismo al evitar fuerzas armónicas irreales. Se introdujo un enfoque post-solver para controlar velocidades Z excesivas y se simplificó la lógica de integración de velocidad. Además, se ajustaron los parámetros de fricción y recuperación para optimizar el comportamiento en situaciones de contacto. Se añadieron nuevas funciones para el seguimiento de densidad de contactos y la calibración visual del deslizamiento.
This commit is contained in:
parent
6887ede5e0
commit
b44bdc5ece
|
@ -34,3 +34,84 @@ simTransporte : Es un box por donde las botellas pueden desplazarse usando un tr
|
|||
|
||||
* Se ha reemplazado el sistema `ProcessPressureSystem` por un nuevo sistema de calibración visual `HighSlippery` que permite ajustar las fricciones de cada componente. Cada botella tiene una propiedad `HighSlippery` (rango 0-9) con sistema de heatup/cooldown que se incrementa durante deslizamiento y se decrementa durante adherencia. El valor se muestra como número en el centro de cada botella para calibración visual. Los `SpringSettings` se mantienen >= 30 para evitar compresiones irreales. El sistema permite calibrar las tasas de heatup/cooldown específicas para transportes (0.2/0.1) y curvas (0.15/0.08), y el umbral de cambio estático/dinámico (HighSlippery > 3). Este sistema elimina el problema del cooldown que reactivaba prematuramente la fricción estática.
|
||||
|
||||
# Sistema de Limitación de Fuerzas para Contactos Múltiples Simultáneos
|
||||
|
||||
## Problema Identificado
|
||||
Durante la simulación de "trenes largos" de botellas que impactan simultáneamente, se generaban fuerzas armónicas irreales que no ocurren en la vida real. Aunque tengas 50 botellas empujando, la fuerza máxima está limitada por la resistencia de los materiales.
|
||||
|
||||
## Solución Implementada
|
||||
|
||||
### 1. Sistema de Tracking de Densidad de Contactos
|
||||
- **Discretización espacial**: El espacio se divide en regiones de 0.5m x 0.5m x 0.5m
|
||||
- **Contador por región**: Se cuenta el número de contactos simultáneos en cada región
|
||||
- **Límite de contactos**: Máximo 10 contactos antes de aplicar limitación de fuerzas
|
||||
|
||||
### 2. Factor de Escala Dinámico
|
||||
```csharp
|
||||
// Sin limitación hasta 10 contactos
|
||||
if (contactCount <= 10) return 1.0f;
|
||||
|
||||
// Reducción progresiva: 5% por cada contacto extra
|
||||
var scaleFactor = 1.0f - (excessContacts * 0.05f);
|
||||
return Math.Max(scaleFactor, 0.3f); // Mínimo 30% de fuerza original
|
||||
```
|
||||
|
||||
### 3. Aplicación Integral
|
||||
- **SpringSettings**: Se escalan proporcionalmente (frecuencia reducida)
|
||||
- **MaximumRecoveryVelocity**: Se reduce proporcionalmente
|
||||
- **Todos los tipos de contacto**: Botella-botella, botella-transporte, botella-curva, etc.
|
||||
|
||||
## Beneficios
|
||||
- **Comportamiento realista**: Elimina fuerzas armónicas irreales en impactos múltiples
|
||||
- **Estabilidad mejorada**: Previene explosiones por acumulación de fuerzas
|
||||
- **Configurabilidad**: Límites y factores ajustables según necesidades
|
||||
- **Mantenimiento de rigidez**: Las fuerzas normales permanecen intactas hasta el límite
|
||||
|
||||
# Sistema de Intervención Post-Solver para Control de Velocidades Z
|
||||
|
||||
## Problema Identificado
|
||||
El sistema anterior de limitación de fuerzas pre-solver no controlaba directamente las velocidades Z excesivas generadas por el solver de BEPU. Las botellas seguían elevándose debido a fuerzas de separación acumuladas que se aplicaban después de las restricciones iniciales.
|
||||
|
||||
## Solución Implementada
|
||||
|
||||
### 1. Enfoque Post-Solver como "Fusible de Energía"
|
||||
- **Timing crítico**: Intervención en `OnSubstepEnded` - después del solver, antes de la integración de posición
|
||||
- **Fusible de energía**: Elimina velocidades Z excesivas sin interferir con el comportamiento normal del solver
|
||||
- **Preservación de física**: Permite al solver trabajar con fuerzas realistas, interviniendo solo cuando es necesario
|
||||
|
||||
### 2. Control de Velocidades Z Granular
|
||||
```csharp
|
||||
// Límites configurables
|
||||
const float maxUpwardVelocity = 0.15f; // Anti-elevación
|
||||
const float maxDownwardVelocity = 2.0f; // Anti-hundimiento
|
||||
const float zDampingFactor = 0.7f; // Amortiguamiento suave
|
||||
|
||||
// Intervención directa en velocidades
|
||||
if (velocity.Linear.Z > maxUpwardVelocity) {
|
||||
velocity.Linear.Z = maxUpwardVelocity;
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Corrección de Posición Extrema
|
||||
- **Detección de altura excesiva**: Si la botella está muy por encima del nivel normal
|
||||
- **Corrección suave**: Aplicar velocidad correctiva hacia abajo (máximo 0.5 m/s)
|
||||
- **Cálculo proporcional**: Velocidad correctiva basada en la altura excesiva
|
||||
|
||||
## Simplificación del Sistema
|
||||
|
||||
### 1. Eliminación de `ApplyZForceLimitation`
|
||||
- **Antes**: Limitación pre-solver que interfería con el comportamiento normal
|
||||
- **Ahora**: Intervención post-solver más directa y efectiva
|
||||
|
||||
### 2. Simplificación de `IntegrateVelocity`
|
||||
- **Antes**: Control complejo de velocidades Z durante la integración
|
||||
- **Ahora**: Solo control angular básico (anti-vuelco) y damping en Z
|
||||
- **Beneficio**: Menor interferencia con la física normal de BEPU
|
||||
|
||||
## Beneficios del Enfoque Post-Solver
|
||||
- **Más efectivo**: Intervención después de que el solver haya calculado todas las fuerzas
|
||||
- **Menos interferencia**: Permite comportamiento físico normal hasta el punto de intervención
|
||||
- **Más directo**: Control directo de velocidades en lugar de modificar parámetros de material
|
||||
- **Más predecible**: Comportamiento determinista independiente de las configuraciones del solver
|
||||
- **Mantenimiento de rigidez**: Las fuerzas normales en XY permanecen intactas
|
||||
|
||||
|
|
|
@ -195,7 +195,7 @@ namespace CtrEditor.ObjetosSim
|
|||
Inercia_desde_simulacion = SimGeometria.Mass; // En BEPU usamos masa en lugar de inercia directa
|
||||
Porcentaje_Traccion = SimGeometria.OverlapPercentage;
|
||||
EnTransporteConFreno = SimGeometria.isOnBrakeTransport; // Actualizar estado de freno
|
||||
NivelDeslizamiento = SimGeometria.HighSlippery; // ✅ NUEVO: Mostrar nivel de deslizamiento para calibración
|
||||
NivelDeslizamiento = 10-SimGeometria.forceScale*10; // ✅ NUEVO: Mostrar nivel de deslizamiento para calibración
|
||||
}
|
||||
|
||||
public override void ucLoaded()
|
||||
|
|
|
@ -95,6 +95,28 @@ namespace CtrEditor.Simulacion
|
|||
SpringSettings = new SpringSettings(120, 8) // ✅ AJUSTADO: Más rígido pero bien amortiguado
|
||||
};
|
||||
|
||||
// ✅ NUEVO: SISTEMA DE LIMITACIÓN DE FUERZAS PARA CONTACTOS MÚLTIPLES
|
||||
float forceScale = 1.0f;
|
||||
if (_simulationManager != null)
|
||||
{
|
||||
var contactPosition = _simulationManager.GetContactPosition(pair);
|
||||
forceScale = _simulationManager.RegisterContactAndGetForceScale(contactPosition);
|
||||
|
||||
// Aplicar limitación de fuerzas si hay demasiados contactos simultáneos
|
||||
if (forceScale < 1.0f)
|
||||
{
|
||||
// Reducir SpringSettings proporcionalmente
|
||||
var originalFreq = pairMaterial.SpringSettings.Frequency;
|
||||
var originalDamp = pairMaterial.SpringSettings.DampingRatio;
|
||||
pairMaterial.SpringSettings = new SpringSettings(originalFreq * forceScale, originalDamp);
|
||||
|
||||
// Reducir velocidad de recuperación proporcionalmente
|
||||
pairMaterial.MaximumRecoveryVelocity *= forceScale;
|
||||
|
||||
//System.Diagnostics.Debug.WriteLine($"[ConfigureContactManifold] ⚠️ LIMITANDO FUERZAS: Factor={forceScale:F2}, Región={contactPosition}");
|
||||
}
|
||||
}
|
||||
|
||||
if (_simulationManager != null)
|
||||
{
|
||||
var botellaA = GetBotellaFromCollidable(pair.A);
|
||||
|
@ -124,6 +146,7 @@ namespace CtrEditor.Simulacion
|
|||
// ✅ CONTACTO BOTELLA-TRANSPORTE: USAR FRICCIÓN Y VELOCIDAD CINEMÁTICA
|
||||
if (botella != null && (transportA != null || transportB != null))
|
||||
{
|
||||
botella.forceScale = forceScale;
|
||||
// ✅ NUEVO: Lógica de control de presión/flotación
|
||||
botella.IsTouchingTransport = true;
|
||||
var botellaCollidable = GetBotellaFromCollidable(pair.A) != null ? pair.A : pair.B;
|
||||
|
@ -164,23 +187,24 @@ namespace CtrEditor.Simulacion
|
|||
}
|
||||
|
||||
botella.isOnBrakeTransport = true;
|
||||
pairMaterial.FrictionCoefficient = 2f;
|
||||
pairMaterial.MaximumRecoveryVelocity = 0.5f; // ✅ AJUSTADO: Velocidad de separación controlada
|
||||
pairMaterial.SpringSettings = new SpringSettings(80, 6f); // ✅ AJUSTADO: Rígido para frenado
|
||||
pairMaterial.FrictionCoefficient = 8f;
|
||||
pairMaterial.MaximumRecoveryVelocity = 0.5f * forceScale; // ✅ APLICAR FACTOR DE ESCALA
|
||||
pairMaterial.SpringSettings = new SpringSettings(80 * forceScale, 6f); // ✅ APLICAR FACTOR DE ESCALA
|
||||
}
|
||||
else
|
||||
{
|
||||
botella.isOnBrakeTransport = false;
|
||||
// ✅ NUEVO: Usar fricción dinámica basada en deslizamiento
|
||||
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
|
||||
pairMaterial.MaximumRecoveryVelocity = 0.6f * forceScale; // ✅ APLICAR FACTOR DE ESCALA
|
||||
pairMaterial.SpringSettings = new SpringSettings(100 * forceScale, 8f); // ✅ APLICAR FACTOR DE ESCALA
|
||||
}
|
||||
|
||||
}
|
||||
// ✅ CONTACTO BOTELLA-CURVA: USAR FRICCIÓN Y VELOCIDAD CINEMÁTICA
|
||||
else if (botella != null && (curveA != null || curveB != null))
|
||||
{
|
||||
botella.forceScale = forceScale;
|
||||
// ✅ NUEVO: Lógica de control de presión/flotación
|
||||
botella.IsTouchingTransport = true;
|
||||
botella.isOnBrakeTransport = false;
|
||||
|
@ -194,16 +218,16 @@ namespace CtrEditor.Simulacion
|
|||
// ✅ NUEVO: Usar fricción dinámica basada en deslizamiento
|
||||
var curve = curveA ?? curveB;
|
||||
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
|
||||
pairMaterial.MaximumRecoveryVelocity = 0.6f * forceScale; // ✅ APLICAR FACTOR DE ESCALA
|
||||
pairMaterial.SpringSettings = new SpringSettings(100 * forceScale, 8f); // ✅ APLICAR FACTOR DE ESCALA
|
||||
}
|
||||
// ✅ 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.01f;
|
||||
pairMaterial.MaximumRecoveryVelocity = 0.5f; // ✅ AJUSTADO: Velocidad de separación normal
|
||||
pairMaterial.SpringSettings = new SpringSettings(120, 10f); // ✅ AJUSTADO: Rígido para guías
|
||||
pairMaterial.MaximumRecoveryVelocity = 0.5f * forceScale; // ✅ APLICAR FACTOR DE ESCALA
|
||||
pairMaterial.SpringSettings = new SpringSettings(120 * forceScale, 10f); // ✅ APLICAR FACTOR DE ESCALA
|
||||
}
|
||||
// ✅ NUEVO: CONTACTO BOTELLA-BOTELLA: Lógica de presión proactiva basada en la penetración.
|
||||
else if (botellaA != null && botellaB != null)
|
||||
|
@ -217,15 +241,15 @@ namespace CtrEditor.Simulacion
|
|||
{
|
||||
// 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
|
||||
pairMaterial.MaximumRecoveryVelocity = 0.3f * forceScale; // ✅ APLICAR FACTOR DE ESCALA
|
||||
pairMaterial.SpringSettings = new SpringSettings(60 * forceScale, 12f); // ✅ APLICAR FACTOR DE ESCALA
|
||||
}
|
||||
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
|
||||
pairMaterial.MaximumRecoveryVelocity = 0.5f * forceScale; // ✅ APLICAR FACTOR DE ESCALA
|
||||
pairMaterial.SpringSettings = new SpringSettings(150 * forceScale, 10f); // ✅ APLICAR FACTOR DE ESCALA
|
||||
}
|
||||
}
|
||||
// Ajustes básicos para otras botellas
|
||||
|
@ -234,8 +258,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 = 0.6f; // ✅ AJUSTADO: Velocidad de separación normal
|
||||
pairMaterial.SpringSettings = new SpringSettings(120, 8); // ✅ AJUSTADO: Rígido pero controlado
|
||||
pairMaterial.MaximumRecoveryVelocity = 0.6f * forceScale; // ✅ APLICAR FACTOR DE ESCALA
|
||||
pairMaterial.SpringSettings = new SpringSettings(120 * forceScale, 8); // ✅ APLICAR FACTOR DE ESCALA
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -479,6 +503,8 @@ namespace CtrEditor.Simulacion
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public bool ConfigureContactManifold(int workerIndex, CollidablePair pair, int childIndexA, int childIndexB, ref ConvexContactManifold manifold)
|
||||
{
|
||||
return true;
|
||||
|
@ -522,7 +548,8 @@ namespace CtrEditor.Simulacion
|
|||
{
|
||||
}
|
||||
|
||||
// ✅ REESCRITO COMPLETAMENTE: para corregir errores de compilación y aplicar la lógica de amortiguamiento de forma segura.
|
||||
// ✅ SIMPLIFICADO: IntegrateVelocity ahora solo aplica física básica
|
||||
// El control de velocidades Z se maneja en OnSubstepEnded (post-solver)
|
||||
public void IntegrateVelocity(Vector<int> bodyIndices, Vector3Wide position, QuaternionWide orientation, BodyInertiaWide localInertia, Vector<int> integrationMask, int workerIndex, Vector<float> dt, ref BodyVelocityWide velocity)
|
||||
{
|
||||
// 1. APLICAR GRAVEDAD Y AMORTIGUAMIENTO ESTÁNDAR (VECTORIZADO)
|
||||
|
@ -537,7 +564,7 @@ namespace CtrEditor.Simulacion
|
|||
velocity.Linear *= linearDampingFactor;
|
||||
velocity.Angular *= angularDampingFactor;
|
||||
|
||||
// 2. LÓGICA PERSONALIZADA PARA BOTELLAS (ESCALAR)
|
||||
// 2. LÓGICA PERSONALIZADA MÍNIMA PARA BOTELLAS (ESCALAR)
|
||||
const float bottleExtraZAngularDamping = 0.8f;
|
||||
var bottleZAngularDampingFactor = MathF.Pow(MathHelper.Clamp(1 - bottleExtraZAngularDamping, 0, 1), dt[0]);
|
||||
|
||||
|
@ -553,51 +580,24 @@ namespace CtrEditor.Simulacion
|
|||
var handle = _simulationManager.simulation.Bodies.ActiveSet.IndexToHandle[bodyIndex];
|
||||
if (_simulationManager.CollidableData.TryGetValue(handle.Value, out simBase baseObj) && baseObj is simBotella)
|
||||
{
|
||||
//// B. DAMPING ANGULAR EXTRA EN EJE Z
|
||||
//// Para modificar un solo carril de un Vector<T>, la forma más segura es extraer
|
||||
//// los valores a un array, modificar el índice y crear un nuevo Vector<T>.
|
||||
//var zVel = new float[Vector<float>.Count];
|
||||
//velocity.Angular.Z.CopyTo(zVel, 0);
|
||||
//zVel[i] *= bottleZAngularDampingFactor;
|
||||
//velocity.Angular.Z = new Vector<float>(zVel);
|
||||
// --- INICIO DE NUESTRA LÓGICA ANTI-SALTO Y ANTI-VUELCO ---
|
||||
|
||||
// 1. EXTRAER VELOCIDADES DEL PAQUETE A ARRAYS
|
||||
var linearX = new float[Vector<float>.Count];
|
||||
var linearY = new float[Vector<float>.Count];
|
||||
var linearZ = new float[Vector<float>.Count];
|
||||
// EXTRAER VELOCIDADES DEL PAQUETE A ARRAYS
|
||||
var angularX = new float[Vector<float>.Count];
|
||||
var angularY = new float[Vector<float>.Count];
|
||||
var angularZ = new float[Vector<float>.Count];
|
||||
|
||||
velocity.Linear.X.CopyTo(linearX, 0);
|
||||
velocity.Linear.Y.CopyTo(linearY, 0);
|
||||
velocity.Linear.Z.CopyTo(linearZ, 0);
|
||||
velocity.Angular.X.CopyTo(angularX, 0);
|
||||
velocity.Angular.Y.CopyTo(angularY, 0);
|
||||
velocity.Angular.Z.CopyTo(angularZ, 0);
|
||||
|
||||
// 2. APLICAR RESTRICCIONES AL CARRIL 'i' (la botella)
|
||||
|
||||
// ANTI-VUELCO: Anular cualquier velocidad angular que tumbe la botella.
|
||||
// CONTROL ANGULAR BÁSICO PARA BOTELLAS
|
||||
// Anti-vuelco: Anular rotaciones que tumben la botella
|
||||
angularX[i] = 0;
|
||||
angularY[i] = 0;
|
||||
|
||||
// ANTI-SALTO: Si la velocidad Z es positiva (hacia arriba), anularla.
|
||||
// Esto permite que la gravedad actúe (Z negativo) pero impide saltos.
|
||||
if (linearZ[i] > 0)
|
||||
{
|
||||
linearZ[i] = 0;
|
||||
}
|
||||
|
||||
// DAMPING ANGULAR EXTRA EN EJE Z (Tu lógica original)
|
||||
// Damping angular extra en Z
|
||||
angularZ[i] *= bottleZAngularDampingFactor;
|
||||
|
||||
|
||||
// 3. RECONSTRUIR LOS VECTORES 'WIDE' CON LOS VALORES MODIFICADOS
|
||||
velocity.Linear.X = new Vector<float>(linearX);
|
||||
velocity.Linear.Y = new Vector<float>(linearY);
|
||||
velocity.Linear.Z = new Vector<float>(linearZ);
|
||||
// RECONSTRUIR LOS VECTORES 'WIDE' CON LOS VALORES MODIFICADOS
|
||||
velocity.Angular.X = new Vector<float>(angularX);
|
||||
velocity.Angular.Y = new Vector<float>(angularY);
|
||||
velocity.Angular.Z = new Vector<float>(angularZ);
|
||||
|
@ -654,6 +654,12 @@ namespace CtrEditor.Simulacion
|
|||
public Dictionary<int, simBase> CollidableData = new Dictionary<int, simBase>();
|
||||
public float GlobalTime { get; private set; }
|
||||
|
||||
// ✅ NUEVO: Sistema de limitación de fuerzas para contactos múltiples simultáneos
|
||||
private Dictionary<Vector3, int> _contactDensityMap = new Dictionary<Vector3, int>();
|
||||
private const int MAX_SIMULTANEOUS_CONTACTS = 10; // Máximo número de contactos antes de aplicar clamp
|
||||
private const float CONTACT_REGION_SIZE = 0.5f; // Tamaño de región para agrupar contactos (metros)
|
||||
private const float MAX_FORCE_SCALE_FACTOR = 0.3f; // Factor mínimo de escala cuando hay muchos contactos
|
||||
|
||||
/// <summary>
|
||||
/// Obtiene el objeto simBase correspondiente a un BodyHandle
|
||||
/// </summary>
|
||||
|
@ -831,10 +837,56 @@ namespace CtrEditor.Simulacion
|
|||
|
||||
/// <summary>
|
||||
/// ✅ NUEVO: Se ejecuta al final de cada sub-paso de la simulación, pero antes de la integración de la pose.
|
||||
/// Pone a cero las velocidades de los cuerpos cinemáticos para evitar que el PoseIntegrator mueva su posición.
|
||||
/// Actúa como "fusible de energía" para eliminar velocidades Z excesivas y mantener objetos cinemáticos estáticos.
|
||||
/// </summary>
|
||||
private void OnSubstepEnded(int substepIndex)
|
||||
{
|
||||
// 1. INTERVENCIÓN POST-SOLVER: Control de velocidades Z para botellas
|
||||
// Esto actúa como un "fusible de energía" que elimina velocidades Z excesivas
|
||||
// después de que el solver haya hecho su trabajo
|
||||
foreach (var botella in Cuerpos.OfType<simBotella>())
|
||||
{
|
||||
try
|
||||
{
|
||||
if (botella?.BodyHandle.Value >= 0 && simulation?.Bodies?.BodyExists(botella.BodyHandle) == true)
|
||||
{
|
||||
var body = simulation.Bodies.GetBodyReference(botella.BodyHandle);
|
||||
var velocity = body.Velocity;
|
||||
|
||||
// CONFIGURACIÓN DE LÍMITES
|
||||
const float maxUpwardVelocity = 0f; // Velocidad Z máxima hacia arriba (m/s)
|
||||
|
||||
// LIMITAR VELOCIDADES Z EXCESIVAS
|
||||
if (velocity.Linear.Z > maxUpwardVelocity)
|
||||
{
|
||||
// Cancelar velocidades hacia arriba excesivas (anti-elevación)
|
||||
velocity.Linear.Z = maxUpwardVelocity;
|
||||
body.Velocity = velocity;
|
||||
}
|
||||
|
||||
// CONTROL DE POSICIÓN EXTREMA
|
||||
// Si la botella está muy por encima del nivel normal, aplicar corrección
|
||||
var position = body.Pose.Position;
|
||||
var maxAllowedZ = simBase.zPos_Transporte + simBase.zAltura_Transporte + botella.Radius * 2;
|
||||
|
||||
if (position.Z > maxAllowedZ)
|
||||
{
|
||||
// Aplicar velocidad correctiva hacia abajo
|
||||
var excessHeight = position.Z - maxAllowedZ;
|
||||
var correctionVelocity = -Math.Min(excessHeight * 5f, 0.5f); // Máximo 0.5 m/s hacia abajo
|
||||
velocity.Linear.Z = correctionVelocity;
|
||||
body.Velocity = velocity;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"[OnSubstepEnded] Error procesando botella {botella?.BodyHandle}: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
// 2. LIMPIAR VELOCIDADES DE OBJETOS CINEMÁTICOS
|
||||
// Mantener transportes y curvas estáticos después del solver
|
||||
foreach (var transport in Cuerpos.OfType<simTransporte>())
|
||||
{
|
||||
if (transport.BodyHandle.Value >= 0 && simulation.Bodies.BodyExists(transport.BodyHandle))
|
||||
|
@ -977,8 +1029,13 @@ namespace CtrEditor.Simulacion
|
|||
botella.ContactPressure = 0f;
|
||||
}
|
||||
|
||||
// ✅ NUEVO: APLICAR LIMITACIÓN DE FUERZAS Z ANTES DEL TIMESTEP
|
||||
ApplyZForceLimitation();
|
||||
// ✅ NUEVO: RESETEAR MAPA DE DENSIDAD DE CONTACTOS
|
||||
lock (_contactsLock)
|
||||
{
|
||||
_contactDensityMap.Clear();
|
||||
}
|
||||
|
||||
// ✅ ELIMINADO: ApplyZForceLimitation - ahora usamos intervención post-solver en OnSubstepEnded
|
||||
|
||||
// ✅ CONSERVAR - validación de deltaTime
|
||||
var currentTime = stopwatch.Elapsed.TotalMilliseconds;
|
||||
|
@ -1541,69 +1598,97 @@ namespace CtrEditor.Simulacion
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// ✅ 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
|
||||
/// ✅ NUEVA FUNCIÓN: Discretiza una posición en regiones para el tracking de densidad de contactos
|
||||
/// </summary>
|
||||
private void ApplyZForceLimitation()
|
||||
private Vector3 GetContactRegion(Vector3 position)
|
||||
{
|
||||
// Discretizar la posición en regiones de tamaño CONTACT_REGION_SIZE
|
||||
return new Vector3(
|
||||
MathF.Floor(position.X / CONTACT_REGION_SIZE) * CONTACT_REGION_SIZE,
|
||||
MathF.Floor(position.Y / CONTACT_REGION_SIZE) * CONTACT_REGION_SIZE,
|
||||
MathF.Floor(position.Z / CONTACT_REGION_SIZE) * CONTACT_REGION_SIZE
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ✅ NUEVA FUNCIÓN: Registra un contacto en el mapa de densidad y retorna el factor de escala de fuerzas
|
||||
/// </summary>
|
||||
public float RegisterContactAndGetForceScale(Vector3 contactPosition)
|
||||
{
|
||||
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
|
||||
var region = GetContactRegion(contactPosition);
|
||||
|
||||
foreach (var botella in Cuerpos.OfType<simBotella>())
|
||||
lock (_contactsLock)
|
||||
{
|
||||
try
|
||||
// Incrementar contador de contactos en esta región
|
||||
if (_contactDensityMap.ContainsKey(region))
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
_contactDensityMap[region]++;
|
||||
}
|
||||
catch (Exception ex)
|
||||
else
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"[ApplyZForceLimitation] Error procesando botella {botella?.BodyHandle}: {ex.Message}");
|
||||
_contactDensityMap[region] = 1;
|
||||
}
|
||||
|
||||
var contactCount = _contactDensityMap[region];
|
||||
|
||||
// Calcular factor de escala basado en número de contactos
|
||||
if (contactCount <= MAX_SIMULTANEOUS_CONTACTS)
|
||||
{
|
||||
return 1.0f; // Sin limitación
|
||||
}
|
||||
else
|
||||
{
|
||||
// Aplicar factor de escala que decrece con más contactos
|
||||
// Factor va desde 1.0 (10 contactos) hasta MAX_FORCE_SCALE_FACTOR (muchos contactos)
|
||||
var excessContacts = contactCount - MAX_SIMULTANEOUS_CONTACTS;
|
||||
var scaleFactor = 1.0f - (excessContacts * 0.05f); // Reducir 5% por cada contacto extra
|
||||
return Math.Max(scaleFactor, MAX_FORCE_SCALE_FACTOR);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"[ApplyZForceLimitation] Error crítico: {ex.Message}");
|
||||
System.Diagnostics.Debug.WriteLine($"[RegisterContactAndGetForceScale] Error: {ex.Message}");
|
||||
return 1.0f; // Sin limitación en caso de error
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ✅ NUEVA FUNCIÓN: Calcula la posición promedio de contacto entre dos collidables
|
||||
/// </summary>
|
||||
public Vector3 GetContactPosition(CollidablePair pair)
|
||||
{
|
||||
try
|
||||
{
|
||||
Vector3 positionA = Vector3.Zero;
|
||||
Vector3 positionB = Vector3.Zero;
|
||||
|
||||
// Obtener posición del primer objeto
|
||||
if (pair.A.BodyHandle.Value >= 0 && simulation?.Bodies?.BodyExists(pair.A.BodyHandle) == true)
|
||||
{
|
||||
positionA = simulation.Bodies[pair.A.BodyHandle].Pose.Position;
|
||||
}
|
||||
|
||||
// Obtener posición del segundo objeto
|
||||
if (pair.B.BodyHandle.Value >= 0 && simulation?.Bodies?.BodyExists(pair.B.BodyHandle) == true)
|
||||
{
|
||||
positionB = simulation.Bodies[pair.B.BodyHandle].Pose.Position;
|
||||
}
|
||||
|
||||
// Retornar punto medio
|
||||
return (positionA + positionB) * 0.5f;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"[GetContactPosition] Error: {ex.Message}");
|
||||
return Vector3.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
// ✅ ELIMINADO: ApplyZForceLimitation - reemplazado por intervención post-solver en OnSubstepEnded
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Clear();
|
||||
|
|
|
@ -38,6 +38,7 @@ namespace CtrEditor.Simulacion
|
|||
// ✅ NUEVO: Sistema de fricción dinámica con cooldown/heatup
|
||||
public float HighSlippery { get; set; } = 0f; // Rango 0-9, donde 9 es máxima presión/deslizamiento
|
||||
public float ContactPressure { get; set; } = 0f; // Rango 0-9, donde 9 es mínima presión/deslizamiento
|
||||
public float forceScale { get; set; } = 1f;
|
||||
|
||||
// ✅ NUEVO: Propiedades para la física personalizada
|
||||
public float BaseInverseMass { get; private set; }
|
||||
|
|
Loading…
Reference in New Issue