Se implementó un sistema de fricción dinámica que simula el comportamiento de fricción estática y dinámica en contactos botella-transporte y botella-curva, mejorando el flujo de materiales y reduciendo atascos. Se reemplazó el sistema de presión por un nuevo sistema de calibración visual que permite ajustar las fricciones de cada componente, mostrando el nivel de deslizamiento en las botellas. Además, se realizaron ajustes en la lógica de colisiones y se optimizó la gestión de fricción en la simulación.

This commit is contained in:
Miguel 2025-07-06 21:07:11 +02:00
parent d1ec7f4d12
commit f7f49d5df0
7 changed files with 289 additions and 166 deletions

View File

@ -30,3 +30,7 @@ simTransporte : Es un box por donde las botellas pueden desplazarse usando un tr
* Se ha mejorado el sistema de guías curvas (`ucTransporteCurvaGuias`) para incluir apertura en cono en los extremos de entrada y salida. Se agregó el parámetro `AnguloAperturaGuias` (por defecto 5 grados) que permite configurar la apertura modificando los radios de las guías en los puntos extremos. En lugar de cambiar ángulos, se reduce el radio de la guía superior (externa) y se aumenta el radio de la guía inferior (interna) en los segmentos inicial y final, creando naturalmente la apertura en cono. La modificación del radio se calcula usando `Math.Sin(anguloApertura)` para obtener el desplazamiento apropiado. Esta apertura facilita la entrada y salida de botellas del transporte curvo, reduciendo atascos y mejorando el flujo de materiales manteniendo la continuidad geométrica de las guías.
* Se ha implementado un sistema de fricción dinámica que simula el comportamiento de fricción estática vs dinámica para contactos botella-transporte y botella-curva. El sistema calcula el deslizamiento relativo entre la botella y la superficie del transporte: cuando el deslizamiento es bajo (< 0.05 m/s), se aplica fricción estática alta para un arrastre efectivo; cuando el deslizamiento es alto, se aplica fricción dinámica menor para permitir un deslizamiento suave. Para curvas, se calcula la velocidad de superficie usando el producto vectorial (v = ω × r). Para contactos botella-botella, se usa detección de profundidad de penetración para aplicar materiales ultra-suaves cuando hay alta presión, previniendo acumulaciones explosivas. Este enfoque proactivo reemplaza el sistema reactivo de `PressureBuildup` y proporciona un comportamiento más físicamente realista y estable en acumulaciones de botellas.
* 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.

View File

@ -5,8 +5,21 @@
<vm:osBotella />
</UserControl.DataContext>
<Ellipse Height="{Binding Diametro, Converter={StaticResource MeterToPixelConverter}}"
Stroke="{Binding ColorButton_oculto}" Fill="Gray"
Width="{Binding Diametro, Converter={StaticResource MeterToPixelConverter}}" StrokeThickness="0.5" />
<Grid>
<Ellipse Height="{Binding Diametro, Converter={StaticResource MeterToPixelConverter}}"
Stroke="{Binding ColorButton_oculto}" Fill="Gray"
Width="{Binding Diametro, Converter={StaticResource MeterToPixelConverter}}" StrokeThickness="0.5" />
<!-- ✅ NUEVO: Número del nivel de deslizamiento en el centro -->
<TextBlock Text="{Binding NivelDeslizamiento, StringFormat='{}{0:F0}'}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="4"
FontWeight="Bold"
Foreground="White"
Background="Gray"
Padding="1"
Opacity="0.8" />
</Grid>
</UserControl>

View File

@ -93,6 +93,12 @@ namespace CtrEditor.ObjetosSim
[property: Name("En Transporte con Freno")]
private bool enTransporteConFreno;
[ObservableProperty]
[property: Category("Calibración")]
[property: Description("Nivel de deslizamiento/presión (0-9) para calibración de fricción")]
[property: Name("Nivel de Deslizamiento")]
private float nivelDeslizamiento;
[ObservableProperty]
[property: Category("Simulación")]
[property: Description("Masa del objeto en kg")]
@ -174,7 +180,7 @@ namespace CtrEditor.ObjetosSim
// Sistema de colores jerarquizado para diferentes estados
if (SimGeometria.isOnBrakeTransport)
ColorButton_oculto = Brushes.Blue; // En transporte con freno (prioridad sobre presión)
else if (!SimGeometria.isOnBrakeTransport && SimGeometria.PressureBuildup>1)
else if (!SimGeometria.isOnBrakeTransport && SimGeometria.ContactPressure > 0)
ColorButton_oculto = Brushes.Red; // La botella tiene mucha presion
else
ColorButton_oculto = Brushes.Gray; // 5. Estado libre
@ -189,6 +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
}
public override void ucLoaded()

View File

@ -124,9 +124,6 @@ namespace CtrEditor.Simulacion
// ✅ CONTACTO BOTELLA-TRANSPORTE: USAR FRICCIÓN Y VELOCIDAD CINEMÁTICA
if (botella != null && (transportA != null || transportB != null))
{
// La velocidad del cuerpo cinemático del transporte se establece directamente
// en la clase simTransporte. El motor de físicas se encarga del resto.
// ✅ NUEVO: Lógica de control de presión/flotación
botella.IsTouchingTransport = true;
var botellaCollidable = GetBotellaFromCollidable(pair.A) != null ? pair.A : pair.B;
@ -135,16 +132,11 @@ namespace CtrEditor.Simulacion
var body = _simulationManager.simulation.Bodies.GetBodyReference(botellaCollidable.BodyHandle);
botella.LastTransportCollisionZ = body.Pose.Position.Z;
}
// Decrementamos la presión acumulada al tocar un transporte. Se reduce más rápido de lo que aumenta.
botella.PressureBuildup = Math.Max(0, botella.PressureBuildup - 0.1f);
// ✅ Fricción ajustada para un arrastre firme pero no excesivo.
var transport = transportA ?? transportB;
if (transport.isBrake)
{
botella.PressureBuildup = 0;
// ✅ NUEVO: Centrar la botella en el transporte de frenado la primera vez que entra.
// if (!botella.isOnBrakeTransport)
{
if (botella.BodyHandle.Value >= 0 && _simulationManager.simulation.Bodies.BodyExists(botella.BodyHandle) &&
transport.BodyHandle.Value >= 0 && _simulationManager.simulation.Bodies.BodyExists(transport.BodyHandle))
@ -172,35 +164,23 @@ namespace CtrEditor.Simulacion
}
botella.isOnBrakeTransport = true;
pairMaterial.FrictionCoefficient = 14f;
pairMaterial.MaximumRecoveryVelocity = 1.0f;
pairMaterial.SpringSettings = new SpringSettings(80, 8);
pairMaterial.FrictionCoefficient = 4f;
pairMaterial.MaximumRecoveryVelocity = 0f;
pairMaterial.SpringSettings = new SpringSettings(120, 8f);
}
else
{
if (botella.PressureBuildup > minPressureToLostFriction)
{
botella.isOnBrakeTransport = false;
pairMaterial.FrictionCoefficient = 0.1f;
pairMaterial.MaximumRecoveryVelocity = 1.0f;
pairMaterial.SpringSettings = new SpringSettings(80, 1);
}
else
{
botella.isOnBrakeTransport = false;
pairMaterial.FrictionCoefficient = 1.2f;
pairMaterial.MaximumRecoveryVelocity = 1.0f;
pairMaterial.SpringSettings = new SpringSettings(80, 1);
}
botella.isOnBrakeTransport = false;
// ✅ NUEVO: Usar fricción dinámica basada en deslizamiento
pairMaterial.FrictionCoefficient = CalculateTransportFriction(botella, transport, botellaCollidable);
pairMaterial.MaximumRecoveryVelocity = 0.1f;
pairMaterial.SpringSettings = new SpringSettings(80, 1f);
}
}
// ✅ CONTACTO BOTELLA-CURVA: USAR FRICCIÓN Y VELOCIDAD CINEMÁTICA
else if (botella != null && (curveA != null || curveB != null))
{
// El motor de físicas usará la velocidad angular del cuerpo cinemático de la curva
// para calcular las fuerzas de fricción y arrastrar la botella.
// ✅ NUEVO: Lógica de control de presión/flotación
botella.IsTouchingTransport = true;
botella.isOnBrakeTransport = false;
@ -210,59 +190,38 @@ namespace CtrEditor.Simulacion
var body = _simulationManager.simulation.Bodies.GetBodyReference(botellaCollidable.BodyHandle);
botella.LastTransportCollisionZ = body.Pose.Position.Z;
}
// Decrementamos la presión acumulada al tocar una curva.
botella.PressureBuildup = Math.Max(0, botella.PressureBuildup - 0.1f);
// ✅ Fricción ajustada para un arrastre firme pero no excesivo.
if (botella.PressureBuildup > minPressureToLostFriction)
{
pairMaterial.FrictionCoefficient = 0.1f;
pairMaterial.MaximumRecoveryVelocity = 1.0f;
pairMaterial.SpringSettings = new SpringSettings(80, 1);
}else
{
pairMaterial.FrictionCoefficient = 1f;
pairMaterial.MaximumRecoveryVelocity = 1.0f;
pairMaterial.SpringSettings = new SpringSettings(80, 1);
}
// ✅ NUEVO: Usar fricción dinámica basada en deslizamiento
var curve = curveA ?? curveB;
pairMaterial.FrictionCoefficient = CalculateCurveFriction(botella, curve, botellaCollidable);
pairMaterial.MaximumRecoveryVelocity = 0.1f;
pairMaterial.SpringSettings = new SpringSettings(80, 1f); // Colision con la cinta por gravedad
}
// ✅ 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 = 2f;
pairMaterial.SpringSettings = new SpringSettings(20, 1);
pairMaterial.FrictionCoefficient = 0.1f;
pairMaterial.MaximumRecoveryVelocity = 0.8f;
pairMaterial.SpringSettings = new SpringSettings(50, 1f); // Rebote lateral moderado
}
// ✅ NUEVO: CONTACTO BOTELLA-BOTELLA: Configuración más suave para evitar el comportamiento "pegajoso".
// ✅ NUEVO: CONTACTO BOTELLA-BOTELLA: Lógica de presión proactiva basada en la penetración.
else if (botellaA != null && botellaB != null)
{
if (botella.isOnBrakeTransport)
{
pairMaterial.FrictionCoefficient = 2.0f; // Un poco menos de fricción.
pairMaterial.MaximumRecoveryVelocity = 1.5f; // Menos rebote.
pairMaterial.SpringSettings = new SpringSettings(80, 1f); // Muelle MUCHO más suave y críticamente amortiguado.
}
else
{
pairMaterial.FrictionCoefficient = 0.01f; // Un poco menos de fricción.
pairMaterial.MaximumRecoveryVelocity = 1.5f; // Menos rebote.
pairMaterial.SpringSettings = new SpringSettings(20, 0.5f); // Muelle MUCHO más suave y críticamente amortiguado.
}
// Si una botella tiene presion esta presion es transmitia a el resto para que el resto pierda el coeficiente de friccion sobre los transportes
if (botellaA.PressureBuildup > 0 && botellaB.PressureBuildup == 0)
botellaB.PressureBuildup = botellaA.PressureBuildup;
if (botellaB.PressureBuildup > 0 && botellaA.PressureBuildup == 0)
botellaA.PressureBuildup = botellaB.PressureBuildup;
// ✅ 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
}
// Ajustes básicos para otras botellas
else if (botella != null)
{
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(80, 1);
pairMaterial.SpringSettings = new SpringSettings(30, 1);
}
}
@ -348,11 +307,139 @@ namespace CtrEditor.Simulacion
/// <summary>
/// ✅ NUEVO MÉTODO: Aplicar fuerzas de frenado directamente en el manifold
/// </summary>
/// <summary>
/// ✅ NUEVO: Calcula la fricción dinámica para contactos botella-transporte basada en el deslizamiento relativo
/// </summary>
private float CalculateTransportFriction(simBotella botella, simTransporte transport, CollidableReference botellaCollidable)
{
try
{
if (botella?.BodyHandle.Value >= 0 && _simulationManager.simulation.Bodies.BodyExists(botella.BodyHandle) &&
transport?.BodyHandle.Value >= 0 && _simulationManager.simulation.Bodies.BodyExists(transport.BodyHandle))
{
var bottleBody = _simulationManager.simulation.Bodies.GetBodyReference(botella.BodyHandle);
var transportBody = _simulationManager.simulation.Bodies.GetBodyReference(transport.BodyHandle);
var bottleVelocity = bottleBody.Velocity.Linear;
var transportVelocity = transport.CalcularVelocidadCinematica(); // La velocidad cinemática se actualiza en OnSubstepStarted
var slipVelocity = bottleVelocity - transportVelocity;
slipVelocity.Z = 0; // Ignoramos el deslizamiento vertical
var slipSpeed = slipVelocity.Length();
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 dynamicFriction = 0.10f;
// Sistema de heatup/cooldown para HighSlippery
if (slipSpeed > slipSpeedThreshold) // && botella.ContactPressure > 0
botella.HighSlippery = Math.Min(9f, botella.HighSlippery + heatupRate);
else
botella.HighSlippery = Math.Max(0f, botella.HighSlippery - cooldownRate);
// Calcular fricción basada en HighSlippery
// HighSlippery > 3 = fricción dinámica, <= 3 = fricción estática
if (botella.HighSlippery > 3f)
return dynamicFriction;
else
return staticFriction;
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"[CalculateTransportFriction] Error: {ex.Message}");
}
// Valor por defecto si hay algún error
return 1.0f;
}
/// <summary>
/// ✅ NUEVO: Calcula la fricción dinámica para contactos botella-curva basada en el deslizamiento relativo
/// </summary>
private float CalculateCurveFriction(simBotella botella, simCurve curve, CollidableReference botellaCollidable)
{
try
{
if (botella?.BodyHandle.Value >= 0 && _simulationManager.simulation.Bodies.BodyExists(botella.BodyHandle) &&
curve?.BodyHandle.Value >= 0 && _simulationManager.simulation.Bodies.BodyExists(curve.BodyHandle))
{
var bottleBody = _simulationManager.simulation.Bodies.GetBodyReference(botella.BodyHandle);
var curveBody = _simulationManager.simulation.Bodies.GetBodyReference(curve.BodyHandle);
// Aproximamos la velocidad de la superficie de la curva en la posición de la botella.
// v = ω x r
var r = bottleBody.Pose.Position - curveBody.Pose.Position;
var curveVelocityAtPoint = Vector3.Cross(curve.CalcularVelocidadCinematica(), r);
var slipVelocity = bottleBody.Velocity.Linear - curveVelocityAtPoint;
slipVelocity.Z = 0; // Ignoramos el deslizamiento vertical
var slipSpeed = slipVelocity.Length();
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 dynamicFriction = 0.10f;
// Sistema de heatup/cooldown para HighSlippery
if (slipSpeed > slipSpeedThreshold) // && botella.ContactPressure > 0
botella.HighSlippery = Math.Min(9f, botella.HighSlippery + heatupRate);
else
botella.HighSlippery = Math.Max(0f, botella.HighSlippery - cooldownRate);
// Calcular fricción basada en HighSlippery
// HighSlippery > 3 = fricción dinámica, <= 3 = fricción estática
if (botella.HighSlippery > 3f)
return dynamicFriction;
else
return staticFriction;
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"[CalculateCurveFriction] Error: {ex.Message}");
}
// Valor por defecto si hay algún error
return 0.8f;
}
/// <summary>
/// ✅ NUEVO: Calcula los parámetros de material para contactos botella-botella basado en la profundidad de penetración
/// </summary>
private void CalculateBottlePression<TManifold>(ref TManifold manifold, simBotella botellaA, simBotella botellaB) where TManifold : unmanaged, IContactManifold<TManifold>
{
var pairMaterial = new PairMaterialProperties();
try
{
const float penetrationDepthThreshold = 0.001f; // Umbral de penetración en metros para detectar alta presión.
// Iteramos sobre todos los puntos de contacto en el manifold.
for (int i = 0; i < manifold.Count; ++i)
{
// Si la profundidad de la penetración supera nuestro umbral,
// consideramos que este contacto está bajo alta presión.
if (manifold.GetDepth(i) > penetrationDepthThreshold)
{
botellaA.ContactPressure = Math.Max(botellaA.ContactPressure, manifold.GetDepth(i) - penetrationDepthThreshold);
botellaB.ContactPressure = Math.Max(botellaB.ContactPressure, manifold.GetDepth(i) - penetrationDepthThreshold);
// break; // Un punto de alta presión es suficiente.
}
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"[CalculateBottleToBottleMaterial] Error: {ex.Message}");
// Valores por defecto seguros
}
}
public bool ConfigureContactManifold(int workerIndex, CollidablePair pair, int childIndexA, int childIndexB, ref ConvexContactManifold manifold)
{
return true;
@ -427,13 +514,54 @@ 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);
//// 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];
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.
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)
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);
velocity.Angular.X = new Vector<float>(angularX);
velocity.Angular.Y = new Vector<float>(angularY);
velocity.Angular.Z = new Vector<float>(angularZ);
}
}
}
@ -511,6 +639,10 @@ namespace CtrEditor.Simulacion
// ✅ NUEVOS - gestión automática de clasificación
private void RegisterObjectHandle(simBase obj)
{
if (obj != null && obj.BodyHandle.Value >= 0)
{
CollidableData[obj.BodyHandle.Value] = obj;
}
switch (obj)
{
case simBotella bottle:
@ -540,6 +672,10 @@ namespace CtrEditor.Simulacion
private void UnregisterObjectHandle(simBase obj)
{
if (obj != null && obj.BodyHandle.Value >= 0)
{
CollidableData.Remove(obj.BodyHandle.Value);
}
switch (obj)
{
case simBotella bottle:
@ -624,8 +760,8 @@ namespace CtrEditor.Simulacion
// ✅ MODIFICADO: Pasar referencia de this al PoseIntegratorCallbacks
var poseIntegratorCallbacks = new PoseIntegratorCallbacks(
gravity: new Vector3(0, 0, -9.81f), // Gravedad en Z
linearDamping: 0.999f, // Amortiguamiento lineal suave
angularDamping: 0.995f, // Amortiguamiento angular más fuerte para detener rotaciones
linearDamping: 0.03f, // 0.999f, // Amortiguamiento lineal suave
angularDamping: 0.03f, // 0.995f, // Amortiguamiento angular más fuerte para detener rotaciones
simulationManager: this // ✅ NUEVA REFERENCIA
);
@ -799,6 +935,7 @@ namespace CtrEditor.Simulacion
foreach (var botella in Cuerpos.OfType<simBotella>())
{
botella.IsTouchingTransport = false;
botella.ContactPressure = 0f;
}
// ✅ CONSERVAR - validación de deltaTime
@ -857,27 +994,26 @@ namespace CtrEditor.Simulacion
// ✅ CONSERVAR - limitación de rotación y mantener botellas despiertas
foreach (var cuerpo in Cuerpos.OfType<simBotella>().ToList())
{
try
{
if (simulation.Bodies.BodyExists(cuerpo.BodyHandle))
{
// cuerpo.LimitRotationToXYPlane();
// simulation.Awakener.AwakenBody(cuerpo.BodyHandle);
}
}
catch (Exception ex)
{
// Error limiting rotation for bottle - continue
}
}
//// ✅ CONSERVAR - limitación de rotación y mantener botellas despiertas
//foreach (var cuerpo in Cuerpos.OfType<simBotella>().ToList())
//{
// try
// {
// if (simulation.Bodies.BodyExists(cuerpo.BodyHandle))
// {
// // cuerpo.LimitRotationToXYPlane();
// // simulation.Awakener.AwakenBody(cuerpo.BodyHandle);
// }
// }
// catch (Exception ex)
// {
// // Error limiting rotation for bottle - continue
// }
//}
// ✅ CONSERVAR - sistemas que funcionan bien
ProcessBarreraContacts();
ProcessDescarteContacts();
ProcessPressureSystem();
ProcessCleanupSystem();
// ✅ SIMPLIFICAR - limpiar solo contactos que usamos
@ -904,59 +1040,7 @@ namespace CtrEditor.Simulacion
/// <summary>
/// ✅ NUEVO: Sistema para controlar la "presión" y evitar que las botellas floten
/// Si una botella no toca un transporte por varios frames, se reestablece su altura.
/// </summary>
private void ProcessPressureSystem()
{
try
{
var botellas = Cuerpos.OfType<simBotella>().ToList();
foreach (var botella in botellas)
{
try
{
if (botella == null || !simulation.Bodies.BodyExists(botella.BodyHandle))
continue;
if (!botella.IsTouchingTransport && botella.LastTransportCollisionZ.HasValue)
{
var body = simulation.Bodies.GetBodyReference(botella.BodyHandle);
var currentZ = body.Pose.Position.Z;
// ✅ NUEVA CONDICIÓN: Solo actuar si la botella ha subido
if (currentZ > botella.LastTransportCollisionZ.Value)
{
// Incrementar el contador de presión.
botella.PressureBuildup++;
// Si se acumula suficiente presión (más de 2 frames sin contacto y subiendo),
// es un indicio de que está flotando. La reestablecemos a su última Z conocida.
if (botella.PressureBuildup > 2)
{
var pose = body.Pose;
pose.Position.Z = botella.LastTransportCollisionZ.Value;
body.Pose = pose;
// Opcional: resetear el contador para no aplicarlo constantemente en cada frame
// botella.PressureBuildup = 0;
}
}
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"[ProcessPressureSystem] Error processing bottle {botella?.BodyHandle}: {ex.Message}");
}
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"[ProcessPressureSystem] Critical error: {ex.Message}");
}
}
/// <summary>
/// ✅ NUEVO: Marca un objeto para eliminación diferida (más seguro)

View File

@ -33,9 +33,12 @@ namespace CtrEditor.Simulacion
// ✅ NUEVO: Propiedades para control de flotación/presión
public float? LastTransportCollisionZ { get; set; }
public float PressureBuildup { get; set; } = 0;
public bool IsTouchingTransport { get; set; } = false;
// ✅ 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
// ✅ NUEVO: Propiedades para la física personalizada
public float BaseInverseMass { get; private set; }
public Vector3 InitialPosition3D { get; private set; }

View File

@ -70,6 +70,23 @@ namespace CtrEditor.Simulacion
ActualizarVelocidadCinematica();
}
public Vector3 CalcularVelocidadCinematica()
{
float effectiveRadius = (_innerRadius + _outerRadius) / 2.0f;
if (effectiveRadius > 0.001f)
{
// La velocidad tangencial (Speed) se convierte a velocidad angular (ω = v / r)
// ✅ CORREGIDO: Convertir velocidad de m/min a m/s para la simulación
float angularSpeed = (Speed / SpeedConversionFactor) / effectiveRadius;
// La rotación es alrededor del eje Z en el sistema de coordenadas de BEPU
return new Vector3(0, 0, angularSpeed);
}
return Vector3.Zero; // Si no hay cuerpo o radio efectivo es muy pequeño, no hay velocidad angular
}
/// <summary>
/// ✅ NUEVO: Aplica la velocidad angular al cuerpo cinemático de BEPU.
/// </summary>
@ -79,20 +96,7 @@ namespace CtrEditor.Simulacion
{
var body = _simulation.Bodies.GetBodyReference(BodyHandle);
float effectiveRadius = (_innerRadius + _outerRadius) / 2.0f;
if (effectiveRadius > 0.001f)
{
// La velocidad tangencial (Speed) se convierte a velocidad angular (ω = v / r)
// ✅ CORREGIDO: Convertir velocidad de m/min a m/s para la simulación
float angularSpeed = (Speed / SpeedConversionFactor) / effectiveRadius;
// La rotación es alrededor del eje Z en el sistema de coordenadas de BEPU
body.Velocity.Angular = new Vector3(0, 0, angularSpeed);
}
else
{
body.Velocity.Angular = Vector3.Zero;
}
body.Velocity.Angular = CalcularVelocidadCinematica();
}
}

View File

@ -149,6 +149,14 @@ namespace CtrEditor.Simulacion
ActualizarVelocidadCinematica();
}
/// <summary>
/// ✅ NUEVO: Aplica la velocidad al cuerpo cinemático de BEPU.
/// </summary>
public Vector3 CalcularVelocidadCinematica()
{
return this.DirectionVector * (this.Speed / SpeedConversionFactor);
}
/// <summary>
/// ✅ NUEVO: Aplica la velocidad al cuerpo cinemático de BEPU.
/// </summary>
@ -158,7 +166,7 @@ namespace CtrEditor.Simulacion
{
var body = _simulation.Bodies.GetBodyReference(BodyHandle);
// ✅ CORREGIDO: Convertir velocidad de m/min a m/s para la simulación
body.Velocity.Linear = this.DirectionVector * (this.Speed / SpeedConversionFactor);
body.Velocity.Linear = CalcularVelocidadCinematica();
}
}