clockwerk-opensim – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 vero 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 public virtual bool AssetFailed()
163 {
164 return ( (this.PrimAssetState == PrimAssetCondition.FailedAssetFetch)
165 || (this.PrimAssetState == PrimAssetCondition.FailedMeshing) );
166 }
167  
168 // The objects base shape information. Null if not a prim type shape.
169 public PrimitiveBaseShape BaseShape { get; protected set; }
170  
171 // When the physical properties are updated, an EntityProperty holds the update values.
172 // Keep the current and last EntityProperties to enable computation of differences
173 // between the current update and the previous values.
174 public EntityProperties CurrentEntityProperties { get; set; }
175 public EntityProperties LastEntityProperties { get; set; }
176  
177 public virtual OMV.Vector3 Scale { get; set; }
178  
179 // It can be confusing for an actor to know if it should move or update an object
180 // depeneding on the setting of 'selected', 'physical, ...
181 // This flag is the true test -- if true, the object is being acted on in the physical world
182 public abstract bool IsPhysicallyActive { get; }
183  
184 // Detailed state of the object.
185 public abstract bool IsSolid { get; }
186 public abstract bool IsStatic { get; }
187 public abstract bool IsSelected { get; }
188 public abstract bool IsVolumeDetect { get; }
189  
190 // Materialness
191 public MaterialAttributes.Material Material { get; private set; }
192 public override void SetMaterial(int material)
193 {
194 Material = (MaterialAttributes.Material)material;
195  
196 // Setting the material sets the material attributes also.
197 // TODO: decide if this is necessary -- the simulator does this.
198 MaterialAttributes matAttrib = BSMaterials.GetAttributes(Material, false);
199 Friction = matAttrib.friction;
200 Restitution = matAttrib.restitution;
201 Density = matAttrib.density;
202 // DetailLog("{0},{1}.SetMaterial,Mat={2},frict={3},rest={4},den={5}", LocalID, TypeName, Material, Friction, Restitution, Density);
203 }
204  
205 public override float Density
206 {
207 get
208 {
209 return base.Density;
210 }
211 set
212 {
213 DetailLog("{0},BSPhysObject.Density,set,den={1}", LocalID, value);
214 base.Density = value;
215 }
216 }
217  
218 // Stop all physical motion.
219 public abstract void ZeroMotion(bool inTaintTime);
220 public abstract void ZeroAngularMotion(bool inTaintTime);
221  
222 // Update the physical location and motion of the object. Called with data from Bullet.
223 public abstract void UpdateProperties(EntityProperties entprop);
224  
225 public virtual OMV.Vector3 RawPosition { get; set; }
226 public abstract OMV.Vector3 ForcePosition { get; set; }
227  
228 public virtual OMV.Quaternion RawOrientation { get; set; }
229 public abstract OMV.Quaternion ForceOrientation { get; set; }
230  
231 public OMV.Vector3 RawVelocity { get; set; }
232 public abstract OMV.Vector3 ForceVelocity { get; set; }
233  
234 public OMV.Vector3 RawForce { get; set; }
235 public OMV.Vector3 RawTorque { get; set; }
236 public override void AddAngularForce(OMV.Vector3 force, bool pushforce)
237 {
238 AddAngularForce(force, pushforce, false);
239 }
240 public abstract void AddAngularForce(OMV.Vector3 force, bool pushforce, bool inTaintTime);
241 public abstract void AddForce(OMV.Vector3 force, bool pushforce, bool inTaintTime);
242  
243 public abstract OMV.Vector3 ForceRotationalVelocity { get; set; }
244  
245 public abstract float ForceBuoyancy { get; set; }
246  
247 public virtual bool ForceBodyShapeRebuild(bool inTaintTime) { return false; }
248  
249 public override bool PIDActive { set { MoveToTargetActive = value; } }
250 public override OMV.Vector3 PIDTarget { set { MoveToTargetTarget = value; } }
251 public override float PIDTau { set { MoveToTargetTau = value; } }
252  
253 public bool MoveToTargetActive { get; set; }
254 public OMV.Vector3 MoveToTargetTarget { get; set; }
255 public float MoveToTargetTau { get; set; }
256  
257 // Used for llSetHoverHeight and maybe vehicle height. Hover Height will override MoveTo target's Z
258 public override bool PIDHoverActive { set { HoverActive = value; } }
259 public override float PIDHoverHeight { set { HoverHeight = value; } }
260 public override PIDHoverType PIDHoverType { set { HoverType = value; } }
261 public override float PIDHoverTau { set { HoverTau = value; } }
262  
263 public bool HoverActive { get; set; }
264 public float HoverHeight { get; set; }
265 public PIDHoverType HoverType { get; set; }
266 public float HoverTau { get; set; }
267  
268 // For RotLookAt
269 public override OMV.Quaternion APIDTarget { set { return; } }
270 public override bool APIDActive { set { return; } }
271 public override float APIDStrength { set { return; } }
272 public override float APIDDamping { set { return; } }
273  
274 // The current velocity forward
275 public virtual float ForwardSpeed
276 {
277 get
278 {
279 OMV.Vector3 characterOrientedVelocity = RawVelocity * OMV.Quaternion.Inverse(OMV.Quaternion.Normalize(RawOrientation));
280 return characterOrientedVelocity.X;
281 }
282 }
283 // The forward speed we are trying to achieve (TargetVelocity)
284 public virtual float TargetVelocitySpeed
285 {
286 get
287 {
288 OMV.Vector3 characterOrientedVelocity = TargetVelocity * OMV.Quaternion.Inverse(OMV.Quaternion.Normalize(RawOrientation));
289 return characterOrientedVelocity.X;
290 }
291 }
292  
293 // The user can optionally set the center of mass. The user's setting will override any
294 // computed center-of-mass (like in linksets).
295 // Note this is a displacement from the root's coordinates. Zero means use the root prim as center-of-mass.
296 public OMV.Vector3? UserSetCenterOfMassDisplacement { get; set; }
297  
298 public OMV.Vector3 LockedLinearAxis { get; set; } // zero means locked. one means free.
299 public OMV.Vector3 LockedAngularAxis { get; set; } // zero means locked. one means free.
300 public const float FreeAxis = 1f;
301 public readonly OMV.Vector3 LockedAxisFree = new OMV.Vector3(FreeAxis, FreeAxis, FreeAxis); // All axis are free
302  
303 // Enable physical actions. Bullet will keep sleeping non-moving physical objects so
304 // they need waking up when parameters are changed.
305 // Called in taint-time!!
306 public void ActivateIfPhysical(bool forceIt)
307 {
308 if (PhysBody.HasPhysicalBody)
309 {
310 if (IsPhysical)
311 {
312 // Physical objects might need activating
313 PhysScene.PE.Activate(PhysBody, forceIt);
314 }
315 else
316 {
317 // Clear the collision cache since we've changed some properties.
318 PhysScene.PE.ClearCollisionProxyCache(PhysScene.World, PhysBody);
319 }
320 }
321 }
322  
323 // 'actors' act on the physical object to change or constrain its motion. These can range from
324 // hovering to complex vehicle motion.
325 // May be called at non-taint time as this just adds the actor to the action list and the real
326 // work is done during the simulation step.
327 // Note that, if the actor is already in the list and we are disabling same, the actor is just left
328 // in the list disabled.
329 public delegate BSActor CreateActor();
330 public void EnableActor(bool enableActor, string actorName, CreateActor creator)
331 {
332 lock (PhysicalActors)
333 {
334 BSActor theActor;
335 if (PhysicalActors.TryGetActor(actorName, out theActor))
336 {
337 // The actor already exists so just turn it on or off
338 DetailLog("{0},BSPhysObject.EnableActor,enablingExistingActor,name={1},enable={2}", LocalID, actorName, enableActor);
339 theActor.Enabled = enableActor;
340 }
341 else
342 {
343 // The actor does not exist. If it should, create it.
344 if (enableActor)
345 {
346 DetailLog("{0},BSPhysObject.EnableActor,creatingActor,name={1}", LocalID, actorName);
347 theActor = creator();
348 PhysicalActors.Add(actorName, theActor);
349 theActor.Enabled = true;
350 }
351 else
352 {
353 DetailLog("{0},BSPhysObject.EnableActor,notCreatingActorSinceNotEnabled,name={1}", LocalID, actorName);
354 }
355 }
356 }
357 }
358  
359 #region Collisions
360  
361 // Requested number of milliseconds between collision events. Zero means disabled.
362 protected int SubscribedEventsMs { get; set; }
363 // Given subscription, the time that a collision may be passed up
364 protected int NextCollisionOkTime { get; set; }
365 // The simulation step that last had a collision
366 protected long CollidingStep { get; set; }
367 // The simulation step that last had a collision with the ground
368 protected long CollidingGroundStep { get; set; }
369 // The simulation step that last collided with an object
370 protected long CollidingObjectStep { get; set; }
371 // The collision flags we think are set in Bullet
372 protected CollisionFlags CurrentCollisionFlags { get; set; }
373 // On a collision, check the collider and remember if the last collider was moving
374 // Used to modify the standing of avatars (avatars on stationary things stand still)
375 public bool ColliderIsMoving;
376 // 'true' if the last collider was a volume detect object
377 public bool ColliderIsVolumeDetect;
378 // Used by BSCharacter to manage standing (and not slipping)
379 public bool IsStationary;
380  
381 // Count of collisions for this object
382 protected long CollisionAccumulation { get; set; }
383  
384 public override bool IsColliding {
385 get { return (CollidingStep == PhysScene.SimulationStep); }
386 set {
387 if (value)
388 CollidingStep = PhysScene.SimulationStep;
389 else
390 CollidingStep = BSScene.NotASimulationStep;
391 }
392 }
393 // Complex objects (like linksets) need to know if there is a collision on any part of
394 // their shape. 'IsColliding' has an existing definition of reporting a collision on
395 // only this specific prim or component of linksets.
396 // 'HasSomeCollision' is defined as reporting if there is a collision on any part of
397 // the complex body that this prim is the root of.
398 public virtual bool HasSomeCollision
399 {
400 get { return IsColliding; }
401 set { IsColliding = value; }
402 }
403 public override bool CollidingGround {
404 get { return (CollidingGroundStep == PhysScene.SimulationStep); }
405 set
406 {
407 if (value)
408 CollidingGroundStep = PhysScene.SimulationStep;
409 else
410 CollidingGroundStep = BSScene.NotASimulationStep;
411 }
412 }
413 public override bool CollidingObj {
414 get { return (CollidingObjectStep == PhysScene.SimulationStep); }
415 set {
416 if (value)
417 CollidingObjectStep = PhysScene.SimulationStep;
418 else
419 CollidingObjectStep = BSScene.NotASimulationStep;
420 }
421 }
422  
423 // The collisions that have been collected for the next collision reporting (throttled by subscription)
424 protected CollisionEventUpdate CollisionCollection;
425 // This is the collision collection last reported to the Simulator.
426 public CollisionEventUpdate CollisionsLastReported;
427 // Remember the collisions recorded in the last tick for fancy collision checking
428 // (like a BSCharacter walking up stairs).
429 public CollisionEventUpdate CollisionsLastTick;
430 private long CollisionsLastTickStep = -1;
431  
432 // The simulation step is telling this object about a collision.
433 // Return 'true' if a collision was processed and should be sent up.
434 // Return 'false' if this object is not enabled/subscribed/appropriate for or has already seen this collision.
435 // Called at taint time from within the Step() function
436 public delegate bool CollideCall(uint collidingWith, BSPhysObject collidee, OMV.Vector3 contactPoint, OMV.Vector3 contactNormal, float pentrationDepth);
437 public virtual bool Collide(uint collidingWith, BSPhysObject collidee,
438 OMV.Vector3 contactPoint, OMV.Vector3 contactNormal, float pentrationDepth)
439 {
440 bool ret = false;
441  
442 // The following lines make IsColliding(), CollidingGround() and CollidingObj work
443 CollidingStep = PhysScene.SimulationStep;
444 if (collidingWith <= PhysScene.TerrainManager.HighestTerrainID)
445 {
446 CollidingGroundStep = PhysScene.SimulationStep;
447 }
448 else
449 {
450 CollidingObjectStep = PhysScene.SimulationStep;
451 }
452  
453 CollisionAccumulation++;
454  
455 // For movement tests, remember if we are colliding with an object that is moving.
456 ColliderIsMoving = collidee != null ? (collidee.RawVelocity != OMV.Vector3.Zero) : false;
457 ColliderIsVolumeDetect = collidee != null ? (collidee.IsVolumeDetect) : false;
458  
459 // Make a collection of the collisions that happened the last simulation tick.
460 // This is different than the collection created for sending up to the simulator as it is cleared every tick.
461 if (CollisionsLastTickStep != PhysScene.SimulationStep)
462 {
463 CollisionsLastTick = new CollisionEventUpdate();
464 CollisionsLastTickStep = PhysScene.SimulationStep;
465 }
466 CollisionsLastTick.AddCollider(collidingWith, new ContactPoint(contactPoint, contactNormal, pentrationDepth));
467  
468 // If someone has subscribed for collision events log the collision so it will be reported up
469 if (SubscribedEvents()) {
470 lock (PhysScene.CollisionLock)
471 {
472 CollisionCollection.AddCollider(collidingWith, new ContactPoint(contactPoint, contactNormal, pentrationDepth));
473 }
474 DetailLog("{0},{1}.Collison.AddCollider,call,with={2},point={3},normal={4},depth={5},colliderMoving={6}",
475 LocalID, TypeName, collidingWith, contactPoint, contactNormal, pentrationDepth, ColliderIsMoving);
476  
477 ret = true;
478 }
479 return ret;
480 }
481  
482 // Send the collected collisions into the simulator.
483 // Called at taint time from within the Step() function thus no locking problems
484 // with CollisionCollection and ObjectsWithNoMoreCollisions.
485 // Called with BSScene.CollisionLock locked to protect the collision lists.
486 // Return 'true' if there were some actual collisions passed up
487 public virtual bool SendCollisions()
488 {
489 bool ret = true;
490  
491 // If no collisions this call but there were collisions last call, force the collision
492 // event to be happen right now so quick collision_end.
493 bool force = (CollisionCollection.Count == 0 && CollisionsLastReported.Count != 0);
494  
495 // throttle the collisions to the number of milliseconds specified in the subscription
496 if (force || (PhysScene.SimulationNowTime >= NextCollisionOkTime))
497 {
498 NextCollisionOkTime = PhysScene.SimulationNowTime + SubscribedEventsMs;
499  
500 // We are called if we previously had collisions. If there are no collisions
501 // this time, send up one last empty event so OpenSim can sense collision end.
502 if (CollisionCollection.Count == 0)
503 {
504 // If I have no collisions this time, remove me from the list of objects with collisions.
505 ret = false;
506 }
507  
508 DetailLog("{0},{1}.SendCollisionUpdate,call,numCollisions={2}", LocalID, TypeName, CollisionCollection.Count);
509 base.SendCollisionUpdate(CollisionCollection);
510  
511 // Remember the collisions from this tick for some collision specific processing.
512 CollisionsLastReported = CollisionCollection;
513  
514 // The CollisionCollection instance is passed around in the simulator.
515 // Make sure we don't have a handle to that one and that a new one is used for next time.
516 // This fixes an interesting 'gotcha'. If we call CollisionCollection.Clear() here,
517 // a race condition is created for the other users of this instance.
518 CollisionCollection = new CollisionEventUpdate();
519 }
520 return ret;
521 }
522  
523 // Subscribe for collision events.
524 // Parameter is the millisecond rate the caller wishes collision events to occur.
525 public override void SubscribeEvents(int ms) {
526 // DetailLog("{0},{1}.SubscribeEvents,subscribing,ms={2}", LocalID, TypeName, ms);
527 SubscribedEventsMs = ms;
528 if (ms > 0)
529 {
530 // make sure first collision happens
531 NextCollisionOkTime = Util.EnvironmentTickCountSubtract(SubscribedEventsMs);
532  
533 PhysScene.TaintedObject(LocalID, TypeName+".SubscribeEvents", delegate()
534 {
535 if (PhysBody.HasPhysicalBody)
536 CurrentCollisionFlags = PhysScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
537 });
538 }
539 else
540 {
541 // Subscribing for zero or less is the same as unsubscribing
542 UnSubscribeEvents();
543 }
544 }
545 public override void UnSubscribeEvents() {
546 // DetailLog("{0},{1}.UnSubscribeEvents,unsubscribing", LocalID, TypeName);
547 SubscribedEventsMs = 0;
548 PhysScene.TaintedObject(LocalID, TypeName+".UnSubscribeEvents", delegate()
549 {
550 // Make sure there is a body there because sometimes destruction happens in an un-ideal order.
551 if (PhysBody.HasPhysicalBody)
552 CurrentCollisionFlags = PhysScene.PE.RemoveFromCollisionFlags(PhysBody, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
553 });
554 }
555 // Return 'true' if the simulator wants collision events
556 public override bool SubscribedEvents() {
557 return (SubscribedEventsMs > 0);
558 }
559 // Because 'CollisionScore' is called many times while sorting, it should not be recomputed
560 // each time called. So this is built to be light weight for each collision and to do
561 // all the processing when the user asks for the info.
562 public void ComputeCollisionScore()
563 {
564 // Scale the collision count by the time since the last collision.
565 // The "+1" prevents dividing by zero.
566 long timeAgo = PhysScene.SimulationStep - CollidingStep + 1;
567 CollisionScore = CollisionAccumulation / timeAgo;
568 }
569 public override float CollisionScore { get; set; }
570  
571 #endregion // Collisions
572  
573 #region Per Simulation Step actions
574  
575 public BSActorCollection PhysicalActors;
576  
577 // When an update to the physical properties happens, this event is fired to let
578 // different actors to modify the update before it is passed around
579 public delegate void PreUpdatePropertyAction(ref EntityProperties entprop);
580 public event PreUpdatePropertyAction OnPreUpdateProperty;
581 protected void TriggerPreUpdatePropertyAction(ref EntityProperties entprop)
582 {
583 PreUpdatePropertyAction actions = OnPreUpdateProperty;
584 if (actions != null)
585 actions(ref entprop);
586 }
587  
588 #endregion // Per Simulation Step actions
589  
590 // High performance detailed logging routine used by the physical objects.
591 protected void DetailLog(string msg, params Object[] args)
592 {
593 if (PhysScene.PhysicsLogging.Enabled)
594 PhysScene.DetailLog(msg, args);
595 }
596  
597 }
598 }