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:
parent
d1ec7f4d12
commit
f7f49d5df0
|
@ -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.
|
||||
|
||||
|
|
|
@ -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>
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue