namespace Sandbox; public sealed partial class ModelPhysics { /// /// Create all the bodies, colliders and joints. /// private void CreatePhysics() { if ( !Active ) return; if ( IsProxy ) return; if ( PhysicsWereCreated ) return; DestroyPhysics(); if ( !Model.IsValid() ) return; var physics = Model.Physics; if ( physics is null || physics.Parts.Count == 0 ) return; PhysicsRebuildCount++; var componentFlags = ComponentFlags.None; if ( Scene.IsEditor ) { componentFlags |= ComponentFlags.NotEditable; } var world = WorldTransform; CreateParts( physics, world, componentFlags ); CreateJoints( physics, componentFlags ); foreach ( var body in Bodies ) { body.Component.Enabled = true; Network?.Refresh( body.Component ); } foreach ( var joint in Joints ) { joint.Component.Enabled = true; Network?.Refresh( joint.Component ); } PhysicsWereCreated = true; } private void CreateJoints( PhysicsGroupDescription physics, ComponentFlags componentFlags ) { var jointFlags = componentFlags; foreach ( var jointDesc in physics.Joints ) { var body1 = Bodies[jointDesc.Body1]; var body2 = Bodies[jointDesc.Body2]; var localFrame1 = jointDesc.Frame1; var localFrame2 = jointDesc.Frame2; Sandbox.Joint joint = null; if ( jointDesc.Type == PhysicsGroupDescription.JointType.Hinge ) { var hingeJoint = body1.Component.AddComponent( false ); if ( jointDesc.EnableTwistLimit ) { hingeJoint.MinAngle = jointDesc.TwistMin; hingeJoint.MaxAngle = jointDesc.TwistMax; } if ( jointDesc.EnableAngularMotor ) { var worldFrame1 = body1.Component.WorldTransform.ToWorld( localFrame1 ); var hingeAxis = worldFrame1.Rotation.Up; var targetVelocity = hingeAxis.Dot( jointDesc.AngularTargetVelocity ); hingeJoint.Motor = HingeJoint.MotorMode.TargetVelocity; hingeJoint.TargetVelocity = targetVelocity.RadianToDegree(); hingeJoint.MaxTorque = jointDesc.MaxTorque; } joint = hingeJoint; } else if ( jointDesc.Type == PhysicsGroupDescription.JointType.Ball ) { var ballJoint = body1.Component.AddComponent( false ); if ( jointDesc.EnableSwingLimit ) { ballJoint.SwingLimitEnabled = true; ballJoint.SwingLimit = new Vector2( jointDesc.SwingMin, jointDesc.SwingMax ); } if ( jointDesc.EnableTwistLimit ) { ballJoint.TwistLimitEnabled = true; ballJoint.TwistLimit = new Vector2( jointDesc.TwistMin, jointDesc.TwistMax ); } joint = ballJoint; } else if ( jointDesc.Type == PhysicsGroupDescription.JointType.Fixed ) { var fixedJoint = body1.Component.AddComponent( false ); fixedJoint.LinearFrequency = jointDesc.LinearFrequency; fixedJoint.LinearDamping = jointDesc.LinearDampingRatio; fixedJoint.AngularFrequency = jointDesc.AngularFrequency; fixedJoint.AngularDamping = jointDesc.AngularDampingRatio; joint = fixedJoint; } else if ( jointDesc.Type == PhysicsGroupDescription.JointType.Slider ) { var sliderJoint = body1.Component.AddComponent( false ); if ( jointDesc.EnableLinearLimit ) { sliderJoint.MinLength = jointDesc.LinearMin; sliderJoint.MaxLength = jointDesc.LinearMax; } var rot = Rotation.FromPitch( -90 ); localFrame1 = localFrame1.WithRotation( rot * localFrame1.Rotation ); localFrame2 = localFrame2.WithRotation( rot * localFrame2.Rotation ); joint = sliderJoint; } if ( joint.IsValid() ) { joint.Flags |= jointFlags; joint.Body = body2.Component.GameObject; joint.Attachment = Sandbox.Joint.AttachmentMode.LocalFrames; joint.LocalFrame1 = localFrame1.WithPosition( jointDesc.Frame1.Position * body1.Component.WorldScale ); joint.LocalFrame2 = localFrame2.WithPosition( jointDesc.Frame2.Position * body2.Component.WorldScale ); joint.EnableCollision = jointDesc.EnableCollision; joint.BreakForce = jointDesc.LinearStrength; joint.BreakTorque = jointDesc.AngularStrength; Joints.Add( new Joint( joint, body1, body2, localFrame1, localFrame2 ) ); } } } private void CreateParts( PhysicsGroupDescription physics, Transform world, ComponentFlags componentFlags ) { var boneObjects = Model.CreateBoneObjects( GameObject ); var bones = Model.Bones; var bodyFlags = componentFlags; var colliderFlags = componentFlags; foreach ( var part in physics.Parts ) { var bone = bones.GetBone( part.BoneName ); if ( !boneObjects.TryGetValue( bone, out var go ) ) continue; if ( !go.Flags.Contains( GameObjectFlags.Absolute | GameObjectFlags.PhysicsBone ) ) { go.Flags |= GameObjectFlags.Absolute | GameObjectFlags.PhysicsBone; if ( !Renderer.IsValid() || !Renderer.TryGetBoneTransform( bone, out var boneWorld ) ) { // There's no renderer bones, use physics bind pose boneWorld = world.ToWorld( part.Transform ); } go.WorldTransform = boneWorld; } var body = go.AddComponent( false ); body.Flags |= bodyFlags; body.RigidbodyFlags = RigidbodyFlags; body.Locking = Locking; body.MotionEnabled = MotionEnabled; body.LinearDamping = part.LinearDamping; body.AngularDamping = part.AngularDamping; body.StartAsleep = StartAsleep; body.MassOverride = part.Mass; body.OverrideMassCenter = part.OverrideMassCenter; body.MassCenterOverride = part.MassCenterOverride; body.GameObjectSource = GameObject; var bodyTransform = body.WorldTransform; BodyTransforms.Set( Bodies.Count, bodyTransform ); Bodies.Add( new Body( body, bone.Index, WorldTransform.ToLocal( bodyTransform ) ) ); foreach ( var sphere in part.Spheres ) { var collider = go.AddComponent(); collider.Flags |= colliderFlags; collider.Center = sphere.Sphere.Center; collider.Radius = sphere.Sphere.Radius; collider.Surface = sphere.Surface; } foreach ( var capsule in part.Capsules ) { var collider = go.AddComponent(); collider.Flags |= colliderFlags; collider.Start = capsule.Capsule.CenterA; collider.End = capsule.Capsule.CenterB; collider.Radius = capsule.Capsule.Radius; collider.Surface = capsule.Surface; } foreach ( var hull in part.Hulls ) { var collider = go.AddComponent(); collider.Flags |= colliderFlags; collider.Type = HullCollider.PrimitiveType.Points; collider.Points = hull.GetPoints().ToList(); collider.Surface = hull.Surface; } } } /// /// Destroy all the bodies, colliders and joints. /// private void DestroyPhysics() { if ( Renderer.IsValid() ) { Renderer.ClearPhysicsBones(); } BodyTransforms.Clear(); if ( !PhysicsWereCreated ) return; PhysicsDestroyCount++; foreach ( var joint in Joints ) { if ( !joint.Component.IsValid() ) continue; joint.Component.Destroy(); Network?.Refresh( joint.Component ); } foreach ( var collider in GetComponentsInChildren( true ) ) { if ( !collider.IsValid() ) continue; if ( !collider.GameObject.Flags.Contains( GameObjectFlags.PhysicsBone ) ) continue; collider.Destroy(); Network?.Refresh( collider ); } foreach ( var body in Bodies ) { if ( !body.Component.IsValid() ) continue; body.Component.GameObject.Flags &= ~GameObjectFlags.Absolute; body.Component.GameObject.Flags &= ~GameObjectFlags.PhysicsBone; body.Component.Destroy(); Network?.Refresh( body.Component ); } Bodies.Clear(); Joints.Clear(); PhysicsWereCreated = false; } internal void OnModelReloaded() { DestroyPhysics(); CreatePhysics(); } }