diff options
Diffstat (limited to 'OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs')
-rw-r--r-- | OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs | 221 |
1 files changed, 125 insertions, 96 deletions
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs index 3f7b5e1..3b77e49 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs | |||
@@ -66,7 +66,6 @@ public sealed class BSCharacter : BSPhysObject | |||
66 | private float _buoyancy; | 66 | private float _buoyancy; |
67 | 67 | ||
68 | // The friction and velocity of the avatar is modified depending on whether walking or not. | 68 | // The friction and velocity of the avatar is modified depending on whether walking or not. |
69 | private OMV.Vector3 _appliedVelocity; // the last velocity applied to the avatar | ||
70 | private float _currentFriction; // the friction currently being used (changed by setVelocity). | 69 | private float _currentFriction; // the friction currently being used (changed by setVelocity). |
71 | 70 | ||
72 | private BSVMotor _velocityMotor; | 71 | private BSVMotor _velocityMotor; |
@@ -85,37 +84,27 @@ public sealed class BSCharacter : BSPhysObject | |||
85 | _physicsActorType = (int)ActorTypes.Agent; | 84 | _physicsActorType = (int)ActorTypes.Agent; |
86 | _position = pos; | 85 | _position = pos; |
87 | 86 | ||
88 | // Old versions of ScenePresence passed only the height. If width and/or depth are zero, | ||
89 | // replace with the default values. | ||
90 | _size = size; | ||
91 | if (_size.X == 0f) _size.X = BSParam.AvatarCapsuleDepth; | ||
92 | if (_size.Y == 0f) _size.Y = BSParam.AvatarCapsuleWidth; | ||
93 | |||
94 | // A motor to control the acceleration and deceleration of the avatar movement. | ||
95 | // _velocityMotor = new BSVMotor("BSCharacter.Velocity", 3f, 5f, BSMotor.InfiniteVector, 1f); | ||
96 | // _velocityMotor = new BSPIDVMotor("BSCharacter.Velocity", 3f, 5f, BSMotor.InfiniteVector, 1f); | ||
97 | // Infinite decay and timescale values so motor only changes current to target values. | ||
98 | _velocityMotor = new BSVMotor("BSCharacter.Velocity", | ||
99 | 0.2f, // time scale | ||
100 | BSMotor.Infinite, // decay time scale | ||
101 | BSMotor.InfiniteVector, // friction timescale | ||
102 | 1f // efficiency | ||
103 | ); | ||
104 | _velocityMotor.PhysicsScene = PhysicsScene; // DEBUG DEBUG so motor will output detail log messages. | ||
105 | |||
106 | _flying = isFlying; | 87 | _flying = isFlying; |
107 | _orientation = OMV.Quaternion.Identity; | 88 | _orientation = OMV.Quaternion.Identity; |
108 | _velocity = OMV.Vector3.Zero; | 89 | _velocity = OMV.Vector3.Zero; |
109 | _appliedVelocity = OMV.Vector3.Zero; | ||
110 | _buoyancy = ComputeBuoyancyFromFlying(isFlying); | 90 | _buoyancy = ComputeBuoyancyFromFlying(isFlying); |
111 | _currentFriction = BSParam.AvatarStandingFriction; | 91 | _currentFriction = BSParam.AvatarStandingFriction; |
112 | _avatarDensity = BSParam.AvatarDensity; | 92 | _avatarDensity = BSParam.AvatarDensity; |
113 | 93 | ||
114 | // The dimensions of the avatar capsule are kept in the scale. | 94 | // Old versions of ScenePresence passed only the height. If width and/or depth are zero, |
95 | // replace with the default values. | ||
96 | _size = size; | ||
97 | if (_size.X == 0f) _size.X = BSParam.AvatarCapsuleDepth; | ||
98 | if (_size.Y == 0f) _size.Y = BSParam.AvatarCapsuleWidth; | ||
99 | |||
100 | // The dimensions of the physical capsule are kept in the scale. | ||
115 | // Physics creates a unit capsule which is scaled by the physics engine. | 101 | // Physics creates a unit capsule which is scaled by the physics engine. |
116 | ComputeAvatarScale(_size); | 102 | Scale = ComputeAvatarScale(_size); |
117 | // set _avatarVolume and _mass based on capsule size, _density and Scale | 103 | // set _avatarVolume and _mass based on capsule size, _density and Scale |
118 | ComputeAvatarVolumeAndMass(); | 104 | ComputeAvatarVolumeAndMass(); |
105 | |||
106 | SetupMovementMotor(); | ||
107 | |||
119 | DetailLog("{0},BSCharacter.create,call,size={1},scale={2},density={3},volume={4},mass={5}", | 108 | DetailLog("{0},BSCharacter.create,call,size={1},scale={2},density={3},volume={4},mass={5}", |
120 | LocalID, _size, Scale, _avatarDensity, _avatarVolume, RawMass); | 109 | LocalID, _size, Scale, _avatarDensity, _avatarVolume, RawMass); |
121 | 110 | ||
@@ -152,13 +141,12 @@ public sealed class BSCharacter : BSPhysObject | |||
152 | 141 | ||
153 | ZeroMotion(true); | 142 | ZeroMotion(true); |
154 | ForcePosition = _position; | 143 | ForcePosition = _position; |
144 | |||
155 | // Set the velocity and compute the proper friction | 145 | // Set the velocity and compute the proper friction |
156 | ForceVelocity = _velocity; | ||
157 | // Setting the current and target in the motor will cause it to start computing any deceleration. | ||
158 | _velocityMotor.Reset(); | 146 | _velocityMotor.Reset(); |
159 | _velocityMotor.SetCurrent(_velocity); | ||
160 | _velocityMotor.SetTarget(_velocity); | 147 | _velocityMotor.SetTarget(_velocity); |
161 | _velocityMotor.Enabled = false; | 148 | _velocityMotor.SetCurrent(_velocity); |
149 | ForceVelocity = _velocity; | ||
162 | 150 | ||
163 | // This will enable or disable the flying buoyancy of the avatar. | 151 | // This will enable or disable the flying buoyancy of the avatar. |
164 | // Needs to be reset especially when an avatar is recreated after crossing a region boundry. | 152 | // Needs to be reset especially when an avatar is recreated after crossing a region boundry. |
@@ -192,6 +180,63 @@ public sealed class BSCharacter : BSPhysObject | |||
192 | PhysBody.ApplyCollisionMask(); | 180 | PhysBody.ApplyCollisionMask(); |
193 | } | 181 | } |
194 | 182 | ||
183 | // The avatar's movement is controlled by this motor that speeds up and slows down | ||
184 | // the avatar seeking to reach the motor's target speed. | ||
185 | // This motor runs as a prestep action for the avatar so it will keep the avatar | ||
186 | // standing as well as moving. Destruction of the avatar will destroy the pre-step action. | ||
187 | private void SetupMovementMotor() | ||
188 | { | ||
189 | |||
190 | // Someday, use a PID motor for asymmetric speed up and slow down | ||
191 | // _velocityMotor = new BSPIDVMotor("BSCharacter.Velocity", 3f, 5f, BSMotor.InfiniteVector, 1f); | ||
192 | |||
193 | // Infinite decay and timescale values so motor only changes current to target values. | ||
194 | _velocityMotor = new BSVMotor("BSCharacter.Velocity", | ||
195 | 0.2f, // time scale | ||
196 | BSMotor.Infinite, // decay time scale | ||
197 | BSMotor.InfiniteVector, // friction timescale | ||
198 | 1f // efficiency | ||
199 | ); | ||
200 | // _velocityMotor.PhysicsScene = PhysicsScene; // DEBUG DEBUG so motor will output detail log messages. | ||
201 | |||
202 | RegisterPreStepAction("BSCharactor.Movement", LocalID, delegate(float timeStep) | ||
203 | { | ||
204 | // TODO: Decide if the step parameters should be changed depending on the avatar's | ||
205 | // state (flying, colliding, ...). There is code in ODE to do this. | ||
206 | |||
207 | OMV.Vector3 stepVelocity = _velocityMotor.Step(timeStep); | ||
208 | |||
209 | // If falling, we keep the world's downward vector no matter what the other axis specify. | ||
210 | if (!Flying && !IsColliding) | ||
211 | { | ||
212 | stepVelocity.Z = _velocity.Z; | ||
213 | DetailLog("{0},BSCharacter.MoveMotor,taint,overrideStepZWithWorldZ,stepVel={1}", | ||
214 | LocalID, stepVelocity); | ||
215 | } | ||
216 | |||
217 | // 'stepVelocity' is now the speed we'd like the avatar to move in. Turn that into an instantanous force. | ||
218 | OMV.Vector3 moveForce = (stepVelocity - _velocity) * Mass / PhysicsScene.LastTimeStep; | ||
219 | |||
220 | /* | ||
221 | // If moveForce is very small, zero things so we don't keep sending microscopic updates to the user | ||
222 | float moveForceMagnitudeSquared = moveForce.LengthSquared(); | ||
223 | if (moveForceMagnitudeSquared < 0.0001) | ||
224 | { | ||
225 | DetailLog("{0},BSCharacter.MoveMotor,zeroMovement,stepVel={1},vel={2},mass={3},magSq={4},moveForce={5}", | ||
226 | LocalID, stepVelocity, _velocity, Mass, moveForceMagnitudeSquared, moveForce); | ||
227 | ForceVelocity = OMV.Vector3.Zero; | ||
228 | } | ||
229 | else | ||
230 | { | ||
231 | AddForce(moveForce, false, true); | ||
232 | } | ||
233 | */ | ||
234 | DetailLog("{0},BSCharacter.MoveMotor,move,stepVel={1},vel={2},mass={3},moveForce={4}", | ||
235 | LocalID, stepVelocity, _velocity, Mass, moveForce); | ||
236 | AddForce(moveForce, false, true); | ||
237 | }); | ||
238 | } | ||
239 | |||
195 | public override void RequestPhysicsterseUpdate() | 240 | public override void RequestPhysicsterseUpdate() |
196 | { | 241 | { |
197 | base.RequestPhysicsterseUpdate(); | 242 | base.RequestPhysicsterseUpdate(); |
@@ -207,14 +252,13 @@ public sealed class BSCharacter : BSPhysObject | |||
207 | } | 252 | } |
208 | 253 | ||
209 | set { | 254 | set { |
210 | // When an avatar's size is set, only the height is changed. | ||
211 | _size = value; | 255 | _size = value; |
212 | // Old versions of ScenePresence passed only the height. If width and/or depth are zero, | 256 | // Old versions of ScenePresence passed only the height. If width and/or depth are zero, |
213 | // replace with the default values. | 257 | // replace with the default values. |
214 | if (_size.X == 0f) _size.X = BSParam.AvatarCapsuleDepth; | 258 | if (_size.X == 0f) _size.X = BSParam.AvatarCapsuleDepth; |
215 | if (_size.Y == 0f) _size.Y = BSParam.AvatarCapsuleWidth; | 259 | if (_size.Y == 0f) _size.Y = BSParam.AvatarCapsuleWidth; |
216 | 260 | ||
217 | ComputeAvatarScale(_size); | 261 | Scale = ComputeAvatarScale(_size); |
218 | ComputeAvatarVolumeAndMass(); | 262 | ComputeAvatarVolumeAndMass(); |
219 | DetailLog("{0},BSCharacter.setSize,call,size={1},scale={2},density={3},volume={4},mass={5}", | 263 | DetailLog("{0},BSCharacter.setSize,call,size={1},scale={2},density={3},volume={4},mass={5}", |
220 | LocalID, _size, Scale, _avatarDensity, _avatarVolume, RawMass); | 264 | LocalID, _size, Scale, _avatarDensity, _avatarVolume, RawMass); |
@@ -433,15 +477,15 @@ public sealed class BSCharacter : BSPhysObject | |||
433 | { | 477 | { |
434 | DetailLog("{0},BSCharacter.setTargetVelocity,call,vel={1}", LocalID, value); | 478 | DetailLog("{0},BSCharacter.setTargetVelocity,call,vel={1}", LocalID, value); |
435 | OMV.Vector3 targetVel = value; | 479 | OMV.Vector3 targetVel = value; |
480 | if (_setAlwaysRun) | ||
481 | targetVel *= BSParam.AvatarAlwaysRunFactor; | ||
482 | |||
436 | PhysicsScene.TaintedObject("BSCharacter.setTargetVelocity", delegate() | 483 | PhysicsScene.TaintedObject("BSCharacter.setTargetVelocity", delegate() |
437 | { | 484 | { |
438 | _velocityMotor.Reset(); | 485 | _velocityMotor.Reset(); |
439 | _velocityMotor.SetTarget(targetVel); | 486 | _velocityMotor.SetTarget(targetVel); |
440 | _velocityMotor.SetCurrent(_velocity); | 487 | _velocityMotor.SetCurrent(_velocity); |
441 | _velocityMotor.Enabled = true; | 488 | _velocityMotor.Enabled = true; |
442 | |||
443 | // Make sure a property update happens next step so the motor gets incorporated. | ||
444 | BulletSimAPI.PushUpdate2(PhysBody.ptr); | ||
445 | }); | 489 | }); |
446 | } | 490 | } |
447 | } | 491 | } |
@@ -490,8 +534,6 @@ public sealed class BSCharacter : BSPhysObject | |||
490 | BulletSimAPI.SetFriction2(PhysBody.ptr, _currentFriction); | 534 | BulletSimAPI.SetFriction2(PhysBody.ptr, _currentFriction); |
491 | } | 535 | } |
492 | } | 536 | } |
493 | // Remember the set velocity so we can suppress the reduction by friction, ... | ||
494 | _appliedVelocity = value; | ||
495 | 537 | ||
496 | BulletSimAPI.SetLinearVelocity2(PhysBody.ptr, _velocity); | 538 | BulletSimAPI.SetLinearVelocity2(PhysBody.ptr, _velocity); |
497 | BulletSimAPI.Activate2(PhysBody.ptr, true); | 539 | BulletSimAPI.Activate2(PhysBody.ptr, true); |
@@ -519,16 +561,16 @@ public sealed class BSCharacter : BSPhysObject | |||
519 | public override OMV.Quaternion Orientation { | 561 | public override OMV.Quaternion Orientation { |
520 | get { return _orientation; } | 562 | get { return _orientation; } |
521 | set { | 563 | set { |
522 | _orientation = value; | 564 | // Orientation is set zillions of times when an avatar is walking. It's like |
523 | // m_log.DebugFormat("{0}: set orientation to {1}", LogHeader, _orientation); | 565 | // the viewer doesn't trust us. |
524 | PhysicsScene.TaintedObject("BSCharacter.setOrientation", delegate() | 566 | if (_orientation != value) |
525 | { | 567 | { |
526 | if (PhysBody.HasPhysicalBody) | 568 | _orientation = value; |
569 | PhysicsScene.TaintedObject("BSCharacter.setOrientation", delegate() | ||
527 | { | 570 | { |
528 | // _position = BulletSimAPI.GetPosition2(BSBody.ptr); | 571 | ForceOrientation = _orientation; |
529 | BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation); | 572 | }); |
530 | } | 573 | } |
531 | }); | ||
532 | } | 574 | } |
533 | } | 575 | } |
534 | // Go directly to Bullet to get/set the value. | 576 | // Go directly to Bullet to get/set the value. |
@@ -542,7 +584,11 @@ public sealed class BSCharacter : BSPhysObject | |||
542 | set | 584 | set |
543 | { | 585 | { |
544 | _orientation = value; | 586 | _orientation = value; |
545 | BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation); | 587 | if (PhysBody.HasPhysicalBody) |
588 | { | ||
589 | // _position = BulletSimAPI.GetPosition2(BSBody.ptr); | ||
590 | BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation); | ||
591 | } | ||
546 | } | 592 | } |
547 | } | 593 | } |
548 | public override int PhysicsActorType { | 594 | public override int PhysicsActorType { |
@@ -668,7 +714,13 @@ public sealed class BSCharacter : BSPhysObject | |||
668 | public override float APIDStrength { set { return; } } | 714 | public override float APIDStrength { set { return; } } |
669 | public override float APIDDamping { set { return; } } | 715 | public override float APIDDamping { set { return; } } |
670 | 716 | ||
671 | public override void AddForce(OMV.Vector3 force, bool pushforce) { | 717 | public override void AddForce(OMV.Vector3 force, bool pushforce) |
718 | { | ||
719 | // Since this force is being applied in only one step, make this a force per second. | ||
720 | OMV.Vector3 addForce = force / PhysicsScene.LastTimeStep; | ||
721 | AddForce(addForce, pushforce, false); | ||
722 | } | ||
723 | private void AddForce(OMV.Vector3 force, bool pushforce, bool inTaintTime) { | ||
672 | if (force.IsFinite()) | 724 | if (force.IsFinite()) |
673 | { | 725 | { |
674 | float magnitude = force.Length(); | 726 | float magnitude = force.Length(); |
@@ -678,10 +730,10 @@ public sealed class BSCharacter : BSPhysObject | |||
678 | force = force / magnitude * BSParam.MaxAddForceMagnitude; | 730 | force = force / magnitude * BSParam.MaxAddForceMagnitude; |
679 | } | 731 | } |
680 | 732 | ||
681 | OMV.Vector3 addForce = force / PhysicsScene.LastTimeStep; | 733 | OMV.Vector3 addForce = force; |
682 | DetailLog("{0},BSCharacter.addForce,call,force={1}", LocalID, addForce); | 734 | // DetailLog("{0},BSCharacter.addForce,call,force={1}", LocalID, addForce); |
683 | 735 | ||
684 | PhysicsScene.TaintedObject("BSCharacter.AddForce", delegate() | 736 | PhysicsScene.TaintedObject(inTaintTime, "BSCharacter.AddForce", delegate() |
685 | { | 737 | { |
686 | // Bullet adds this central force to the total force for this tick | 738 | // Bullet adds this central force to the total force for this tick |
687 | DetailLog("{0},BSCharacter.addForce,taint,force={1}", LocalID, addForce); | 739 | DetailLog("{0},BSCharacter.addForce,taint,force={1}", LocalID, addForce); |
@@ -703,21 +755,31 @@ public sealed class BSCharacter : BSPhysObject | |||
703 | public override void SetMomentum(OMV.Vector3 momentum) { | 755 | public override void SetMomentum(OMV.Vector3 momentum) { |
704 | } | 756 | } |
705 | 757 | ||
706 | private void ComputeAvatarScale(OMV.Vector3 size) | 758 | private OMV.Vector3 ComputeAvatarScale(OMV.Vector3 size) |
707 | { | 759 | { |
708 | OMV.Vector3 newScale = size; | 760 | OMV.Vector3 newScale; |
709 | // newScale.X = PhysicsScene.Params.avatarCapsuleWidth; | 761 | |
710 | // newScale.Y = PhysicsScene.Params.avatarCapsuleDepth; | 762 | // Bullet's capsule total height is the "passed height + radius * 2"; |
711 | 763 | // The base capsule is 1 diameter and 2 height (passed radius=0.5, passed height = 1) | |
712 | // From the total height, remove the capsule half spheres that are at each end | 764 | // The number we pass in for 'scaling' is the multiplier to get that base |
713 | // The 1.15f came from ODE. Not sure what this factors in. | 765 | // shape to be the size desired. |
714 | // newScale.Z = (size.Z * 1.15f) - (newScale.X + newScale.Y); | 766 | // So, when creating the scale for the avatar height, we take the passed height |
767 | // (size.Z) and remove the caps. | ||
768 | // Another oddity of the Bullet capsule implementation is that it presumes the Y | ||
769 | // dimension is the radius of the capsule. Even though some of the code allows | ||
770 | // for a asymmetrical capsule, other parts of the code presume it is cylindrical. | ||
771 | |||
772 | // Scale is multiplier of radius with one of "0.5" | ||
773 | newScale.X = size.X / 2f; | ||
774 | newScale.Y = size.Y / 2f; | ||
715 | 775 | ||
716 | // The total scale height is the central cylindar plus the caps on the two ends. | 776 | // The total scale height is the central cylindar plus the caps on the two ends. |
717 | newScale.Z = size.Z + (Math.Min(size.X, size.Y) * 2f); | 777 | newScale.Z = (size.Z + (Math.Min(size.X, size.Y) * 2)) / 2f; |
778 | // If smaller than the endcaps, just fake like we're almost that small | ||
779 | if (newScale.Z < 0) | ||
780 | newScale.Z = 0.1f; | ||
718 | 781 | ||
719 | // Convert diameters to radii and height to half height -- the way Bullet expects it. | 782 | return newScale; |
720 | Scale = newScale / 2f; | ||
721 | } | 783 | } |
722 | 784 | ||
723 | // set _avatarVolume and _mass based on capsule size, _density and Scale | 785 | // set _avatarVolume and _mass based on capsule size, _density and Scale |
@@ -725,14 +787,14 @@ public sealed class BSCharacter : BSPhysObject | |||
725 | { | 787 | { |
726 | _avatarVolume = (float)( | 788 | _avatarVolume = (float)( |
727 | Math.PI | 789 | Math.PI |
728 | * Scale.X | 790 | * Size.X / 2f |
729 | * Scale.Y // the area of capsule cylinder | 791 | * Size.Y / 2f // the area of capsule cylinder |
730 | * Scale.Z // times height of capsule cylinder | 792 | * Size.Z // times height of capsule cylinder |
731 | + 1.33333333f | 793 | + 1.33333333f |
732 | * Math.PI | 794 | * Math.PI |
733 | * Scale.X | 795 | * Size.X / 2f |
734 | * Math.Min(Scale.X, Scale.Y) | 796 | * Math.Min(Size.X, Size.Y) / 2 |
735 | * Scale.Y // plus the volume of the capsule end caps | 797 | * Size.Y / 2f // plus the volume of the capsule end caps |
736 | ); | 798 | ); |
737 | _mass = _avatarDensity * _avatarVolume; | 799 | _mass = _avatarDensity * _avatarVolume; |
738 | } | 800 | } |
@@ -750,39 +812,6 @@ public sealed class BSCharacter : BSPhysObject | |||
750 | // Do some sanity checking for the avatar. Make sure it's above ground and inbounds. | 812 | // Do some sanity checking for the avatar. Make sure it's above ground and inbounds. |
751 | PositionSanityCheck(true); | 813 | PositionSanityCheck(true); |
752 | 814 | ||
753 | if (_velocityMotor.Enabled) | ||
754 | { | ||
755 | // TODO: Decide if the step parameters should be changed depending on the avatar's | ||
756 | // state (flying, colliding, ...). | ||
757 | |||
758 | OMV.Vector3 stepVelocity = _velocityMotor.Step(PhysicsScene.LastTimeStep); | ||
759 | |||
760 | // Check for cases to turn off the motor. | ||
761 | if ( | ||
762 | // If the walking motor is all done, turn it off | ||
763 | (_velocityMotor.TargetValue.ApproxEquals(OMV.Vector3.Zero, 0.01f) && _velocityMotor.ErrorIsZero) ) | ||
764 | { | ||
765 | ZeroMotion(true); | ||
766 | stepVelocity = OMV.Vector3.Zero; | ||
767 | _velocityMotor.Enabled = false; | ||
768 | DetailLog("{0},BSCharacter.UpdateProperties,taint,disableVelocityMotor,m={1}", LocalID, _velocityMotor); | ||
769 | } | ||
770 | else | ||
771 | { | ||
772 | // If the motor is not being turned off... | ||
773 | // If falling, we keep the world's downward vector no matter what the other axis specify. | ||
774 | if (!Flying && !IsColliding) | ||
775 | { | ||
776 | stepVelocity.Z = entprop.Velocity.Z; | ||
777 | DetailLog("{0},BSCharacter.UpdateProperties,taint,overrideStepZWithWorldZ,stepVel={1}", LocalID, stepVelocity); | ||
778 | } | ||
779 | } | ||
780 | |||
781 | _velocity = stepVelocity; | ||
782 | entprop.Velocity = _velocity; | ||
783 | BulletSimAPI.SetLinearVelocity2(PhysBody.ptr, _velocity); | ||
784 | } | ||
785 | |||
786 | // remember the current and last set values | 815 | // remember the current and last set values |
787 | LastEntityProperties = CurrentEntityProperties; | 816 | LastEntityProperties = CurrentEntityProperties; |
788 | CurrentEntityProperties = entprop; | 817 | CurrentEntityProperties = entprop; |