Se añadió una nueva librería para la simulación y se realizaron mejoras en la gestión de motores en simTransporte y simCurve. Se unificaron métodos para la creación de motores lineales y angulares, optimizando la lógica de detección de colisiones y la visualización de triángulos en 3D. Además, se corrigieron errores en la extracción de triángulos de BEPU, asegurando que se utilicen coordenadas locales para evitar transformaciones duplicadas. Se implementaron métodos para activar y desactivar el modo de depuración de triángulos, mejorando la experiencia de visualización.
This commit is contained in:
parent
501f0ffb9b
commit
c1584e8d55
|
@ -5,6 +5,9 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "../Libraries/LibS7Adv"
|
"path": "../Libraries/LibS7Adv"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "../Librerias/bepuphysics2-master"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"settings": {}
|
"settings": {}
|
||||||
|
|
|
@ -860,11 +860,11 @@ namespace CtrEditor.Simulacion
|
||||||
public simTransporte CurrentBrakeTransport { get; set; } // ✅ NUEVA: Referencia al transporte de freno actual
|
public simTransporte CurrentBrakeTransport { get; set; } // ✅ NUEVA: Referencia al transporte de freno actual
|
||||||
|
|
||||||
// ✅ NUEVO SISTEMA SIMPLIFICADO: Un solo motor por botella
|
// ✅ NUEVO SISTEMA SIMPLIFICADO: Un solo motor por botella
|
||||||
public enum MotorType { None, Linear, Angular }
|
public ConstraintHandle CurrentMotor { get; set; } = default;
|
||||||
public MotorType CurrentMotorType { get; set; } = MotorType.None;
|
public ConstraintHandle CurrentDistanceLimit { get; set; } = default;
|
||||||
public simTransporte CurrentTransport { get; set; }
|
public BodyHandle CurrentElementHandle { get; set; } = default; // Body del elemento actual (transporte o curva)
|
||||||
public ConstraintHandle CurrentMotor { get; set; }
|
|
||||||
public bool HasActiveMotor => CurrentMotor.Value != 0;
|
public bool HasActiveMotor => CurrentMotor.Value != 0;
|
||||||
|
public bool HasDistanceLimit => CurrentDistanceLimit.Value != 0;
|
||||||
|
|
||||||
private List<Action> _deferredActions;
|
private List<Action> _deferredActions;
|
||||||
|
|
||||||
|
@ -1069,51 +1069,44 @@ namespace CtrEditor.Simulacion
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ SISTEMA SIMPLIFICADO: Métodos unificados de gestión de motores
|
// ✅ SISTEMA SIMPLIFICADO: Métodos unificados de gestión de motores
|
||||||
public void AssignLinearMotor(simTransporte transport, ConstraintHandle motor)
|
public void AssignMotor(ConstraintHandle motor, BodyHandle elementHandle)
|
||||||
{
|
{
|
||||||
RemoveCurrentMotor(); // Eliminar motor anterior automáticamente
|
|
||||||
CurrentMotor = motor;
|
CurrentMotor = motor;
|
||||||
CurrentMotorType = MotorType.Linear;
|
CurrentElementHandle = elementHandle;
|
||||||
CurrentTransport = transport;
|
|
||||||
if (transport.isBrake)
|
|
||||||
{
|
|
||||||
isOnBrakeTransport = true;
|
|
||||||
CurrentBrakeTransport = transport;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AssignAngularMotor(simCurve curve, ConstraintHandle motor)
|
public void AssignDistanceLimit(ConstraintHandle distanceLimit)
|
||||||
{
|
{
|
||||||
RemoveCurrentMotor(); // Eliminar motor anterior automáticamente
|
CurrentDistanceLimit = distanceLimit;
|
||||||
CurrentMotor = motor;
|
|
||||||
CurrentMotorType = MotorType.Angular;
|
|
||||||
// Las curvas no usan CurrentTransport
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemoveCurrentMotor()
|
public void RemoveAllConstraints()
|
||||||
{
|
{
|
||||||
if (HasActiveMotor)
|
|
||||||
{
|
|
||||||
// El MotorManager se encargará de eliminar el motor del solver
|
|
||||||
CurrentMotor = default;
|
CurrentMotor = default;
|
||||||
CurrentMotorType = MotorType.None;
|
CurrentDistanceLimit = default;
|
||||||
CurrentTransport = null;
|
CurrentElementHandle = default;
|
||||||
isOnBrakeTransport = false;
|
|
||||||
CurrentBrakeTransport = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ LEGACY: Mantener compatibilidad con código existente
|
// ✅ LEGACY: Mantener compatibilidad con código existente
|
||||||
public void AssignToTransport(simTransporte transport, ConstraintHandle motor)
|
public void AssignToTransport(simTransporte transport, ConstraintHandle motor)
|
||||||
{
|
{
|
||||||
AssignLinearMotor(transport, motor);
|
AssignMotor(motor, transport.BodyHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemoveFromTransport()
|
public void RemoveFromTransport()
|
||||||
{
|
{
|
||||||
RemoveCurrentMotor();
|
RemoveAllConstraints();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void AssignAngularMotor(simCurve curve, ConstraintHandle motor)
|
||||||
|
{
|
||||||
|
AssignMotor(motor, curve.BodyHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveCurrentMotor()
|
||||||
|
{
|
||||||
|
RemoveAllConstraints();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -1228,8 +1221,8 @@ namespace CtrEditor.Simulacion
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// ✅ NUEVO: Obtiene los triángulos reales creados para la curva
|
/// ✅ CORREGIDO: Obtiene los triángulos reales creados para la curva
|
||||||
/// Devuelve los triángulos transformados a coordenadas mundiales usando la posición del cuerpo
|
/// Devuelve los triángulos en coordenadas locales para evitar transformación duplicada
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Triangle[] GetRealBEPUTriangles()
|
public Triangle[] GetRealBEPUTriangles()
|
||||||
{
|
{
|
||||||
|
@ -1241,11 +1234,10 @@ namespace CtrEditor.Simulacion
|
||||||
return new Triangle[0];
|
return new Triangle[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ TRANSFORMAR a coordenadas mundiales
|
// ✅ CORREGIDO: Devolver triángulos en coordenadas locales
|
||||||
var worldTriangles = TransformTrianglesToWorldCoordinates(_storedTriangles);
|
// La visualización 3D aplicará la transformación una sola vez
|
||||||
|
System.Diagnostics.Debug.WriteLine($"[GetRealBEPUTriangles] Devolviendo {_storedTriangles.Length} triángulos en coordenadas locales");
|
||||||
System.Diagnostics.Debug.WriteLine($"[GetRealBEPUTriangles] Devolviendo {worldTriangles.Length} triángulos transformados a coordenadas mundiales");
|
return _storedTriangles;
|
||||||
return worldTriangles;
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
@ -1254,6 +1246,29 @@ namespace CtrEditor.Simulacion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ✅ NUEVO: Obtiene los triángulos transformados a coordenadas mundiales (solo para debugging)
|
||||||
|
/// </summary>
|
||||||
|
public Triangle[] GetWorldBEPUTriangles()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (_storedTriangles == null || _storedTriangles.Length == 0)
|
||||||
|
{
|
||||||
|
return new Triangle[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
var worldTriangles = TransformTrianglesToWorldCoordinates(_storedTriangles);
|
||||||
|
System.Diagnostics.Debug.WriteLine($"[GetWorldBEPUTriangles] Devolviendo {worldTriangles.Length} triángulos en coordenadas mundiales");
|
||||||
|
return worldTriangles;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
System.Diagnostics.Debug.WriteLine($"[GetWorldBEPUTriangles] Error: {ex.Message}");
|
||||||
|
return new Triangle[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// ✅ NUEVO: Transforma triángulos de coordenadas locales a mundiales
|
/// ✅ NUEVO: Transforma triángulos de coordenadas locales a mundiales
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -1336,10 +1351,10 @@ namespace CtrEditor.Simulacion
|
||||||
info.AppendLine($" C: ({triangle.C.X:F3}, {triangle.C.Y:F3}, {triangle.C.Z:F3})");
|
info.AppendLine($" C: ({triangle.C.X:F3}, {triangle.C.Y:F3}, {triangle.C.Z:F3})");
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ NUEVO: Mostrar triángulos transformados a coordenadas mundiales
|
// ✅ CORREGIDO: Mostrar triángulos transformados a coordenadas mundiales usando nuevo método
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var worldTriangles = TransformTrianglesToWorldCoordinates(_storedTriangles);
|
var worldTriangles = GetWorldBEPUTriangles();
|
||||||
info.AppendLine($"");
|
info.AppendLine($"");
|
||||||
info.AppendLine($"Primeros {triangleCount} triángulos MUNDIALES (transformados):");
|
info.AppendLine($"Primeros {triangleCount} triángulos MUNDIALES (transformados):");
|
||||||
|
|
||||||
|
@ -1657,12 +1672,12 @@ namespace CtrEditor.Simulacion
|
||||||
|
|
||||||
public bool ConfigureContactManifold<TManifold>(int workerIndex, CollidablePair pair, ref TManifold manifold, out PairMaterialProperties pairMaterial) where TManifold : unmanaged, IContactManifold<TManifold>
|
public bool ConfigureContactManifold<TManifold>(int workerIndex, CollidablePair pair, ref TManifold manifold, out PairMaterialProperties pairMaterial) where TManifold : unmanaged, IContactManifold<TManifold>
|
||||||
{
|
{
|
||||||
// ✅ SIMPLIFICADO - configuración básica de materiales físicos
|
// ✅ CONFIGURACIÓN BÁSICA de materiales físicos
|
||||||
pairMaterial = new PairMaterialProperties
|
pairMaterial = new PairMaterialProperties
|
||||||
{
|
{
|
||||||
FrictionCoefficient = 0.6f, // Fricción moderada por defecto
|
FrictionCoefficient = 0.6f,
|
||||||
MaximumRecoveryVelocity = 2f, // Velocidad máxima de recuperación
|
MaximumRecoveryVelocity = 2f,
|
||||||
SpringSettings = new SpringSettings(60, 4) // Rigidez y amortiguamiento estándar
|
SpringSettings = new SpringSettings(60, 4)
|
||||||
};
|
};
|
||||||
|
|
||||||
if (_simulationManager != null)
|
if (_simulationManager != null)
|
||||||
|
@ -1675,13 +1690,13 @@ namespace CtrEditor.Simulacion
|
||||||
var descarteB = GetDescarteFromCollidable(pair.B);
|
var descarteB = GetDescarteFromCollidable(pair.B);
|
||||||
var botella = botellaA ?? botellaB;
|
var botella = botellaA ?? botellaB;
|
||||||
|
|
||||||
// ✅ CONSERVAR - barreras como sensores puros
|
// ✅ BARRERAS como sensores puros
|
||||||
if (barreraA != null || barreraB != null)
|
if (barreraA != null || barreraB != null)
|
||||||
{
|
{
|
||||||
return false; // NO generar contacto físico para barreras
|
return false; // NO generar contacto físico
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ CONSERVAR - descartes como sensores puros
|
// ✅ DESCARTES como sensores puros
|
||||||
if (descarteA != null || descarteB != null)
|
if (descarteA != null || descarteB != null)
|
||||||
{
|
{
|
||||||
var descarte = descarteA ?? descarteB;
|
var descarte = descarteA ?? descarteB;
|
||||||
|
@ -1689,65 +1704,50 @@ namespace CtrEditor.Simulacion
|
||||||
{
|
{
|
||||||
_simulationManager.RegisterDescarteContact(descarte, botella);
|
_simulationManager.RegisterDescarteContact(descarte, botella);
|
||||||
}
|
}
|
||||||
return false; // NO generar contacto físico para descartes
|
return false; // NO generar contacto físico
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ SIMPLIFICADO: Detección directa de transportes y curvas
|
// ✅ DETECCIÓN SIMPLIFICADA de transportes y curvas
|
||||||
var transportA = GetTransportFromCollidable(pair.A);
|
var transportA = GetTransportFromCollidable(pair.A);
|
||||||
var transportB = GetTransportFromCollidable(pair.B);
|
var transportB = GetTransportFromCollidable(pair.B);
|
||||||
var curveA = GetCurveFromCollidable(pair.A);
|
var curveA = GetCurveFromCollidable(pair.A);
|
||||||
var curveB = GetCurveFromCollidable(pair.B);
|
var curveB = GetCurveFromCollidable(pair.B);
|
||||||
|
|
||||||
// ✅ VERIFICAR SI YA SE CREÓ MOTOR PARA EVITAR DUPLICADOS
|
// ✅ CONTACTO BOTELLA-TRANSPORTE
|
||||||
BodyHandle? bottleHandle = null;
|
|
||||||
BodyHandle? elementHandle = null;
|
|
||||||
|
|
||||||
if (botella != null && (transportA != null || transportB != null))
|
if (botella != null && (transportA != null || transportB != null))
|
||||||
{
|
{
|
||||||
var transport = transportA ?? transportB;
|
var transport = transportA ?? transportB;
|
||||||
bottleHandle = botella.BodyHandle;
|
|
||||||
elementHandle = transport.BodyHandle;
|
|
||||||
|
|
||||||
var pairKey = (bottleHandle.Value, elementHandle.Value);
|
// Verificar si ya tiene motor para este elemento específico
|
||||||
if (!_simulationManager._motorsCreated.Contains(pairKey))
|
if (botella.CurrentElementHandle.Value == 0 || !botella.CurrentElementHandle.Equals(transport.BodyHandle))
|
||||||
{
|
{
|
||||||
_simulationManager.TryCreateTransportMotor(botella, transport);
|
_simulationManager.TryCreateTransportMotor(botella, transport);
|
||||||
_simulationManager._motorsCreated.Add(pairKey);
|
|
||||||
System.Diagnostics.Debug.WriteLine($"[Manifold] Motor transporte: {bottleHandle} → {elementHandle}");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fricción alta para transportes (SIEMPRE aplicar para colisión física)
|
// Fricción alta para transportes
|
||||||
pairMaterial.FrictionCoefficient = 0.9f;
|
pairMaterial.FrictionCoefficient = 0.9f;
|
||||||
pairMaterial.MaximumRecoveryVelocity = 1f;
|
pairMaterial.MaximumRecoveryVelocity = 1f;
|
||||||
pairMaterial.SpringSettings = new SpringSettings(80, 6);
|
pairMaterial.SpringSettings = new SpringSettings(80, 6);
|
||||||
}
|
}
|
||||||
// ✅ SIMPLIFICADO: Si hay contacto botella-curva, crear AngularAxisMotor
|
// ✅ CONTACTO BOTELLA-CURVA (ahora también usa LinearAxisMotor + DistanceLimit)
|
||||||
else if (botella != null && (curveA != null || curveB != null))
|
else if (botella != null && (curveA != null || curveB != null))
|
||||||
{
|
{
|
||||||
var curve = curveA ?? curveB;
|
var curve = curveA ?? curveB;
|
||||||
|
|
||||||
bottleHandle = botella.BodyHandle;
|
// Verificar si ya tiene motor para este elemento específico
|
||||||
elementHandle = curve.BodyHandle;
|
if (botella.CurrentElementHandle.Value == 0 || !botella.CurrentElementHandle.Equals(curve.BodyHandle))
|
||||||
|
|
||||||
// ✅ SIMPLIFICADO: Crear motor angular usando el cuerpo principal de la curva
|
|
||||||
var pairKey = (bottleHandle.Value, elementHandle.Value);
|
|
||||||
if (!_simulationManager._motorsCreated.Contains(pairKey))
|
|
||||||
{
|
{
|
||||||
_simulationManager.TryCreateCurveAngularMotor(botella, curve);
|
_simulationManager.TryCreateCurveLinearMotor(botella, curve);
|
||||||
_simulationManager._motorsCreated.Add(pairKey);
|
|
||||||
System.Diagnostics.Debug.WriteLine($"[Manifold] Motor angular curva: {bottleHandle} → {elementHandle}");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fricción alta para curvas (SIEMPRE aplicar para colisión física)
|
// Fricción alta para curvas
|
||||||
pairMaterial.FrictionCoefficient = 0.9f;
|
pairMaterial.FrictionCoefficient = 0.9f;
|
||||||
pairMaterial.MaximumRecoveryVelocity = 1f;
|
pairMaterial.MaximumRecoveryVelocity = 1f;
|
||||||
pairMaterial.SpringSettings = new SpringSettings(80, 6);
|
pairMaterial.SpringSettings = new SpringSettings(80, 6);
|
||||||
|
|
||||||
}
|
}
|
||||||
// Solo ajustes básicos de fricción para otras botellas
|
// Ajustes básicos para otras botellas
|
||||||
else if (botella != null)
|
else if (botella != null)
|
||||||
{
|
{
|
||||||
// Fricción alta para mayor estabilidad de botellas
|
|
||||||
pairMaterial.FrictionCoefficient = 0.9f;
|
pairMaterial.FrictionCoefficient = 0.9f;
|
||||||
pairMaterial.MaximumRecoveryVelocity = 1f;
|
pairMaterial.MaximumRecoveryVelocity = 1f;
|
||||||
pairMaterial.SpringSettings = new SpringSettings(80, 6);
|
pairMaterial.SpringSettings = new SpringSettings(80, 6);
|
||||||
|
@ -1899,430 +1899,124 @@ namespace CtrEditor.Simulacion
|
||||||
public class MotorManager
|
public class MotorManager
|
||||||
{
|
{
|
||||||
private SimulationManagerBEPU _simulationManager;
|
private SimulationManagerBEPU _simulationManager;
|
||||||
private Dictionary<BodyHandle, ConstraintHandle> _activeMotors;
|
private Dictionary<BodyHandle, (ConstraintHandle motor, ConstraintHandle? distanceLimit, BodyHandle elementHandle)> _bottleConstraints;
|
||||||
private Dictionary<ConstraintHandle, simBotella> _motorToBottle;
|
|
||||||
|
|
||||||
public MotorManager(SimulationManagerBEPU simulationManager)
|
public MotorManager(SimulationManagerBEPU simulationManager)
|
||||||
{
|
{
|
||||||
_simulationManager = simulationManager;
|
_simulationManager = simulationManager;
|
||||||
_activeMotors = new Dictionary<BodyHandle, ConstraintHandle>();
|
_bottleConstraints = new Dictionary<BodyHandle, (ConstraintHandle, ConstraintHandle?, BodyHandle)>();
|
||||||
_motorToBottle = new Dictionary<ConstraintHandle, simBotella>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void CreateTransportMotor(simBotella bottle, simTransporte transport)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// ✅ VALIDACIONES CRÍTICAS
|
|
||||||
if (bottle == null || transport == null || _simulationManager?.simulation == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Validar que los BodyHandle existan
|
|
||||||
if (!_simulationManager.simulation.Bodies.BodyExists(bottle.BodyHandle) ||
|
|
||||||
!_simulationManager.simulation.Bodies.BodyExists(transport.BodyHandle))
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Asegurar que DirectionVector esté inicializado
|
|
||||||
transport.UpdateCachedProperties();
|
|
||||||
|
|
||||||
// Validar que DirectionVector no sea cero
|
|
||||||
if (transport.DirectionVector.Length() < 0.001f)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// ✅ CORREGIDO - LocalAxis debe estar en coordenadas locales del transporte
|
|
||||||
// Para transportes, el eje local de movimiento es siempre UnitX (eje largo)
|
|
||||||
var localAxis = Vector3.UnitX; // Siempre UnitX en coordenadas locales del transporte
|
|
||||||
|
|
||||||
var motor = new LinearAxisMotor()
|
|
||||||
{
|
|
||||||
LocalOffsetA = Vector3.Zero,
|
|
||||||
LocalOffsetB = Vector3.Zero,
|
|
||||||
LocalAxis = localAxis, // ✅ Usar eje local, no mundial
|
|
||||||
TargetVelocity = transport.SpeedMetersPerSecond,
|
|
||||||
Settings = new MotorSettings(Math.Max(bottle.Mass * 30f, 10f), 5f)
|
|
||||||
};
|
|
||||||
|
|
||||||
// 🔍 DEBUG: Comparar eje local vs mundial
|
|
||||||
System.Diagnostics.Debug.WriteLine($"[Motor] Transport angle: {transport.GetRotationZ()}°");
|
|
||||||
System.Diagnostics.Debug.WriteLine($"[Motor] World DirectionVector: {transport.DirectionVector}");
|
|
||||||
System.Diagnostics.Debug.WriteLine($"[Motor] Local Axis: {localAxis}");
|
|
||||||
System.Diagnostics.Debug.WriteLine($"[Motor] Target Velocity: {transport.SpeedMetersPerSecond}");
|
|
||||||
|
|
||||||
var motorHandle = _simulationManager.simulation.Solver.Add(
|
|
||||||
transport.BodyHandle,
|
|
||||||
bottle.BodyHandle,
|
|
||||||
motor
|
|
||||||
);
|
|
||||||
|
|
||||||
_activeMotors[bottle.BodyHandle] = motorHandle;
|
|
||||||
_motorToBottle[motorHandle] = bottle;
|
|
||||||
bottle.AssignToTransport(transport, motorHandle);
|
|
||||||
transport.BottlesOnTransport.Add(bottle);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
System.Diagnostics.Debug.WriteLine($"[MotorManager] Error creating transport motor: {ex.Message}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void CreateCurveMotor(simBotella bottle, simCurve curve)
|
|
||||||
{
|
|
||||||
// ✅ SIMPLIFICADO: Usar AngularAxisMotor para curvas
|
|
||||||
CreateCurveAngularMotor(bottle, curve);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// ✅ NUEVO: Crear motor angular para curvas (simplificado)
|
/// ✅ ÚNICO MÉTODO: Crear motor LinearAxisMotor con dirección calculada
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void CreateCurveAngularMotor(simBotella bottle, simCurve curve)
|
public bool CreateLinearMotor(simBotella bottle, BodyHandle elementHandle, Vector3 direction, float speed, bool isCurve = false)
|
||||||
{
|
{
|
||||||
try
|
// Verificar si ya tenemos motor para este elemento específico
|
||||||
|
if (_bottleConstraints.TryGetValue(bottle.BodyHandle, out var existing))
|
||||||
{
|
{
|
||||||
// ✅ VALIDACIONES CRÍTICAS
|
if (existing.elementHandle.Equals(elementHandle))
|
||||||
if (bottle == null || curve == null || _simulationManager?.simulation == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Validar que los BodyHandle existan
|
|
||||||
if (!_simulationManager.simulation.Bodies.BodyExists(bottle.BodyHandle) ||
|
|
||||||
!_simulationManager.simulation.Bodies.BodyExists(curve.BodyHandle))
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Verificar que la curva tenga velocidad
|
|
||||||
if (Math.Abs(curve.Speed) < 0.001f)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// ✅ SIMPLIFICADO: AngularAxisMotor para rotación en el plano XY
|
|
||||||
var angularMotor = new AngularAxisMotor()
|
|
||||||
{
|
{
|
||||||
LocalAxisA = Vector3.UnitZ, // Eje Z para rotación en plano XY
|
return false; // Ya tiene motor para este elemento, no recrear
|
||||||
TargetVelocity = curve.Speed, // Velocidad angular directa en rad/s
|
|
||||||
Settings = new MotorSettings(Math.Max(bottle.Mass * 10f, 5f), 3f)
|
|
||||||
};
|
|
||||||
|
|
||||||
var motorHandle = _simulationManager.simulation.Solver.Add(
|
|
||||||
curve.BodyHandle,
|
|
||||||
bottle.BodyHandle,
|
|
||||||
angularMotor
|
|
||||||
);
|
|
||||||
|
|
||||||
_activeMotors[bottle.BodyHandle] = motorHandle;
|
|
||||||
_motorToBottle[motorHandle] = bottle;
|
|
||||||
bottle.AssignAngularMotor(curve, motorHandle);
|
|
||||||
curve.BottlesOnCurve.Add(bottle);
|
|
||||||
|
|
||||||
System.Diagnostics.Debug.WriteLine($"[MotorManager] Angular motor created: {bottle.BodyHandle} → {curve.BodyHandle}");
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
System.Diagnostics.Debug.WriteLine($"[MotorManager] Error creating angular motor: {ex.Message}");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemoveMotor(simBotella bottle)
|
// Tiene motor para otro elemento, eliminarlo primero
|
||||||
{
|
RemoveBottleConstraints(bottle);
|
||||||
try
|
|
||||||
{
|
|
||||||
if (bottle == null || _simulationManager?.simulation == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (_activeMotors.TryGetValue(bottle.BodyHandle, out var motorHandle))
|
|
||||||
{
|
|
||||||
// Validar que el solver existe antes de remover
|
|
||||||
if (_simulationManager.simulation.Solver != null)
|
|
||||||
{
|
|
||||||
_simulationManager.simulation.Solver.Remove(motorHandle);
|
|
||||||
System.Diagnostics.Debug.WriteLine($"[RemoveMotor] Motor eliminado: {motorHandle}");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_activeMotors.Remove(bottle.BodyHandle);
|
// Validaciones básicas
|
||||||
_motorToBottle.Remove(motorHandle);
|
|
||||||
|
|
||||||
// ✅ NUEVO: Limpiar pares de motores creados para esta botella
|
|
||||||
var bottleHandle = bottle.BodyHandle;
|
|
||||||
var motorsToRemove = _simulationManager._motorsCreated
|
|
||||||
.Where(pair => pair.bottle == bottleHandle)
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
foreach (var pair in motorsToRemove)
|
|
||||||
{
|
|
||||||
_simulationManager._motorsCreated.Remove(pair);
|
|
||||||
System.Diagnostics.Debug.WriteLine($"[MotorManager] Motor pair cleaned: {pair.bottle} - {pair.element}");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Limpiar referencias de forma segura
|
|
||||||
bottle.CurrentTransport?.BottlesOnTransport.Remove(bottle);
|
|
||||||
|
|
||||||
// ✅ SIMPLIFICADO: Solo limpiar listas de botellas en curvas
|
|
||||||
var curves = _simulationManager.Cuerpos.OfType<simCurve>();
|
|
||||||
foreach (var curve in curves)
|
|
||||||
{
|
|
||||||
curve.BottlesOnCurve.Remove(bottle);
|
|
||||||
}
|
|
||||||
|
|
||||||
bottle.RemoveFromTransport();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
System.Diagnostics.Debug.WriteLine($"[MotorManager] Error removing motor: {ex.Message}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateTransportSpeed(simTransporte transport)
|
|
||||||
{
|
|
||||||
// TODO: Implementar actualización de velocidad cuando se resuelva la API correcta
|
|
||||||
// Por ahora, recreamos los motores como alternativa
|
|
||||||
foreach (var bottle in transport.BottlesOnTransport.ToList())
|
|
||||||
{
|
|
||||||
if (_activeMotors.ContainsKey(bottle.BodyHandle))
|
|
||||||
{
|
|
||||||
RemoveMotor(bottle);
|
|
||||||
CreateTransportMotor(bottle, transport);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateCurveSpeed(simCurve curve)
|
|
||||||
{
|
|
||||||
// ✅ Las curvas requieren recrear motores cuando cambia la velocidad
|
|
||||||
// Esto es porque la dirección tangencial puede cambiar y es más eficiente recrear
|
|
||||||
foreach (var bottle in curve.BottlesOnCurve.ToList())
|
|
||||||
{
|
|
||||||
if (_activeMotors.ContainsKey(bottle.BodyHandle))
|
|
||||||
{
|
|
||||||
RemoveMotor(bottle);
|
|
||||||
CreateCurveMotor(bottle, curve);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// ✅ NUEVO: Actualiza la dirección de un motor de curva existente para un nuevo triángulo
|
|
||||||
/// </summary>
|
|
||||||
public void UpdateCurveMotorDirection(simBotella bottle, simCurve curve, Vector3 newDirection, BodyHandle newTriangleHandle)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (bottle == null || curve == null || _simulationManager?.simulation == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!_activeMotors.ContainsKey(bottle.BodyHandle))
|
|
||||||
return; // No hay motor activo
|
|
||||||
|
|
||||||
// Validar que la dirección sea válida
|
|
||||||
if (newDirection.Length() < 0.001f)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// ✅ ESTRATEGIA: Recrear motor con nueva dirección (más eficiente que actualizar en BEPU)
|
|
||||||
var oldMotorHandle = _activeMotors[bottle.BodyHandle];
|
|
||||||
|
|
||||||
// Eliminar motor anterior sin limpiar pares silenciados (los conservamos)
|
|
||||||
if (_simulationManager.simulation.Solver != null)
|
|
||||||
{
|
|
||||||
_simulationManager.simulation.Solver.Remove(oldMotorHandle);
|
|
||||||
// Motor anterior eliminado y recreado con nueva dirección
|
|
||||||
}
|
|
||||||
|
|
||||||
_activeMotors.Remove(bottle.BodyHandle);
|
|
||||||
_motorToBottle.Remove(oldMotorHandle);
|
|
||||||
|
|
||||||
// Crear nuevo motor con nueva dirección (reutilizar CreateCurveMotorWithDirection)
|
|
||||||
CreateCurveMotorWithDirection(bottle, curve, newDirection);
|
|
||||||
|
|
||||||
System.Diagnostics.Debug.WriteLine($"[UpdateCurveMotorDirection] Motor actualizado con nueva dirección para triángulo: {newTriangleHandle}");
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
System.Diagnostics.Debug.WriteLine($"[MotorManager] Error updating curve motor direction: {ex.Message}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// ✅ NUEVO - Crea motor de curva con dirección precalculada
|
|
||||||
/// </summary>
|
|
||||||
public void CreateCurveMotorWithDirection(simBotella bottle, simCurve curve, Vector3 direction)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// ✅ VALIDACIONES CRÍTICAS
|
|
||||||
if (bottle == null || curve == null || _simulationManager?.simulation == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Validar que los BodyHandle existan
|
|
||||||
if (!_simulationManager.simulation.Bodies.BodyExists(bottle.BodyHandle) ||
|
|
||||||
!_simulationManager.simulation.Bodies.BodyExists(curve.BodyHandle))
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Validar que la dirección no sea cero
|
|
||||||
if (direction.Length() < 0.001f)
|
if (direction.Length() < 0.001f)
|
||||||
return;
|
return false;
|
||||||
|
|
||||||
// ✅ CONVERTIR DIRECCIÓN MUNDIAL A COORDENADAS LOCALES DE LA CURVA
|
|
||||||
// El cuerpo principal de la curva no tiene rotación, pero aún necesitamos coordenadas locales
|
|
||||||
Vector3 localAxisDirection;
|
|
||||||
|
|
||||||
if (_simulationManager.simulation.Bodies.BodyExists(curve.BodyHandle))
|
|
||||||
{
|
|
||||||
var curveBody = _simulationManager.simulation.Bodies[curve.BodyHandle];
|
|
||||||
var inverseCurveOrientation = Quaternion.Conjugate(curveBody.Pose.Orientation);
|
|
||||||
localAxisDirection = Vector3.Transform(direction, inverseCurveOrientation);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Fallback: usar dirección mundial si no se puede obtener orientación
|
|
||||||
localAxisDirection = direction;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// ✅ SOLO LinearAxisMotor - sin AngularAxisMotor
|
||||||
var motor = new LinearAxisMotor()
|
var motor = new LinearAxisMotor()
|
||||||
{
|
{
|
||||||
LocalOffsetA = Vector3.Zero,
|
LocalOffsetA = Vector3.Zero,
|
||||||
LocalOffsetB = Vector3.Zero,
|
LocalOffsetB = Vector3.Zero,
|
||||||
LocalAxis = localAxisDirection, // ✅ Usar dirección en coordenadas locales
|
LocalAxis = Vector3.Normalize(direction),
|
||||||
TargetVelocity = curve.Speed, // ✅ SIMPLIFICADO: Usar velocidad directa
|
TargetVelocity = speed,
|
||||||
Settings = new MotorSettings(Math.Max(bottle.Mass * 20f, 8f), 4f)
|
Settings = new MotorSettings(Math.Max(bottle.Mass * 20f, 8f), 4f)
|
||||||
};
|
};
|
||||||
|
|
||||||
var motorHandle = _simulationManager.simulation.Solver.Add(
|
var motorHandle = _simulationManager.simulation.Solver.Add(elementHandle, bottle.BodyHandle, motor);
|
||||||
curve.BodyHandle,
|
|
||||||
bottle.BodyHandle,
|
|
||||||
motor
|
|
||||||
);
|
|
||||||
// Logging con coordenadas mundiales y locales para depuración
|
|
||||||
System.Diagnostics.Debug.WriteLine($"[CreateCurveMotorWithDirection] Bottle: {bottle.BodyHandle} World: ({direction.X:F2}, {direction.Y:F2}) Local: ({localAxisDirection.X:F2}, {localAxisDirection.Y:F2}) Speed: {curve.Speed}");
|
|
||||||
|
|
||||||
_activeMotors[bottle.BodyHandle] = motorHandle;
|
ConstraintHandle? distanceHandle = null;
|
||||||
_motorToBottle[motorHandle] = bottle;
|
|
||||||
bottle.CurrentMotor = motorHandle;
|
// Si es curva, crear también DistanceLimit
|
||||||
curve.BottlesOnCurve.Add(bottle);
|
if (isCurve)
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
{
|
||||||
System.Diagnostics.Debug.WriteLine($"[MotorManager] Error creating curve motor with direction: {ex.Message}");
|
var element = _simulationManager.GetSimBaseFromBodyHandle(elementHandle);
|
||||||
|
if (element is simCurve curve)
|
||||||
|
{
|
||||||
|
var radius = (bottle.GetPosition() - curve.GetPosition()).Length();
|
||||||
|
if (radius > 1e-3f)
|
||||||
|
{
|
||||||
|
var distanceLimit = new DistanceLimit()
|
||||||
|
{
|
||||||
|
LocalOffsetA = Vector3.Zero,
|
||||||
|
LocalOffsetB = Vector3.Zero,
|
||||||
|
MinimumDistance = 4*radius,
|
||||||
|
MaximumDistance = 4*radius,
|
||||||
|
SpringSettings = new SpringSettings(30f, 1f)
|
||||||
|
};
|
||||||
|
|
||||||
|
distanceHandle = _simulationManager.simulation.Solver.Add(elementHandle, bottle.BodyHandle, distanceLimit);
|
||||||
|
bottle.AssignDistanceLimit(distanceHandle.Value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Registrar constraints
|
||||||
|
_bottleConstraints[bottle.BodyHandle] = (motorHandle, distanceHandle, elementHandle);
|
||||||
|
bottle.AssignMotor(motorHandle, elementHandle);
|
||||||
|
|
||||||
|
System.Diagnostics.Debug.WriteLine($"[MotorManager] Motor creado: {bottle.BodyHandle} → {elementHandle} (Curve: {isCurve})");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// ✅ OPTIMIZADO: Elimina todos los motors conectados a un body específico de forma eficiente
|
/// ✅ ÚNICO MÉTODO DE ELIMINACIÓN: Eliminar todos los constraints de una botella
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void RemoveMotorsByBodyHandle(BodyHandle bodyHandle)
|
public void RemoveBottleConstraints(simBotella bottle)
|
||||||
|
{
|
||||||
|
if (_bottleConstraints.TryGetValue(bottle.BodyHandle, out var constraints))
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (_simulationManager?.simulation?.Solver == null)
|
_simulationManager.simulation.Solver.Remove(constraints.motor);
|
||||||
return;
|
|
||||||
|
|
||||||
// ✅ OPTIMIZACIÓN: Solo procesar si hay motors activos
|
|
||||||
if (_activeMotors.Count == 0)
|
|
||||||
{
|
|
||||||
return; // No hay motors, salir inmediatamente
|
|
||||||
}
|
}
|
||||||
|
catch { }
|
||||||
|
|
||||||
// ✅ OPTIMIZACIÓN: Identificar rápidamente el tipo de body
|
if (constraints.distanceLimit.HasValue)
|
||||||
var targetObject = _simulationManager.GetSimBaseFromBodyHandle(bodyHandle);
|
|
||||||
if (targetObject == null)
|
|
||||||
{
|
|
||||||
// ✅ Podría ser un triángulo de curva, buscar curva que lo contenga
|
|
||||||
var curve = _simulationManager.GetCurveFromTriangleBodyHandle(bodyHandle);
|
|
||||||
if (curve == null)
|
|
||||||
{
|
|
||||||
return; // No es un objeto relevante
|
|
||||||
}
|
|
||||||
targetObject = curve;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encontrar motors que deben eliminarse
|
|
||||||
var motorsToRemove = new List<(BodyHandle bottleHandle, ConstraintHandle motorHandle)>();
|
|
||||||
|
|
||||||
foreach (var kvp in _activeMotors)
|
|
||||||
{
|
|
||||||
var bottleHandle = kvp.Key;
|
|
||||||
var motorHandle = kvp.Value;
|
|
||||||
|
|
||||||
if (_motorToBottle.TryGetValue(motorHandle, out var bottle))
|
|
||||||
{
|
|
||||||
bool shouldRemove = false;
|
|
||||||
|
|
||||||
// ✅ OPTIMIZADO: Verificación directa por tipo de objeto
|
|
||||||
switch (targetObject)
|
|
||||||
{
|
|
||||||
case simTransporte transport:
|
|
||||||
shouldRemove = bottle.CurrentTransport?.BodyHandle.Equals(bodyHandle) == true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case simCurve curve:
|
|
||||||
// Verificar si la botella está en esta curva específica
|
|
||||||
shouldRemove = curve.BottlesOnCurve.Contains(bottle);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shouldRemove)
|
|
||||||
{
|
|
||||||
motorsToRemove.Add((bottleHandle, motorHandle));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Eliminar motors encontrados
|
|
||||||
foreach (var (bottleHandle, motorHandle) in motorsToRemove)
|
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_simulationManager.simulation.Solver.Remove(motorHandle);
|
_simulationManager.simulation.Solver.Remove(constraints.distanceLimit.Value);
|
||||||
_activeMotors.Remove(bottleHandle);
|
|
||||||
|
|
||||||
if (_motorToBottle.TryGetValue(motorHandle, out var bottle))
|
|
||||||
{
|
|
||||||
bottle.RemoveFromTransport();
|
|
||||||
_motorToBottle.Remove(motorHandle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
System.Diagnostics.Debug.WriteLine($"[MotorManager] Error removing specific motor for body {bodyHandle}: {ex.Message}");
|
|
||||||
}
|
}
|
||||||
|
catch { }
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ SOLO LOG SI SE ELIMINARON MOTORS
|
_bottleConstraints.Remove(bottle.BodyHandle);
|
||||||
if (motorsToRemove.Count > 0)
|
|
||||||
{
|
|
||||||
System.Diagnostics.Debug.WriteLine($"[MotorManager] Removed {motorsToRemove.Count} motors connected to body {bodyHandle}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
System.Diagnostics.Debug.WriteLine($"[MotorManager] Error in RemoveMotorsByBodyHandle: {ex.Message}");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bottle.RemoveAllConstraints();
|
||||||
|
|
||||||
|
System.Diagnostics.Debug.WriteLine($"[MotorManager] Constraints removidos para botella: {bottle.BodyHandle}");
|
||||||
|
}
|
||||||
|
|
||||||
public void Clear()
|
public void Clear()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (_simulationManager?.simulation?.Solver != null)
|
foreach (var kvp in _bottleConstraints)
|
||||||
{
|
{
|
||||||
foreach (var motorHandle in _activeMotors.Values)
|
try { _simulationManager.simulation.Solver.Remove(kvp.Value.motor); } catch { }
|
||||||
|
if (kvp.Value.distanceLimit.HasValue)
|
||||||
{
|
{
|
||||||
try
|
try { _simulationManager.simulation.Solver.Remove(kvp.Value.distanceLimit.Value); } catch { }
|
||||||
{
|
|
||||||
_simulationManager.simulation.Solver.Remove(motorHandle);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
System.Diagnostics.Debug.WriteLine($"[MotorManager] Error removing motor during clear: {ex.Message}");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
_bottleConstraints.Clear();
|
||||||
_activeMotors.Clear();
|
|
||||||
_motorToBottle.Clear();
|
|
||||||
|
|
||||||
// ✅ SIMPLIFICADO: Solo limpiar pares de motores creados
|
|
||||||
if (_simulationManager?.Cuerpos != null)
|
|
||||||
{
|
|
||||||
// Limpiar pares de motores creados
|
|
||||||
_simulationManager._motorsCreated?.Clear();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
@ -2373,6 +2067,8 @@ namespace CtrEditor.Simulacion
|
||||||
|
|
||||||
private object _contactsLock = new object();
|
private object _contactsLock = new object();
|
||||||
|
|
||||||
|
private Dictionary<BodyHandle, ConstraintHandle> _distanceLimits; // ✅ NUEVO: DistanceLimit por botella
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Obtiene el objeto simBase correspondiente a un BodyHandle
|
/// Obtiene el objeto simBase correspondiente a un BodyHandle
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -2401,15 +2097,12 @@ namespace CtrEditor.Simulacion
|
||||||
break;
|
break;
|
||||||
case simTransporte transport:
|
case simTransporte transport:
|
||||||
_transportHandles.Add(transport.BodyHandle);
|
_transportHandles.Add(transport.BodyHandle);
|
||||||
// Suscribirse a cambios de velocidad
|
|
||||||
transport.OnSpeedChanged += _motorManager.UpdateTransportSpeed;
|
|
||||||
// Calcular propiedades iniciales
|
// Calcular propiedades iniciales
|
||||||
transport.UpdateCachedProperties();
|
transport.UpdateCachedProperties();
|
||||||
break;
|
break;
|
||||||
case simCurve curve:
|
case simCurve curve:
|
||||||
_curveHandles.Add(curve.BodyHandle);
|
_curveHandles.Add(curve.BodyHandle);
|
||||||
// ✅ SIMPLIFICADO: Ya no hay triángulos separados, solo el cuerpo principal
|
// ✅ SIMPLIFICADO: Ya no hay triángulos separados, solo el cuerpo principal
|
||||||
curve.OnSpeedChanged += _motorManager.UpdateCurveSpeed;
|
|
||||||
break;
|
break;
|
||||||
case simBarrera barrier:
|
case simBarrera barrier:
|
||||||
_barrierHandles.Add(barrier.BodyHandle);
|
_barrierHandles.Add(barrier.BodyHandle);
|
||||||
|
@ -2427,18 +2120,16 @@ namespace CtrEditor.Simulacion
|
||||||
case simBotella bottle:
|
case simBotella bottle:
|
||||||
_bottleHandles.Remove(bottle.BodyHandle);
|
_bottleHandles.Remove(bottle.BodyHandle);
|
||||||
// Eliminar motor si existe
|
// Eliminar motor si existe
|
||||||
_motorManager.RemoveMotor(bottle);
|
_motorManager.RemoveBottleConstraints(bottle);
|
||||||
// Eliminar de pares de motores creados
|
// Eliminar de pares de motores creados
|
||||||
RemoveFromMotorsCreated(bottle.BodyHandle);
|
RemoveFromMotorsCreated(bottle.BodyHandle);
|
||||||
break;
|
break;
|
||||||
case simTransporte transport:
|
case simTransporte transport:
|
||||||
_transportHandles.Remove(transport.BodyHandle);
|
_transportHandles.Remove(transport.BodyHandle);
|
||||||
transport.OnSpeedChanged -= _motorManager.UpdateTransportSpeed;
|
|
||||||
break;
|
break;
|
||||||
case simCurve curve:
|
case simCurve curve:
|
||||||
_curveHandles.Remove(curve.BodyHandle);
|
_curveHandles.Remove(curve.BodyHandle);
|
||||||
// ✅ SIMPLIFICADO: Ya no hay triángulos separados
|
// ✅ SIMPLIFICADO: Ya no hay triángulos separados
|
||||||
curve.OnSpeedChanged -= _motorManager.UpdateCurveSpeed;
|
|
||||||
break;
|
break;
|
||||||
case simBarrera barrier:
|
case simBarrera barrier:
|
||||||
_barrierHandles.Remove(barrier.BodyHandle);
|
_barrierHandles.Remove(barrier.BodyHandle);
|
||||||
|
@ -2739,16 +2430,21 @@ namespace CtrEditor.Simulacion
|
||||||
if (_frameCount % 10 != 0) return;
|
if (_frameCount % 10 != 0) return;
|
||||||
|
|
||||||
var activeBottles = Cuerpos.OfType<simBotella>()
|
var activeBottles = Cuerpos.OfType<simBotella>()
|
||||||
.Where(b => b.HasActiveMotor && b.CurrentTransport != null)
|
.Where(b => b.HasActiveMotor && b.CurrentElementHandle.Value != 0)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
foreach (var bottle in activeBottles)
|
foreach (var bottle in activeBottles)
|
||||||
{
|
{
|
||||||
if (!IsBottleOnTransport(bottle, bottle.CurrentTransport))
|
var element = GetSimBaseFromBodyHandle(bottle.CurrentElementHandle);
|
||||||
|
if (element is simTransporte transport)
|
||||||
|
{
|
||||||
|
if (!IsBottleOnTransport(bottle, transport))
|
||||||
{
|
{
|
||||||
ProcessBottleExitsTransport(bottle);
|
ProcessBottleExitsTransport(bottle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Las curvas no necesitan verificación de salida porque usan DistanceLimit
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool IsBottleOnTransport(simBotella bottle, simTransporte transport)
|
private bool IsBottleOnTransport(simBotella bottle, simTransporte transport)
|
||||||
|
@ -2768,14 +2464,14 @@ namespace CtrEditor.Simulacion
|
||||||
private void ProcessBottleExitsTransport(simBotella bottle)
|
private void ProcessBottleExitsTransport(simBotella bottle)
|
||||||
{
|
{
|
||||||
RemoveCurrentMotorPair(bottle);
|
RemoveCurrentMotorPair(bottle);
|
||||||
_motorManager.RemoveMotor(bottle);
|
_motorManager.RemoveBottleConstraints(bottle);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RemoveCurrentMotorPair(simBotella bottle)
|
private void RemoveCurrentMotorPair(simBotella bottle)
|
||||||
{
|
{
|
||||||
if (bottle.CurrentTransport != null)
|
if (bottle.CurrentElementHandle.Value != 0)
|
||||||
{
|
{
|
||||||
_motorsCreated.Remove((bottle.BodyHandle, bottle.CurrentTransport.BodyHandle));
|
_motorsCreated.Remove((bottle.BodyHandle, bottle.CurrentElementHandle));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2884,7 +2580,15 @@ namespace CtrEditor.Simulacion
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void RemoveMotorsConnectedToBody(BodyHandle bodyHandle)
|
public void RemoveMotorsConnectedToBody(BodyHandle bodyHandle)
|
||||||
{
|
{
|
||||||
_motorManager?.RemoveMotorsByBodyHandle(bodyHandle);
|
// Encontrar todas las botellas conectadas a este body y eliminar sus constraints
|
||||||
|
var bottlesToRemoveConstraints = Cuerpos.OfType<simBotella>()
|
||||||
|
.Where(b => b.HasActiveMotor && b.CurrentElementHandle.Equals(bodyHandle))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
foreach (var bottle in bottlesToRemoveConstraints)
|
||||||
|
{
|
||||||
|
_motorManager.RemoveBottleConstraints(bottle);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -2907,19 +2611,18 @@ namespace CtrEditor.Simulacion
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Validaciones básicas
|
|
||||||
if (bottle == null || transport == null || _motorManager == null)
|
if (bottle == null || transport == null || _motorManager == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Verificar si ya tiene un motor activo
|
|
||||||
if (bottle.HasActiveMotor)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Asegurar que el transporte tenga propiedades inicializadas
|
|
||||||
if (Math.Abs(transport.Speed) < 0.001f)
|
if (Math.Abs(transport.Speed) < 0.001f)
|
||||||
return; // No crear motor para transportes detenidos
|
return; // No crear motor para transportes detenidos
|
||||||
|
|
||||||
_motorManager.CreateTransportMotor(bottle, transport);
|
// Calcular dirección del transporte
|
||||||
|
transport.UpdateCachedProperties();
|
||||||
|
var direction = transport.DirectionVector;
|
||||||
|
var speed = transport.SpeedMetersPerSecond;
|
||||||
|
|
||||||
|
_motorManager.CreateLinearMotor(bottle, transport.BodyHandle, direction, speed, false);
|
||||||
|
|
||||||
System.Diagnostics.Debug.WriteLine($"[TryCreateTransportMotor] Motor creado: {bottle.BodyHandle} - {transport.BodyHandle}");
|
System.Diagnostics.Debug.WriteLine($"[TryCreateTransportMotor] Motor creado: {bottle.BodyHandle} - {transport.BodyHandle}");
|
||||||
}
|
}
|
||||||
|
@ -2930,31 +2633,33 @@ namespace CtrEditor.Simulacion
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// ✅ NUEVO: Intenta crear un motor angular para curvas si no existe uno activo
|
/// ✅ NUEVO: Crear motor de curva usando LinearAxisMotor + DistanceLimit
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void TryCreateCurveAngularMotor(simBotella bottle, simCurve curve)
|
public void TryCreateCurveLinearMotor(simBotella bottle, simCurve curve)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Validaciones básicas
|
|
||||||
if (bottle == null || curve == null || _motorManager == null)
|
if (bottle == null || curve == null || _motorManager == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Verificar si ya tiene un motor activo
|
|
||||||
if (bottle.HasActiveMotor)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Asegurar que la curva tenga velocidad
|
|
||||||
if (Math.Abs(curve.Speed) < 0.001f)
|
if (Math.Abs(curve.Speed) < 0.001f)
|
||||||
return; // No crear motor para curvas detenidas
|
return; // No crear motor para curvas detenidas
|
||||||
|
|
||||||
_motorManager.CreateCurveAngularMotor(bottle, curve);
|
// Calcular dirección tangencial de la curva
|
||||||
|
var bottlePos = bottle.GetPosition();
|
||||||
|
var curvePos = curve.GetPosition();
|
||||||
|
var radial = bottlePos - curvePos;
|
||||||
|
var normalizedRadial = Vector3.Normalize(radial);
|
||||||
|
// Para una curva en el plano XY, la tangente se obtiene rotando el vector radial 90 grados
|
||||||
|
var tangentDirection = new Vector3(-normalizedRadial.Y, normalizedRadial.X, 0f);
|
||||||
|
|
||||||
System.Diagnostics.Debug.WriteLine($"[TryCreateCurveAngularMotor] Motor angular creado: {bottle.BodyHandle} - {curve.BodyHandle}");
|
_motorManager.CreateLinearMotor(bottle, curve.BodyHandle, tangentDirection, curve.Speed, true);
|
||||||
|
|
||||||
|
System.Diagnostics.Debug.WriteLine($"[TryCreateCurveLinearMotor] Motor creado: {bottle.BodyHandle} - {curve.BodyHandle}");
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
System.Diagnostics.Debug.WriteLine($"[SimulationManager] Error in TryCreateCurveAngularMotor: {ex.Message}");
|
System.Diagnostics.Debug.WriteLine($"[SimulationManager] Error in TryCreateCurveLinearMotor: {ex.Message}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3427,54 +3132,5 @@ namespace CtrEditor.Simulacion
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// ✅ MODIFICADO: Sistema dinámico de motores de curva - permite actualización entre triángulos
|
|
||||||
/// </summary>
|
|
||||||
public void TryCreateCurveMotor(simBotella bottle, simCurve curve, Vector3 triangleDirection, BodyHandle triangleHandle)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Validaciones básicas
|
|
||||||
if (bottle == null || curve == null || _motorManager == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Verificar que la curva tenga velocidad
|
|
||||||
if (Math.Abs(curve.Speed) < 0.001f)
|
|
||||||
return; // No crear motor para curvas detenidas
|
|
||||||
|
|
||||||
// Verificar que la dirección del triángulo sea válida
|
|
||||||
if (triangleDirection.Length() < 0.001f)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// ✅ NUEVO: Si ya tiene motor de curva, actualizarlo; si no, crear uno nuevo
|
|
||||||
if (bottle.HasActiveMotor)
|
|
||||||
{
|
|
||||||
// Verificar si el motor actual es de una curva
|
|
||||||
var currentCurves = Cuerpos.OfType<simCurve>().Where(c => c.BottlesOnCurve.Contains(bottle));
|
|
||||||
if (currentCurves.Any())
|
|
||||||
{
|
|
||||||
// Actualizar motor existente con nueva dirección del triángulo
|
|
||||||
_motorManager.UpdateCurveMotorDirection(bottle, curve, triangleDirection, triangleHandle);
|
|
||||||
|
|
||||||
System.Diagnostics.Debug.WriteLine($"[TryCreateCurveMotor] Motor actualizado: {bottle.BodyHandle} → Triangle {triangleHandle}");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Tiene motor de transporte, no interferir
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ✅ Crear nuevo motor de curva
|
|
||||||
_motorManager.CreateCurveMotorWithDirection(bottle, curve, triangleDirection);
|
|
||||||
|
|
||||||
System.Diagnostics.Debug.WriteLine($"[TryCreateCurveMotor] Nuevo motor: {bottle.BodyHandle} → Triangle {triangleHandle}");
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
System.Diagnostics.Debug.WriteLine($"[SimulationManager] Error in TryCreateCurveMotor with direction: {ex.Message}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -71,7 +71,7 @@ namespace CtrEditor.Simulacion
|
||||||
private Dictionary<simBase, ModelVisual3D> simBaseToModelMap;
|
private Dictionary<simBase, ModelVisual3D> simBaseToModelMap;
|
||||||
private Dictionary<simBase, ShapeDimensions> lastKnownDimensions;
|
private Dictionary<simBase, ShapeDimensions> lastKnownDimensions;
|
||||||
|
|
||||||
// ✅ NUEVO: Flag de debug para mostrar triángulos individuales de curvas
|
// ✅ CORREGIDO: Flag de debug para mostrar triángulos individuales de curvas (true temporalmente para verificar)
|
||||||
public static bool DebugShowIndividualTriangles { get; set; } = true;
|
public static bool DebugShowIndividualTriangles { get; set; } = true;
|
||||||
|
|
||||||
public HelixViewport3D Viewport3D
|
public HelixViewport3D Viewport3D
|
||||||
|
@ -386,103 +386,85 @@ namespace CtrEditor.Simulacion
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// ✅ NUEVO: Crea mesh directamente desde los triángulos reales de BEPU
|
/// ✅ CORREGIDO: Crea mesh desde los triángulos locales de BEPU
|
||||||
/// Extrae la geometría exacta que está almacenada en la simulación física
|
/// Extrae la geometría exacta en coordenadas locales para evitar transformación duplicada
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void CreateCurveMeshFromBEPUTriangles(MeshBuilder meshBuilder, simCurve curve)
|
private void CreateCurveMeshFromBEPUTriangles(MeshBuilder meshBuilder, simCurve curve)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// ✅ EXTRAER TRIÁNGULOS REALES DE BEPU
|
// ✅ EXTRAER TRIÁNGULOS LOCALES DE BEPU (sin transformación duplicada)
|
||||||
var realTriangles = curve.GetRealBEPUTriangles();
|
var localTriangles = curve.GetRealBEPUTriangles();
|
||||||
|
|
||||||
if (realTriangles.Length == 0)
|
if (localTriangles.Length == 0)
|
||||||
{
|
{
|
||||||
System.Diagnostics.Debug.WriteLine($"[3D BEPU] WARNING: No se pudieron extraer triángulos reales, usando fallback");
|
System.Diagnostics.Debug.WriteLine($"[3D BEPU] WARNING: No se pudieron extraer triángulos locales, usando fallback");
|
||||||
CreateCurveMeshFallback(meshBuilder, curve);
|
CreateCurveMeshFallback(meshBuilder, curve);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
System.Diagnostics.Debug.WriteLine($"[3D BEPU] Creando mesh desde {realTriangles.Length} triángulos reales de BEPU");
|
System.Diagnostics.Debug.WriteLine($"[3D BEPU] Creando mesh desde {localTriangles.Length} triángulos locales de BEPU");
|
||||||
|
|
||||||
// ✅ USAR TRIÁNGULOS EXACTOS DE BEPU
|
// ✅ USAR TRIÁNGULOS LOCALES DE BEPU (la transformación se aplicará automáticamente en UpdateVisualization)
|
||||||
foreach (var triangle in realTriangles)
|
foreach (var triangle in localTriangles)
|
||||||
{
|
{
|
||||||
// Convertir triángulos de BEPU a puntos 3D de Helix
|
// Convertir triángulos de BEPU a puntos 3D de Helix
|
||||||
var pointA = new Point3D(triangle.A.X, triangle.A.Y, triangle.A.Z);
|
var pointA = new Point3D(triangle.A.X, triangle.A.Y, triangle.A.Z);
|
||||||
var pointB = new Point3D(triangle.B.X, triangle.B.Y, triangle.B.Z);
|
var pointB = new Point3D(triangle.B.X, triangle.B.Y, triangle.B.Z);
|
||||||
var pointC = new Point3D(triangle.C.X, triangle.C.Y, triangle.C.Z);
|
var pointC = new Point3D(triangle.C.X, triangle.C.Y, triangle.C.Z);
|
||||||
|
|
||||||
// Agregar triángulo exacto al mesh
|
// Agregar triángulo en coordenadas locales al mesh
|
||||||
meshBuilder.AddTriangle(pointA, pointB, pointC);
|
meshBuilder.AddTriangle(pointA, pointB, pointC);
|
||||||
}
|
}
|
||||||
|
|
||||||
System.Diagnostics.Debug.WriteLine($"[3D BEPU] ✅ Mesh creado usando triángulos reales de BEPU");
|
System.Diagnostics.Debug.WriteLine($"[3D BEPU] ✅ Mesh creado usando triángulos locales de BEPU (transformación aplicada automáticamente)");
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
System.Diagnostics.Debug.WriteLine($"[3D BEPU] ERROR extrayendo triángulos reales: {ex.Message}");
|
System.Diagnostics.Debug.WriteLine($"[3D BEPU] ERROR extrayendo triángulos locales: {ex.Message}");
|
||||||
System.Diagnostics.Debug.WriteLine($"[3D BEPU] Fallback a geometría recreada");
|
System.Diagnostics.Debug.WriteLine($"[3D BEPU] Fallback a geometría recreada");
|
||||||
CreateCurveMeshFallback(meshBuilder, curve);
|
CreateCurveMeshFallback(meshBuilder, curve);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// ✅ NUEVO: Función de debug que muestra triángulos individuales reales de BEPU
|
/// ✅ CORREGIDO: Función de debug que muestra triángulos reales de BEPU sin offset artificial
|
||||||
/// Cada triángulo se renderiza de manera separada con offset para poder hacer debug visual
|
/// Los triángulos se muestran exactamente como están en la física (planos, sin separación)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void CreateCurveDebugMeshWithIndividualTriangles(MeshBuilder meshBuilder, simCurve curve)
|
private void CreateCurveDebugMeshWithIndividualTriangles(MeshBuilder meshBuilder, simCurve curve)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// ✅ EXTRAER TRIÁNGULOS REALES DE BEPU PARA DEBUG
|
// ✅ EXTRAER TRIÁNGULOS LOCALES DE BEPU PARA DEBUG
|
||||||
var realTriangles = curve.GetRealBEPUTriangles();
|
var localTriangles = curve.GetRealBEPUTriangles();
|
||||||
|
|
||||||
if (realTriangles.Length == 0)
|
if (localTriangles.Length == 0)
|
||||||
{
|
{
|
||||||
System.Diagnostics.Debug.WriteLine($"[3D Debug] WARNING: No hay triángulos reales para debug, usando fallback");
|
System.Diagnostics.Debug.WriteLine($"[3D Debug] WARNING: No hay triángulos locales para debug, usando fallback");
|
||||||
CreateCurveMeshFallback(meshBuilder, curve);
|
CreateCurveMeshFallback(meshBuilder, curve);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
System.Diagnostics.Debug.WriteLine($"[3D Debug] Creando debug de {realTriangles.Length} triángulos reales individuales");
|
System.Diagnostics.Debug.WriteLine($"[3D Debug] Creando debug de {localTriangles.Length} triángulos reales planos");
|
||||||
|
|
||||||
// ✅ MOSTRAR CADA TRIÁNGULO SEPARADO CON OFFSET PARA DEBUG
|
// ✅ MOSTRAR TRIÁNGULOS REALES SIN OFFSET ARTIFICIAL
|
||||||
float triangleSeparation = 0.02f; // Separación entre triángulos para debug visual
|
for (int i = 0; i < localTriangles.Length; i++)
|
||||||
|
|
||||||
for (int i = 0; i < realTriangles.Length; i++)
|
|
||||||
{
|
{
|
||||||
var triangle = realTriangles[i];
|
var triangle = localTriangles[i];
|
||||||
|
|
||||||
// Calcular centroide del triángulo
|
// ✅ USAR TRIÁNGULOS EXACTOS SIN MODIFICACIÓN
|
||||||
var centroid = (triangle.A + triangle.B + triangle.C) / 3f;
|
var pointA = new Point3D(triangle.A.X, triangle.A.Y, triangle.A.Z);
|
||||||
|
var pointB = new Point3D(triangle.B.X, triangle.B.Y, triangle.B.Z);
|
||||||
|
var pointC = new Point3D(triangle.C.X, triangle.C.Y, triangle.C.Z);
|
||||||
|
|
||||||
// Calcular normal del triángulo
|
// Agregar triángulo real sin modificaciones
|
||||||
var edge1 = triangle.B - triangle.A;
|
|
||||||
var edge2 = triangle.C - triangle.A;
|
|
||||||
var normal = Vector3.Normalize(Vector3.Cross(edge1, edge2));
|
|
||||||
|
|
||||||
// Offset hacia arriba para separar triángulos visualmente
|
|
||||||
var offset = normal * triangleSeparation * (i + 1);
|
|
||||||
|
|
||||||
// Aplicar offset a todos los vértices
|
|
||||||
var offsetA = triangle.A + offset;
|
|
||||||
var offsetB = triangle.B + offset;
|
|
||||||
var offsetC = triangle.C + offset;
|
|
||||||
|
|
||||||
// Convertir a puntos 3D de Helix
|
|
||||||
var pointA = new Point3D(offsetA.X, offsetA.Y, offsetA.Z);
|
|
||||||
var pointB = new Point3D(offsetB.X, offsetB.Y, offsetB.Z);
|
|
||||||
var pointC = new Point3D(offsetC.X, offsetC.Y, offsetC.Z);
|
|
||||||
|
|
||||||
// Agregar triángulo separado
|
|
||||||
meshBuilder.AddTriangle(pointA, pointB, pointC);
|
meshBuilder.AddTriangle(pointA, pointB, pointC);
|
||||||
|
|
||||||
// ✅ DEBUG: Agregar bordes para visualizar mejor cada triángulo
|
// ✅ DEBUG: Agregar bordes delgados para visualizar límites entre triángulos
|
||||||
AddDebugTriangleEdges(meshBuilder, pointA, pointB, pointC, 0.005f);
|
AddDebugTriangleEdges(meshBuilder, pointA, pointB, pointC, 0.002f);
|
||||||
}
|
}
|
||||||
|
|
||||||
System.Diagnostics.Debug.WriteLine($"[3D Debug] ✅ Debug mesh creado con triángulos reales separados");
|
System.Diagnostics.Debug.WriteLine($"[3D Debug] ✅ Debug mesh creado con triángulos reales planos (sin offset artificial)");
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
@ -985,16 +967,26 @@ namespace CtrEditor.Simulacion
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// ✅ NUEVO: Activa o desactiva el modo debug para mostrar triángulos individuales de curvas
|
/// ✅ MODO DEBUG DE TRIÁNGULOS DE CURVAS:
|
||||||
|
///
|
||||||
|
/// TRUE (Debug Mode): Muestra triángulos individuales reales de BEPU con bordes visibles
|
||||||
|
/// FALSE (Normal Mode): Muestra superficie continua suave
|
||||||
|
///
|
||||||
|
/// Uso desde código:
|
||||||
|
/// // Activar modo debug para ver triángulos reales
|
||||||
|
/// visualizationManager.ShowRealTriangles();
|
||||||
|
///
|
||||||
|
/// // Desactivar para ver superficie continua
|
||||||
|
/// visualizationManager.ShowContinuousSurface();
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="enableDebug">True para activar debug, false para modo normal</param>
|
/// <param name="enableDebug">True para mostrar triángulos reales, false para superficie continua</param>
|
||||||
/// <param name="forceRefresh">True para forzar actualización inmediata de todas las curvas</param>
|
/// <param name="forceRefresh">True para forzar actualización inmediata de todas las curvas</param>
|
||||||
public void SetDebugTrianglesMode(bool enableDebug, bool forceRefresh = true)
|
public void SetDebugTrianglesMode(bool enableDebug, bool forceRefresh = true)
|
||||||
{
|
{
|
||||||
bool wasChanged = DebugShowIndividualTriangles != enableDebug;
|
bool wasChanged = DebugShowIndividualTriangles != enableDebug;
|
||||||
DebugShowIndividualTriangles = enableDebug;
|
DebugShowIndividualTriangles = enableDebug;
|
||||||
|
|
||||||
System.Diagnostics.Debug.WriteLine($"[3D Debug] Debug triangles mode: {(enableDebug ? "ENABLED" : "DISABLED")}");
|
System.Diagnostics.Debug.WriteLine($"[3D Debug] Modo triángulos reales: {(enableDebug ? "ACTIVADO (triángulos individuales)" : "DESACTIVADO (superficie continua)")}");
|
||||||
|
|
||||||
if (wasChanged && forceRefresh)
|
if (wasChanged && forceRefresh)
|
||||||
{
|
{
|
||||||
|
@ -1002,6 +994,22 @@ namespace CtrEditor.Simulacion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ✅ NUEVO: Método simple para activar el modo debug - muestra triángulos reales planos
|
||||||
|
/// </summary>
|
||||||
|
public void ShowRealTriangles()
|
||||||
|
{
|
||||||
|
SetDebugTrianglesMode(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ✅ NUEVO: Método simple para activar el modo superficie continua
|
||||||
|
/// </summary>
|
||||||
|
public void ShowContinuousSurface()
|
||||||
|
{
|
||||||
|
SetDebugTrianglesMode(false);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// ✅ NUEVO: Fuerza la regeneración de todas las visualizaciones de curvas
|
/// ✅ NUEVO: Fuerza la regeneración de todas las visualizaciones de curvas
|
||||||
/// Útil cuando se cambia el modo debug
|
/// Útil cuando se cambia el modo debug
|
||||||
|
@ -1077,17 +1085,17 @@ namespace CtrEditor.Simulacion
|
||||||
debugInfo.AppendLine(meshInfo);
|
debugInfo.AppendLine(meshInfo);
|
||||||
debugInfo.AppendLine($"");
|
debugInfo.AppendLine($"");
|
||||||
|
|
||||||
// ✅ EXTRAER Y MOSTRAR TRIÁNGULOS REALES
|
// ✅ EXTRAER Y MOSTRAR TRIÁNGULOS REALES (LOCALES)
|
||||||
var realTriangles = curve.GetRealBEPUTriangles();
|
var localTriangles = curve.GetRealBEPUTriangles();
|
||||||
debugInfo.AppendLine($"TRIÁNGULOS REALES EXTRAÍDOS: {realTriangles.Length}");
|
debugInfo.AppendLine($"TRIÁNGULOS REALES EXTRAÍDOS (LOCALES): {localTriangles.Length}");
|
||||||
debugInfo.AppendLine($"");
|
debugInfo.AppendLine($"");
|
||||||
|
|
||||||
// Mostrar los primeros triángulos con detalles completos
|
// Mostrar los primeros triángulos locales con detalles completos
|
||||||
int maxToShow = Math.Min(10, realTriangles.Length);
|
int maxToShow = Math.Min(5, localTriangles.Length);
|
||||||
for (int i = 0; i < maxToShow; i++)
|
for (int i = 0; i < maxToShow; i++)
|
||||||
{
|
{
|
||||||
var triangle = realTriangles[i];
|
var triangle = localTriangles[i];
|
||||||
debugInfo.AppendLine($"Triángulo {i + 1}:");
|
debugInfo.AppendLine($"Triángulo LOCAL {i + 1}:");
|
||||||
debugInfo.AppendLine($" A: ({triangle.A.X:F4}, {triangle.A.Y:F4}, {triangle.A.Z:F4})");
|
debugInfo.AppendLine($" A: ({triangle.A.X:F4}, {triangle.A.Y:F4}, {triangle.A.Z:F4})");
|
||||||
debugInfo.AppendLine($" B: ({triangle.B.X:F4}, {triangle.B.Y:F4}, {triangle.B.Z:F4})");
|
debugInfo.AppendLine($" B: ({triangle.B.X:F4}, {triangle.B.Y:F4}, {triangle.B.Z:F4})");
|
||||||
debugInfo.AppendLine($" C: ({triangle.C.X:F4}, {triangle.C.Y:F4}, {triangle.C.Z:F4})");
|
debugInfo.AppendLine($" C: ({triangle.C.X:F4}, {triangle.C.Y:F4}, {triangle.C.Z:F4})");
|
||||||
|
@ -1101,9 +1109,24 @@ namespace CtrEditor.Simulacion
|
||||||
debugInfo.AppendLine($"");
|
debugInfo.AppendLine($"");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (realTriangles.Length > maxToShow)
|
// ✅ NUEVO: Mostrar también algunos triángulos en coordenadas mundiales
|
||||||
|
var worldTriangles = curve.GetWorldBEPUTriangles();
|
||||||
|
debugInfo.AppendLine($"TRIÁNGULOS REALES EXTRAÍDOS (MUNDIALES): {worldTriangles.Length}");
|
||||||
|
debugInfo.AppendLine($"");
|
||||||
|
|
||||||
|
for (int i = 0; i < Math.Min(3, worldTriangles.Length); i++)
|
||||||
{
|
{
|
||||||
debugInfo.AppendLine($"... y {realTriangles.Length - maxToShow} triángulos más");
|
var triangle = worldTriangles[i];
|
||||||
|
debugInfo.AppendLine($"Triángulo MUNDIAL {i + 1}:");
|
||||||
|
debugInfo.AppendLine($" A: ({triangle.A.X:F4}, {triangle.A.Y:F4}, {triangle.A.Z:F4})");
|
||||||
|
debugInfo.AppendLine($" B: ({triangle.B.X:F4}, {triangle.B.Y:F4}, {triangle.B.Z:F4})");
|
||||||
|
debugInfo.AppendLine($" C: ({triangle.C.X:F4}, {triangle.C.Y:F4}, {triangle.C.Z:F4})");
|
||||||
|
debugInfo.AppendLine($"");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (localTriangles.Length > maxToShow)
|
||||||
|
{
|
||||||
|
debugInfo.AppendLine($"... y {localTriangles.Length - maxToShow} triángulos locales más");
|
||||||
}
|
}
|
||||||
|
|
||||||
debugInfo.AppendLine($"");
|
debugInfo.AppendLine($"");
|
||||||
|
@ -1118,18 +1141,18 @@ namespace CtrEditor.Simulacion
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// ✅ NUEVO: Activa la visualización de triángulos reales de BEPU
|
/// ✅ CORREGIDO: Activa la visualización de triángulos locales de BEPU
|
||||||
/// Muestra los triángulos exactos extraídos de la simulación física
|
/// Muestra los triángulos exactos extraídos de la simulación física en coordenadas locales
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="enable">True para mostrar triángulos reales, false para superficie normal</param>
|
/// <param name="enable">True para mostrar triángulos locales, false para superficie normal</param>
|
||||||
public void SetRealBEPUTrianglesMode(bool enable)
|
public void SetRealBEPUTrianglesMode(bool enable)
|
||||||
{
|
{
|
||||||
SetDebugTrianglesMode(enable);
|
SetDebugTrianglesMode(enable);
|
||||||
System.Diagnostics.Debug.WriteLine($"[3D BEPU] Modo triángulos reales: {(enable ? "ACTIVADO" : "DESACTIVADO")}");
|
System.Diagnostics.Debug.WriteLine($"[3D BEPU] Modo triángulos locales: {(enable ? "ACTIVADO" : "DESACTIVADO")}");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// ✅ NUEVO: Verifica si una curva tiene triángulos válidos en BEPU
|
/// ✅ CORREGIDO: Verifica si una curva tiene triángulos válidos en BEPU
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="curve">Curva a verificar</param>
|
/// <param name="curve">Curva a verificar</param>
|
||||||
/// <returns>True si tiene triángulos válidos</returns>
|
/// <returns>True si tiene triángulos válidos</returns>
|
||||||
|
@ -1139,7 +1162,7 @@ namespace CtrEditor.Simulacion
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var triangles = curve.GetRealBEPUTriangles();
|
var triangles = curve.GetRealBEPUTriangles(); // Obtiene triángulos locales
|
||||||
bool hasTriangles = triangles.Length > 0;
|
bool hasTriangles = triangles.Length > 0;
|
||||||
|
|
||||||
System.Diagnostics.Debug.WriteLine($"[3D BEPU] Curva tiene {triangles.Length} triángulos válidos: {hasTriangles}");
|
System.Diagnostics.Debug.WriteLine($"[3D BEPU] Curva tiene {triangles.Length} triángulos válidos: {hasTriangles}");
|
||||||
|
|
Loading…
Reference in New Issue