opensim – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 eva 1 /*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyrightD
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27 using System;
28 using System.Collections.Generic;
29 using System.Text;
30  
31 using OMV = OpenMetaverse;
32 using OpenSim.Framework;
33 using OpenSim.Region.Physics.Manager;
34  
35 namespace OpenSim.Region.Physics.BulletSPlugin
36 {
37 /*
38 * Class to wrap all objects.
39 * The rest of BulletSim doesn't need to keep checking for avatars or prims
40 * unless the difference is significant.
41 *
42 * Variables in the physicsl objects are in three forms:
43 * VariableName: used by the simulator and performs taint operations, etc
44 * RawVariableName: direct reference to the BulletSim storage for the variable value
45 * ForceVariableName: direct reference (store and fetch) to the value in the physics engine.
46 * The last one should only be referenced in taint-time.
47 */
48  
49 /*
50 * As of 20121221, the following are the call sequences (going down) for different script physical functions:
51 * llApplyImpulse llApplyRotImpulse llSetTorque llSetForce
52 * SOP.ApplyImpulse SOP.ApplyAngularImpulse SOP.SetAngularImpulse SOP.SetForce
53 * SOG.ApplyImpulse SOG.ApplyAngularImpulse SOG.SetAngularImpulse
54 * PA.AddForce PA.AddAngularForce PA.Torque = v PA.Force = v
55 * BS.ApplyCentralForce BS.ApplyTorque
56 */
57  
58 // Flags used to denote which properties updates when making UpdateProperties calls to linksets, etc.
59 public enum UpdatedProperties : uint
60 {
61 Position = 1 << 0,
62 Orientation = 1 << 1,
63 Velocity = 1 << 2,
64 Acceleration = 1 << 3,
65 RotationalVelocity = 1 << 4,
66 EntPropUpdates = Position | Orientation | Velocity | Acceleration | RotationalVelocity,
67 }
68 public abstract class BSPhysObject : PhysicsActor
69 {
70 protected BSPhysObject()
71 {
72 }
73 protected BSPhysObject(BSScene parentScene, uint localID, string name, string typeName)
74 {
75 IsInitialized = false;
76  
77 PhysScene = parentScene;
78 LocalID = localID;
79 PhysObjectName = name;
80 Name = name; // PhysicsActor also has the name of the object. Someday consolidate.
81 TypeName = typeName;
82  
83 // The collection of things that push me around
84 PhysicalActors = new BSActorCollection(PhysScene);
85  
86 // Initialize variables kept in base.
87 GravModifier = 1.0f;
88 Gravity = new OMV.Vector3(0f, 0f, BSParam.Gravity);
89 HoverActive = false;
90  
91 // We don't have any physical representation yet.
92 PhysBody = new BulletBody(localID);
93 PhysShape = new BSShapeNull();
94  
95 UserSetCenterOfMassDisplacement = null;
96  
97 PrimAssetState = PrimAssetCondition.Unknown;
98  
99 // Default material type. Also sets Friction, Restitution and Density.
100 SetMaterial((int)MaterialAttributes.Material.Wood);
101  
102 CollisionCollection = new CollisionEventUpdate();
103 CollisionsLastReported = CollisionCollection;
104 CollisionsLastTick = new CollisionEventUpdate();
105 CollisionsLastTickStep = -1;
106  
107 SubscribedEventsMs = 0;
108 // Crazy values that will never be true
109 CollidingStep = BSScene.NotASimulationStep;
110 CollidingGroundStep = BSScene.NotASimulationStep;
111 CollisionAccumulation = BSScene.NotASimulationStep;
112 ColliderIsMoving = false;
113 CollisionScore = 0;
114  
115 // All axis free.
116 LockedLinearAxis = LockedAxisFree;
117 LockedAngularAxis = LockedAxisFree;
118 }
119  
120 // Tell the object to clean up.
121 public virtual void Destroy()
122 {
123 PhysicalActors.Enable(false);
124 PhysScene.TaintedObject(LocalID, "BSPhysObject.Destroy", delegate()
125 {
126 PhysicalActors.Dispose();
127 });
128 }
129  
130 public BSScene PhysScene { get; protected set; }
131 // public override uint LocalID { get; set; } // Use the LocalID definition in PhysicsActor
132 public string PhysObjectName { get; protected set; }
133 public string TypeName { get; protected set; }
134  
135 // Set to 'true' when the object is completely initialized.
136 // This mostly prevents property updates and collisions until the object is completely here.
137 public bool IsInitialized { get; protected set; }
138  
139 // Return the object mass without calculating it or having side effects
140 public abstract float RawMass { get; }
141 // Set the raw mass but also update physical mass properties (inertia, ...)
142 // 'inWorld' true if the object has already been added to the dynamic world.
143 public abstract void UpdatePhysicalMassProperties(float mass, bool inWorld);
144  
145 // The gravity being applied to the object. A function of default grav, GravityModifier and Buoyancy.
146 public virtual OMV.Vector3 Gravity { get; set; }
147 // The last value calculated for the prim's inertia
148 public OMV.Vector3 Inertia { get; set; }
149  
150 // Reference to the physical body (btCollisionObject) of this object
151 public BulletBody PhysBody;
152 // Reference to the physical shape (btCollisionShape) of this object
153 public BSShape PhysShape;
154  
155 // The physical representation of the prim might require an asset fetch.
156 // The asset state is first 'Unknown' then 'Waiting' then either 'Failed' or 'Fetched'.
157 public enum PrimAssetCondition
158 {
159 Unknown, Waiting, FailedAssetFetch, FailedMeshing, Fetched
160 }
161 public PrimAssetCondition PrimAssetState { get; set; }
162  
163 // The objects base shape information. Null if not a prim type shape.
164 public PrimitiveBaseShape BaseShape { get; protected set; }
165  
166 // When the physical properties are updated, an EntityProperty holds the update values.
167 // Keep the current and last EntityProperties to enable computation of differences
168 // between the current update and the previous values.
169 public EntityProperties CurrentEntityProperties { get; set; }
170 public EntityProperties LastEntityProperties { get; set; }
171  
172 public virtual OMV.Vector3 Scale { get; set; }
173  
174 // It can be confusing for an actor to know if it should move or update an object
175 // depeneding on the setting of 'selected', 'physical, ...
176 // This flag is the true test -- if true, the object is being acted on in the physical world
177 public abstract bool IsPhysicallyActive { get; }
178  
179 // Detailed state of the object.
180 public abstract bool IsSolid { get; }
181 public abstract bool IsStatic { get; }
182 public abstract bool IsSelected { get; }
183 public abstract bool IsVolumeDetect { get; }
184  
185 // Materialness
186 public MaterialAttributes.Material Material { get; private set; }
187 public override void SetMaterial(int material)
188 {
189 Material = (MaterialAttributes.Material)material;
190  
191 // Setting the material sets the material attributes also.
192 // TODO: decide if this is necessary -- the simulator does this.
193 MaterialAttributes matAttrib = BSMaterials.GetAttributes(Material, false);
194 Friction = matAttrib.friction;
195 Restitution = matAttrib.restitution;
196 Density = matAttrib.density;
197 // DetailLog("{0},{1}.SetMaterial,Mat={2},frict={3},rest={4},den={5}", LocalID, TypeName, Material, Friction, Restitution, Density);
198 }
199  
200 public override float Density
201 {
202 get
203 {
204 return base.Density;
205 }
206 set
207 {
208 DetailLog("{0},BSPhysObject.Density,set,den={1}", LocalID, value);
209 base.Density = value;
210 }
211 }
212  
213 // Stop all physical motion.
214 public abstract void ZeroMotion(bool inTaintTime);
215 public abstract void ZeroAngularMotion(bool inTaintTime);
216  
217 // Update the physical location and motion of the object. Called with data from Bullet.
218 public abstract void UpdateProperties(EntityProperties entprop);
219  
220 public virtual OMV.Vector3 RawPosition { get; set; }
221 public abstract OMV.Vector3 ForcePosition { get; set; }
222  
223 public virtual OMV.Quaternion RawOrientation { get; set; }
224 public abstract OMV.Quaternion ForceOrientation { get; set; }
225  
226 public OMV.Vector3 RawVelocity { get; set; }
227 public abstract OMV.Vector3 ForceVelocity { get; set; }
228  
229 public OMV.Vector3 RawForce { get; set; }
230 public OMV.Vector3 RawTorque { get; set; }
231 public override void AddAngularForce(OMV.Vector3 force, bool pushforce)
232 {
233 AddAngularForce(force, pushforce, false);
234 }
235 public abstract void AddAngularForce(OMV.Vector3 force, bool pushforce, bool inTaintTime);
236 public abstract void AddForce(OMV.Vector3 force, bool pushforce, bool inTaintTime);
237  
238 public abstract OMV.Vector3 ForceRotationalVelocity { get; set; }
239  
240 public abstract float ForceBuoyancy { get; set; }
241  
242 public virtual bool ForceBodyShapeRebuild(bool inTaintTime) { return false; }
243  
244 public override bool PIDActive { set { MoveToTargetActive = value; } }
245 public override OMV.Vector3 PIDTarget { set { MoveToTargetTarget = value; } }
246 public override float PIDTau { set { MoveToTargetTau = value; } }
247  
248 public bool MoveToTargetActive { get; set; }
249 public OMV.Vector3 MoveToTargetTarget { get; set; }
250 public float MoveToTargetTau { get; set; }
251  
252 // Used for llSetHoverHeight and maybe vehicle height. Hover Height will override MoveTo target's Z
253 public override bool PIDHoverActive { set { HoverActive = value; } }
254 public override float PIDHoverHeight { set { HoverHeight = value; } }
255 public override PIDHoverType PIDHoverType { set { HoverType = value; } }
256 public override float PIDHoverTau { set { HoverTau = value; } }
257  
258 public bool HoverActive { get; set; }
259 public float HoverHeight { get; set; }
260 public PIDHoverType HoverType { get; set; }
261 public float HoverTau { get; set; }
262  
263 // For RotLookAt
264 public override OMV.Quaternion APIDTarget { set { return; } }
265 public override bool APIDActive { set { return; } }
266 public override float APIDStrength { set { return; } }
267 public override float APIDDamping { set { return; } }
268  
269 // The current velocity forward
270 public virtual float ForwardSpeed
271 {
272 get
273 {
274 OMV.Vector3 characterOrientedVelocity = RawVelocity * OMV.Quaternion.Inverse(OMV.Quaternion.Normalize(RawOrientation));
275 return characterOrientedVelocity.X;
276 }
277 }
278 // The forward speed we are trying to achieve (TargetVelocity)
279 public virtual float TargetVelocitySpeed
280 {
281 get
282 {
283 OMV.Vector3 characterOrientedVelocity = TargetVelocity * OMV.Quaternion.Inverse(OMV.Quaternion.Normalize(RawOrientation));
284 return characterOrientedVelocity.X;
285 }
286 }
287  
288 // The user can optionally set the center of mass. The user's setting will override any
289 // computed center-of-mass (like in linksets).
290 // Note this is a displacement from the root's coordinates. Zero means use the root prim as center-of-mass.
291 public OMV.Vector3? UserSetCenterOfMassDisplacement { get; set; }
292  
293 public OMV.Vector3 LockedLinearAxis { get; set; } // zero means locked. one means free.
294 public OMV.Vector3 LockedAngularAxis { get; set; } // zero means locked. one means free.
295 public const float FreeAxis = 1f;
296 public readonly OMV.Vector3 LockedAxisFree = new OMV.Vector3(FreeAxis, FreeAxis, FreeAxis); // All axis are free
297  
298 // Enable physical actions. Bullet will keep sleeping non-moving physical objects so
299 // they need waking up when parameters are changed.
300 // Called in taint-time!!
301 public void ActivateIfPhysical(bool forceIt)
302 {
303 if (PhysBody.HasPhysicalBody)
304 {
305 if (IsPhysical)
306 {
307 // Physical objects might need activating
308 PhysScene.PE.Activate(PhysBody, forceIt);
309 }
310 else
311 {
312 // Clear the collision cache since we've changed some properties.
313 PhysScene.PE.ClearCollisionProxyCache(PhysScene.World, PhysBody);
314 }
315 }
316 }
317  
318 // 'actors' act on the physical object to change or constrain its motion. These can range from
319 // hovering to complex vehicle motion.
320 // May be called at non-taint time as this just adds the actor to the action list and the real
321 // work is done during the simulation step.
322 // Note that, if the actor is already in the list and we are disabling same, the actor is just left
323 // in the list disabled.
324 public delegate BSActor CreateActor();
325 public void EnableActor(bool enableActor, string actorName, CreateActor creator)
326 {
327 lock (PhysicalActors)
328 {
329 BSActor theActor;
330 if (PhysicalActors.TryGetActor(actorName, out theActor))
331 {
332 // The actor already exists so just turn it on or off
333 DetailLog("{0},BSPhysObject.EnableActor,enablingExistingActor,name={1},enable={2}", LocalID, actorName, enableActor);
334 theActor.Enabled = enableActor;
335 }
336 else
337 {
338 // The actor does not exist. If it should, create it.
339 if (enableActor)
340 {
341 DetailLog("{0},BSPhysObject.EnableActor,creatingActor,name={1}", LocalID, actorName);
342 theActor = creator();
343 PhysicalActors.Add(actorName, theActor);
344 theActor.Enabled = true;
345 }
346 else
347 {
348 DetailLog("{0},BSPhysObject.EnableActor,notCreatingActorSinceNotEnabled,name={1}", LocalID, actorName);
349 }
350 }
351 }
352 }
353  
354 #region Collisions
355  
356 // Requested number of milliseconds between collision events. Zero means disabled.
357 protected int SubscribedEventsMs { get; set; }
358 // Given subscription, the time that a collision may be passed up
359 protected int NextCollisionOkTime { get; set; }
360 // The simulation step that last had a collision
361 protected long CollidingStep { get; set; }
362 // The simulation step that last had a collision with the ground
363 protected long CollidingGroundStep { get; set; }
364 // The simulation step that last collided with an object
365 protected long CollidingObjectStep { get; set; }
366 // The collision flags we think are set in Bullet
367 protected CollisionFlags CurrentCollisionFlags { get; set; }
368 // On a collision, check the collider and remember if the last collider was moving
369 // Used to modify the standing of avatars (avatars on stationary things stand still)
370 public bool ColliderIsMoving;
371 // 'true' if the last collider was a volume detect object
372 public bool ColliderIsVolumeDetect;
373 // Used by BSCharacter to manage standing (and not slipping)
374 public bool IsStationary;
375  
376 // Count of collisions for this object
377 protected long CollisionAccumulation { get; set; }
378  
379 public override bool IsColliding {
380 get { return (CollidingStep == PhysScene.SimulationStep); }
381 set {
382 if (value)
383 CollidingStep = PhysScene.SimulationStep;
384 else
385 CollidingStep = BSScene.NotASimulationStep;
386 }
387 }
388 // Complex objects (like linksets) need to know if there is a collision on any part of
389 // their shape. 'IsColliding' has an existing definition of reporting a collision on
390 // only this specific prim or component of linksets.
391 // 'HasSomeCollision' is defined as reporting if there is a collision on any part of
392 // the complex body that this prim is the root of.
393 public virtual bool HasSomeCollision
394 {
395 get { return IsColliding; }
396 set { IsColliding = value; }
397 }
398 public override bool CollidingGround {
399 get { return (CollidingGroundStep == PhysScene.SimulationStep); }
400 set
401 {
402 if (value)
403 CollidingGroundStep = PhysScene.SimulationStep;
404 else
405 CollidingGroundStep = BSScene.NotASimulationStep;
406 }
407 }
408 public override bool CollidingObj {
409 get { return (CollidingObjectStep == PhysScene.SimulationStep); }
410 set {
411 if (value)
412 CollidingObjectStep = PhysScene.SimulationStep;
413 else
414 CollidingObjectStep = BSScene.NotASimulationStep;
415 }
416 }
417  
418 // The collisions that have been collected for the next collision reporting (throttled by subscription)
419 protected CollisionEventUpdate CollisionCollection;
420 // This is the collision collection last reported to the Simulator.
421 public CollisionEventUpdate CollisionsLastReported;
422 // Remember the collisions recorded in the last tick for fancy collision checking
423 // (like a BSCharacter walking up stairs).
424 public CollisionEventUpdate CollisionsLastTick;
425 private long CollisionsLastTickStep = -1;
426  
427 // The simulation step is telling this object about a collision.
428 // Return 'true' if a collision was processed and should be sent up.
429 // Return 'false' if this object is not enabled/subscribed/appropriate for or has already seen this collision.
430 // Called at taint time from within the Step() function
431 public delegate bool CollideCall(uint collidingWith, BSPhysObject collidee, OMV.Vector3 contactPoint, OMV.Vector3 contactNormal, float pentrationDepth);
432 public virtual bool Collide(uint collidingWith, BSPhysObject collidee,
433 OMV.Vector3 contactPoint, OMV.Vector3 contactNormal, float pentrationDepth)
434 {
435 bool ret = false;
436  
437 // The following lines make IsColliding(), CollidingGround() and CollidingObj work
438 CollidingStep = PhysScene.SimulationStep;
439 if (collidingWith <= PhysScene.TerrainManager.HighestTerrainID)
440 {
441 CollidingGroundStep = PhysScene.SimulationStep;
442 }
443 else
444 {
445 CollidingObjectStep = PhysScene.SimulationStep;
446 }
447  
448 CollisionAccumulation++;
449  
450 // For movement tests, remember if we are colliding with an object that is moving.
451 ColliderIsMoving = collidee != null ? (collidee.RawVelocity != OMV.Vector3.Zero) : false;
452 ColliderIsVolumeDetect = collidee != null ? (collidee.IsVolumeDetect) : false;
453  
454 // Make a collection of the collisions that happened the last simulation tick.
455 // This is different than the collection created for sending up to the simulator as it is cleared every tick.
456 if (CollisionsLastTickStep != PhysScene.SimulationStep)
457 {
458 CollisionsLastTick = new CollisionEventUpdate();
459 CollisionsLastTickStep = PhysScene.SimulationStep;
460 }
461 CollisionsLastTick.AddCollider(collidingWith, new ContactPoint(contactPoint, contactNormal, pentrationDepth));
462  
463 // If someone has subscribed for collision events log the collision so it will be reported up
464 if (SubscribedEvents()) {
465 CollisionCollection.AddCollider(collidingWith, new ContactPoint(contactPoint, contactNormal, pentrationDepth));
466 DetailLog("{0},{1}.Collison.AddCollider,call,with={2},point={3},normal={4},depth={5},colliderMoving={6}",
467 LocalID, TypeName, collidingWith, contactPoint, contactNormal, pentrationDepth, ColliderIsMoving);
468  
469 ret = true;
470 }
471 return ret;
472 }
473  
474 // Send the collected collisions into the simulator.
475 // Called at taint time from within the Step() function thus no locking problems
476 // with CollisionCollection and ObjectsWithNoMoreCollisions.
477 // Return 'true' if there were some actual collisions passed up
478 public virtual bool SendCollisions()
479 {
480 bool ret = true;
481  
482 // If the 'no collision' call, force it to happen right now so quick collision_end
483 bool force = (CollisionCollection.Count == 0 && CollisionsLastReported.Count != 0);
484  
485 // throttle the collisions to the number of milliseconds specified in the subscription
486 if (force || (PhysScene.SimulationNowTime >= NextCollisionOkTime))
487 {
488 NextCollisionOkTime = PhysScene.SimulationNowTime + SubscribedEventsMs;
489  
490 // We are called if we previously had collisions. If there are no collisions
491 // this time, send up one last empty event so OpenSim can sense collision end.
492 if (CollisionCollection.Count == 0)
493 {
494 // If I have no collisions this time, remove me from the list of objects with collisions.
495 ret = false;
496 }
497  
498 DetailLog("{0},{1}.SendCollisionUpdate,call,numCollisions={2}", LocalID, TypeName, CollisionCollection.Count);
499 base.SendCollisionUpdate(CollisionCollection);
500  
501 // Remember the collisions from this tick for some collision specific processing.
502 CollisionsLastReported = CollisionCollection;
503  
504 // The CollisionCollection instance is passed around in the simulator.
505 // Make sure we don't have a handle to that one and that a new one is used for next time.
506 // This fixes an interesting 'gotcha'. If we call CollisionCollection.Clear() here,
507 // a race condition is created for the other users of this instance.
508 CollisionCollection = new CollisionEventUpdate();
509 }
510 return ret;
511 }
512  
513 // Subscribe for collision events.
514 // Parameter is the millisecond rate the caller wishes collision events to occur.
515 public override void SubscribeEvents(int ms) {
516 // DetailLog("{0},{1}.SubscribeEvents,subscribing,ms={2}", LocalID, TypeName, ms);
517 SubscribedEventsMs = ms;
518 if (ms > 0)
519 {
520 // make sure first collision happens
521 NextCollisionOkTime = Util.EnvironmentTickCountSubtract(SubscribedEventsMs);
522  
523 PhysScene.TaintedObject(LocalID, TypeName+".SubscribeEvents", delegate()
524 {
525 if (PhysBody.HasPhysicalBody)
526 CurrentCollisionFlags = PhysScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
527 });
528 }
529 else
530 {
531 // Subscribing for zero or less is the same as unsubscribing
532 UnSubscribeEvents();
533 }
534 }
535 public override void UnSubscribeEvents() {
536 // DetailLog("{0},{1}.UnSubscribeEvents,unsubscribing", LocalID, TypeName);
537 SubscribedEventsMs = 0;
538 PhysScene.TaintedObject(LocalID, TypeName+".UnSubscribeEvents", delegate()
539 {
540 // Make sure there is a body there because sometimes destruction happens in an un-ideal order.
541 if (PhysBody.HasPhysicalBody)
542 CurrentCollisionFlags = PhysScene.PE.RemoveFromCollisionFlags(PhysBody, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
543 });
544 }
545 // Return 'true' if the simulator wants collision events
546 public override bool SubscribedEvents() {
547 return (SubscribedEventsMs > 0);
548 }
549 // Because 'CollisionScore' is called many times while sorting, it should not be recomputed
550 // each time called. So this is built to be light weight for each collision and to do
551 // all the processing when the user asks for the info.
552 public void ComputeCollisionScore()
553 {
554 // Scale the collision count by the time since the last collision.
555 // The "+1" prevents dividing by zero.
556 long timeAgo = PhysScene.SimulationStep - CollidingStep + 1;
557 CollisionScore = CollisionAccumulation / timeAgo;
558 }
559 public override float CollisionScore { get; set; }
560  
561 #endregion // Collisions
562  
563 #region Per Simulation Step actions
564  
565 public BSActorCollection PhysicalActors;
566  
567 // When an update to the physical properties happens, this event is fired to let
568 // different actors to modify the update before it is passed around
569 public delegate void PreUpdatePropertyAction(ref EntityProperties entprop);
570 public event PreUpdatePropertyAction OnPreUpdateProperty;
571 protected void TriggerPreUpdatePropertyAction(ref EntityProperties entprop)
572 {
573 PreUpdatePropertyAction actions = OnPreUpdateProperty;
574 if (actions != null)
575 actions(ref entprop);
576 }
577  
578 #endregion // Per Simulation Step actions
579  
580 // High performance detailed logging routine used by the physical objects.
581 protected void DetailLog(string msg, params Object[] args)
582 {
583 if (PhysScene.PhysicsLogging.Enabled)
584 PhysScene.DetailLog(msg, args);
585 }
586  
587 }
588 }