Implementación de un sistema simplificado para la gestión de motores en simCurve, unificando métodos y eliminando redundancias. Se introdujeron nuevas propiedades para el manejo de motores lineales y angulares, y se mejoró la creación de triángulos en la simulación. Además, se optimizó la visualización 3D extrayendo triángulos reales de BEPU y se añadieron métodos para depuración y verificación de triángulos válidos. Se eliminaron métodos y propiedades obsoletas, mejorando la eficiencia del código.
This commit is contained in:
parent
4ff93a5802
commit
501f0ffb9b
|
@ -859,7 +859,9 @@ namespace CtrEditor.Simulacion
|
|||
public bool isOnBrakeTransport; // Nueva propiedad para marcar si está en un transporte con freno
|
||||
public simTransporte CurrentBrakeTransport { get; set; } // ✅ NUEVA: Referencia al transporte de freno actual
|
||||
|
||||
// ✅ NUEVAS PROPIEDADES - gestión de motores
|
||||
// ✅ NUEVO SISTEMA SIMPLIFICADO: Un solo motor por botella
|
||||
public enum MotorType { None, Linear, Angular }
|
||||
public MotorType CurrentMotorType { get; set; } = MotorType.None;
|
||||
public simTransporte CurrentTransport { get; set; }
|
||||
public ConstraintHandle CurrentMotor { get; set; }
|
||||
public bool HasActiveMotor => CurrentMotor.Value != 0;
|
||||
|
@ -1066,11 +1068,13 @@ namespace CtrEditor.Simulacion
|
|||
return isOnTransports > 0;
|
||||
}
|
||||
|
||||
// ✅ NUEVOS MÉTODOS - gestión de estado
|
||||
public void AssignToTransport(simTransporte transport, ConstraintHandle motor)
|
||||
// ✅ SISTEMA SIMPLIFICADO: Métodos unificados de gestión de motores
|
||||
public void AssignLinearMotor(simTransporte transport, ConstraintHandle motor)
|
||||
{
|
||||
CurrentTransport = transport;
|
||||
RemoveCurrentMotor(); // Eliminar motor anterior automáticamente
|
||||
CurrentMotor = motor;
|
||||
CurrentMotorType = MotorType.Linear;
|
||||
CurrentTransport = transport;
|
||||
if (transport.isBrake)
|
||||
{
|
||||
isOnBrakeTransport = true;
|
||||
|
@ -1078,12 +1082,36 @@ namespace CtrEditor.Simulacion
|
|||
}
|
||||
}
|
||||
|
||||
public void RemoveFromTransport()
|
||||
public void AssignAngularMotor(simCurve curve, ConstraintHandle motor)
|
||||
{
|
||||
CurrentTransport = null;
|
||||
RemoveCurrentMotor(); // Eliminar motor anterior automáticamente
|
||||
CurrentMotor = motor;
|
||||
CurrentMotorType = MotorType.Angular;
|
||||
// Las curvas no usan CurrentTransport
|
||||
}
|
||||
|
||||
public void RemoveCurrentMotor()
|
||||
{
|
||||
if (HasActiveMotor)
|
||||
{
|
||||
// El MotorManager se encargará de eliminar el motor del solver
|
||||
CurrentMotor = default;
|
||||
CurrentMotorType = MotorType.None;
|
||||
CurrentTransport = null;
|
||||
isOnBrakeTransport = false;
|
||||
CurrentBrakeTransport = null;
|
||||
}
|
||||
}
|
||||
|
||||
// ✅ LEGACY: Mantener compatibilidad con código existente
|
||||
public void AssignToTransport(simTransporte transport, ConstraintHandle motor)
|
||||
{
|
||||
AssignLinearMotor(transport, motor);
|
||||
}
|
||||
|
||||
public void RemoveFromTransport()
|
||||
{
|
||||
RemoveCurrentMotor();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1104,54 +1132,43 @@ namespace CtrEditor.Simulacion
|
|||
public float Speed { get; set; } // Velocidad para efectos de cinta transportadora (m/s)
|
||||
|
||||
private List<Action> _deferredActions;
|
||||
internal List<BodyHandle> _triangleBodyHandles; // Lista de todos los triángulos que componen la curva
|
||||
private List<List<Vector3>> _originalTriangles; // ✅ NUEVO: Triángulos originales para visualización debug
|
||||
|
||||
// ✅ SIMPLIFICADO: Solo necesitamos velocidad en m/s, no sistema complejo de direcciones
|
||||
public float SpeedMetersPerSecond { get; private set; }
|
||||
// ✅ SIMPLIFICADO: Propiedades esenciales únicamente
|
||||
public List<simBotella> BottlesOnCurve { get; private set; } = new List<simBotella>();
|
||||
|
||||
// ✅ NUEVO EVENTO - para actualización de motores
|
||||
// ✅ EVENTO para actualización de motores
|
||||
public event Action<simCurve> OnSpeedChanged;
|
||||
|
||||
// ✅ NUEVA REFERENCIA - para limpiar motors
|
||||
// ✅ REFERENCIA para limpiar motors
|
||||
private SimulationManagerBEPU _simulationManager;
|
||||
|
||||
// ✅ NUEVO: Direcciones tangenciales precalculadas para cada triángulo
|
||||
internal Dictionary<BodyHandle, Vector3> _triangleDirections = new Dictionary<BodyHandle, Vector3>();
|
||||
// ✅ NUEVO: Almacenar triángulos creados para acceso directo
|
||||
private Triangle[] _storedTriangles;
|
||||
|
||||
public float InnerRadius => _innerRadius;
|
||||
public float OuterRadius => _outerRadius;
|
||||
public float StartAngle => CoordinateConverter.BepuRadiansToWpfDegrees(_startAngle); // Convertir de radianes BEPU internos a grados WPF
|
||||
public float EndAngle => CoordinateConverter.BepuRadiansToWpfDegrees(_endAngle); // Convertir de radianes BEPU internos a grados WPF
|
||||
|
||||
/// <summary>
|
||||
/// ✅ NUEVO: Expone los triángulos originales para visualización debug
|
||||
/// </summary>
|
||||
public List<List<Vector3>> GetOriginalTriangles() => _originalTriangles ?? new List<List<Vector3>>();
|
||||
|
||||
public simCurve(Simulation simulation, List<Action> deferredActions, float innerRadius, float outerRadius, float startAngle, float endAngle, Vector2 topLeft, float unused = 0, SimulationManagerBEPU simulationManager = null)
|
||||
{
|
||||
_simulation = simulation;
|
||||
_deferredActions = deferredActions;
|
||||
_simulationManager = simulationManager; // ✅ NUEVA REFERENCIA
|
||||
_simulationManager = simulationManager;
|
||||
_innerRadius = innerRadius;
|
||||
_outerRadius = outerRadius;
|
||||
// ✅ CORREGIDO: Usar conversión directa WPF grados → BEPU radianes
|
||||
// ✅ SIMPLIFICADO: Usar conversión directa WPF grados → BEPU radianes
|
||||
_startAngle = CoordinateConverter.WpfDegreesToBepuRadians(startAngle);
|
||||
_endAngle = CoordinateConverter.WpfDegreesToBepuRadians(endAngle);
|
||||
_triangleBodyHandles = new List<BodyHandle>();
|
||||
_originalTriangles = new List<List<Vector3>>(); // ✅ NUEVO: Inicializar lista de triángulos
|
||||
|
||||
// Crear la curva con los ángulos que definen el sector
|
||||
// ✅ SIMPLIFICADO: Crear la curva directamente
|
||||
Create(innerRadius, outerRadius, startAngle, endAngle, topLeft, 0);
|
||||
}
|
||||
|
||||
// ✅ MODIFICAR MÉTODO EXISTENTE - disparar evento
|
||||
// ✅ SIMPLIFICADO: Configurar velocidad angular para AngularAxisMotor
|
||||
public void SetSpeed(float speed)
|
||||
{
|
||||
Speed = -speed;
|
||||
SpeedMetersPerSecond = Math.Abs(Speed) / simBase.SPEED_CONVERSION_FACTOR;
|
||||
Speed = speed; // Velocidad angular directa (sin inversión)
|
||||
OnSpeedChanged?.Invoke(this);
|
||||
}
|
||||
|
||||
|
@ -1210,34 +1227,159 @@ namespace CtrEditor.Simulacion
|
|||
return CoordinateConverter.CalculateWpfTopLeftFromBepuCenter(bepuCenter, curveSize, curveSize, 0f);
|
||||
}
|
||||
|
||||
public new void RemoverBody()
|
||||
/// <summary>
|
||||
/// ✅ NUEVO: Obtiene los triángulos reales creados para la curva
|
||||
/// Devuelve los triángulos transformados a coordenadas mundiales usando la posición del cuerpo
|
||||
/// </summary>
|
||||
public Triangle[] GetRealBEPUTriangles()
|
||||
{
|
||||
// ✅ CRÍTICO: Limpiar todos los motors conectados a esta curva ANTES de eliminar los bodies
|
||||
RemoveConnectedMotors();
|
||||
|
||||
// ✅ NUEVO: Limpiar cachés de direcciones
|
||||
_triangleDirections.Clear();
|
||||
|
||||
// Remover todos los triángulos de la curva
|
||||
if (_triangleBodyHandles != null && _simulation != null)
|
||||
try
|
||||
{
|
||||
foreach (var triangleHandle in _triangleBodyHandles)
|
||||
if (_storedTriangles == null || _storedTriangles.Length == 0)
|
||||
{
|
||||
if (_simulation.Bodies.BodyExists(triangleHandle))
|
||||
System.Diagnostics.Debug.WriteLine($"[GetRealBEPUTriangles] No hay triángulos almacenados");
|
||||
return new Triangle[0];
|
||||
}
|
||||
|
||||
// ✅ TRANSFORMAR a coordenadas mundiales
|
||||
var worldTriangles = TransformTrianglesToWorldCoordinates(_storedTriangles);
|
||||
|
||||
System.Diagnostics.Debug.WriteLine($"[GetRealBEPUTriangles] Devolviendo {worldTriangles.Length} triángulos transformados a coordenadas mundiales");
|
||||
return worldTriangles;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"[GetRealBEPUTriangles] Error: {ex.Message}");
|
||||
return new Triangle[0];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ✅ NUEVO: Transforma triángulos de coordenadas locales a mundiales
|
||||
/// </summary>
|
||||
private Triangle[] TransformTrianglesToWorldCoordinates(Triangle[] localTriangles)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_simulation == null || !_simulation.Bodies.BodyExists(BodyHandle))
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"[TransformTriangles] Cuerpo no existe, devolviendo triángulos locales");
|
||||
return localTriangles; // Fallback: devolver triángulos sin transformar
|
||||
}
|
||||
|
||||
var body = _simulation.Bodies[BodyHandle];
|
||||
var bodyPosition = body.Pose.Position;
|
||||
var bodyOrientation = body.Pose.Orientation;
|
||||
|
||||
var transformedTriangles = new Triangle[localTriangles.Length];
|
||||
|
||||
for (int i = 0; i < localTriangles.Length; i++)
|
||||
{
|
||||
var localTriangle = localTriangles[i];
|
||||
|
||||
// Transformar cada vértice del triángulo a coordenadas mundiales
|
||||
var worldA = bodyPosition + Vector3.Transform(localTriangle.A, bodyOrientation);
|
||||
var worldB = bodyPosition + Vector3.Transform(localTriangle.B, bodyOrientation);
|
||||
var worldC = bodyPosition + Vector3.Transform(localTriangle.C, bodyOrientation);
|
||||
|
||||
transformedTriangles[i] = new Triangle(worldA, worldB, worldC);
|
||||
}
|
||||
|
||||
System.Diagnostics.Debug.WriteLine($"[TransformTriangles] Transformados {transformedTriangles.Length} triángulos. Body pos: {bodyPosition}");
|
||||
return transformedTriangles;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"[TransformTriangles] Error: {ex.Message}, devolviendo triángulos locales");
|
||||
return localTriangles; // Fallback en caso de error
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ✅ NUEVO: Información de debug sobre el Mesh real de BEPU
|
||||
/// </summary>
|
||||
public string GetBEPUMeshDebugInfo()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_simulation == null || !_simulation.Bodies.BodyExists(BodyHandle))
|
||||
return "ERROR: Body no existe en simulación";
|
||||
|
||||
var body = _simulation.Bodies[BodyHandle];
|
||||
var collidable = body.Collidable;
|
||||
var shapeIndex = collidable.Shape;
|
||||
|
||||
var info = new System.Text.StringBuilder();
|
||||
info.AppendLine("=== DEBUG INFO MESH REAL DE BEPU ===");
|
||||
info.AppendLine($"Position: {body.Pose.Position}");
|
||||
info.AppendLine($"Orientation: {body.Pose.Orientation}");
|
||||
info.AppendLine($"Shape Index: {shapeIndex.Index}");
|
||||
info.AppendLine($"Shape Type: {shapeIndex.Type} (Mesh.Id = {BepuPhysics.Collidables.Mesh.Id})");
|
||||
info.AppendLine($"Shape exists: {shapeIndex.Exists}");
|
||||
|
||||
if (_storedTriangles != null)
|
||||
{
|
||||
info.AppendLine($"Triángulos almacenados (locales): {_storedTriangles.Length}");
|
||||
|
||||
// Mostrar algunos triángulos locales de ejemplo
|
||||
int triangleCount = Math.Min(3, _storedTriangles.Length);
|
||||
info.AppendLine($"");
|
||||
info.AppendLine($"Primeros {triangleCount} triángulos LOCALES:");
|
||||
|
||||
for (int i = 0; i < triangleCount; i++)
|
||||
{
|
||||
_simulation.Bodies.Remove(triangleHandle);
|
||||
var triangle = _storedTriangles[i];
|
||||
|
||||
info.AppendLine($" Triángulo LOCAL {i}:");
|
||||
info.AppendLine($" A: ({triangle.A.X:F3}, {triangle.A.Y:F3}, {triangle.A.Z:F3})");
|
||||
info.AppendLine($" B: ({triangle.B.X:F3}, {triangle.B.Y:F3}, {triangle.B.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
|
||||
try
|
||||
{
|
||||
var worldTriangles = TransformTrianglesToWorldCoordinates(_storedTriangles);
|
||||
info.AppendLine($"");
|
||||
info.AppendLine($"Primeros {triangleCount} triángulos MUNDIALES (transformados):");
|
||||
|
||||
for (int i = 0; i < Math.Min(triangleCount, worldTriangles.Length); i++)
|
||||
{
|
||||
var triangle = worldTriangles[i];
|
||||
|
||||
info.AppendLine($" Triángulo MUNDIAL {i}:");
|
||||
info.AppendLine($" A: ({triangle.A.X:F3}, {triangle.A.Y:F3}, {triangle.A.Z:F3})");
|
||||
info.AppendLine($" B: ({triangle.B.X:F3}, {triangle.B.Y:F3}, {triangle.B.Z:F3})");
|
||||
info.AppendLine($" C: ({triangle.C.X:F3}, {triangle.C.Y:F3}, {triangle.C.Z:F3})");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
info.AppendLine($"ERROR obteniendo triángulos mundiales: {ex.Message}");
|
||||
}
|
||||
}
|
||||
_triangleBodyHandles.Clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
info.AppendLine($"ERROR: No hay triángulos almacenados");
|
||||
}
|
||||
|
||||
// ✅ NUEVO: Limpiar también los triángulos originales
|
||||
if (_originalTriangles != null)
|
||||
return info.ToString();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_originalTriangles.Clear();
|
||||
return $"ERROR obteniendo debug info: {ex.Message}";
|
||||
}
|
||||
}
|
||||
|
||||
// Remover el cuerpo principal (si existe)
|
||||
public new void RemoverBody()
|
||||
{
|
||||
// ✅ SIMPLIFICADO: Limpiar motors conectados
|
||||
RemoveConnectedMotors();
|
||||
|
||||
// ✅ NUEVO: Limpiar triángulos almacenados
|
||||
_storedTriangles = null;
|
||||
|
||||
// ✅ SIMPLIFICADO: Solo remover el cuerpo principal (Mesh único)
|
||||
base.RemoverBody();
|
||||
}
|
||||
|
||||
|
@ -1251,17 +1393,8 @@ namespace CtrEditor.Simulacion
|
|||
// ✅ USAR REFERENCIA DIRECTA al SimulationManager
|
||||
if (_simulationManager != null)
|
||||
{
|
||||
// Limpiar motors del cuerpo principal
|
||||
// ✅ SIMPLIFICADO: Solo limpiar motors del cuerpo principal
|
||||
_simulationManager.RemoveMotorsConnectedToBody(BodyHandle);
|
||||
|
||||
// Limpiar motors de todos los triángulos
|
||||
if (_triangleBodyHandles != null)
|
||||
{
|
||||
foreach (var triangleHandle in _triangleBodyHandles)
|
||||
{
|
||||
_simulationManager.RemoveMotorsConnectedToBody(triangleHandle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Limpiar la lista local de botellas
|
||||
|
@ -1300,237 +1433,84 @@ namespace CtrEditor.Simulacion
|
|||
{
|
||||
RemoverBody();
|
||||
|
||||
// Crear triángulos para la curva - los ángulos ya definen la forma correcta
|
||||
var triangles = CreateTriangulatedCurve(_innerRadius, _outerRadius, -_startAngle, -_endAngle);
|
||||
// ✅ SIMPLIFICADO: Crear superficie usando Triangle de BEPU directamente
|
||||
var triangles = CreateSimpleArcTriangles(_innerRadius, _outerRadius, _startAngle, _endAngle);
|
||||
|
||||
// ✅ NUEVO: Almacenar triángulos originales para visualización debug
|
||||
_originalTriangles = new List<List<Vector3>>(triangles);
|
||||
// ✅ ALMACENAR triángulos para acceso directo
|
||||
_storedTriangles = triangles;
|
||||
|
||||
foreach (var triangle in triangles)
|
||||
// ✅ SIMPLIFICADO: Crear un solo cuerpo con múltiples Triangle shapes usando Mesh
|
||||
if (triangles.Length > 0)
|
||||
{
|
||||
CreateTriangleBody(triangle, position);
|
||||
}
|
||||
|
||||
// Crear un cuerpo principal invisible para referencia de posición
|
||||
CreateMainBody(position);
|
||||
}
|
||||
|
||||
private void CreateMainBody(Vector3 position)
|
||||
{
|
||||
// Crear un cuerpo principal muy pequeño e invisible solo para referencia
|
||||
var smallBox = new Box(0.01f, 0.01f, 0.01f);
|
||||
var shapeIndex = _simulation.Shapes.Add(smallBox);
|
||||
// ✅ CREAR MESH CON LA API CORRECTA DE BEPU
|
||||
var triangleBuffer = new BepuUtilities.Memory.Buffer<Triangle>(triangles.Length, _simulation.BufferPool);
|
||||
for (int i = 0; i < triangles.Length; i++)
|
||||
{
|
||||
triangleBuffer[i] = triangles[i];
|
||||
}
|
||||
var mesh = new BepuPhysics.Collidables.Mesh(triangleBuffer, Vector3.One, _simulation.BufferPool);
|
||||
var shapeIndex = _simulation.Shapes.Add(mesh);
|
||||
|
||||
var bodyDescription = BodyDescription.CreateKinematic(
|
||||
new RigidPose(position), // Sin rotación - la forma ya está definida por los ángulos
|
||||
new CollidableDescription(shapeIndex, 0f), // Sin speculative margin
|
||||
new RigidPose(position),
|
||||
new CollidableDescription(shapeIndex, 0.001f), // ✅ SpeculativeMargin bajo para detección precisa
|
||||
new BodyActivityDescription(0.01f)
|
||||
);
|
||||
|
||||
BodyHandle = _simulation.Bodies.Add(bodyDescription);
|
||||
_bodyCreated = true;
|
||||
}
|
||||
|
||||
private void CreateTriangleBody(List<Vector3> triangle, Vector3 basePosition)
|
||||
{
|
||||
if (triangle.Count != 3)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
// Calcular centro y dimensiones del triángulo
|
||||
var center = (triangle[0] + triangle[1] + triangle[2]) / 3f;
|
||||
var worldCenter = center + basePosition;
|
||||
|
||||
// ✅ NUEVO: Calcular dirección tangencial con compensación centrípeta
|
||||
// Usamos la posición del centro del triángulo para calcular la tangente
|
||||
var radiusVector = new Vector2(center.X, center.Y);
|
||||
if (radiusVector.Length() > 0.001f)
|
||||
{
|
||||
// Vector tangente (perpendicular al radio) en el plano XY
|
||||
var tangent = new Vector3(-radiusVector.Y, radiusVector.X, 0);
|
||||
tangent = Vector3.Normalize(tangent);
|
||||
|
||||
// ✅ COMPENSACIÓN CENTRÍPETA: Inclinar la dirección hacia el centro
|
||||
var radiusVector3D = new Vector3(radiusVector.X, radiusVector.Y, 0);
|
||||
var toCenter = -Vector3.Normalize(radiusVector3D); // Vector hacia el centro
|
||||
|
||||
// Mezclar tangente con componente centrípeta (10% hacia el centro)
|
||||
const float CENTRIPETAL_FACTOR = 0.1f; // 10% de fuerza hacia el centro
|
||||
var adjustedDirection = Vector3.Normalize(tangent + toCenter * CENTRIPETAL_FACTOR);
|
||||
|
||||
// Ajustar dirección según la velocidad (signo)
|
||||
if (Speed < 0)
|
||||
adjustedDirection = -adjustedDirection;
|
||||
|
||||
// Almacenar dirección ajustada antes de crear el body
|
||||
var triangleDirection = adjustedDirection;
|
||||
|
||||
// Calcular dimensiones aproximadas del triángulo para crear una caja plana
|
||||
float minX = Math.Min(Math.Min(triangle[0].X, triangle[1].X), triangle[2].X);
|
||||
float maxX = Math.Max(Math.Max(triangle[0].X, triangle[1].X), triangle[2].X);
|
||||
float minY = Math.Min(Math.Min(triangle[0].Y, triangle[1].Y), triangle[2].Y);
|
||||
float maxY = Math.Max(Math.Max(triangle[0].Y, triangle[1].Y), triangle[2].Y);
|
||||
|
||||
float width = Math.Max(maxX - minX, 0.01f);
|
||||
float height = Math.Max(maxY - minY, 0.01f);
|
||||
float thickness = zAltura_Curve; // Altura muy pequeña para triángulos planos
|
||||
|
||||
// Crear una caja plana que aproxime el triángulo
|
||||
var box = new Box(width, height, thickness);
|
||||
var shapeIndex = _simulation.Shapes.Add(box);
|
||||
|
||||
// Crear como cuerpo estático sólido (NO sensor)
|
||||
var activityDescription = new BodyActivityDescription(0.01f);
|
||||
|
||||
var bodyDescription = BodyDescription.CreateKinematic(
|
||||
new RigidPose(worldCenter),
|
||||
new CollidableDescription(shapeIndex, 0.001f), // Speculative margin normal
|
||||
activityDescription
|
||||
);
|
||||
|
||||
var triangleHandle = _simulation.Bodies.Add(bodyDescription);
|
||||
_triangleBodyHandles.Add(triangleHandle);
|
||||
|
||||
// ✅ NUEVO: Almacenar la dirección tangencial para este triángulo
|
||||
_triangleDirections[triangleHandle] = triangleDirection;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"[simCurve] Error creating triangle: {ex.Message}");
|
||||
System.Diagnostics.Debug.WriteLine($"[simCurve] Creado con {triangles.Length} triángulos reales almacenados");
|
||||
}
|
||||
}
|
||||
|
||||
private const float SegmentationFactor = 32f / 3f;
|
||||
private const int MinSegments = 8;
|
||||
private const int MaxSegments = 64;
|
||||
// ✅ MÉTODOS ELIMINADOS: CreateMainBody y CreateTriangleBody ya no son necesarios
|
||||
// La curva ahora se crea como un solo Mesh en el método Create simplificado
|
||||
|
||||
/// <summary>
|
||||
/// Crea triángulos para representar una curva/arco en la simulación BEPU.
|
||||
/// IMPORTANTE: Los parámetros startAngle y endAngle ya están convertidos a radianes BEPU internamente,
|
||||
/// pero además se invierte la dirección del arco para mantener coherencia visual con WPF.
|
||||
/// ✅ SIMPLIFICADO: Crea triángulos usando Triangle de BEPU directamente
|
||||
/// Solo superficie superior, eliminando complejidad innecesaria
|
||||
/// Los triángulos se crean en coordenadas locales (Z=0) y la posición del cuerpo los ubica correctamente
|
||||
/// </summary>
|
||||
/// <param name="innerRadius">Radio interno del arco</param>
|
||||
/// <param name="outerRadius">Radio externo del arco</param>
|
||||
/// <param name="startAngle">Ángulo inicial en radianes BEPU (ya convertido desde grados WPF)</param>
|
||||
/// <param name="endAngle">Ángulo final en radianes BEPU (ya convertido desde grados WPF)</param>
|
||||
/// <returns>Lista de triángulos que forman el arco</returns>
|
||||
private List<List<Vector3>> CreateTriangulatedCurve(float innerRadius, float outerRadius, float startAngle, float endAngle)
|
||||
/// <param name="startAngle">Ángulo inicial en radianes BEPU</param>
|
||||
/// <param name="endAngle">Ángulo final en radianes BEPU</param>
|
||||
/// <returns>Array de triángulos nativos de BEPU en coordenadas locales</returns>
|
||||
private Triangle[] CreateSimpleArcTriangles(float innerRadius, float outerRadius, float startAngle, float endAngle)
|
||||
{
|
||||
var triangles = new List<List<Vector3>>();
|
||||
var triangles = new List<Triangle>();
|
||||
|
||||
// Calcular número de segmentos basado en el tamaño del arco
|
||||
// ✅ SIMPLIFICADO: Menos segmentos, menos complejidad
|
||||
float arcLength = Math.Abs(endAngle - startAngle) * ((innerRadius + outerRadius) / 2f);
|
||||
int segments = (int)(arcLength * SegmentationFactor);
|
||||
segments = Math.Max(MinSegments, Math.Min(segments, MaxSegments));
|
||||
int segments = Math.Max(8, Math.Min((int)(arcLength * 8), 32)); // Menos segmentos
|
||||
float angleStep = (endAngle - startAngle) / segments;
|
||||
|
||||
// ✅ CRÍTICO: Inversión de dirección del arco para conversión WPF ↔ BEPU
|
||||
//
|
||||
// Los ángulos individuales ya están convertidos (WPF grados → BEPU radianes con signo invertido),
|
||||
// pero también necesitamos invertir la DIRECCIÓN del arco:
|
||||
//
|
||||
// • WPF: startAngle → endAngle (sentido horario, Y+ hacia abajo)
|
||||
// • BEPU: -endAngle → -startAngle (sentido antihorario, Y+ hacia arriba)
|
||||
//
|
||||
// Ejemplo: WPF arco de 0° a 90° → BEPU arco de -90° a 0° (equivalente visual)
|
||||
float fromAngle = -endAngle; // Empezar desde el ángulo final negativo
|
||||
float toAngle = -startAngle; // Terminar en el ángulo inicial negativo
|
||||
float angleStep = (toAngle - fromAngle) / segments;
|
||||
|
||||
// Generar vértices para los arcos interior y exterior
|
||||
var innerPoints = new Vector3[segments + 1];
|
||||
var outerPoints = new Vector3[segments + 1];
|
||||
|
||||
for (int i = 0; i <= segments; i++)
|
||||
{
|
||||
float angle = fromAngle + i * angleStep;
|
||||
float cosAngle = (float)Math.Cos(angle);
|
||||
float sinAngle = (float)Math.Sin(angle);
|
||||
|
||||
// Triángulos planos en el plano XY (Z = 0 relativo)
|
||||
innerPoints[i] = new Vector3(innerRadius * cosAngle, innerRadius * sinAngle, 0);
|
||||
outerPoints[i] = new Vector3(outerRadius * cosAngle, outerRadius * sinAngle, 0);
|
||||
}
|
||||
|
||||
// Crear triángulos
|
||||
// ✅ SIMPLIFICADO: Sin inversión compleja de ángulos
|
||||
for (int i = 0; i < segments; i++)
|
||||
{
|
||||
// Primer triángulo del sector
|
||||
var upperTriangle = new List<Vector3>
|
||||
{
|
||||
innerPoints[i],
|
||||
outerPoints[i],
|
||||
outerPoints[i + 1]
|
||||
};
|
||||
triangles.Add(upperTriangle);
|
||||
float angle1 = startAngle + i * angleStep;
|
||||
float angle2 = startAngle + (i + 1) * angleStep;
|
||||
|
||||
// Segundo triángulo del sector
|
||||
var lowerTriangle = new List<Vector3>
|
||||
{
|
||||
innerPoints[i],
|
||||
outerPoints[i + 1],
|
||||
innerPoints[i + 1]
|
||||
};
|
||||
triangles.Add(lowerTriangle);
|
||||
// ✅ COORDENADAS LOCALES: Triángulos en Z=0, el cuerpo los posiciona correctamente
|
||||
var inner1 = new Vector3(innerRadius * (float)Math.Cos(angle1), innerRadius * (float)Math.Sin(angle1), 0);
|
||||
var outer1 = new Vector3(outerRadius * (float)Math.Cos(angle1), outerRadius * (float)Math.Sin(angle1), 0);
|
||||
var inner2 = new Vector3(innerRadius * (float)Math.Cos(angle2), innerRadius * (float)Math.Sin(angle2), 0);
|
||||
var outer2 = new Vector3(outerRadius * (float)Math.Cos(angle2), outerRadius * (float)Math.Sin(angle2), 0);
|
||||
|
||||
// ✅ USAR Triangle NATIVO DE BEPU (solo superficie superior)
|
||||
triangles.Add(new Triangle(inner1, outer1, outer2));
|
||||
triangles.Add(new Triangle(inner1, outer2, inner2));
|
||||
}
|
||||
|
||||
return triangles;
|
||||
System.Diagnostics.Debug.WriteLine($"[CreateSimpleArcTriangles] Creados {triangles.Count} triángulos en coordenadas locales");
|
||||
return triangles.ToArray();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Calcula la dirección tangencial para aplicar fuerzas de curva.
|
||||
/// NOTA: Este cálculo es dinámico (basado en posición actual) y no se ve afectado
|
||||
/// por la inversión de dirección del arco en CreateTriangulatedCurve.
|
||||
/// </summary>
|
||||
public Vector3 GetTangentialDirection(Vector3 bottlePosition)
|
||||
{
|
||||
try
|
||||
{
|
||||
var curvePosition = GetPosition();
|
||||
var centerToBottle = new Vector2(bottlePosition.X - curvePosition.X, bottlePosition.Y - curvePosition.Y);
|
||||
|
||||
if (centerToBottle.Length() < 0.001f)
|
||||
return Vector3.Zero;
|
||||
|
||||
// Vector tangente (perpendicular al radio) en el plano XY
|
||||
var tangent = new Vector3(-centerToBottle.Y, centerToBottle.X, 0);
|
||||
|
||||
// Normalizar
|
||||
if (tangent.Length() > 0.001f)
|
||||
{
|
||||
tangent = Vector3.Normalize(tangent);
|
||||
}
|
||||
|
||||
// Ajustar dirección según la velocidad (signo)
|
||||
if (Speed < 0)
|
||||
tangent = -tangent;
|
||||
|
||||
return tangent;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"[simCurve] Error calculating tangential direction: {ex.Message}");
|
||||
return Vector3.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
// ✅ MÉTODO MEJORADO - versión optimizada
|
||||
public Vector3 GetCachedTangentialDirection(Vector3 bottlePosition)
|
||||
{
|
||||
var direction = GetTangentialDirection(bottlePosition);
|
||||
return Speed < 0 ? -direction : direction;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ✅ NUEVO - Obtiene la dirección tangencial fija de un triángulo específico
|
||||
/// </summary>
|
||||
public Vector3 GetTriangleDirection(BodyHandle triangleHandle)
|
||||
{
|
||||
return _triangleDirections.GetValueOrDefault(triangleHandle, Vector3.Zero);
|
||||
}
|
||||
// ✅ MÉTODOS ELIMINADOS: Los cálculos tangenciales complejos ya no son necesarios
|
||||
// AngularAxisMotor maneja automáticamente la rotación en curvas
|
||||
}
|
||||
|
||||
public class simDescarte : simBase
|
||||
|
@ -1712,11 +1692,11 @@ namespace CtrEditor.Simulacion
|
|||
return false; // NO generar contacto físico para descartes
|
||||
}
|
||||
|
||||
// ✅ PROCESAR CREACIÓN DE MOTORES para transportes y curvas
|
||||
// ✅ SIMPLIFICADO: Detección directa de transportes y curvas
|
||||
var transportA = GetTransportFromCollidable(pair.A);
|
||||
var transportB = GetTransportFromCollidable(pair.B);
|
||||
(simCurve curveA, Vector3 directionA) = GetCurveAndDirectionFromCollidable(pair.A);
|
||||
(simCurve curveB, Vector3 directionB) = GetCurveAndDirectionFromCollidable(pair.B);
|
||||
var curveA = GetCurveFromCollidable(pair.A);
|
||||
var curveB = GetCurveFromCollidable(pair.B);
|
||||
|
||||
// ✅ VERIFICAR SI YA SE CREÓ MOTOR PARA EVITAR DUPLICADOS
|
||||
BodyHandle? bottleHandle = null;
|
||||
|
@ -1741,23 +1721,21 @@ namespace CtrEditor.Simulacion
|
|||
pairMaterial.MaximumRecoveryVelocity = 1f;
|
||||
pairMaterial.SpringSettings = new SpringSettings(80, 6);
|
||||
}
|
||||
// ✅ SIMPLIFICADO: Si hay contacto botella-triángulo de curva, crear motor con dirección específica
|
||||
// ✅ SIMPLIFICADO: Si hay contacto botella-curva, crear AngularAxisMotor
|
||||
else if (botella != null && (curveA != null || curveB != null))
|
||||
{
|
||||
var curve = curveA ?? curveB;
|
||||
var direction = curveA != null ? directionA : directionB;
|
||||
var triangleHandle = curveA != null ? pair.A.BodyHandle : pair.B.BodyHandle;
|
||||
|
||||
bottleHandle = botella.BodyHandle;
|
||||
elementHandle = triangleHandle;
|
||||
elementHandle = curve.BodyHandle;
|
||||
|
||||
// ✅ SIMPLIFICADO: Crear motor usando sistema natural de colisiones de BEPU
|
||||
// ✅ SIMPLIFICADO: Crear motor angular usando el cuerpo principal de la curva
|
||||
var pairKey = (bottleHandle.Value, elementHandle.Value);
|
||||
if (!_simulationManager._motorsCreated.Contains(pairKey))
|
||||
{
|
||||
_simulationManager.TryCreateCurveMotor(botella, curve, direction, triangleHandle);
|
||||
_simulationManager.TryCreateCurveAngularMotor(botella, curve);
|
||||
_simulationManager._motorsCreated.Add(pairKey);
|
||||
System.Diagnostics.Debug.WriteLine($"[Manifold] Motor curva: {bottleHandle} → {triangleHandle}");
|
||||
System.Diagnostics.Debug.WriteLine($"[Manifold] Motor angular curva: {bottleHandle} → {elementHandle}");
|
||||
}
|
||||
|
||||
// Fricción alta para curvas (SIEMPRE aplicar para colisión física)
|
||||
|
@ -1842,38 +1820,13 @@ namespace CtrEditor.Simulacion
|
|||
if (collidable.Mobility == CollidableMobility.Kinematic)
|
||||
{
|
||||
var bodyHandle = collidable.BodyHandle;
|
||||
|
||||
// ✅ NUEVO: Buscar específicamente en los triángulos de las curvas
|
||||
return _simulationManager.GetCurveFromTriangleBodyHandle(bodyHandle);
|
||||
var simBase = _simulationManager.GetSimBaseFromBodyHandle(bodyHandle);
|
||||
return simBase as simCurve;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ✅ NUEVO: Obtiene curva y dirección específica del triángulo
|
||||
/// </summary>
|
||||
private (simCurve curve, Vector3 direction) GetCurveAndDirectionFromCollidable(CollidableReference collidable)
|
||||
{
|
||||
if (_simulationManager?.simulation != null)
|
||||
{
|
||||
if (collidable.Mobility == CollidableMobility.Kinematic)
|
||||
{
|
||||
var bodyHandle = collidable.BodyHandle;
|
||||
|
||||
// Buscar curva que contenga este triángulo
|
||||
var curve = _simulationManager.GetCurveFromTriangleBodyHandle(bodyHandle);
|
||||
if (curve != null)
|
||||
{
|
||||
// Obtener dirección específica del triángulo
|
||||
var direction = curve.GetTriangleDirection(bodyHandle);
|
||||
return (curve, direction);
|
||||
}
|
||||
}
|
||||
}
|
||||
return (null, Vector3.Zero);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ✅ NUEVO MÉTODO: Aplicar fuerzas de frenado directamente en el manifold
|
||||
/// </summary>
|
||||
|
@ -2013,6 +1966,15 @@ namespace CtrEditor.Simulacion
|
|||
}
|
||||
|
||||
public void CreateCurveMotor(simBotella bottle, simCurve curve)
|
||||
{
|
||||
// ✅ SIMPLIFICADO: Usar AngularAxisMotor para curvas
|
||||
CreateCurveAngularMotor(bottle, curve);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ✅ NUEVO: Crear motor angular para curvas (simplificado)
|
||||
/// </summary>
|
||||
public void CreateCurveAngularMotor(simBotella bottle, simCurve curve)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -2025,50 +1987,34 @@ namespace CtrEditor.Simulacion
|
|||
!_simulationManager.simulation.Bodies.BodyExists(curve.BodyHandle))
|
||||
return;
|
||||
|
||||
var direction = curve.GetCachedTangentialDirection(bottle.GetPosition());
|
||||
|
||||
// Validar que la dirección no sea cero
|
||||
if (direction.Length() < 0.001f)
|
||||
// Verificar que la curva tenga velocidad
|
||||
if (Math.Abs(curve.Speed) < 0.001f)
|
||||
return;
|
||||
|
||||
// ✅ CONVERTIR DIRECCIÓN MUNDIAL A COORDENADAS LOCALES DE LA CURVA
|
||||
Vector3 localAxisDirection;
|
||||
|
||||
if (_simulationManager.simulation.Bodies.BodyExists(curve.BodyHandle))
|
||||
// ✅ SIMPLIFICADO: AngularAxisMotor para rotación en el plano XY
|
||||
var angularMotor = new AngularAxisMotor()
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
var motor = new LinearAxisMotor()
|
||||
{
|
||||
LocalOffsetA = Vector3.Zero,
|
||||
LocalOffsetB = Vector3.Zero,
|
||||
LocalAxis = localAxisDirection, // ✅ Usar dirección en coordenadas locales
|
||||
TargetVelocity = curve.SpeedMetersPerSecond,
|
||||
Settings = new MotorSettings(Math.Max(bottle.Mass * 20f, 8f), 4f)
|
||||
LocalAxisA = Vector3.UnitZ, // Eje Z para rotación en plano XY
|
||||
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, // ✅ USAR CUERPO DE LA CURVA
|
||||
curve.BodyHandle,
|
||||
bottle.BodyHandle,
|
||||
motor
|
||||
angularMotor
|
||||
);
|
||||
|
||||
_activeMotors[bottle.BodyHandle] = motorHandle;
|
||||
_motorToBottle[motorHandle] = bottle;
|
||||
bottle.CurrentMotor = motorHandle;
|
||||
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 curve motor: {ex.Message}");
|
||||
System.Diagnostics.Debug.WriteLine($"[MotorManager] Error creating angular motor: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2232,7 +2178,7 @@ namespace CtrEditor.Simulacion
|
|||
LocalOffsetA = Vector3.Zero,
|
||||
LocalOffsetB = Vector3.Zero,
|
||||
LocalAxis = localAxisDirection, // ✅ Usar dirección en coordenadas locales
|
||||
TargetVelocity = curve.SpeedMetersPerSecond,
|
||||
TargetVelocity = curve.Speed, // ✅ SIMPLIFICADO: Usar velocidad directa
|
||||
Settings = new MotorSettings(Math.Max(bottle.Mass * 20f, 8f), 4f)
|
||||
};
|
||||
|
||||
|
@ -2242,7 +2188,7 @@ namespace CtrEditor.Simulacion
|
|||
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.SpeedMetersPerSecond}");
|
||||
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;
|
||||
_motorToBottle[motorHandle] = bottle;
|
||||
|
@ -2371,16 +2317,10 @@ namespace CtrEditor.Simulacion
|
|||
_activeMotors.Clear();
|
||||
_motorToBottle.Clear();
|
||||
|
||||
// ✅ NUEVO: Limpiar cachés de direcciones de todas las curvas y pares silenciados
|
||||
// ✅ SIMPLIFICADO: Solo limpiar pares de motores creados
|
||||
if (_simulationManager?.Cuerpos != null)
|
||||
{
|
||||
var curves = _simulationManager.Cuerpos.OfType<simCurve>();
|
||||
foreach (var curve in curves)
|
||||
{
|
||||
curve._triangleDirections?.Clear();
|
||||
}
|
||||
|
||||
// También limpiar pares de motores creados
|
||||
// Limpiar pares de motores creados
|
||||
_simulationManager._motorsCreated?.Clear();
|
||||
}
|
||||
}
|
||||
|
@ -2442,22 +2382,11 @@ namespace CtrEditor.Simulacion
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// ✅ NUEVO: Obtiene una simCurve si el BodyHandle pertenece a uno de sus triángulos
|
||||
/// ✅ SIMPLIFICADO: Obtiene una simCurve desde su BodyHandle principal
|
||||
/// </summary>
|
||||
public simCurve GetCurveFromTriangleBodyHandle(BodyHandle bodyHandle)
|
||||
{
|
||||
// Buscar en todas las curvas si alguna contiene este BodyHandle en sus triángulos
|
||||
var curves = Cuerpos.OfType<simCurve>();
|
||||
|
||||
foreach (var curve in curves)
|
||||
{
|
||||
if (curve._triangleBodyHandles != null && curve._triangleBodyHandles.Contains(bodyHandle))
|
||||
{
|
||||
return curve;
|
||||
}
|
||||
}
|
||||
|
||||
// Si no se encuentra en triángulos, buscar en cuerpos principales como fallback
|
||||
// ✅ SIMPLIFICADO: Solo buscar en cuerpos principales (ya no hay triángulos separados)
|
||||
var simBase = GetSimBaseFromBodyHandle(bodyHandle);
|
||||
return simBase as simCurve;
|
||||
}
|
||||
|
@ -2479,14 +2408,7 @@ namespace CtrEditor.Simulacion
|
|||
break;
|
||||
case simCurve curve:
|
||||
_curveHandles.Add(curve.BodyHandle);
|
||||
// También añadir todos sus triángulos
|
||||
if (curve._triangleBodyHandles != null)
|
||||
{
|
||||
foreach (var triangleHandle in curve._triangleBodyHandles)
|
||||
{
|
||||
_curveHandles.Add(triangleHandle);
|
||||
}
|
||||
}
|
||||
// ✅ SIMPLIFICADO: Ya no hay triángulos separados, solo el cuerpo principal
|
||||
curve.OnSpeedChanged += _motorManager.UpdateCurveSpeed;
|
||||
break;
|
||||
case simBarrera barrier:
|
||||
|
@ -2515,13 +2437,7 @@ namespace CtrEditor.Simulacion
|
|||
break;
|
||||
case simCurve curve:
|
||||
_curveHandles.Remove(curve.BodyHandle);
|
||||
if (curve._triangleBodyHandles != null)
|
||||
{
|
||||
foreach (var triangleHandle in curve._triangleBodyHandles)
|
||||
{
|
||||
_curveHandles.Remove(triangleHandle);
|
||||
}
|
||||
}
|
||||
// ✅ SIMPLIFICADO: Ya no hay triángulos separados
|
||||
curve.OnSpeedChanged -= _motorManager.UpdateCurveSpeed;
|
||||
break;
|
||||
case simBarrera barrier:
|
||||
|
@ -2689,7 +2605,7 @@ namespace CtrEditor.Simulacion
|
|||
foreach (var curve in Cuerpos.OfType<simCurve>())
|
||||
{
|
||||
// ✅ CORREGIDO: Usar método público para actualizar velocidad
|
||||
curve.SetSpeed(curve.Speed); // Esto actualiza automáticamente SpeedMetersPerSecond internamente
|
||||
curve.SetSpeed(curve.Speed); // ✅ SIMPLIFICADO: Reinicializar velocidad
|
||||
}
|
||||
|
||||
stopwatch.Start();
|
||||
|
@ -3013,6 +2929,35 @@ namespace CtrEditor.Simulacion
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ✅ NUEVO: Intenta crear un motor angular para curvas si no existe uno activo
|
||||
/// </summary>
|
||||
public void TryCreateCurveAngularMotor(simBotella bottle, simCurve curve)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Validaciones básicas
|
||||
if (bottle == null || curve == null || _motorManager == null)
|
||||
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)
|
||||
return; // No crear motor para curvas detenidas
|
||||
|
||||
_motorManager.CreateCurveAngularMotor(bottle, curve);
|
||||
|
||||
System.Diagnostics.Debug.WriteLine($"[TryCreateCurveAngularMotor] Motor angular creado: {bottle.BodyHandle} - {curve.BodyHandle}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"[SimulationManager] Error in TryCreateCurveAngularMotor: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -386,153 +386,130 @@ namespace CtrEditor.Simulacion
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// ✅ NUEVO: Crea mesh directamente desde los triángulos de BEPU (debug real)
|
||||
/// ✅ NUEVO: Crea mesh directamente desde los triángulos reales de BEPU
|
||||
/// Extrae la geometría exacta que está almacenada en la simulación física
|
||||
/// </summary>
|
||||
private void CreateCurveMeshFromBEPUTriangles(MeshBuilder meshBuilder, simCurve curve)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Obtener los triángulos originales directamente de BEPU
|
||||
var originalTriangles = curve.GetOriginalTriangles();
|
||||
// ✅ EXTRAER TRIÁNGULOS REALES DE BEPU
|
||||
var realTriangles = curve.GetRealBEPUTriangles();
|
||||
|
||||
if (originalTriangles.Count == 0)
|
||||
if (realTriangles.Length == 0)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"[3D Debug] WARNING: No triangles found in BEPU curve");
|
||||
System.Diagnostics.Debug.WriteLine($"[3D BEPU] WARNING: No se pudieron extraer triángulos reales, usando fallback");
|
||||
CreateCurveMeshFallback(meshBuilder, curve);
|
||||
return;
|
||||
}
|
||||
|
||||
// Altura para simular triángulos planos (misma que en BEPU)
|
||||
const float curveHeight = 0.05f; // simCurve.zAltura_Curve
|
||||
System.Diagnostics.Debug.WriteLine($"[3D BEPU] Creando mesh desde {realTriangles.Length} triángulos reales de BEPU");
|
||||
|
||||
// Convertir cada triángulo de BEPU a triángulos en Helix
|
||||
foreach (var triangle in originalTriangles)
|
||||
// ✅ USAR TRIÁNGULOS EXACTOS DE BEPU
|
||||
foreach (var triangle in realTriangles)
|
||||
{
|
||||
if (triangle.Count != 3)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"[3D Debug] WARNING: Invalid triangle with {triangle.Count} vertices");
|
||||
continue;
|
||||
}
|
||||
// Convertir triángulos de BEPU a puntos 3D de Helix
|
||||
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);
|
||||
|
||||
// Convertir Vector3 de BEPU a Point3D de Helix
|
||||
// Los triángulos en BEPU están en el plano XY (Z=0), crear superficie 3D
|
||||
|
||||
// Puntos de la superficie inferior (Z = 0)
|
||||
var p1Bottom = new Point3D(triangle[0].X, triangle[0].Y, 0);
|
||||
var p2Bottom = new Point3D(triangle[1].X, triangle[1].Y, 0);
|
||||
var p3Bottom = new Point3D(triangle[2].X, triangle[2].Y, 0);
|
||||
|
||||
// Puntos de la superficie superior (Z = curveHeight)
|
||||
var p1Top = new Point3D(triangle[0].X, triangle[0].Y, curveHeight);
|
||||
var p2Top = new Point3D(triangle[1].X, triangle[1].Y, curveHeight);
|
||||
var p3Top = new Point3D(triangle[2].X, triangle[2].Y, curveHeight);
|
||||
|
||||
// Crear superficie superior del triángulo
|
||||
meshBuilder.AddTriangle(p1Top, p2Top, p3Top);
|
||||
|
||||
// Crear superficie inferior del triángulo (orden inverso para normales correctas)
|
||||
meshBuilder.AddTriangle(p1Bottom, p3Bottom, p2Bottom);
|
||||
|
||||
// Crear paredes laterales del triángulo (3 quads)
|
||||
meshBuilder.AddQuad(p1Bottom, p2Bottom, p2Top, p1Top); // Lado 1-2
|
||||
meshBuilder.AddQuad(p2Bottom, p3Bottom, p3Top, p2Top); // Lado 2-3
|
||||
meshBuilder.AddQuad(p3Bottom, p1Bottom, p1Top, p3Top); // Lado 3-1
|
||||
// Agregar triángulo exacto al mesh
|
||||
meshBuilder.AddTriangle(pointA, pointB, pointC);
|
||||
}
|
||||
|
||||
System.Diagnostics.Debug.WriteLine($"[3D BEPU] ✅ Mesh creado usando triángulos reales de BEPU");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"[3D Debug] ERROR creating mesh from BEPU triangles: {ex.Message}");
|
||||
|
||||
// Fallback: usar el método anterior si falla la lectura de BEPU
|
||||
System.Diagnostics.Debug.WriteLine($"[3D Debug] Falling back to recreated geometry");
|
||||
System.Diagnostics.Debug.WriteLine($"[3D BEPU] ERROR extrayendo triángulos reales: {ex.Message}");
|
||||
System.Diagnostics.Debug.WriteLine($"[3D BEPU] Fallback a geometría recreada");
|
||||
CreateCurveMeshFallback(meshBuilder, curve);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ✅ NUEVO: Función de debug que muestra triángulos individuales de BEPU
|
||||
/// Cada triángulo se renderiza de manera separada para poder hacer debug visual
|
||||
/// ✅ NUEVO: Función de debug que muestra triángulos individuales reales de BEPU
|
||||
/// Cada triángulo se renderiza de manera separada con offset para poder hacer debug visual
|
||||
/// </summary>
|
||||
private void CreateCurveDebugMeshWithIndividualTriangles(MeshBuilder meshBuilder, simCurve curve)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Obtener los triángulos originales directamente de BEPU
|
||||
var originalTriangles = curve.GetOriginalTriangles();
|
||||
// ✅ EXTRAER TRIÁNGULOS REALES DE BEPU PARA DEBUG
|
||||
var realTriangles = curve.GetRealBEPUTriangles();
|
||||
|
||||
if (originalTriangles.Count == 0)
|
||||
if (realTriangles.Length == 0)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"[3D Debug] WARNING: No triangles found in BEPU curve");
|
||||
System.Diagnostics.Debug.WriteLine($"[3D Debug] WARNING: No hay triángulos reales para debug, usando fallback");
|
||||
CreateCurveMeshFallback(meshBuilder, curve);
|
||||
return;
|
||||
}
|
||||
|
||||
System.Diagnostics.Debug.WriteLine($"[3D Debug] Creating debug visualization for {originalTriangles.Count} triangles");
|
||||
System.Diagnostics.Debug.WriteLine($"[3D Debug] Creando debug de {realTriangles.Length} triángulos reales individuales");
|
||||
|
||||
// Altura muy pequeña para mostrar triángulos planos (como en BEPU)
|
||||
const float debugHeight = 0.02f; // Más bajo que la superficie normal para diferenciarlo
|
||||
const float triangleSeparation = 0.01f; // Separación pequeña entre triángulos para distinguirlos
|
||||
// ✅ MOSTRAR CADA TRIÁNGULO SEPARADO CON OFFSET PARA DEBUG
|
||||
float triangleSeparation = 0.02f; // Separación entre triángulos para debug visual
|
||||
|
||||
// Convertir cada triángulo de BEPU a un triángulo 3D individual
|
||||
for (int i = 0; i < originalTriangles.Count; i++)
|
||||
for (int i = 0; i < realTriangles.Length; i++)
|
||||
{
|
||||
var triangle = originalTriangles[i];
|
||||
var triangle = realTriangles[i];
|
||||
|
||||
if (triangle.Count != 3)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"[3D Debug] WARNING: Invalid triangle {i} with {triangle.Count} vertices");
|
||||
continue;
|
||||
}
|
||||
// Calcular centroide del triángulo
|
||||
var centroid = (triangle.A + triangle.B + triangle.C) / 3f;
|
||||
|
||||
// Calcular el centro del triángulo para aplicar separación
|
||||
var center = new System.Numerics.Vector3(
|
||||
(triangle[0].X + triangle[1].X + triangle[2].X) / 3f,
|
||||
(triangle[0].Y + triangle[1].Y + triangle[2].Y) / 3f,
|
||||
0
|
||||
);
|
||||
// Calcular normal del triángulo
|
||||
var edge1 = triangle.B - triangle.A;
|
||||
var edge2 = triangle.C - triangle.A;
|
||||
var normal = Vector3.Normalize(Vector3.Cross(edge1, edge2));
|
||||
|
||||
// Aplicar una pequeña separación desde el centro para distinguir triángulos
|
||||
var offset = center * triangleSeparation;
|
||||
// Offset hacia arriba para separar triángulos visualmente
|
||||
var offset = normal * triangleSeparation * (i + 1);
|
||||
|
||||
// Crear triángulo superior (visible desde arriba)
|
||||
var p1Top = new Point3D(triangle[0].X + offset.X, triangle[0].Y + offset.Y, debugHeight);
|
||||
var p2Top = new Point3D(triangle[1].X + offset.X, triangle[1].Y + offset.Y, debugHeight);
|
||||
var p3Top = new Point3D(triangle[2].X + offset.X, triangle[2].Y + offset.Y, debugHeight);
|
||||
// Aplicar offset a todos los vértices
|
||||
var offsetA = triangle.A + offset;
|
||||
var offsetB = triangle.B + offset;
|
||||
var offsetC = triangle.C + offset;
|
||||
|
||||
// Crear triángulo inferior (visible desde abajo)
|
||||
var p1Bottom = new Point3D(triangle[0].X + offset.X, triangle[0].Y + offset.Y, 0);
|
||||
var p2Bottom = new Point3D(triangle[1].X + offset.X, triangle[1].Y + offset.Y, 0);
|
||||
var p3Bottom = new Point3D(triangle[2].X + offset.X, triangle[2].Y + offset.Y, 0);
|
||||
// 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 superior (normal hacia arriba)
|
||||
meshBuilder.AddTriangle(p1Top, p2Top, p3Top);
|
||||
// Agregar triángulo separado
|
||||
meshBuilder.AddTriangle(pointA, pointB, pointC);
|
||||
|
||||
// Agregar triángulo inferior (normal hacia abajo)
|
||||
meshBuilder.AddTriangle(p1Bottom, p3Bottom, p2Bottom);
|
||||
|
||||
// ✅ OPCIONAL: Agregar bordes/wireframe para mejor visualización del debug
|
||||
// Crear líneas delgadas en los bordes del triángulo para verlo mejor
|
||||
const float edgeThickness = 0.005f;
|
||||
|
||||
// Borde 1-2
|
||||
AddDebugEdge(meshBuilder, p1Top, p2Top, p1Bottom, p2Bottom, edgeThickness);
|
||||
// Borde 2-3
|
||||
AddDebugEdge(meshBuilder, p2Top, p3Top, p2Bottom, p3Bottom, edgeThickness);
|
||||
// Borde 3-1
|
||||
AddDebugEdge(meshBuilder, p3Top, p1Top, p3Bottom, p1Bottom, edgeThickness);
|
||||
// ✅ DEBUG: Agregar bordes para visualizar mejor cada triángulo
|
||||
AddDebugTriangleEdges(meshBuilder, pointA, pointB, pointC, 0.005f);
|
||||
}
|
||||
|
||||
System.Diagnostics.Debug.WriteLine($"[3D Debug] Successfully created debug mesh with {originalTriangles.Count} individual triangles");
|
||||
System.Diagnostics.Debug.WriteLine($"[3D Debug] ✅ Debug mesh creado con triángulos reales separados");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"[3D Debug] ERROR creating debug mesh: {ex.Message}");
|
||||
|
||||
// Fallback: mostrar mensaje de error en consola y usar geometría básica
|
||||
System.Diagnostics.Debug.WriteLine($"[3D Debug] Falling back to basic debug geometry");
|
||||
CreateBasicDebugGeometry(meshBuilder, curve);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ✅ NUEVO: Agrega bordes debug a un triángulo individual
|
||||
/// </summary>
|
||||
private void AddDebugTriangleEdges(MeshBuilder meshBuilder, Point3D a, Point3D b, Point3D c, double edgeThickness)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Crear cilindros delgados para los bordes del triángulo
|
||||
meshBuilder.AddCylinder(a, b, edgeThickness, 4, false, false);
|
||||
meshBuilder.AddCylinder(b, c, edgeThickness, 4, false, false);
|
||||
meshBuilder.AddCylinder(c, a, edgeThickness, 4, false, false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"[3D Debug] ERROR adding triangle edges: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ✅ NUEVO: Agrega un borde delgado entre dos puntos para debug visual
|
||||
/// </summary>
|
||||
|
@ -1071,8 +1048,8 @@ namespace CtrEditor.Simulacion
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// ✅ NUEVO: Obtiene información detallada de debug sobre los triángulos de una curva
|
||||
/// Útil para debug sin cambiar la visualización
|
||||
/// ✅ NUEVO: Obtiene información detallada de debug sobre los triángulos reales de BEPU
|
||||
/// Extrae y muestra los triángulos exactos almacenados en la simulación física
|
||||
/// </summary>
|
||||
/// <param name="curve">Curva para analizar</param>
|
||||
/// <returns>Información de debug como string</returns>
|
||||
|
@ -1083,40 +1060,95 @@ namespace CtrEditor.Simulacion
|
|||
|
||||
try
|
||||
{
|
||||
var triangles = curve.GetOriginalTriangles();
|
||||
var debugInfo = new System.Text.StringBuilder();
|
||||
|
||||
debugInfo.AppendLine($"=== DEBUG INFO PARA CURVA ===");
|
||||
debugInfo.AppendLine($"=== DEBUG INFO TRIÁNGULOS REALES DE BEPU ===");
|
||||
debugInfo.AppendLine($"Parámetros de curva:");
|
||||
debugInfo.AppendLine($" - Radio Interior: {curve.InnerRadius}");
|
||||
debugInfo.AppendLine($" - Radio Exterior: {curve.OuterRadius}");
|
||||
debugInfo.AppendLine($" - Ángulo Inicio: {curve.StartAngle} rad ({curve.StartAngle * 180 / Math.PI:F1}°)");
|
||||
debugInfo.AppendLine($" - Ángulo Fin: {curve.EndAngle} rad ({curve.EndAngle * 180 / Math.PI:F1}°)");
|
||||
debugInfo.AppendLine($" - Ángulo Inicio: {curve.StartAngle}° ({curve.StartAngle * Math.PI / 180:F3} rad)");
|
||||
debugInfo.AppendLine($" - Ángulo Fin: {curve.EndAngle}° ({curve.EndAngle * Math.PI / 180:F3} rad)");
|
||||
debugInfo.AppendLine($" - Velocidad: {curve.Speed} rad/s");
|
||||
debugInfo.AppendLine($"");
|
||||
debugInfo.AppendLine($"Triángulos en BEPU: {triangles.Count}");
|
||||
|
||||
for (int i = 0; i < Math.Min(triangles.Count, 10); i++) // Mostrar máximo 10 triángulos
|
||||
// ✅ OBTENER INFORMACIÓN REAL DEL MESH DE BEPU
|
||||
var meshInfo = curve.GetBEPUMeshDebugInfo();
|
||||
debugInfo.AppendLine("INFORMACIÓN DEL MESH EN BEPU:");
|
||||
debugInfo.AppendLine(meshInfo);
|
||||
debugInfo.AppendLine($"");
|
||||
|
||||
// ✅ EXTRAER Y MOSTRAR TRIÁNGULOS REALES
|
||||
var realTriangles = curve.GetRealBEPUTriangles();
|
||||
debugInfo.AppendLine($"TRIÁNGULOS REALES EXTRAÍDOS: {realTriangles.Length}");
|
||||
debugInfo.AppendLine($"");
|
||||
|
||||
// Mostrar los primeros triángulos con detalles completos
|
||||
int maxToShow = Math.Min(10, realTriangles.Length);
|
||||
for (int i = 0; i < maxToShow; i++)
|
||||
{
|
||||
var triangle = triangles[i];
|
||||
debugInfo.AppendLine($" Triángulo {i + 1}:");
|
||||
debugInfo.AppendLine($" P1: ({triangle[0].X:F3}, {triangle[0].Y:F3}, {triangle[0].Z:F3})");
|
||||
debugInfo.AppendLine($" P2: ({triangle[1].X:F3}, {triangle[1].Y:F3}, {triangle[1].Z:F3})");
|
||||
debugInfo.AppendLine($" P3: ({triangle[2].X:F3}, {triangle[2].Y:F3}, {triangle[2].Z:F3})");
|
||||
var triangle = realTriangles[i];
|
||||
debugInfo.AppendLine($"Triángulo {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})");
|
||||
|
||||
// Calcular área del triángulo
|
||||
var edge1 = triangle.B - triangle.A;
|
||||
var edge2 = triangle.C - triangle.A;
|
||||
var cross = Vector3.Cross(edge1, edge2);
|
||||
var area = cross.Length() / 2f;
|
||||
debugInfo.AppendLine($" Área: {area:F6}");
|
||||
debugInfo.AppendLine($"");
|
||||
}
|
||||
|
||||
if (triangles.Count > 10)
|
||||
if (realTriangles.Length > maxToShow)
|
||||
{
|
||||
debugInfo.AppendLine($" ... y {triangles.Count - 10} triángulos más");
|
||||
debugInfo.AppendLine($"... y {realTriangles.Length - maxToShow} triángulos más");
|
||||
}
|
||||
|
||||
debugInfo.AppendLine($"");
|
||||
debugInfo.AppendLine($"Modo debug actual: {(DebugShowIndividualTriangles ? "ACTIVADO" : "DESACTIVADO")}");
|
||||
debugInfo.AppendLine($"Modo debug visual: {(DebugShowIndividualTriangles ? "ACTIVADO (triángulos separados)" : "DESACTIVADO (superficie continua)")}");
|
||||
|
||||
return debugInfo.ToString();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return $"ERROR obteniendo info de debug: {ex.Message}";
|
||||
return $"ERROR obteniendo info de debug real: {ex.Message}";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ✅ NUEVO: Activa la visualización de triángulos reales de BEPU
|
||||
/// Muestra los triángulos exactos extraídos de la simulación física
|
||||
/// </summary>
|
||||
/// <param name="enable">True para mostrar triángulos reales, false para superficie normal</param>
|
||||
public void SetRealBEPUTrianglesMode(bool enable)
|
||||
{
|
||||
SetDebugTrianglesMode(enable);
|
||||
System.Diagnostics.Debug.WriteLine($"[3D BEPU] Modo triángulos reales: {(enable ? "ACTIVADO" : "DESACTIVADO")}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ✅ NUEVO: Verifica si una curva tiene triángulos válidos en BEPU
|
||||
/// </summary>
|
||||
/// <param name="curve">Curva a verificar</param>
|
||||
/// <returns>True si tiene triángulos válidos</returns>
|
||||
public bool HasValidBEPUTriangles(simCurve curve)
|
||||
{
|
||||
if (curve == null) return false;
|
||||
|
||||
try
|
||||
{
|
||||
var triangles = curve.GetRealBEPUTriangles();
|
||||
bool hasTriangles = triangles.Length > 0;
|
||||
|
||||
System.Diagnostics.Debug.WriteLine($"[3D BEPU] Curva tiene {triangles.Length} triángulos válidos: {hasTriangles}");
|
||||
return hasTriangles;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"[3D BEPU] Error verificando triángulos: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue