// // The graphics engine GLScene // unit GLS.PhysInertias; interface uses System.SysUtils, System.Classes, GLS.PersistentClasses, GLS.XCollection, GLS.BaseClasses, Stage.VectorGeometry, Stage.VectorTypes, GLS.PhysManager, GLS.Coordinates, Stage.Strings, GLS.Scene, GLS.Behaviours; type TGLParticleInertia = class(TGLBaseInertia) // modified from TGLBInertia private FMass: Single; FTranslationSpeed: TGLCoordinates; FTranslationDamping: TGLDamping; protected function CalcLinearPositionDot(): TAffineVector; function CalcLinearMomentumDot(): TAffineVector; procedure SetTranslationSpeed(const val: TGLCoordinates); procedure SetTranslationDamping(const val: TGLDamping); public fForce: TAffineVector; LinearPosition: TAffineVector; LinearMomentum: TAffineVector; procedure StateToArray(var StateArray: TStateArray; StatePos: Integer); override; procedure ArrayToState( { var } StateArray: TStateArray; StatePos: Integer); override; procedure CalcStateDot(var StateArray: TStateArray; StatePos: Integer); override; procedure RemoveForces(); override; procedure CalculateForceFieldForce(ForceFieldEmitter : TGLBaseForceFieldEmitter); override; procedure CalcAuxiliary(); override; procedure SetUpStartingState(); override; function CalculateKE(): Real; override; function CalculatePE(): Real; override; procedure SetForce(x, y, z: Real); virtual; procedure ApplyForce(x, y, z: Real); overload; virtual; procedure ApplyForce(Force: TAffineVector); overload; virtual; procedure ApplyForce(pos, Force: TAffineVector); overload; virtual; procedure ApplyLocalForce(pos, Force: TAffineVector); virtual; procedure ApplyImpulse(j, x, y, z: Real); overload; virtual; procedure ApplyImpulse(j: Single; normal: TAffineVector); overload; virtual; procedure ApplyDamping(damping: TGLDamping); virtual; constructor Create(aOwner: TXCollection); override; destructor Destroy; override; procedure Assign(Source: TPersistent); override; procedure WriteToFiler(writer: TWriter); override; procedure ReadFromFiler(reader: TReader); override; class function FriendlyName: String; override; class function FriendlyDescription: String; override; class function UniqueItem: Boolean; override; // Inverts the translation vector procedure MirrorTranslation; (* Bounce speed as if hitting a surface. restitution is the coefficient of restituted energy (1=no energy loss, 0=no bounce). The normal is NOT assumed to be normalized. *) procedure SurfaceBounce(const surfaceNormal: TGLVector; restitution: Single); published property Mass: Single read FMass write FMass; property TranslationSpeed: TGLCoordinates read FTranslationSpeed write SetTranslationSpeed; (* Enable/Disable damping (damping has a high cpu-cycle cost). Damping is enabled by default. *) // property DampingEnabled : Boolean read FDampingEnabled write FDampingEnabled; (* Damping applied to translation speed.
Note that it is not "exactly" applied, ie. if damping would stop your object after 0.5 time unit, and your progression steps are of 1 time unit, there will be an integration error of 0.5 time unit. *) property TranslationDamping: TGLDamping read FTranslationDamping write SetTranslationDamping; end; TGLRigidBodyInertia = class; (* Stores Inertia Tensor for TGLRigidBodyInertia model *) TGLInertiaTensor = class(TGLUpdateAbleObject) private fm11, fm12, fm13, fm21, fm22, fm23, fm31, fm32, fm33: Single; public constructor Create(aOwner: TPersistent); override; destructor Destroy; override; procedure Assign(Source: TPersistent); override; procedure WriteToFiler(writer: TWriter); procedure ReadFromFiler(reader: TReader); published property m11: Single read fm11 write fm11; property m12: Single read fm12 write fm12; property m13: Single read fm13 write fm13; property m21: Single read fm21 write fm21; property m22: Single read fm22 write fm22; property m23: Single read fm23 write fm23; property m31: Single read fm31 write fm31; property m32: Single read fm32 write fm32; property m33: Single read fm33 write fm33; end; (* A more complex model than TGLBInertia for Inertia *) TGLRigidBodyInertia = class(TGLParticleInertia) private fDensity: Real; fBodyInertiaTensor: TAffineMAtrix; fBodyInverseInertiaTensor: TAffineMAtrix; fInertiaTensor: TGLInertiaTensor; InverseInertiaTensor: TAffineMAtrix; // LinearVelocity:TAffineVector; fRotationSpeed: TGLCoordinates; /// AngularVelocity:TAffineVector; //rotation about axis, magnitude=speed // damping properties FRotationDamping: TGLDamping; protected // torques fTorque: TAffineVector; procedure SetLinearDamping(const val: TGLDamping); procedure SetAngularDamping(const val: TGLDamping); public AngularOrientation: TQuaternion; // As Quat will help improve accuracy R: TMatrix3f; // corresponds to AngularOrientation AngularMomentum: TAffineVector; procedure StateToArray(var StateArray: TStateArray; StatePos: Integer); override; procedure ArrayToState( (* var *) StateArray: TStateArray; StatePos: Integer); override; procedure CalcStateDot(var StateArray: TStateArray; StatePos: Integer); override; procedure ApplyImpulse(j, xpos, ypos, zpos, x, y, z: Real); overload; procedure ApplyImpulse(j: Single; position, normal: TAffineVector); overload; procedure ApplyDamping(damping: TGLDamping); override; // function CalcLinearPositionDot():TAffineVector; // function CalcLinearMomentumDot():TAffineVector; function CalcAngularOrientationDot(): TQuaternion; function CalcAngularVelocityDot(): TAffineVector; function CalculateKE(): Real; override; function CalculatePE(): Real; override; procedure CalcAuxiliary(); override; procedure SetUpStartingState(); override; constructor Create(aOwner: TXCollection); override; destructor Destroy; override; procedure Assign(Source: TPersistent); override; procedure WriteToFiler(writer: TWriter); override; procedure ReadFromFiler(reader: TReader); override; class function FriendlyName: String; override; class function FriendlyDescription: String; override; class function UniqueItem: Boolean; override; // function Star(Vector:TAffineVector):TGLMatrix; function QuaternionToString(Quat: TQuaternion): String; procedure RemoveForces(); override; procedure SetTorque(x, y, z: Real); procedure ApplyTorque(x, y, z: Real); procedure ApplyForce(pos, Force: TAffineVector); override; procedure ApplyLocalForce(pos, Force: TVector3f); override; procedure ApplyLocalImpulse(xpos, ypos, zpos, x, y, z: Real); procedure SetInertiaTensor(newVal: TGLInertiaTensor); procedure SetRotationSpeed(const val: TGLCoordinates); procedure SetRotationDamping(const val: TGLDamping); published property Density: Real read fDensity write fDensity; property InertiaTensor: TGLInertiaTensor read fInertiaTensor write SetInertiaTensor; property RotationSpeed: TGLCoordinates read fRotationSpeed write SetRotationSpeed; property RotationDamping: TGLDamping read FRotationDamping write SetRotationDamping; end; (* Returns or creates the TGLParticleInertia within the given behaviours. This helper function is convenient way to access a TGLParticleInertia. *) function GetOrCreateParticleInertia(behaviours: TGLBehaviours): TGLParticleInertia; overload; (* Returns or creates the TGLParticleInertia within the given object's behaviours. This helper function is convenient way to access a TGLParticleInertia. *) function GetOrCreateParticleInertia(obj: TGLBaseSceneObject): TGLParticleInertia; overload; (* Returns or creates the TGLRigidBodyInertia within the given behaviours. This helper function is convenient way to access a TGLRigidBodyInertia. *) function GetOrCreateRigidBodyInertia(behaviours: TGLBehaviours): TGLRigidBodyInertia; overload; (* Returns or creates the TGLRigidBodyInertia within the given object's behaviours. This helper function is convenient way to access a TGLRigidBodyInertia. *) function GetOrCreateRigidBodyInertia(obj: TGLBaseSceneObject): TGLRigidBodyInertia; overload; const DebugMode = false; // ------------------------------------------------------------------ implementation // ------------------------------------------------------------------ // ------------------ // ------------------ TGLParticleInertia ------------------ // ------------------ constructor TGLParticleInertia.Create(aOwner: TXCollection); begin inherited Create(aOwner); FMass := 1; StateSize := 6; FTranslationSpeed := TGLCoordinates.CreateInitialized(Self, NullHmgVector, csVector); LinearPosition := OwnerBaseSceneObject.position.AsAffineVector; LinearMomentum := FTranslationSpeed.AsAffineVector; FTranslationDamping := TGLDamping.Create(Self); end; destructor TGLParticleInertia.Destroy; begin FTranslationDamping.Free; FTranslationSpeed.Free; inherited Destroy; end; procedure TGLParticleInertia.Assign(Source: TPersistent); begin if Source.ClassType = Self.ClassType then begin FMass := TGLParticleInertia(Source).FMass; FTranslationSpeed.Assign(TGLParticleInertia(Source).FTranslationSpeed); LinearPosition := TGLParticleInertia(Source).LinearPosition; LinearMomentum := TGLParticleInertia(Source).LinearMomentum; // FDampingEnabled:=TGLInertia(Source).DampingEnabled; FTranslationDamping.Assign(TGLParticleInertia(Source).TranslationDamping); // FRotationDamping.Assign(TGLBInertia(Source).RotationDamping); end; inherited Assign(Source); end; procedure TGLParticleInertia.WriteToFiler(writer: TWriter); begin inherited; with writer do begin WriteInteger(0); // Archive Version 0 WriteFloat(FMass); Write(LinearPosition, SizeOf(LinearPosition)); Write(LinearMomentum, SizeOf(LinearMomentum)); Write(fForce, SizeOf(fForce)); FTranslationSpeed.WriteToFiler(writer); FTranslationDamping.WriteToFiler(writer); end; end; procedure TGLParticleInertia.ReadFromFiler(reader: TReader); begin inherited; with reader do begin ReadInteger; // ignore archiveVersion FMass := ReadFloat; Read(LinearPosition, SizeOf(LinearPosition)); Read(LinearMomentum, SizeOf(LinearMomentum)); Read(fForce, SizeOf(fForce)); FTranslationSpeed.ReadFromFiler(reader); FTranslationDamping.ReadFromFiler(reader); end; // Loaded; SetUpStartingState(); end; procedure TGLParticleInertia.SetTranslationSpeed(const val: TGLCoordinates); begin FTranslationSpeed.Assign(val); LinearMomentum := VectorScale(FTranslationSpeed.AsAffineVector, FMass); end; procedure TGLParticleInertia.SetUpStartingState(); begin LinearPosition := OwnerBaseSceneObject.position.AsAffineVector; LinearMomentum := VectorScale(TranslationSpeed.AsAffineVector, Mass); end; procedure TGLParticleInertia.CalcAuxiliary( { RBody:TGLRigidBody } ); begin TranslationSpeed.AsAffineVector := VectorScale(LinearMomentum, 1 / Mass); // OwnerBaseSceneObject.Matrix:=QuaternionToMatrix(AngularOrientation); OwnerBaseSceneObject.position.AsAffineVector := LinearPosition; // position // OwnerBaseSceneObject.position.x:=LinearPosition[0];//position // OwnerBaseSceneObject.position.y:=LinearPosition[1]; // OwnerBaseSceneObject.position.z:=LinearPosition[2]; end; procedure TGLParticleInertia.RemoveForces(); begin fForce := nullVector; end; procedure TGLParticleInertia.CalculateForceFieldForce(ForceFieldEmitter : TGLBaseForceFieldEmitter); begin ForceFieldEmitter.CalculateForceField(Self.OwnerBaseSceneObject); end; function TGLParticleInertia.CalculateKE(): Real; begin Result := 1 / (2 * Mass) * VectorNorm(LinearMomentum); end; function TGLParticleInertia.CalculatePE(): Real; begin // need to find potentials due to fields acting on body // may be easier to do via ForceFieldEmitters? Result := 0; end; procedure TGLParticleInertia.SetForce(x, y, z: Real); begin fForce.x := x; fForce.y := y; fForce.z := z; end; procedure TGLParticleInertia.ApplyForce(x, y, z: Real); begin fForce.X := fForce.X + x; fForce.Y := fForce.Y + y; fForce.Z := fForce.Z + z; end; procedure TGLParticleInertia.ApplyForce(Force: TAffineVector); begin fForce := VectorAdd(fForce, Force); end; procedure TGLParticleInertia.ApplyForce(pos, Force: TAffineVector); // var // abspos:TAffineVector; begin fForce := VectorAdd(fForce, Force); // abspos:=VectorTransform(pos,R); // fTorque:=VectorAdd(fTorque,VectorCrossProduct(abspos,force)); // fForce:=VectorAdd(fForce,force); end; procedure TGLParticleInertia.ApplyLocalForce(pos, Force: TAffineVector); // var // abspos:TAffineVector; // absForce:TAffineVector; begin // abspos:=VectorTransform(pos,R); // absForce:=VectorTransform(Force,R); // fTorque:=VectorAdd(fTorque,VectorCrossProduct(abspos,absforce)); fForce := VectorAdd(fForce, Force); end; procedure TGLParticleInertia.ApplyImpulse(j, x, y, z: Real); begin // V2 = V1 + (j/M)n // V2.M = V1.M +j.n LinearMomentum.X := LinearMomentum.X + j * x; LinearMomentum.Y := LinearMomentum.Y + j * y; LinearMomentum.Z := LinearMomentum.Z + j * z; end; procedure TGLParticleInertia.ApplyImpulse(j: Single; normal: TAffineVector); begin CombineVector(LinearMomentum, normal, j); end; procedure TGLParticleInertia.ApplyDamping(damping: TGLDamping); var velocity: TAffineVector; v: Real; dampingForce: TAffineVector; begin velocity := VectorScale(LinearMomentum, 1 / Mass); // v = p/m // apply force in opposite direction to velocity v := VectorLength(velocity); // F = -Normalised(V)*( Constant + (Linear)*(V) + (Quadtratic)*(V)*(V) ) dampingForce := VectorScale(VectorNormalize(velocity), -(damping.Constant + damping.Linear * v + damping.Quadratic * v * v)); (* dampingForce:=VectorScale(VectorNormalize(velocity), -(Damping.Constant+Damping.Linear*v+Damping.Quadratic*v*v)); *) ApplyForce(dampingForce); end; procedure TGLParticleInertia.SetTranslationDamping(const val: TGLDamping); begin FTranslationDamping.Assign(val); end; class function TGLParticleInertia.FriendlyName: String; begin Result := 'Particle Inertia'; end; class function TGLParticleInertia.FriendlyDescription: String; begin Result := 'A simple translation inertia'; end; class function TGLParticleInertia.UniqueItem: Boolean; begin Result := True; end; function TGLParticleInertia.CalcLinearPositionDot(): TAffineVector; begin Result := VectorScale(LinearMomentum, 1 / FMass); // Result:=FTranslationSpeed.AsAffineVector; end; function TGLParticleInertia.CalcLinearMomentumDot(): TAffineVector; begin Result := fForce; end; procedure TGLParticleInertia.StateToArray(var StateArray: TStateArray; StatePos: Integer); begin // SetLength(Result,StateSize); StateArray[StatePos] := LinearPosition.X; // position StateArray[StatePos + 1] := LinearPosition.Y; StateArray[StatePos + 2] := LinearPosition.Z; StateArray[StatePos + 3] := LinearMomentum.X; // momentum StateArray[StatePos + 4] := LinearMomentum.Y; StateArray[StatePos + 5] := LinearMomentum.Z; end; procedure TGLParticleInertia.ArrayToState(StateArray: TStateArray; StatePos: Integer); begin LinearPosition.X := StateArray[StatePos]; LinearPosition.Y := StateArray[StatePos + 1]; LinearPosition.Z := StateArray[StatePos + 2]; LinearMomentum.X := StateArray[StatePos + 3]; LinearMomentum.Y := StateArray[StatePos + 4]; LinearMomentum.Z := StateArray[StatePos + 5]; // TODO change? (* OwnerBaseSceneObject.position.x:=StateArray[StatePos];//position OwnerBaseSceneObject.position.y:=StateArray[StatePos+1]; OwnerBaseSceneObject.position.z:=StateArray[StatePos+2]; FTranslationSpeed.X:=StateArray[StatePos+3]/fMass;//velocity FTranslationSpeed.Y:=StateArray[StatePos+4]/fMass; FTranslationSpeed.Z:=StateArray[StatePos+5]/fMass; *) end; procedure TGLParticleInertia.CalcStateDot(var StateArray: TStateArray; StatePos: Integer); var LinPos, LinMom: TAffineVector; begin LinPos := CalcLinearPositionDot(); LinMom := CalcLinearMomentumDot(); StateArray[StatePos] := LinPos.X; StateArray[StatePos + 1] := LinPos.Y; StateArray[StatePos + 2] := LinPos.Z; StateArray[StatePos + 3] := LinMom.X; StateArray[StatePos + 4] := LinMom.Y; StateArray[StatePos + 5] := LinMom.Z; end; procedure TGLParticleInertia.MirrorTranslation; begin FTranslationSpeed.Invert; end; procedure TGLParticleInertia.SurfaceBounce(const surfaceNormal: TGLVector; restitution: Single); var f: Single; begin // does the current speed vector comply? f := VectorDotProduct(FTranslationSpeed.AsVector, surfaceNormal); if f < 0 then begin // remove the non-complying part of the speed vector FTranslationSpeed.AddScaledVector(-f / VectorNorm(surfaceNormal) * (1 + restitution), surfaceNormal); end; end; function GetOrCreateParticleInertia(behaviours: TGLBehaviours) : TGLParticleInertia; var i: Integer; begin i := behaviours.IndexOfClass(TGLParticleInertia); if i >= 0 then Result := TGLParticleInertia(behaviours[i]) else Result := TGLParticleInertia.Create(behaviours); end; function GetOrCreateParticleInertia(obj: TGLBaseSceneObject) : TGLParticleInertia; begin Result := GetOrCreateParticleInertia(obj.behaviours); end; // ----------------------------------------------------------------------- // ------------ TGLInertiaTensor // ----------------------------------------------------------------------- constructor TGLInertiaTensor.Create(aOwner: TPersistent); begin inherited Create(aOwner); fm11 := 1; fm22 := 1; fm33 := 1; end; destructor TGLInertiaTensor.Destroy; begin inherited Destroy; end; procedure TGLInertiaTensor.Assign(Source: TPersistent); begin inherited; fm11 := TGLInertiaTensor(Source).fm11; fm12 := TGLInertiaTensor(Source).fm12; fm13 := TGLInertiaTensor(Source).fm13; fm21 := TGLInertiaTensor(Source).fm21; fm22 := TGLInertiaTensor(Source).fm22; fm23 := TGLInertiaTensor(Source).fm23; fm31 := TGLInertiaTensor(Source).fm31; fm32 := TGLInertiaTensor(Source).fm32; fm33 := TGLInertiaTensor(Source).fm33; end; procedure TGLInertiaTensor.WriteToFiler(writer: TWriter); begin inherited; with writer do begin WriteInteger(0); // Archive Version 0 WriteFloat(fm11); WriteFloat(fm12); WriteFloat(fm13); WriteFloat(fm21); WriteFloat(fm22); WriteFloat(fm23); WriteFloat(fm31); WriteFloat(fm32); WriteFloat(fm33); end; end; procedure TGLInertiaTensor.ReadFromFiler(reader: TReader); begin inherited; with reader do begin ReadInteger(); // Archive Version 0 fm11 := ReadFloat(); fm12 := ReadFloat(); fm13 := ReadFloat(); fm21 := ReadFloat(); fm22 := ReadFloat(); fm23 := ReadFloat(); fm31 := ReadFloat(); fm32 := ReadFloat(); fm33 := ReadFloat(); end; end; //-------------------------- // TGLRigidBodyInertia //-------------------------- procedure TGLRigidBodyInertia.SetInertiaTensor(newVal: TGLInertiaTensor); begin fInertiaTensor := newVal; end; procedure TGLRigidBodyInertia.SetRotationSpeed(const val: TGLCoordinates); begin AngularMomentum := VectorTransform(val.AsAffineVector, fBodyInertiaTensor); fRotationSpeed.Assign(val); end; procedure TGLRigidBodyInertia.SetRotationDamping(const val: TGLDamping); begin FRotationDamping.Assign(val); end; procedure TGLRigidBodyInertia.ApplyImpulse(j, xpos, ypos, zpos, x, y, z: Real); begin // V2 = V1 + (j/M)n // V2.M = V1.M +j.n LinearMomentum.X := LinearMomentum.X + j * x; LinearMomentum.Y := LinearMomentum.Y + j * y; LinearMomentum.Z := LinearMomentum.Z + j * z; AngularMomentum.X := AngularMomentum.X + j * x * xpos; AngularMomentum.Y := AngularMomentum.Y + j * y * ypos; AngularMomentum.Z := AngularMomentum.Z + j * z * zpos; end; procedure TGLRigidBodyInertia.ApplyImpulse(j: Single; position, normal: TAffineVector); begin CombineVector(LinearMomentum, normal, j); CombineVector(AngularMomentum, VectorCrossProduct(position, normal), j); // ? end; procedure TGLRigidBodyInertia.ApplyDamping(damping: TGLDamping); var velocity, angularvelocity: TAffineVector; v, angularv: Real; dampingForce: TAffineVector; angulardampingForce: TAffineVector; begin velocity := VectorScale(LinearMomentum, 1 / Mass); // v = p/m // apply force in opposite direction to velocity v := VectorLength(velocity); // F = -Normalised(V)*( Constant + (Linear)*(V) + (Quadtratic)*(V)*(V) ) dampingForce := VectorScale(VectorNormalize(velocity), -(damping.Constant + damping.Linear * v + damping.Quadratic * v * v)); // ScaleVector(AngularMomentum,0.999); // ScaleVector(AngularVelocity,Damping.Constant); // dampingForce:=VectorScale(VectorNormalize(velocity),-(Damping.Constant+Damping.Linear*v+Damping.Quadratic*v*v)); ApplyForce(dampingForce); angularvelocity := RotationSpeed.AsAffineVector; // v = p/m // apply force in opposite direction to velocity angularv := VectorLength(angularvelocity); // F = -Normalised(V)*( Constant + (Linear)*(V) + (Quadtratic)*(V)*(V) ) angulardampingForce := VectorScale(VectorNormalize(angularvelocity), -(RotationDamping.Constant + RotationDamping.Linear * v + RotationDamping.Quadratic * v * v)); // ScaleVector(AngularMomentum,0.999); // ScaleVector(AngularVelocity,Damping.Constant); // dampingForce:=VectorScale(VectorNormalize(velocity),-(Damping.Constant+Damping.Linear*v+Damping.Quadratic*v*v)); ApplyTorque(angulardampingForce.X, angulardampingForce.Y, angulardampingForce.Z); end; procedure TGLRigidBodyInertia.CalcStateDot(var StateArray: TStateArray; StatePos: Integer); var LinPos, LinMom, AngMom: TAffineVector; AngPos: TQuaternion; begin LinPos := CalcLinearPositionDot(); LinMom := CalcLinearMomentumDot(); AngPos := CalcAngularOrientationDot(); AngMom := CalcAngularVelocityDot(); // SetLength(Result,StateSize); StateArray[StatePos] := LinPos.X; StateArray[StatePos + 1] := LinPos.Y; StateArray[StatePos + 2] := LinPos.Z; StateArray[StatePos + 3] := LinMom.X; StateArray[StatePos + 4] := LinMom.Y; StateArray[StatePos + 5] := LinMom.Z; StateArray[StatePos + 6] := AngPos.imagPart.X; StateArray[StatePos + 7] := AngPos.imagPart.Y; StateArray[StatePos + 8] := AngPos.imagPart.Z; StateArray[StatePos + 9] := AngPos.RealPart; StateArray[StatePos + 10] := AngMom.X; StateArray[StatePos + 11] := AngMom.Y; StateArray[StatePos + 12] := AngMom.Z; end; function TGLRigidBodyInertia.CalculateKE(): Real; begin // Result:= "Linear KE" + "Angular KE" // only linear part so far Result := 1 / (2 * Mass) * VectorNorm(LinearMomentum); end; function TGLRigidBodyInertia.CalculatePE(): Real; begin // need to find potentials due to fields acting on body // may be easier to do via forcefieldemitters? Result := 0; end; function TGLRigidBodyInertia.CalcAngularOrientationDot(): TQuaternion; var q1: TQuaternion; begin q1.imagPart := VectorScale(RotationSpeed.AsAffineVector, 1 / 2); // v1; q1.RealPart := 0; Result := QuaternionMultiply(q1, AngularOrientation); end; function TGLRigidBodyInertia.CalcAngularVelocityDot(): TAffineVector; begin Result := fTorque; end; function TGLRigidBodyInertia.QuaternionToString(Quat: TQuaternion): String; begin Result := '' + FloatToSTr(Quat.imagPart.X) + ',' + FloatToSTr(Quat.imagPart.Y) + ',' + FloatToSTr(Quat.imagPart.Z) + '' + FloatToSTr(Quat.RealPart) + ''; end; (* function TGLRigidBodyInertia.Star(Vector:TAffineVector):TGLMatrix; begin Result.X.X:=0; Result[0][1]:=-Vector[2]; Result[0][2]:=Vector[1]; Result[0][3]:=0; Result[1][0]:=Vector[2]; Result[1][1]:=0; Result[1][2]:=-Vector[0]; Result[1][3]:=0; Result[2][0]:=Vector[1]; Result[2][1]:=Vector[0]; Result[2][2]:=0; Result[2][3]:=0; Result[3][0]:=0; Result[3][1]:=0; Result[3][2]:=0; Result[3][3]:=1; end; *) procedure TGLRigidBodyInertia.SetTorque(x, y, z: Real); begin fTorque.X := x; fTorque.Y := y; fTorque.Z := z; end; procedure TGLRigidBodyInertia.ApplyTorque(x, y, z: Real); begin fTorque.X := fTorque.X + x; fTorque.Y := fTorque.Y + y; fTorque.Z := fTorque.Z + z; end; (* procedure TGLRigidBodyInertia.ApplyImpulse(x,y,z:Real); begin // end; *) procedure TGLRigidBodyInertia.RemoveForces(); begin fForce := nullVector; fTorque := nullVector; end; procedure TGLRigidBodyInertia.ApplyForce(pos, Force: TVector3f); var abspos: TAffineVector; begin abspos := VectorTransform(pos, R); fTorque := VectorAdd(fTorque, VectorCrossProduct(abspos, Force)); fForce := VectorAdd(fForce, Force); end; procedure TGLRigidBodyInertia.ApplyLocalForce(pos, Force: TVector3f); var abspos: TAffineVector; absForce: TAffineVector; begin abspos := VectorTransform(pos, R); absForce := VectorTransform(Force, R); fTorque := VectorAdd(fTorque, VectorCrossProduct(abspos, absForce)); fForce := VectorAdd(fForce, absForce); end; procedure TGLRigidBodyInertia.ApplyLocalImpulse(xpos, ypos, zpos, x, y, z: Real); begin // end; procedure TGLRigidBodyInertia.SetUpStartingState(); begin // inherited SetUpStartingState(); fBodyInertiaTensor.X.X := InertiaTensor.fm11; fBodyInertiaTensor.X.Y := InertiaTensor.fm12; fBodyInertiaTensor.X.Z := InertiaTensor.fm13; fBodyInertiaTensor.Y.X := InertiaTensor.fm21; fBodyInertiaTensor.Y.Y := InertiaTensor.fm22; fBodyInertiaTensor.Y.Z := InertiaTensor.fm23; fBodyInertiaTensor.Z.X := InertiaTensor.fm31; fBodyInertiaTensor.Z.Y := InertiaTensor.fm32; fBodyInertiaTensor.Z.Z := InertiaTensor.fm33; fBodyInverseInertiaTensor := fBodyInertiaTensor; InvertMatrix(fBodyInverseInertiaTensor); // Write (* Messagedlg('setting BodyIit: '+ Format('%f,%f,%f,%f,%f,%f,%f,%f,%f', [fBodyInverseInertiaTensor[0][0],fBodyInverseInertiaTensor[0][1],fBodyInverseInertiaTensor[0][2], fBodyInverseInertiaTensor[1][0],fBodyInverseInertiaTensor[1][1],fBodyInverseInertiaTensor[1][2], fBodyInverseInertiaTensor[2][0],fBodyInverseInertiaTensor[2][1], fBodyInverseInertiaTensor[2][2]]),mtinformation,[mbok],0); *) AngularOrientation := IdentityQuaternion; AngularMomentum := VectorTransform(RotationSpeed.AsAffineVector, fBodyInertiaTensor); end; procedure TGLRigidBodyInertia.CalcAuxiliary(); var IRt: TAffineMAtrix; Rt: TAffineMAtrix; Scale: TAffineVector; RMatrix: TGLMatrix; begin // TODO: sort this out fBodyInverseInertiaTensor := IdentityMatrix; // compute auxiliary variables R := QuaternionToAffineMatrix(AngularOrientation); Rt := R; TransposeMatrix(Rt); IRt := MatrixMultiply(fBodyInverseInertiaTensor, Rt); InverseInertiaTensor := MatrixMultiply(R, IRt); RotationSpeed.AsAffineVector := VectorTransform(AngularMomentum, InverseInertiaTensor); TranslationSpeed.AsAffineVector := VectorScale(LinearMomentum, 1 / Mass); Scale := OwnerBaseSceneObject.Scale.AsAffineVector; OwnerBaseSceneObject.BeginUpdate; SetMatrix(RMatrix, R); OwnerBaseSceneObject.SetMatrix(RMatrix); // OwnerBaseSceneObject.Matrix:=QuaternionToMatrix(AngularOrientation); OwnerBaseSceneObject.Scale.AsAffineVector := Scale; OwnerBaseSceneObject.position.x := LinearPosition.X; // position OwnerBaseSceneObject.position.y := LinearPosition.Y; OwnerBaseSceneObject.position.z := LinearPosition.Z; OwnerBaseSceneObject.EndUpdate; end; procedure TGLRigidBodyInertia.StateToArray(var StateArray: TStateArray; StatePos: Integer); begin // with State do begin // copy Linear Position StateArray[StatePos] := LinearPosition.X; StateArray[StatePos + 1] := LinearPosition.Y; StateArray[StatePos + 2] := LinearPosition.Z; // copy Linear Momentum StateArray[StatePos + 3] := LinearMomentum.X; StateArray[StatePos + 4] := LinearMomentum.Y; StateArray[StatePos + 5] := LinearMomentum.Z; // copy Angular Orientation StateArray[StatePos + 6] := AngularOrientation.imagPart.X; StateArray[StatePos + 7] := AngularOrientation.imagPart.Y; StateArray[StatePos + 8] := AngularOrientation.imagPart.Z; StateArray[StatePos + 9] := AngularOrientation.RealPart; // copy Angular Momentum StateArray[StatePos + 10] := AngularMomentum.X; StateArray[StatePos + 11] := AngularMomentum.Y; StateArray[StatePos + 12] := AngularMomentum.Z; end; end; procedure TGLRigidBodyInertia.ArrayToState( { var } StateArray: TStateArray; StatePos: Integer); begin // restore Linear Position LinearPosition.X := StateArray[StatePos]; LinearPosition.Y := StateArray[StatePos + 1]; LinearPosition.Z := StateArray[StatePos + 2]; // restore Linear Momentum LinearMomentum.X := StateArray[StatePos + 3]; LinearMomentum.Y := StateArray[StatePos + 4]; LinearMomentum.Z := StateArray[StatePos + 5]; // restore Angular Orientation AngularOrientation.imagPart.X := StateArray[StatePos + 6]; AngularOrientation.imagPart.Y := StateArray[StatePos + 7]; AngularOrientation.imagPart.Z := StateArray[StatePos + 8]; AngularOrientation.RealPart := StateArray[StatePos + 9]; // restore Angular Momentum AngularMomentum.X := StateArray[StatePos + 10]; AngularMomentum.Y := StateArray[StatePos + 11]; AngularMomentum.Z := StateArray[StatePos + 12]; end; procedure TGLRigidBodyInertia.SetLinearDamping(const val: TGLDamping); begin // FLinearDamping.Assign(val); end; procedure TGLRigidBodyInertia.SetAngularDamping(const val: TGLDamping); begin // FAngularDamping.Assign(val); end; constructor TGLRigidBodyInertia.Create(aOwner: TXCollection); begin inherited Create(aOwner); Mass := 1; fDensity := 1; StateSize := 13; fInertiaTensor := TGLInertiaTensor.Create(Self); fRotationSpeed := TGLCoordinates.CreateInitialized(Self, VectorMake(0, 0, 0)); // LinearPosition:=OwnerBaseSceneObject.Position.AsAffineVector; AngularOrientation := IdentityQuaternion; // fromAngleAxis(0,XVector); fTorque := nullVector; fForce := nullVector; // DampingEnabled:=False; // FTranslationDamping:=TGLDamping.Create(Self); FRotationDamping := TGLDamping.Create(Self); // RotationDamping:=TGLDamping.Create(Self); R := IdentityMatrix; InverseInertiaTensor := IdentityMatrix; // CalcAuxiliary(); // SetDESolver(ssEuler); end; //--------------------------------------------------------------------------- destructor TGLRigidBodyInertia.Destroy; begin // FLinearDamping.Free; // FAngularDamping.Free; fInertiaTensor.Free(); fRotationSpeed.Free(); FRotationDamping.Free; inherited Destroy; end; procedure TGLRigidBodyInertia.Assign(Source: TPersistent); begin if Source.ClassType = Self.ClassType then begin // FRigidBody.Assign(TGLRigidBodyInertia(Source)); Mass := TGLRigidBodyInertia(Source).Mass; fDensity := TGLRigidBodyInertia(Source).fDensity; fBodyInertiaTensor := TGLRigidBodyInertia(Source).fBodyInertiaTensor; fBodyInverseInertiaTensor := TGLRigidBodyInertia(Source) .fBodyInverseInertiaTensor; InertiaTensor.Assign(TGLRigidBodyInertia(Source).InertiaTensor); LinearPosition := TGLRigidBodyInertia(Source).LinearPosition; AngularOrientation := TGLRigidBodyInertia(Source).AngularOrientation; LinearMomentum := TGLRigidBodyInertia(Source).LinearMomentum; AngularMomentum := TGLRigidBodyInertia(Source).AngularMomentum; // TranslationSpeed.AsAffineVector:=TGLRigidBodyInertia(Source).TranslationSpeed.AsAffineVector; RotationSpeed.Assign(TGLRigidBodyInertia(Source).RotationSpeed); // fForce:=TGLRigidBodyInertia(Source).fForce; fTorque := TGLRigidBodyInertia(Source).fTorque; // fInverseInertiaTensor:=TGLRigidBodyInertia(Source).fInverseInertiaTensor; // RigidBody.fTorque:=TGLRigidBodyInertia(Source).fTorque; // RigidBody.fForce:=TGLRigidBodyInertia(Source).fForce; FRotationDamping.Assign(TGLRigidBodyInertia(Source).FRotationDamping); // DampingEnabled:=TGLRigidBodyInertia(Source).DampingEnabled; // FTranslationDamping.Assign(TGLRigidBodyInertia(Source).LinearDamping); // FRotationDamping.Assign(TGLRigidBodyInertia(Source).AngularDamping); end; inherited Assign(Source); end; class function TGLRigidBodyInertia.FriendlyName: String; begin Result := 'Rigid Body Inertia'; end; class function TGLRigidBodyInertia.FriendlyDescription: String; begin Result := 'An inertia model for rigid bodies'; end; class function TGLRigidBodyInertia.UniqueItem: Boolean; begin Result := True; end; // ************************************************************************** // ***************** DoProgress ************************************ // ************************************************************************** (* procedure TGLRigidBodyInertia.DoProgress(const progressTime : TProgressTimes); var TempScale:TaffineVector; UndampedLinearMomentum,DampedLinearMomentum:Real; UnDampedAngularMomentum,DampedAngularMomentum:Real; i:integer; begin // Write('Calculating next state...'); with OwnerBaseSceneObject do with progressTime do begin if (DampingEnabled=true) then begin UndampedLinearMomentum:=VectorLength(LinearMomentum); DampedLinearMomentum:=TranslationDamping.Calculate(UndampedLinearMomentum,deltaTime); { if Stage.VectorGeometry.vSIMD=1 then // RigidBody.LinearMomentum:=VectorScale(VectorNormalize(RigidBody.LinearMomentum),DampedLinearMomentum) else } begin if Length(LinearMomentum)<>0 then LinearMomentum:=VectorScale(VectorNormalize(LinearMomentum),DampedLinearMomentum) else LinearMomentum:=NullVector; //line not required end; UndampedAngularMomentum:=VectorLength(AngularMomentum); DampedAngularMomentum:=RotationDamping.Calculate(UndampedAngularMomentum,deltaTime); AngularMomentum:=VectorScale(VectorNormalize(AngularMomentum),DampedAngularMomentum); // ApplyForce(VectorScale(RigidBody.LinearVelocity,-0.5)); //Either apply resistive force & torque // ApplyTorque(VectorLength(RigidBody.AngularVelocity)); //or use TGLDamping end; // Euler(RigidBody,deltaTime); // RungeKutta4(DeltaTime); //DESolver(RigidBody,DeltaTime); //update OwnerBaseSceneObject TempScale:=Scale.AsAffineVector; Matrix:=QuaternionToMatrix(AngularOrientation); position.AsAffineVector:=LinearPosition; Scale.AsAffineVector:=TempScale; //calc auxiliary variables for next iteration CalcAuxiliary(); end; end; *) procedure TGLRigidBodyInertia.WriteToFiler(writer: TWriter); begin inherited WriteToFiler(writer); with writer do begin // WriteInteger(0); // Archive Version 0 // FRigidBody.WriteToFiler(writer); // WriteFloat(fMass); WriteFloat(fDensity); Write(fBodyInertiaTensor, SizeOf(fBodyInertiaTensor)); Write(fBodyInverseInertiaTensor, SizeOf(fBodyInverseInertiaTensor)); fInertiaTensor.WriteToFiler(writer); Write(AngularOrientation, SizeOf(AngularOrientation)); Write(AngularMomentum, SizeOf(AngularMomentum)); // Write(LinearVelocity,SizeOf(LinearVelocity)); RotationSpeed.WriteToFiler(writer); // Write(AngularVelocity,SizeOf(AngularVelocity)); Write(fTorque, SizeOf(fTorque)); // Write(fForce,SizeOf(fForce)); FRotationDamping.WriteToFiler(writer); // WriteInteger(Integer(FDESolverType)); // WriteBoolean(FDampingEnabled); // FLinearDamping.WriteToFiler(writer); // FAngularDamping.WriteToFiler(writer); end; end; procedure TGLRigidBodyInertia.ReadFromFiler(reader: TReader); begin inherited ReadFromFiler(reader); with reader do begin // ReadInteger; // ignore archiveVersion // FRigidBody.ReadFromFiler(Reader); // fMass:=ReadFloat; fDensity := ReadFloat; Read(fBodyInertiaTensor, SizeOf(fBodyInertiaTensor)); Read(fBodyInverseInertiaTensor, SizeOf(fBodyInverseInertiaTensor)); InertiaTensor.ReadFromFiler(reader); Read(AngularOrientation, SizeOf(AngularOrientation)); Read(AngularMomentum, SizeOf(AngularMomentum)); // Read(LinearVelocity,SizeOf(LinearVelocity)); RotationSpeed.ReadFromFiler(reader); // Read(AngularVelocity,SizeOf(AngularVelocity)); Read(fTorque, SizeOf(fTorque)); // Read(fForce, SizeOf(fForce)); FRotationDamping.ReadFromFiler(reader); // SetDESolver(TDESolverType(ReadInteger)); // FDampingEnabled:=ReadBoolean; // FLinearDamping.ReadFromFiler(reader); // FAngularDamping.ReadFromFiler(reader); end; // SetDESolver(fDESolverType); // CalcAuxiliary(); SetUpStartingState(); end; function GetOrCreateRigidBodyInertia(behaviours: TGLBehaviours) : TGLRigidBodyInertia; var i: Integer; begin i := behaviours.IndexOfClass(TGLRigidBodyInertia); if i >= 0 then Result := TGLRigidBodyInertia(behaviours[i]) else Result := TGLRigidBodyInertia.Create(behaviours); end; function GetOrCreateRigidBodyInertia(obj: TGLBaseSceneObject) : TGLRigidBodyInertia; begin Result := GetOrCreateRigidBodyInertia(obj.behaviours); end; // ------------------------------------------------------------------ initialization // ------------------------------------------------------------------ // class registrations RegisterXCollectionItemClass(TGLParticleInertia); RegisterXCollectionItemClass(TGLRigidBodyInertia); end.