Code/Vehicle/Wheel/WheelCollider.Trace.cs
using System;
using System.Collections.Generic;
using System.Linq;
using Sandbox;
namespace Meteor.VehicleTool.Vehicle.Wheel;
public partial class WheelCollider
{
private static Rotation CylinderOffset => Rotation.FromRoll( 90 );
[Property] public TagSet IgnoredTags { get; set; }
public bool IsGrounded { get; private set; }
private GroundHit GroundHit;
public Vector3 ContactPoint { get; private set; }
public Vector3 ContactNormal { get; private set; }
private void DoTrace()
{
var rot = CarBody.WorldRotation;
var startPos = WorldPosition + rot.Up * MinSuspensionLength;
var endPos = WorldPosition + rot.Down * MaxSuspensionLength;
GroundHit = new( Scene.Trace
.IgnoreGameObjectHierarchy( Controller.GameObject )
.FromTo( startPos, endPos )
.Cylinder( Width, Radius )
.Rotated( TransformRotationSteer * CylinderOffset )
.UseRenderMeshes( false )
.UseHitPosition( false )
.WithoutTags( IgnoredTags )
.Run() );
IsGrounded = GroundHit.Hit;
ContactPoint = WorldTransform.PointToWorld( Vector3.Down * Radius );
ContactNormal = GroundHit.Normal;
}
public static Model CreateWheelMesh( float radius, float length, bool topHalf, int segments = 16 )
{
if ( radius <= 0 || length <= 0 )
return null;
List<Vector3> vertices = [new( 0, -length / 2, 0 ), new( 0, length / 2, 0 )];
float angleStep = 2 * MathF.PI / segments;
int startAngleSegment = topHalf ? 0 : segments / 2;
int endAngleSegment = topHalf ? segments / 2 : segments;
for ( int i = startAngleSegment; i <= endAngleSegment; i++ )
{
float angle = i * angleStep;
float x = radius * MathF.Cos( angle );
float z = radius * MathF.Sin( angle );
vertices.Add( new( x, -length / 2, z ) );
vertices.Add( new( x, length / 2, z ) );
}
return Model.Builder.AddCollisionHull( vertices.ToList() ).WithMass( 0 ).Create();
}
}