diff options
Diffstat (limited to 'OpenSim/Region/PhysicsModules/BulletS/BSPrim.cs')
-rw-r--r-- | OpenSim/Region/PhysicsModules/BulletS/BSPrim.cs | 1896 |
1 files changed, 1896 insertions, 0 deletions
diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSPrim.cs b/OpenSim/Region/PhysicsModules/BulletS/BSPrim.cs new file mode 100644 index 0000000..a00991f --- /dev/null +++ b/OpenSim/Region/PhysicsModules/BulletS/BSPrim.cs | |||
@@ -0,0 +1,1896 @@ | |||
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 | |||
28 | using System; | ||
29 | using System.Reflection; | ||
30 | using System.Collections.Generic; | ||
31 | using System.Xml; | ||
32 | using log4net; | ||
33 | using OMV = OpenMetaverse; | ||
34 | using OpenSim.Framework; | ||
35 | using OpenSim.Region.Physics.Manager; | ||
36 | using OpenSim.Region.Physics.ConvexDecompositionDotNet; | ||
37 | using OpenSim.Region.OptionalModules.Scripting; // for ExtendedPhysics | ||
38 | |||
39 | namespace OpenSim.Region.Physics.BulletSPlugin | ||
40 | { | ||
41 | |||
42 | [Serializable] | ||
43 | public class BSPrim : BSPhysObject | ||
44 | { | ||
45 | protected static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
46 | private static readonly string LogHeader = "[BULLETS PRIM]"; | ||
47 | |||
48 | // _size is what the user passed. Scale is what we pass to the physics engine with the mesh. | ||
49 | private OMV.Vector3 _size; // the multiplier for each mesh dimension as passed by the user | ||
50 | |||
51 | private bool _grabbed; | ||
52 | private bool _isSelected; | ||
53 | private bool _isVolumeDetect; | ||
54 | |||
55 | private float _mass; // the mass of this object | ||
56 | private OMV.Vector3 _acceleration; | ||
57 | private int _physicsActorType; | ||
58 | private bool _isPhysical; | ||
59 | private bool _flying; | ||
60 | private bool _setAlwaysRun; | ||
61 | private bool _throttleUpdates; | ||
62 | private bool _floatOnWater; | ||
63 | private OMV.Vector3 _rotationalVelocity; | ||
64 | private bool _kinematic; | ||
65 | private float _buoyancy; | ||
66 | |||
67 | private int CrossingFailures { get; set; } | ||
68 | |||
69 | // Keep a handle to the vehicle actor so it is easy to set parameters on same. | ||
70 | public const string VehicleActorName = "BasicVehicle"; | ||
71 | |||
72 | // Parameters for the hover actor | ||
73 | public const string HoverActorName = "BSPrim.HoverActor"; | ||
74 | // Parameters for the axis lock actor | ||
75 | public const String LockedAxisActorName = "BSPrim.LockedAxis"; | ||
76 | // Parameters for the move to target actor | ||
77 | public const string MoveToTargetActorName = "BSPrim.MoveToTargetActor"; | ||
78 | // Parameters for the setForce and setTorque actors | ||
79 | public const string SetForceActorName = "BSPrim.SetForceActor"; | ||
80 | public const string SetTorqueActorName = "BSPrim.SetTorqueActor"; | ||
81 | |||
82 | public BSPrim(uint localID, String primName, BSScene parent_scene, OMV.Vector3 pos, OMV.Vector3 size, | ||
83 | OMV.Quaternion rotation, PrimitiveBaseShape pbs, bool pisPhysical) | ||
84 | : base(parent_scene, localID, primName, "BSPrim") | ||
85 | { | ||
86 | // m_log.DebugFormat("{0}: BSPrim creation of {1}, id={2}", LogHeader, primName, localID); | ||
87 | _physicsActorType = (int)ActorTypes.Prim; | ||
88 | RawPosition = pos; | ||
89 | _size = size; | ||
90 | Scale = size; // prims are the size the user wants them to be (different for BSCharactes). | ||
91 | RawOrientation = rotation; | ||
92 | _buoyancy = 0f; | ||
93 | RawVelocity = OMV.Vector3.Zero; | ||
94 | _rotationalVelocity = OMV.Vector3.Zero; | ||
95 | BaseShape = pbs; | ||
96 | _isPhysical = pisPhysical; | ||
97 | _isVolumeDetect = false; | ||
98 | |||
99 | _mass = CalculateMass(); | ||
100 | |||
101 | DetailLog("{0},BSPrim.constructor,pbs={1}", LocalID, BSScene.PrimitiveBaseShapeToString(pbs)); | ||
102 | // DetailLog("{0},BSPrim.constructor,call", LocalID); | ||
103 | // do the actual object creation at taint time | ||
104 | PhysScene.TaintedObject(LocalID, "BSPrim.create", delegate() | ||
105 | { | ||
106 | // Make sure the object is being created with some sanity. | ||
107 | ExtremeSanityCheck(true /* inTaintTime */); | ||
108 | |||
109 | CreateGeomAndObject(true); | ||
110 | |||
111 | CurrentCollisionFlags = PhysScene.PE.GetCollisionFlags(PhysBody); | ||
112 | |||
113 | IsInitialized = true; | ||
114 | }); | ||
115 | } | ||
116 | |||
117 | // called when this prim is being destroyed and we should free all the resources | ||
118 | public override void Destroy() | ||
119 | { | ||
120 | // m_log.DebugFormat("{0}: Destroy, id={1}", LogHeader, LocalID); | ||
121 | IsInitialized = false; | ||
122 | |||
123 | base.Destroy(); | ||
124 | |||
125 | // Undo any vehicle properties | ||
126 | this.VehicleType = (int)Vehicle.TYPE_NONE; | ||
127 | |||
128 | PhysScene.TaintedObject(LocalID, "BSPrim.Destroy", delegate() | ||
129 | { | ||
130 | DetailLog("{0},BSPrim.Destroy,taint,", LocalID); | ||
131 | // If there are physical body and shape, release my use of same. | ||
132 | PhysScene.Shapes.DereferenceBody(PhysBody, null); | ||
133 | PhysBody.Clear(); | ||
134 | PhysShape.Dereference(PhysScene); | ||
135 | PhysShape = new BSShapeNull(); | ||
136 | }); | ||
137 | } | ||
138 | |||
139 | // No one uses this property. | ||
140 | public override bool Stopped { | ||
141 | get { return false; } | ||
142 | } | ||
143 | |||
144 | public override bool IsIncomplete { | ||
145 | get { | ||
146 | return ShapeRebuildScheduled; | ||
147 | } | ||
148 | } | ||
149 | |||
150 | // 'true' if this object's shape is in need of a rebuild and a rebuild has been queued. | ||
151 | // The prim is still available but its underlying shape will change soon. | ||
152 | // This is protected by a 'lock(this)'. | ||
153 | public bool ShapeRebuildScheduled { get; protected set; } | ||
154 | |||
155 | public override OMV.Vector3 Size { | ||
156 | get { return _size; } | ||
157 | set { | ||
158 | // We presume the scale and size are the same. If scale must be changed for | ||
159 | // the physical shape, that is done when the geometry is built. | ||
160 | _size = value; | ||
161 | Scale = _size; | ||
162 | ForceBodyShapeRebuild(false); | ||
163 | } | ||
164 | } | ||
165 | |||
166 | public override PrimitiveBaseShape Shape { | ||
167 | set { | ||
168 | BaseShape = value; | ||
169 | DetailLog("{0},BSPrim.changeShape,pbs={1}", LocalID, BSScene.PrimitiveBaseShapeToString(BaseShape)); | ||
170 | PrimAssetState = PrimAssetCondition.Unknown; | ||
171 | ForceBodyShapeRebuild(false); | ||
172 | } | ||
173 | } | ||
174 | // Cause the body and shape of the prim to be rebuilt if necessary. | ||
175 | // If there are no changes required, this is quick and does not make changes to the prim. | ||
176 | // If rebuilding is necessary (like changing from static to physical), that will happen. | ||
177 | // The 'ShapeRebuildScheduled' tells any checker that the body/shape may change shortly. | ||
178 | // The return parameter is not used by anyone. | ||
179 | public override bool ForceBodyShapeRebuild(bool inTaintTime) | ||
180 | { | ||
181 | if (inTaintTime) | ||
182 | { | ||
183 | // If called in taint time, do the operation immediately | ||
184 | _mass = CalculateMass(); // changing the shape changes the mass | ||
185 | CreateGeomAndObject(true); | ||
186 | } | ||
187 | else | ||
188 | { | ||
189 | lock (this) | ||
190 | { | ||
191 | // If a rebuild is not already in the queue | ||
192 | if (!ShapeRebuildScheduled) | ||
193 | { | ||
194 | // Remember that a rebuild is queued -- this is used to flag an incomplete object | ||
195 | ShapeRebuildScheduled = true; | ||
196 | PhysScene.TaintedObject(LocalID, "BSPrim.ForceBodyShapeRebuild", delegate() | ||
197 | { | ||
198 | _mass = CalculateMass(); // changing the shape changes the mass | ||
199 | CreateGeomAndObject(true); | ||
200 | ShapeRebuildScheduled = false; | ||
201 | }); | ||
202 | } | ||
203 | } | ||
204 | } | ||
205 | return true; | ||
206 | } | ||
207 | public override bool Grabbed { | ||
208 | set { _grabbed = value; | ||
209 | } | ||
210 | } | ||
211 | public override bool Selected { | ||
212 | set | ||
213 | { | ||
214 | if (value != _isSelected) | ||
215 | { | ||
216 | _isSelected = value; | ||
217 | PhysScene.TaintedObject(LocalID, "BSPrim.setSelected", delegate() | ||
218 | { | ||
219 | DetailLog("{0},BSPrim.selected,taint,selected={1}", LocalID, _isSelected); | ||
220 | SetObjectDynamic(false); | ||
221 | }); | ||
222 | } | ||
223 | } | ||
224 | } | ||
225 | public override bool IsSelected | ||
226 | { | ||
227 | get { return _isSelected; } | ||
228 | } | ||
229 | |||
230 | public override void CrossingFailure() | ||
231 | { | ||
232 | CrossingFailures++; | ||
233 | if (CrossingFailures > BSParam.CrossingFailuresBeforeOutOfBounds) | ||
234 | { | ||
235 | base.RaiseOutOfBounds(RawPosition); | ||
236 | } | ||
237 | else if (CrossingFailures == BSParam.CrossingFailuresBeforeOutOfBounds) | ||
238 | { | ||
239 | m_log.WarnFormat("{0} Too many crossing failures for {1}", LogHeader, Name); | ||
240 | } | ||
241 | return; | ||
242 | } | ||
243 | |||
244 | // link me to the specified parent | ||
245 | public override void link(PhysicsActor obj) { | ||
246 | } | ||
247 | |||
248 | // delink me from my linkset | ||
249 | public override void delink() { | ||
250 | } | ||
251 | |||
252 | // Set motion values to zero. | ||
253 | // Do it to the properties so the values get set in the physics engine. | ||
254 | // Push the setting of the values to the viewer. | ||
255 | // Called at taint time! | ||
256 | public override void ZeroMotion(bool inTaintTime) | ||
257 | { | ||
258 | RawVelocity = OMV.Vector3.Zero; | ||
259 | _acceleration = OMV.Vector3.Zero; | ||
260 | _rotationalVelocity = OMV.Vector3.Zero; | ||
261 | |||
262 | // Zero some other properties in the physics engine | ||
263 | PhysScene.TaintedObject(inTaintTime, LocalID, "BSPrim.ZeroMotion", delegate() | ||
264 | { | ||
265 | if (PhysBody.HasPhysicalBody) | ||
266 | PhysScene.PE.ClearAllForces(PhysBody); | ||
267 | }); | ||
268 | } | ||
269 | public override void ZeroAngularMotion(bool inTaintTime) | ||
270 | { | ||
271 | _rotationalVelocity = OMV.Vector3.Zero; | ||
272 | // Zero some other properties in the physics engine | ||
273 | PhysScene.TaintedObject(inTaintTime, LocalID, "BSPrim.ZeroMotion", delegate() | ||
274 | { | ||
275 | // DetailLog("{0},BSPrim.ZeroAngularMotion,call,rotVel={1}", LocalID, _rotationalVelocity); | ||
276 | if (PhysBody.HasPhysicalBody) | ||
277 | { | ||
278 | PhysScene.PE.SetInterpolationAngularVelocity(PhysBody, _rotationalVelocity); | ||
279 | PhysScene.PE.SetAngularVelocity(PhysBody, _rotationalVelocity); | ||
280 | } | ||
281 | }); | ||
282 | } | ||
283 | |||
284 | public override void LockAngularMotion(OMV.Vector3 axis) | ||
285 | { | ||
286 | DetailLog("{0},BSPrim.LockAngularMotion,call,axis={1}", LocalID, axis); | ||
287 | |||
288 | ApplyAxisLimits(ExtendedPhysics.PHYS_AXIS_UNLOCK_ANGULAR, 0f, 0f); | ||
289 | if (axis.X != 1) | ||
290 | { | ||
291 | ApplyAxisLimits(ExtendedPhysics.PHYS_AXIS_LOCK_ANGULAR_X, 0f, 0f); | ||
292 | } | ||
293 | if (axis.Y != 1) | ||
294 | { | ||
295 | ApplyAxisLimits(ExtendedPhysics.PHYS_AXIS_LOCK_ANGULAR_Y, 0f, 0f); | ||
296 | } | ||
297 | if (axis.Z != 1) | ||
298 | { | ||
299 | ApplyAxisLimits(ExtendedPhysics.PHYS_AXIS_LOCK_ANGULAR_Z, 0f, 0f); | ||
300 | } | ||
301 | |||
302 | InitializeAxisActor(); | ||
303 | |||
304 | return; | ||
305 | } | ||
306 | |||
307 | public override OMV.Vector3 Position { | ||
308 | get { | ||
309 | // don't do the GetObjectPosition for root elements because this function is called a zillion times. | ||
310 | // RawPosition = ForcePosition; | ||
311 | return RawPosition; | ||
312 | } | ||
313 | set { | ||
314 | // If the position must be forced into the physics engine, use ForcePosition. | ||
315 | // All positions are given in world positions. | ||
316 | if (RawPosition == value) | ||
317 | { | ||
318 | DetailLog("{0},BSPrim.setPosition,call,positionNotChanging,pos={1},orient={2}", LocalID, RawPosition, RawOrientation); | ||
319 | return; | ||
320 | } | ||
321 | RawPosition = value; | ||
322 | PositionSanityCheck(false); | ||
323 | |||
324 | PhysScene.TaintedObject(LocalID, "BSPrim.setPosition", delegate() | ||
325 | { | ||
326 | DetailLog("{0},BSPrim.SetPosition,taint,pos={1},orient={2}", LocalID, RawPosition, RawOrientation); | ||
327 | ForcePosition = RawPosition; | ||
328 | }); | ||
329 | } | ||
330 | } | ||
331 | |||
332 | // NOTE: overloaded by BSPrimDisplaced to handle offset for center-of-gravity. | ||
333 | public override OMV.Vector3 ForcePosition { | ||
334 | get { | ||
335 | RawPosition = PhysScene.PE.GetPosition(PhysBody); | ||
336 | return RawPosition; | ||
337 | } | ||
338 | set { | ||
339 | RawPosition = value; | ||
340 | if (PhysBody.HasPhysicalBody) | ||
341 | { | ||
342 | PhysScene.PE.SetTranslation(PhysBody, RawPosition, RawOrientation); | ||
343 | ActivateIfPhysical(false); | ||
344 | } | ||
345 | } | ||
346 | } | ||
347 | |||
348 | // Check that the current position is sane and, if not, modify the position to make it so. | ||
349 | // Check for being below terrain and being out of bounds. | ||
350 | // Returns 'true' of the position was made sane by some action. | ||
351 | private bool PositionSanityCheck(bool inTaintTime) | ||
352 | { | ||
353 | bool ret = false; | ||
354 | |||
355 | // We don't care where non-physical items are placed | ||
356 | if (!IsPhysicallyActive) | ||
357 | return ret; | ||
358 | |||
359 | if (!PhysScene.TerrainManager.IsWithinKnownTerrain(RawPosition)) | ||
360 | { | ||
361 | // The physical object is out of the known/simulated area. | ||
362 | // Upper levels of code will handle the transition to other areas so, for | ||
363 | // the time, we just ignore the position. | ||
364 | return ret; | ||
365 | } | ||
366 | |||
367 | float terrainHeight = PhysScene.TerrainManager.GetTerrainHeightAtXYZ(RawPosition); | ||
368 | OMV.Vector3 upForce = OMV.Vector3.Zero; | ||
369 | float approxSize = Math.Max(Size.X, Math.Max(Size.Y, Size.Z)); | ||
370 | if ((RawPosition.Z + approxSize / 2f) < terrainHeight) | ||
371 | { | ||
372 | DetailLog("{0},BSPrim.PositionAdjustUnderGround,call,pos={1},terrain={2}", LocalID, RawPosition, terrainHeight); | ||
373 | float targetHeight = terrainHeight + (Size.Z / 2f); | ||
374 | // If the object is below ground it just has to be moved up because pushing will | ||
375 | // not get it through the terrain | ||
376 | RawPosition = new OMV.Vector3(RawPosition.X, RawPosition.Y, targetHeight); | ||
377 | if (inTaintTime) | ||
378 | { | ||
379 | ForcePosition = RawPosition; | ||
380 | } | ||
381 | // If we are throwing the object around, zero its other forces | ||
382 | ZeroMotion(inTaintTime); | ||
383 | ret = true; | ||
384 | } | ||
385 | |||
386 | if ((CurrentCollisionFlags & CollisionFlags.BS_FLOATS_ON_WATER) != 0) | ||
387 | { | ||
388 | float waterHeight = PhysScene.TerrainManager.GetWaterLevelAtXYZ(RawPosition); | ||
389 | // TODO: a floating motor so object will bob in the water | ||
390 | if (Math.Abs(RawPosition.Z - waterHeight) > 0.1f) | ||
391 | { | ||
392 | // Upforce proportional to the distance away from the water. Correct the error in 1 sec. | ||
393 | upForce.Z = (waterHeight - RawPosition.Z) * 1f; | ||
394 | |||
395 | // Apply upforce and overcome gravity. | ||
396 | OMV.Vector3 correctionForce = upForce - PhysScene.DefaultGravity; | ||
397 | DetailLog("{0},BSPrim.PositionSanityCheck,applyForce,pos={1},upForce={2},correctionForce={3}", LocalID, RawPosition, upForce, correctionForce); | ||
398 | AddForce(correctionForce, false, inTaintTime); | ||
399 | ret = true; | ||
400 | } | ||
401 | } | ||
402 | |||
403 | return ret; | ||
404 | } | ||
405 | |||
406 | // Occasionally things will fly off and really get lost. | ||
407 | // Find the wanderers and bring them back. | ||
408 | // Return 'true' if some parameter need some sanity. | ||
409 | private bool ExtremeSanityCheck(bool inTaintTime) | ||
410 | { | ||
411 | bool ret = false; | ||
412 | |||
413 | int wayOverThere = -1000; | ||
414 | int wayOutThere = 10000; | ||
415 | // There have been instances of objects getting thrown way out of bounds and crashing | ||
416 | // the border crossing code. | ||
417 | if ( RawPosition.X < wayOverThere || RawPosition.X > wayOutThere | ||
418 | || RawPosition.Y < wayOverThere || RawPosition.X > wayOutThere | ||
419 | || RawPosition.Z < wayOverThere || RawPosition.X > wayOutThere) | ||
420 | { | ||
421 | RawPosition = new OMV.Vector3(10, 10, 50); | ||
422 | ZeroMotion(inTaintTime); | ||
423 | ret = true; | ||
424 | } | ||
425 | if (RawVelocity.LengthSquared() > BSParam.MaxLinearVelocitySquared) | ||
426 | { | ||
427 | RawVelocity = Util.ClampV(RawVelocity, BSParam.MaxLinearVelocity); | ||
428 | ret = true; | ||
429 | } | ||
430 | if (_rotationalVelocity.LengthSquared() > BSParam.MaxAngularVelocitySquared) | ||
431 | { | ||
432 | _rotationalVelocity = Util.ClampV(_rotationalVelocity, BSParam.MaxAngularVelocity); | ||
433 | ret = true; | ||
434 | } | ||
435 | |||
436 | return ret; | ||
437 | } | ||
438 | |||
439 | // Return the effective mass of the object. | ||
440 | // The definition of this call is to return the mass of the prim. | ||
441 | // If the simulator cares about the mass of the linkset, it will sum it itself. | ||
442 | public override float Mass | ||
443 | { | ||
444 | get { return _mass; } | ||
445 | } | ||
446 | // TotalMass returns the mass of the large object the prim may be in (overridden by linkset code) | ||
447 | public virtual float TotalMass | ||
448 | { | ||
449 | get { return _mass; } | ||
450 | } | ||
451 | // used when we only want this prim's mass and not the linkset thing | ||
452 | public override float RawMass { | ||
453 | get { return _mass; } | ||
454 | } | ||
455 | // Set the physical mass to the passed mass. | ||
456 | // Note that this does not change _mass! | ||
457 | public override void UpdatePhysicalMassProperties(float physMass, bool inWorld) | ||
458 | { | ||
459 | if (PhysBody.HasPhysicalBody && PhysShape.HasPhysicalShape) | ||
460 | { | ||
461 | if (IsStatic) | ||
462 | { | ||
463 | PhysScene.PE.SetGravity(PhysBody, PhysScene.DefaultGravity); | ||
464 | Inertia = OMV.Vector3.Zero; | ||
465 | PhysScene.PE.SetMassProps(PhysBody, 0f, Inertia); | ||
466 | PhysScene.PE.UpdateInertiaTensor(PhysBody); | ||
467 | } | ||
468 | else | ||
469 | { | ||
470 | if (inWorld) | ||
471 | { | ||
472 | // Changing interesting properties doesn't change proxy and collision cache | ||
473 | // information. The Bullet solution is to re-add the object to the world | ||
474 | // after parameters are changed. | ||
475 | PhysScene.PE.RemoveObjectFromWorld(PhysScene.World, PhysBody); | ||
476 | } | ||
477 | |||
478 | // The computation of mass props requires gravity to be set on the object. | ||
479 | Gravity = ComputeGravity(Buoyancy); | ||
480 | PhysScene.PE.SetGravity(PhysBody, Gravity); | ||
481 | |||
482 | // OMV.Vector3 currentScale = PhysScene.PE.GetLocalScaling(PhysShape.physShapeInfo); // DEBUG DEBUG | ||
483 | // DetailLog("{0},BSPrim.UpdateMassProperties,currentScale{1},shape={2}", LocalID, currentScale, PhysShape.physShapeInfo); // DEBUG DEBUG | ||
484 | |||
485 | Inertia = PhysScene.PE.CalculateLocalInertia(PhysShape.physShapeInfo, physMass); | ||
486 | PhysScene.PE.SetMassProps(PhysBody, physMass, Inertia); | ||
487 | PhysScene.PE.UpdateInertiaTensor(PhysBody); | ||
488 | |||
489 | DetailLog("{0},BSPrim.UpdateMassProperties,mass={1},localInertia={2},grav={3},inWorld={4}", | ||
490 | LocalID, physMass, Inertia, Gravity, inWorld); | ||
491 | |||
492 | if (inWorld) | ||
493 | { | ||
494 | AddObjectToPhysicalWorld(); | ||
495 | } | ||
496 | } | ||
497 | } | ||
498 | } | ||
499 | |||
500 | // Return what gravity should be set to this very moment | ||
501 | public OMV.Vector3 ComputeGravity(float buoyancy) | ||
502 | { | ||
503 | OMV.Vector3 ret = PhysScene.DefaultGravity; | ||
504 | |||
505 | if (!IsStatic) | ||
506 | { | ||
507 | ret *= (1f - buoyancy); | ||
508 | ret *= GravModifier; | ||
509 | } | ||
510 | |||
511 | return ret; | ||
512 | } | ||
513 | |||
514 | // Is this used? | ||
515 | public override OMV.Vector3 CenterOfMass | ||
516 | { | ||
517 | get { return RawPosition; } | ||
518 | } | ||
519 | |||
520 | // Is this used? | ||
521 | public override OMV.Vector3 GeometricCenter | ||
522 | { | ||
523 | get { return RawPosition; } | ||
524 | } | ||
525 | |||
526 | public override OMV.Vector3 Force { | ||
527 | get { return RawForce; } | ||
528 | set { | ||
529 | RawForce = value; | ||
530 | EnableActor(RawForce != OMV.Vector3.Zero, SetForceActorName, delegate() | ||
531 | { | ||
532 | return new BSActorSetForce(PhysScene, this, SetForceActorName); | ||
533 | }); | ||
534 | |||
535 | // Call update so actor Refresh() is called to start things off | ||
536 | PhysScene.TaintedObject(LocalID, "BSPrim.setForce", delegate() | ||
537 | { | ||
538 | UpdatePhysicalParameters(); | ||
539 | }); | ||
540 | } | ||
541 | } | ||
542 | |||
543 | // Find and return a handle to the current vehicle actor. | ||
544 | // Return 'null' if there is no vehicle actor. | ||
545 | public BSDynamics GetVehicleActor(bool createIfNone) | ||
546 | { | ||
547 | BSDynamics ret = null; | ||
548 | BSActor actor; | ||
549 | if (PhysicalActors.TryGetActor(VehicleActorName, out actor)) | ||
550 | { | ||
551 | ret = actor as BSDynamics; | ||
552 | } | ||
553 | else | ||
554 | { | ||
555 | if (createIfNone) | ||
556 | { | ||
557 | ret = new BSDynamics(PhysScene, this, VehicleActorName); | ||
558 | PhysicalActors.Add(ret.ActorName, ret); | ||
559 | } | ||
560 | } | ||
561 | return ret; | ||
562 | } | ||
563 | |||
564 | public override int VehicleType { | ||
565 | get { | ||
566 | int ret = (int)Vehicle.TYPE_NONE; | ||
567 | BSDynamics vehicleActor = GetVehicleActor(false /* createIfNone */); | ||
568 | if (vehicleActor != null) | ||
569 | ret = (int)vehicleActor.Type; | ||
570 | return ret; | ||
571 | } | ||
572 | set { | ||
573 | Vehicle type = (Vehicle)value; | ||
574 | |||
575 | PhysScene.TaintedObject(LocalID, "setVehicleType", delegate() | ||
576 | { | ||
577 | // Some vehicle scripts change vehicle type on the fly as an easy way to | ||
578 | // change all the parameters. Like a plane changing to CAR when on the | ||
579 | // ground. In this case, don't want to zero motion. | ||
580 | // ZeroMotion(true /* inTaintTime */); | ||
581 | if (type == Vehicle.TYPE_NONE) | ||
582 | { | ||
583 | // Vehicle type is 'none' so get rid of any actor that may have been allocated. | ||
584 | BSDynamics vehicleActor = GetVehicleActor(false /* createIfNone */); | ||
585 | if (vehicleActor != null) | ||
586 | { | ||
587 | PhysicalActors.RemoveAndRelease(vehicleActor.ActorName); | ||
588 | } | ||
589 | } | ||
590 | else | ||
591 | { | ||
592 | // Vehicle type is not 'none' so create an actor and set it running. | ||
593 | BSDynamics vehicleActor = GetVehicleActor(true /* createIfNone */); | ||
594 | if (vehicleActor != null) | ||
595 | { | ||
596 | vehicleActor.ProcessTypeChange(type); | ||
597 | ActivateIfPhysical(false); | ||
598 | } | ||
599 | } | ||
600 | }); | ||
601 | } | ||
602 | } | ||
603 | public override void VehicleFloatParam(int param, float value) | ||
604 | { | ||
605 | PhysScene.TaintedObject(LocalID, "BSPrim.VehicleFloatParam", delegate() | ||
606 | { | ||
607 | BSDynamics vehicleActor = GetVehicleActor(true /* createIfNone */); | ||
608 | if (vehicleActor != null) | ||
609 | { | ||
610 | vehicleActor.ProcessFloatVehicleParam((Vehicle)param, value); | ||
611 | ActivateIfPhysical(false); | ||
612 | } | ||
613 | }); | ||
614 | } | ||
615 | public override void VehicleVectorParam(int param, OMV.Vector3 value) | ||
616 | { | ||
617 | PhysScene.TaintedObject(LocalID, "BSPrim.VehicleVectorParam", delegate() | ||
618 | { | ||
619 | BSDynamics vehicleActor = GetVehicleActor(true /* createIfNone */); | ||
620 | if (vehicleActor != null) | ||
621 | { | ||
622 | vehicleActor.ProcessVectorVehicleParam((Vehicle)param, value); | ||
623 | ActivateIfPhysical(false); | ||
624 | } | ||
625 | }); | ||
626 | } | ||
627 | public override void VehicleRotationParam(int param, OMV.Quaternion rotation) | ||
628 | { | ||
629 | PhysScene.TaintedObject(LocalID, "BSPrim.VehicleRotationParam", delegate() | ||
630 | { | ||
631 | BSDynamics vehicleActor = GetVehicleActor(true /* createIfNone */); | ||
632 | if (vehicleActor != null) | ||
633 | { | ||
634 | vehicleActor.ProcessRotationVehicleParam((Vehicle)param, rotation); | ||
635 | ActivateIfPhysical(false); | ||
636 | } | ||
637 | }); | ||
638 | } | ||
639 | public override void VehicleFlags(int param, bool remove) | ||
640 | { | ||
641 | PhysScene.TaintedObject(LocalID, "BSPrim.VehicleFlags", delegate() | ||
642 | { | ||
643 | BSDynamics vehicleActor = GetVehicleActor(true /* createIfNone */); | ||
644 | if (vehicleActor != null) | ||
645 | { | ||
646 | vehicleActor.ProcessVehicleFlags(param, remove); | ||
647 | } | ||
648 | }); | ||
649 | } | ||
650 | |||
651 | // Allows the detection of collisions with inherently non-physical prims. see llVolumeDetect for more | ||
652 | public override void SetVolumeDetect(int param) { | ||
653 | bool newValue = (param != 0); | ||
654 | if (_isVolumeDetect != newValue) | ||
655 | { | ||
656 | _isVolumeDetect = newValue; | ||
657 | PhysScene.TaintedObject(LocalID, "BSPrim.SetVolumeDetect", delegate() | ||
658 | { | ||
659 | // DetailLog("{0},setVolumeDetect,taint,volDetect={1}", LocalID, _isVolumeDetect); | ||
660 | SetObjectDynamic(true); | ||
661 | }); | ||
662 | } | ||
663 | return; | ||
664 | } | ||
665 | public override bool IsVolumeDetect | ||
666 | { | ||
667 | get { return _isVolumeDetect; } | ||
668 | } | ||
669 | public override void SetMaterial(int material) | ||
670 | { | ||
671 | base.SetMaterial(material); | ||
672 | PhysScene.TaintedObject(LocalID, "BSPrim.SetMaterial", delegate() | ||
673 | { | ||
674 | UpdatePhysicalParameters(); | ||
675 | }); | ||
676 | } | ||
677 | public override float Friction | ||
678 | { | ||
679 | get { return base.Friction; } | ||
680 | set | ||
681 | { | ||
682 | if (base.Friction != value) | ||
683 | { | ||
684 | base.Friction = value; | ||
685 | PhysScene.TaintedObject(LocalID, "BSPrim.setFriction", delegate() | ||
686 | { | ||
687 | UpdatePhysicalParameters(); | ||
688 | }); | ||
689 | } | ||
690 | } | ||
691 | } | ||
692 | public override float Restitution | ||
693 | { | ||
694 | get { return base.Restitution; } | ||
695 | set | ||
696 | { | ||
697 | if (base.Restitution != value) | ||
698 | { | ||
699 | base.Restitution = value; | ||
700 | PhysScene.TaintedObject(LocalID, "BSPrim.setRestitution", delegate() | ||
701 | { | ||
702 | UpdatePhysicalParameters(); | ||
703 | }); | ||
704 | } | ||
705 | } | ||
706 | } | ||
707 | // The simulator/viewer keep density as 100kg/m3. | ||
708 | // Remember to use BSParam.DensityScaleFactor to create the physical density. | ||
709 | public override float Density | ||
710 | { | ||
711 | get { return base.Density; } | ||
712 | set | ||
713 | { | ||
714 | if (base.Density != value) | ||
715 | { | ||
716 | base.Density = value; | ||
717 | PhysScene.TaintedObject(LocalID, "BSPrim.setDensity", delegate() | ||
718 | { | ||
719 | UpdatePhysicalParameters(); | ||
720 | }); | ||
721 | } | ||
722 | } | ||
723 | } | ||
724 | public override float GravModifier | ||
725 | { | ||
726 | get { return base.GravModifier; } | ||
727 | set | ||
728 | { | ||
729 | if (base.GravModifier != value) | ||
730 | { | ||
731 | base.GravModifier = value; | ||
732 | PhysScene.TaintedObject(LocalID, "BSPrim.setGravityModifier", delegate() | ||
733 | { | ||
734 | UpdatePhysicalParameters(); | ||
735 | }); | ||
736 | } | ||
737 | } | ||
738 | } | ||
739 | public override OMV.Vector3 Velocity { | ||
740 | get { return RawVelocity; } | ||
741 | set { | ||
742 | RawVelocity = value; | ||
743 | PhysScene.TaintedObject(LocalID, "BSPrim.setVelocity", delegate() | ||
744 | { | ||
745 | // DetailLog("{0},BSPrim.SetVelocity,taint,vel={1}", LocalID, RawVelocity); | ||
746 | ForceVelocity = RawVelocity; | ||
747 | }); | ||
748 | } | ||
749 | } | ||
750 | public override OMV.Vector3 ForceVelocity { | ||
751 | get { return RawVelocity; } | ||
752 | set { | ||
753 | PhysScene.AssertInTaintTime("BSPrim.ForceVelocity"); | ||
754 | |||
755 | RawVelocity = Util.ClampV(value, BSParam.MaxLinearVelocity); | ||
756 | if (PhysBody.HasPhysicalBody) | ||
757 | { | ||
758 | DetailLog("{0},BSPrim.ForceVelocity,taint,vel={1}", LocalID, RawVelocity); | ||
759 | PhysScene.PE.SetLinearVelocity(PhysBody, RawVelocity); | ||
760 | ActivateIfPhysical(false); | ||
761 | } | ||
762 | } | ||
763 | } | ||
764 | public override OMV.Vector3 Torque { | ||
765 | get { return RawTorque; } | ||
766 | set { | ||
767 | RawTorque = value; | ||
768 | EnableActor(RawTorque != OMV.Vector3.Zero, SetTorqueActorName, delegate() | ||
769 | { | ||
770 | return new BSActorSetTorque(PhysScene, this, SetTorqueActorName); | ||
771 | }); | ||
772 | DetailLog("{0},BSPrim.SetTorque,call,torque={1}", LocalID, RawTorque); | ||
773 | |||
774 | // Call update so actor Refresh() is called to start things off | ||
775 | PhysScene.TaintedObject(LocalID, "BSPrim.setTorque", delegate() | ||
776 | { | ||
777 | UpdatePhysicalParameters(); | ||
778 | }); | ||
779 | } | ||
780 | } | ||
781 | public override OMV.Vector3 Acceleration { | ||
782 | get { return _acceleration; } | ||
783 | set { _acceleration = value; } | ||
784 | } | ||
785 | |||
786 | public override OMV.Quaternion Orientation { | ||
787 | get { | ||
788 | return RawOrientation; | ||
789 | } | ||
790 | set { | ||
791 | if (RawOrientation == value) | ||
792 | return; | ||
793 | RawOrientation = value; | ||
794 | |||
795 | PhysScene.TaintedObject(LocalID, "BSPrim.setOrientation", delegate() | ||
796 | { | ||
797 | ForceOrientation = RawOrientation; | ||
798 | }); | ||
799 | } | ||
800 | } | ||
801 | // Go directly to Bullet to get/set the value. | ||
802 | public override OMV.Quaternion ForceOrientation | ||
803 | { | ||
804 | get | ||
805 | { | ||
806 | RawOrientation = PhysScene.PE.GetOrientation(PhysBody); | ||
807 | return RawOrientation; | ||
808 | } | ||
809 | set | ||
810 | { | ||
811 | RawOrientation = value; | ||
812 | if (PhysBody.HasPhysicalBody) | ||
813 | PhysScene.PE.SetTranslation(PhysBody, RawPosition, RawOrientation); | ||
814 | } | ||
815 | } | ||
816 | public override int PhysicsActorType { | ||
817 | get { return _physicsActorType; } | ||
818 | set { _physicsActorType = value; } | ||
819 | } | ||
820 | public override bool IsPhysical { | ||
821 | get { return _isPhysical; } | ||
822 | set { | ||
823 | if (_isPhysical != value) | ||
824 | { | ||
825 | _isPhysical = value; | ||
826 | PhysScene.TaintedObject(LocalID, "BSPrim.setIsPhysical", delegate() | ||
827 | { | ||
828 | DetailLog("{0},setIsPhysical,taint,isPhys={1}", LocalID, _isPhysical); | ||
829 | SetObjectDynamic(true); | ||
830 | // whether phys-to-static or static-to-phys, the object is not moving. | ||
831 | ZeroMotion(true); | ||
832 | |||
833 | }); | ||
834 | } | ||
835 | } | ||
836 | } | ||
837 | |||
838 | // An object is static (does not move) if selected or not physical | ||
839 | public override bool IsStatic | ||
840 | { | ||
841 | get { return _isSelected || !IsPhysical; } | ||
842 | } | ||
843 | |||
844 | // An object is solid if it's not phantom and if it's not doing VolumeDetect | ||
845 | public override bool IsSolid | ||
846 | { | ||
847 | get { return !IsPhantom && !_isVolumeDetect; } | ||
848 | } | ||
849 | |||
850 | // The object is moving and is actively being dynamic in the physical world | ||
851 | public override bool IsPhysicallyActive | ||
852 | { | ||
853 | get { return !_isSelected && IsPhysical; } | ||
854 | } | ||
855 | |||
856 | // Make gravity work if the object is physical and not selected | ||
857 | // Called at taint-time!! | ||
858 | private void SetObjectDynamic(bool forceRebuild) | ||
859 | { | ||
860 | // Recreate the physical object if necessary | ||
861 | CreateGeomAndObject(forceRebuild); | ||
862 | } | ||
863 | |||
864 | // Convert the simulator's physical properties into settings on BulletSim objects. | ||
865 | // There are four flags we're interested in: | ||
866 | // IsStatic: Object does not move, otherwise the object has mass and moves | ||
867 | // isSolid: other objects bounce off of this object | ||
868 | // isVolumeDetect: other objects pass through but can generate collisions | ||
869 | // collisionEvents: whether this object returns collision events | ||
870 | // NOTE: overloaded by BSPrimLinkable to also update linkset physical parameters. | ||
871 | public virtual void UpdatePhysicalParameters() | ||
872 | { | ||
873 | if (!PhysBody.HasPhysicalBody) | ||
874 | { | ||
875 | // This would only happen if updates are called for during initialization when the body is not set up yet. | ||
876 | // DetailLog("{0},BSPrim.UpdatePhysicalParameters,taint,calledWithNoPhysBody", LocalID); | ||
877 | return; | ||
878 | } | ||
879 | |||
880 | // Mangling all the physical properties requires the object not be in the physical world. | ||
881 | // This is a NOOP if the object is not in the world (BulletSim and Bullet ignore objects not found). | ||
882 | PhysScene.PE.RemoveObjectFromWorld(PhysScene.World, PhysBody); | ||
883 | |||
884 | // Set up the object physicalness (does gravity and collisions move this object) | ||
885 | MakeDynamic(IsStatic); | ||
886 | |||
887 | // Update vehicle specific parameters (after MakeDynamic() so can change physical parameters) | ||
888 | PhysicalActors.Refresh(); | ||
889 | |||
890 | // Arrange for collision events if the simulator wants them | ||
891 | EnableCollisions(SubscribedEvents()); | ||
892 | |||
893 | // Make solid or not (do things bounce off or pass through this object). | ||
894 | MakeSolid(IsSolid); | ||
895 | |||
896 | AddObjectToPhysicalWorld(); | ||
897 | |||
898 | // Rebuild its shape | ||
899 | PhysScene.PE.UpdateSingleAabb(PhysScene.World, PhysBody); | ||
900 | |||
901 | DetailLog("{0},BSPrim.UpdatePhysicalParameters,taintExit,static={1},solid={2},mass={3},collide={4},cf={5:X},cType={6},body={7},shape={8}", | ||
902 | LocalID, IsStatic, IsSolid, Mass, SubscribedEvents(), | ||
903 | CurrentCollisionFlags, PhysBody.collisionType, PhysBody, PhysShape); | ||
904 | } | ||
905 | |||
906 | // "Making dynamic" means changing to and from static. | ||
907 | // When static, gravity does not effect the object and it is fixed in space. | ||
908 | // When dynamic, the object can fall and be pushed by others. | ||
909 | // This is independent of its 'solidness' which controls what passes through | ||
910 | // this object and what interacts with it. | ||
911 | protected virtual void MakeDynamic(bool makeStatic) | ||
912 | { | ||
913 | if (makeStatic) | ||
914 | { | ||
915 | // Become a Bullet 'static' object type | ||
916 | CurrentCollisionFlags = PhysScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.CF_STATIC_OBJECT); | ||
917 | // Stop all movement | ||
918 | ZeroMotion(true); | ||
919 | |||
920 | // Set various physical properties so other object interact properly | ||
921 | PhysScene.PE.SetFriction(PhysBody, Friction); | ||
922 | PhysScene.PE.SetRestitution(PhysBody, Restitution); | ||
923 | PhysScene.PE.SetContactProcessingThreshold(PhysBody, BSParam.ContactProcessingThreshold); | ||
924 | |||
925 | // Mass is zero which disables a bunch of physics stuff in Bullet | ||
926 | UpdatePhysicalMassProperties(0f, false); | ||
927 | // Set collision detection parameters | ||
928 | if (BSParam.CcdMotionThreshold > 0f) | ||
929 | { | ||
930 | PhysScene.PE.SetCcdMotionThreshold(PhysBody, BSParam.CcdMotionThreshold); | ||
931 | PhysScene.PE.SetCcdSweptSphereRadius(PhysBody, BSParam.CcdSweptSphereRadius); | ||
932 | } | ||
933 | |||
934 | // The activation state is 'disabled' so Bullet will not try to act on it. | ||
935 | // PhysicsScene.PE.ForceActivationState(PhysBody, ActivationState.DISABLE_SIMULATION); | ||
936 | // Start it out sleeping and physical actions could wake it up. | ||
937 | PhysScene.PE.ForceActivationState(PhysBody, ActivationState.ISLAND_SLEEPING); | ||
938 | |||
939 | // This collides like a static object | ||
940 | PhysBody.collisionType = CollisionType.Static; | ||
941 | } | ||
942 | else | ||
943 | { | ||
944 | // Not a Bullet static object | ||
945 | CurrentCollisionFlags = PhysScene.PE.RemoveFromCollisionFlags(PhysBody, CollisionFlags.CF_STATIC_OBJECT); | ||
946 | |||
947 | // Set various physical properties so other object interact properly | ||
948 | PhysScene.PE.SetFriction(PhysBody, Friction); | ||
949 | PhysScene.PE.SetRestitution(PhysBody, Restitution); | ||
950 | // DetailLog("{0},BSPrim.MakeDynamic,frict={1},rest={2}", LocalID, Friction, Restitution); | ||
951 | |||
952 | // per http://www.bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=3382 | ||
953 | // Since this can be called multiple times, only zero forces when becoming physical | ||
954 | // PhysicsScene.PE.ClearAllForces(BSBody); | ||
955 | |||
956 | // For good measure, make sure the transform is set through to the motion state | ||
957 | ForcePosition = RawPosition; | ||
958 | ForceVelocity = RawVelocity; | ||
959 | ForceRotationalVelocity = _rotationalVelocity; | ||
960 | |||
961 | // A dynamic object has mass | ||
962 | UpdatePhysicalMassProperties(RawMass, false); | ||
963 | |||
964 | // Set collision detection parameters | ||
965 | if (BSParam.CcdMotionThreshold > 0f) | ||
966 | { | ||
967 | PhysScene.PE.SetCcdMotionThreshold(PhysBody, BSParam.CcdMotionThreshold); | ||
968 | PhysScene.PE.SetCcdSweptSphereRadius(PhysBody, BSParam.CcdSweptSphereRadius); | ||
969 | } | ||
970 | |||
971 | // Various values for simulation limits | ||
972 | PhysScene.PE.SetDamping(PhysBody, BSParam.LinearDamping, BSParam.AngularDamping); | ||
973 | PhysScene.PE.SetDeactivationTime(PhysBody, BSParam.DeactivationTime); | ||
974 | PhysScene.PE.SetSleepingThresholds(PhysBody, BSParam.LinearSleepingThreshold, BSParam.AngularSleepingThreshold); | ||
975 | PhysScene.PE.SetContactProcessingThreshold(PhysBody, BSParam.ContactProcessingThreshold); | ||
976 | |||
977 | // This collides like an object. | ||
978 | PhysBody.collisionType = CollisionType.Dynamic; | ||
979 | |||
980 | // Force activation of the object so Bullet will act on it. | ||
981 | // Must do the ForceActivationState2() to overcome the DISABLE_SIMULATION from static objects. | ||
982 | PhysScene.PE.ForceActivationState(PhysBody, ActivationState.ACTIVE_TAG); | ||
983 | } | ||
984 | } | ||
985 | |||
986 | // "Making solid" means that other object will not pass through this object. | ||
987 | // To make transparent, we create a Bullet ghost object. | ||
988 | // Note: This expects to be called from the UpdatePhysicalParameters() routine as | ||
989 | // the functions after this one set up the state of a possibly newly created collision body. | ||
990 | private void MakeSolid(bool makeSolid) | ||
991 | { | ||
992 | CollisionObjectTypes bodyType = (CollisionObjectTypes)PhysScene.PE.GetBodyType(PhysBody); | ||
993 | if (makeSolid) | ||
994 | { | ||
995 | // Verify the previous code created the correct shape for this type of thing. | ||
996 | if ((bodyType & CollisionObjectTypes.CO_RIGID_BODY) == 0) | ||
997 | { | ||
998 | m_log.ErrorFormat("{0} MakeSolid: physical body of wrong type for solidity. id={1}, type={2}", LogHeader, LocalID, bodyType); | ||
999 | } | ||
1000 | CurrentCollisionFlags = PhysScene.PE.RemoveFromCollisionFlags(PhysBody, CollisionFlags.CF_NO_CONTACT_RESPONSE); | ||
1001 | } | ||
1002 | else | ||
1003 | { | ||
1004 | if ((bodyType & CollisionObjectTypes.CO_GHOST_OBJECT) == 0) | ||
1005 | { | ||
1006 | m_log.ErrorFormat("{0} MakeSolid: physical body of wrong type for non-solidness. id={1}, type={2}", LogHeader, LocalID, bodyType); | ||
1007 | } | ||
1008 | CurrentCollisionFlags = PhysScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.CF_NO_CONTACT_RESPONSE); | ||
1009 | |||
1010 | // Change collision info from a static object to a ghosty collision object | ||
1011 | PhysBody.collisionType = CollisionType.VolumeDetect; | ||
1012 | } | ||
1013 | } | ||
1014 | |||
1015 | // Turn on or off the flag controlling whether collision events are returned to the simulator. | ||
1016 | private void EnableCollisions(bool wantsCollisionEvents) | ||
1017 | { | ||
1018 | if (wantsCollisionEvents) | ||
1019 | { | ||
1020 | CurrentCollisionFlags = PhysScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); | ||
1021 | } | ||
1022 | else | ||
1023 | { | ||
1024 | CurrentCollisionFlags = PhysScene.PE.RemoveFromCollisionFlags(PhysBody, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); | ||
1025 | } | ||
1026 | } | ||
1027 | |||
1028 | // Add me to the physical world. | ||
1029 | // Object MUST NOT already be in the world. | ||
1030 | // This routine exists because some assorted properties get mangled by adding to the world. | ||
1031 | internal void AddObjectToPhysicalWorld() | ||
1032 | { | ||
1033 | if (PhysBody.HasPhysicalBody) | ||
1034 | { | ||
1035 | PhysScene.PE.AddObjectToWorld(PhysScene.World, PhysBody); | ||
1036 | } | ||
1037 | else | ||
1038 | { | ||
1039 | m_log.ErrorFormat("{0} Attempt to add physical object without body. id={1}", LogHeader, LocalID); | ||
1040 | DetailLog("{0},BSPrim.AddObjectToPhysicalWorld,addObjectWithoutBody,cType={1}", LocalID, PhysBody.collisionType); | ||
1041 | } | ||
1042 | } | ||
1043 | |||
1044 | // prims don't fly | ||
1045 | public override bool Flying { | ||
1046 | get { return _flying; } | ||
1047 | set { | ||
1048 | _flying = value; | ||
1049 | } | ||
1050 | } | ||
1051 | public override bool SetAlwaysRun { | ||
1052 | get { return _setAlwaysRun; } | ||
1053 | set { _setAlwaysRun = value; } | ||
1054 | } | ||
1055 | public override bool ThrottleUpdates { | ||
1056 | get { return _throttleUpdates; } | ||
1057 | set { _throttleUpdates = value; } | ||
1058 | } | ||
1059 | public bool IsPhantom { | ||
1060 | get { | ||
1061 | // SceneObjectPart removes phantom objects from the physics scene | ||
1062 | // so, although we could implement touching and such, we never | ||
1063 | // are invoked as a phantom object | ||
1064 | return false; | ||
1065 | } | ||
1066 | } | ||
1067 | public override bool FloatOnWater { | ||
1068 | set { | ||
1069 | _floatOnWater = value; | ||
1070 | PhysScene.TaintedObject(LocalID, "BSPrim.setFloatOnWater", delegate() | ||
1071 | { | ||
1072 | if (_floatOnWater) | ||
1073 | CurrentCollisionFlags = PhysScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.BS_FLOATS_ON_WATER); | ||
1074 | else | ||
1075 | CurrentCollisionFlags = PhysScene.PE.RemoveFromCollisionFlags(PhysBody, CollisionFlags.BS_FLOATS_ON_WATER); | ||
1076 | }); | ||
1077 | } | ||
1078 | } | ||
1079 | public override OMV.Vector3 RotationalVelocity { | ||
1080 | get { | ||
1081 | return _rotationalVelocity; | ||
1082 | } | ||
1083 | set { | ||
1084 | _rotationalVelocity = value; | ||
1085 | Util.ClampV(_rotationalVelocity, BSParam.MaxAngularVelocity); | ||
1086 | // m_log.DebugFormat("{0}: RotationalVelocity={1}", LogHeader, _rotationalVelocity); | ||
1087 | PhysScene.TaintedObject(LocalID, "BSPrim.setRotationalVelocity", delegate() | ||
1088 | { | ||
1089 | ForceRotationalVelocity = _rotationalVelocity; | ||
1090 | }); | ||
1091 | } | ||
1092 | } | ||
1093 | public override OMV.Vector3 ForceRotationalVelocity { | ||
1094 | get { | ||
1095 | return _rotationalVelocity; | ||
1096 | } | ||
1097 | set { | ||
1098 | _rotationalVelocity = Util.ClampV(value, BSParam.MaxAngularVelocity); | ||
1099 | if (PhysBody.HasPhysicalBody) | ||
1100 | { | ||
1101 | DetailLog("{0},BSPrim.ForceRotationalVel,taint,rotvel={1}", LocalID, _rotationalVelocity); | ||
1102 | PhysScene.PE.SetAngularVelocity(PhysBody, _rotationalVelocity); | ||
1103 | // PhysicsScene.PE.SetInterpolationAngularVelocity(PhysBody, _rotationalVelocity); | ||
1104 | ActivateIfPhysical(false); | ||
1105 | } | ||
1106 | } | ||
1107 | } | ||
1108 | public override bool Kinematic { | ||
1109 | get { return _kinematic; } | ||
1110 | set { _kinematic = value; | ||
1111 | // m_log.DebugFormat("{0}: Kinematic={1}", LogHeader, _kinematic); | ||
1112 | } | ||
1113 | } | ||
1114 | public override float Buoyancy { | ||
1115 | get { return _buoyancy; } | ||
1116 | set { | ||
1117 | _buoyancy = value; | ||
1118 | PhysScene.TaintedObject(LocalID, "BSPrim.setBuoyancy", delegate() | ||
1119 | { | ||
1120 | ForceBuoyancy = _buoyancy; | ||
1121 | }); | ||
1122 | } | ||
1123 | } | ||
1124 | public override float ForceBuoyancy { | ||
1125 | get { return _buoyancy; } | ||
1126 | set { | ||
1127 | _buoyancy = value; | ||
1128 | // DetailLog("{0},BSPrim.setForceBuoyancy,taint,buoy={1}", LocalID, _buoyancy); | ||
1129 | // Force the recalculation of the various inertia,etc variables in the object | ||
1130 | UpdatePhysicalMassProperties(RawMass, true); | ||
1131 | DetailLog("{0},BSPrim.ForceBuoyancy,buoy={1},mass={2},grav={3}", LocalID, _buoyancy, RawMass, Gravity); | ||
1132 | ActivateIfPhysical(false); | ||
1133 | } | ||
1134 | } | ||
1135 | |||
1136 | public override bool PIDActive | ||
1137 | { | ||
1138 | get | ||
1139 | { | ||
1140 | return MoveToTargetActive; | ||
1141 | } | ||
1142 | |||
1143 | set | ||
1144 | { | ||
1145 | MoveToTargetActive = value; | ||
1146 | |||
1147 | EnableActor(MoveToTargetActive, MoveToTargetActorName, delegate() | ||
1148 | { | ||
1149 | return new BSActorMoveToTarget(PhysScene, this, MoveToTargetActorName); | ||
1150 | }); | ||
1151 | |||
1152 | // Call update so actor Refresh() is called to start things off | ||
1153 | PhysScene.TaintedObject(LocalID, "BSPrim.PIDActive", delegate() | ||
1154 | { | ||
1155 | UpdatePhysicalParameters(); | ||
1156 | }); | ||
1157 | } | ||
1158 | } | ||
1159 | |||
1160 | public override OMV.Vector3 PIDTarget | ||
1161 | { | ||
1162 | set | ||
1163 | { | ||
1164 | base.PIDTarget = value; | ||
1165 | BSActor actor; | ||
1166 | if (PhysicalActors.TryGetActor(MoveToTargetActorName, out actor)) | ||
1167 | { | ||
1168 | // if the actor exists, tell it to refresh its values. | ||
1169 | actor.Refresh(); | ||
1170 | } | ||
1171 | |||
1172 | } | ||
1173 | } | ||
1174 | // Used for llSetHoverHeight and maybe vehicle height | ||
1175 | // Hover Height will override MoveTo target's Z | ||
1176 | public override bool PIDHoverActive { | ||
1177 | set { | ||
1178 | base.HoverActive = value; | ||
1179 | EnableActor(HoverActive, HoverActorName, delegate() | ||
1180 | { | ||
1181 | return new BSActorHover(PhysScene, this, HoverActorName); | ||
1182 | }); | ||
1183 | |||
1184 | // Call update so actor Refresh() is called to start things off | ||
1185 | PhysScene.TaintedObject(LocalID, "BSPrim.PIDHoverActive", delegate() | ||
1186 | { | ||
1187 | UpdatePhysicalParameters(); | ||
1188 | }); | ||
1189 | } | ||
1190 | } | ||
1191 | |||
1192 | public override void AddForce(OMV.Vector3 force, bool pushforce) { | ||
1193 | // Per documentation, max force is limited. | ||
1194 | OMV.Vector3 addForce = Util.ClampV(force, BSParam.MaxAddForceMagnitude); | ||
1195 | |||
1196 | // Since this force is being applied in only one step, make this a force per second. | ||
1197 | addForce /= PhysScene.LastTimeStep; | ||
1198 | AddForce(addForce, pushforce, false /* inTaintTime */); | ||
1199 | } | ||
1200 | |||
1201 | // Applying a force just adds this to the total force on the object. | ||
1202 | // This added force will only last the next simulation tick. | ||
1203 | public override void AddForce(OMV.Vector3 force, bool pushforce, bool inTaintTime) { | ||
1204 | // for an object, doesn't matter if force is a pushforce or not | ||
1205 | if (IsPhysicallyActive) | ||
1206 | { | ||
1207 | if (force.IsFinite()) | ||
1208 | { | ||
1209 | // DetailLog("{0},BSPrim.addForce,call,force={1}", LocalID, addForce); | ||
1210 | |||
1211 | OMV.Vector3 addForce = force; | ||
1212 | PhysScene.TaintedObject(inTaintTime, LocalID, "BSPrim.AddForce", delegate() | ||
1213 | { | ||
1214 | // Bullet adds this central force to the total force for this tick. | ||
1215 | // Deep down in Bullet: | ||
1216 | // linearVelocity += totalForce / mass * timeStep; | ||
1217 | DetailLog("{0},BSPrim.addForce,taint,force={1}", LocalID, addForce); | ||
1218 | if (PhysBody.HasPhysicalBody) | ||
1219 | { | ||
1220 | PhysScene.PE.ApplyCentralForce(PhysBody, addForce); | ||
1221 | ActivateIfPhysical(false); | ||
1222 | } | ||
1223 | }); | ||
1224 | } | ||
1225 | else | ||
1226 | { | ||
1227 | m_log.WarnFormat("{0}: AddForce: Got a NaN force applied to a prim. LocalID={1}", LogHeader, LocalID); | ||
1228 | return; | ||
1229 | } | ||
1230 | } | ||
1231 | } | ||
1232 | |||
1233 | public void AddForceImpulse(OMV.Vector3 impulse, bool pushforce, bool inTaintTime) { | ||
1234 | // for an object, doesn't matter if force is a pushforce or not | ||
1235 | if (!IsPhysicallyActive) | ||
1236 | { | ||
1237 | if (impulse.IsFinite()) | ||
1238 | { | ||
1239 | OMV.Vector3 addImpulse = Util.ClampV(impulse, BSParam.MaxAddForceMagnitude); | ||
1240 | // DetailLog("{0},BSPrim.addForceImpulse,call,impulse={1}", LocalID, impulse); | ||
1241 | |||
1242 | PhysScene.TaintedObject(inTaintTime, LocalID, "BSPrim.AddImpulse", delegate() | ||
1243 | { | ||
1244 | // Bullet adds this impulse immediately to the velocity | ||
1245 | DetailLog("{0},BSPrim.addForceImpulse,taint,impulseforce={1}", LocalID, addImpulse); | ||
1246 | if (PhysBody.HasPhysicalBody) | ||
1247 | { | ||
1248 | PhysScene.PE.ApplyCentralImpulse(PhysBody, addImpulse); | ||
1249 | ActivateIfPhysical(false); | ||
1250 | } | ||
1251 | }); | ||
1252 | } | ||
1253 | else | ||
1254 | { | ||
1255 | m_log.WarnFormat("{0}: AddForceImpulse: Got a NaN impulse applied to a prim. LocalID={1}", LogHeader, LocalID); | ||
1256 | return; | ||
1257 | } | ||
1258 | } | ||
1259 | } | ||
1260 | |||
1261 | // BSPhysObject.AddAngularForce() | ||
1262 | public override void AddAngularForce(OMV.Vector3 force, bool pushforce, bool inTaintTime) | ||
1263 | { | ||
1264 | if (force.IsFinite()) | ||
1265 | { | ||
1266 | OMV.Vector3 angForce = force; | ||
1267 | PhysScene.TaintedObject(inTaintTime, LocalID, "BSPrim.AddAngularForce", delegate() | ||
1268 | { | ||
1269 | if (PhysBody.HasPhysicalBody) | ||
1270 | { | ||
1271 | DetailLog("{0},BSPrim.AddAngularForce,taint,angForce={1}", LocalID, angForce); | ||
1272 | PhysScene.PE.ApplyTorque(PhysBody, angForce); | ||
1273 | ActivateIfPhysical(false); | ||
1274 | } | ||
1275 | }); | ||
1276 | } | ||
1277 | else | ||
1278 | { | ||
1279 | m_log.WarnFormat("{0}: Got a NaN force applied to a prim. LocalID={1}", LogHeader, LocalID); | ||
1280 | return; | ||
1281 | } | ||
1282 | } | ||
1283 | |||
1284 | // A torque impulse. | ||
1285 | // ApplyTorqueImpulse adds torque directly to the angularVelocity. | ||
1286 | // AddAngularForce accumulates the force and applied it to the angular velocity all at once. | ||
1287 | // Computed as: angularVelocity += impulse * inertia; | ||
1288 | public void ApplyTorqueImpulse(OMV.Vector3 impulse, bool inTaintTime) | ||
1289 | { | ||
1290 | OMV.Vector3 applyImpulse = impulse; | ||
1291 | PhysScene.TaintedObject(inTaintTime, LocalID, "BSPrim.ApplyTorqueImpulse", delegate() | ||
1292 | { | ||
1293 | if (PhysBody.HasPhysicalBody) | ||
1294 | { | ||
1295 | PhysScene.PE.ApplyTorqueImpulse(PhysBody, applyImpulse); | ||
1296 | ActivateIfPhysical(false); | ||
1297 | } | ||
1298 | }); | ||
1299 | } | ||
1300 | |||
1301 | public override void SetMomentum(OMV.Vector3 momentum) { | ||
1302 | // DetailLog("{0},BSPrim.SetMomentum,call,mom={1}", LocalID, momentum); | ||
1303 | } | ||
1304 | #region Mass Calculation | ||
1305 | |||
1306 | private float CalculateMass() | ||
1307 | { | ||
1308 | float volume = _size.X * _size.Y * _size.Z; // default | ||
1309 | float tmp; | ||
1310 | |||
1311 | float returnMass = 0; | ||
1312 | float hollowAmount = (float)BaseShape.ProfileHollow * 2.0e-5f; | ||
1313 | float hollowVolume = hollowAmount * hollowAmount; | ||
1314 | |||
1315 | switch (BaseShape.ProfileShape) | ||
1316 | { | ||
1317 | case ProfileShape.Square: | ||
1318 | // default box | ||
1319 | |||
1320 | if (BaseShape.PathCurve == (byte)Extrusion.Straight) | ||
1321 | { | ||
1322 | if (hollowAmount > 0.0) | ||
1323 | { | ||
1324 | switch (BaseShape.HollowShape) | ||
1325 | { | ||
1326 | case HollowShape.Square: | ||
1327 | case HollowShape.Same: | ||
1328 | break; | ||
1329 | |||
1330 | case HollowShape.Circle: | ||
1331 | |||
1332 | hollowVolume *= 0.78539816339f; | ||
1333 | break; | ||
1334 | |||
1335 | case HollowShape.Triangle: | ||
1336 | |||
1337 | hollowVolume *= (0.5f * .5f); | ||
1338 | break; | ||
1339 | |||
1340 | default: | ||
1341 | hollowVolume = 0; | ||
1342 | break; | ||
1343 | } | ||
1344 | volume *= (1.0f - hollowVolume); | ||
1345 | } | ||
1346 | } | ||
1347 | |||
1348 | else if (BaseShape.PathCurve == (byte)Extrusion.Curve1) | ||
1349 | { | ||
1350 | //a tube | ||
1351 | |||
1352 | volume *= 0.78539816339e-2f * (float)(200 - BaseShape.PathScaleX); | ||
1353 | tmp= 1.0f -2.0e-2f * (float)(200 - BaseShape.PathScaleY); | ||
1354 | volume -= volume*tmp*tmp; | ||
1355 | |||
1356 | if (hollowAmount > 0.0) | ||
1357 | { | ||
1358 | hollowVolume *= hollowAmount; | ||
1359 | |||
1360 | switch (BaseShape.HollowShape) | ||
1361 | { | ||
1362 | case HollowShape.Square: | ||
1363 | case HollowShape.Same: | ||
1364 | break; | ||
1365 | |||
1366 | case HollowShape.Circle: | ||
1367 | hollowVolume *= 0.78539816339f;; | ||
1368 | break; | ||
1369 | |||
1370 | case HollowShape.Triangle: | ||
1371 | hollowVolume *= 0.5f * 0.5f; | ||
1372 | break; | ||
1373 | default: | ||
1374 | hollowVolume = 0; | ||
1375 | break; | ||
1376 | } | ||
1377 | volume *= (1.0f - hollowVolume); | ||
1378 | } | ||
1379 | } | ||
1380 | |||
1381 | break; | ||
1382 | |||
1383 | case ProfileShape.Circle: | ||
1384 | |||
1385 | if (BaseShape.PathCurve == (byte)Extrusion.Straight) | ||
1386 | { | ||
1387 | volume *= 0.78539816339f; // elipse base | ||
1388 | |||
1389 | if (hollowAmount > 0.0) | ||
1390 | { | ||
1391 | switch (BaseShape.HollowShape) | ||
1392 | { | ||
1393 | case HollowShape.Same: | ||
1394 | case HollowShape.Circle: | ||
1395 | break; | ||
1396 | |||
1397 | case HollowShape.Square: | ||
1398 | hollowVolume *= 0.5f * 2.5984480504799f; | ||
1399 | break; | ||
1400 | |||
1401 | case HollowShape.Triangle: | ||
1402 | hollowVolume *= .5f * 1.27323954473516f; | ||
1403 | break; | ||
1404 | |||
1405 | default: | ||
1406 | hollowVolume = 0; | ||
1407 | break; | ||
1408 | } | ||
1409 | volume *= (1.0f - hollowVolume); | ||
1410 | } | ||
1411 | } | ||
1412 | |||
1413 | else if (BaseShape.PathCurve == (byte)Extrusion.Curve1) | ||
1414 | { | ||
1415 | volume *= 0.61685027506808491367715568749226e-2f * (float)(200 - BaseShape.PathScaleX); | ||
1416 | tmp = 1.0f - .02f * (float)(200 - BaseShape.PathScaleY); | ||
1417 | volume *= (1.0f - tmp * tmp); | ||
1418 | |||
1419 | if (hollowAmount > 0.0) | ||
1420 | { | ||
1421 | |||
1422 | // calculate the hollow volume by it's shape compared to the prim shape | ||
1423 | hollowVolume *= hollowAmount; | ||
1424 | |||
1425 | switch (BaseShape.HollowShape) | ||
1426 | { | ||
1427 | case HollowShape.Same: | ||
1428 | case HollowShape.Circle: | ||
1429 | break; | ||
1430 | |||
1431 | case HollowShape.Square: | ||
1432 | hollowVolume *= 0.5f * 2.5984480504799f; | ||
1433 | break; | ||
1434 | |||
1435 | case HollowShape.Triangle: | ||
1436 | hollowVolume *= .5f * 1.27323954473516f; | ||
1437 | break; | ||
1438 | |||
1439 | default: | ||
1440 | hollowVolume = 0; | ||
1441 | break; | ||
1442 | } | ||
1443 | volume *= (1.0f - hollowVolume); | ||
1444 | } | ||
1445 | } | ||
1446 | break; | ||
1447 | |||
1448 | case ProfileShape.HalfCircle: | ||
1449 | if (BaseShape.PathCurve == (byte)Extrusion.Curve1) | ||
1450 | { | ||
1451 | volume *= 0.52359877559829887307710723054658f; | ||
1452 | } | ||
1453 | break; | ||
1454 | |||
1455 | case ProfileShape.EquilateralTriangle: | ||
1456 | |||
1457 | if (BaseShape.PathCurve == (byte)Extrusion.Straight) | ||
1458 | { | ||
1459 | volume *= 0.32475953f; | ||
1460 | |||
1461 | if (hollowAmount > 0.0) | ||
1462 | { | ||
1463 | |||
1464 | // calculate the hollow volume by it's shape compared to the prim shape | ||
1465 | switch (BaseShape.HollowShape) | ||
1466 | { | ||
1467 | case HollowShape.Same: | ||
1468 | case HollowShape.Triangle: | ||
1469 | hollowVolume *= .25f; | ||
1470 | break; | ||
1471 | |||
1472 | case HollowShape.Square: | ||
1473 | hollowVolume *= 0.499849f * 3.07920140172638f; | ||
1474 | break; | ||
1475 | |||
1476 | case HollowShape.Circle: | ||
1477 | // Hollow shape is a perfect cyllinder in respect to the cube's scale | ||
1478 | // Cyllinder hollow volume calculation | ||
1479 | |||
1480 | hollowVolume *= 0.1963495f * 3.07920140172638f; | ||
1481 | break; | ||
1482 | |||
1483 | default: | ||
1484 | hollowVolume = 0; | ||
1485 | break; | ||
1486 | } | ||
1487 | volume *= (1.0f - hollowVolume); | ||
1488 | } | ||
1489 | } | ||
1490 | else if (BaseShape.PathCurve == (byte)Extrusion.Curve1) | ||
1491 | { | ||
1492 | volume *= 0.32475953f; | ||
1493 | volume *= 0.01f * (float)(200 - BaseShape.PathScaleX); | ||
1494 | tmp = 1.0f - .02f * (float)(200 - BaseShape.PathScaleY); | ||
1495 | volume *= (1.0f - tmp * tmp); | ||
1496 | |||
1497 | if (hollowAmount > 0.0) | ||
1498 | { | ||
1499 | |||
1500 | hollowVolume *= hollowAmount; | ||
1501 | |||
1502 | switch (BaseShape.HollowShape) | ||
1503 | { | ||
1504 | case HollowShape.Same: | ||
1505 | case HollowShape.Triangle: | ||
1506 | hollowVolume *= .25f; | ||
1507 | break; | ||
1508 | |||
1509 | case HollowShape.Square: | ||
1510 | hollowVolume *= 0.499849f * 3.07920140172638f; | ||
1511 | break; | ||
1512 | |||
1513 | case HollowShape.Circle: | ||
1514 | |||
1515 | hollowVolume *= 0.1963495f * 3.07920140172638f; | ||
1516 | break; | ||
1517 | |||
1518 | default: | ||
1519 | hollowVolume = 0; | ||
1520 | break; | ||
1521 | } | ||
1522 | volume *= (1.0f - hollowVolume); | ||
1523 | } | ||
1524 | } | ||
1525 | break; | ||
1526 | |||
1527 | default: | ||
1528 | break; | ||
1529 | } | ||
1530 | |||
1531 | |||
1532 | |||
1533 | float taperX1; | ||
1534 | float taperY1; | ||
1535 | float taperX; | ||
1536 | float taperY; | ||
1537 | float pathBegin; | ||
1538 | float pathEnd; | ||
1539 | float profileBegin; | ||
1540 | float profileEnd; | ||
1541 | |||
1542 | if (BaseShape.PathCurve == (byte)Extrusion.Straight || BaseShape.PathCurve == (byte)Extrusion.Flexible) | ||
1543 | { | ||
1544 | taperX1 = BaseShape.PathScaleX * 0.01f; | ||
1545 | if (taperX1 > 1.0f) | ||
1546 | taperX1 = 2.0f - taperX1; | ||
1547 | taperX = 1.0f - taperX1; | ||
1548 | |||
1549 | taperY1 = BaseShape.PathScaleY * 0.01f; | ||
1550 | if (taperY1 > 1.0f) | ||
1551 | taperY1 = 2.0f - taperY1; | ||
1552 | taperY = 1.0f - taperY1; | ||
1553 | } | ||
1554 | else | ||
1555 | { | ||
1556 | taperX = BaseShape.PathTaperX * 0.01f; | ||
1557 | if (taperX < 0.0f) | ||
1558 | taperX = -taperX; | ||
1559 | taperX1 = 1.0f - taperX; | ||
1560 | |||
1561 | taperY = BaseShape.PathTaperY * 0.01f; | ||
1562 | if (taperY < 0.0f) | ||
1563 | taperY = -taperY; | ||
1564 | taperY1 = 1.0f - taperY; | ||
1565 | |||
1566 | } | ||
1567 | |||
1568 | |||
1569 | volume *= (taperX1 * taperY1 + 0.5f * (taperX1 * taperY + taperX * taperY1) + 0.3333333333f * taperX * taperY); | ||
1570 | |||
1571 | pathBegin = (float)BaseShape.PathBegin * 2.0e-5f; | ||
1572 | pathEnd = 1.0f - (float)BaseShape.PathEnd * 2.0e-5f; | ||
1573 | volume *= (pathEnd - pathBegin); | ||
1574 | |||
1575 | // this is crude aproximation | ||
1576 | profileBegin = (float)BaseShape.ProfileBegin * 2.0e-5f; | ||
1577 | profileEnd = 1.0f - (float)BaseShape.ProfileEnd * 2.0e-5f; | ||
1578 | volume *= (profileEnd - profileBegin); | ||
1579 | |||
1580 | returnMass = Density * BSParam.DensityScaleFactor * volume; | ||
1581 | |||
1582 | returnMass = Util.Clamp(returnMass, BSParam.MinimumObjectMass, BSParam.MaximumObjectMass); | ||
1583 | // DetailLog("{0},BSPrim.CalculateMass,den={1},vol={2},mass={3}", LocalID, Density, volume, returnMass); | ||
1584 | DetailLog("{0},BSPrim.CalculateMass,den={1},vol={2},mass={3},pathB={4},pathE={5},profB={6},profE={7},siz={8}", | ||
1585 | LocalID, Density, volume, returnMass, pathBegin, pathEnd, profileBegin, profileEnd, _size); | ||
1586 | |||
1587 | return returnMass; | ||
1588 | }// end CalculateMass | ||
1589 | #endregion Mass Calculation | ||
1590 | |||
1591 | // Rebuild the geometry and object. | ||
1592 | // This is called when the shape changes so we need to recreate the mesh/hull. | ||
1593 | // Called at taint-time!!! | ||
1594 | public void CreateGeomAndObject(bool forceRebuild) | ||
1595 | { | ||
1596 | // Create the correct physical representation for this type of object. | ||
1597 | // Updates base.PhysBody and base.PhysShape with the new information. | ||
1598 | // Ignore 'forceRebuild'. 'GetBodyAndShape' makes the right choices and changes of necessary. | ||
1599 | PhysScene.Shapes.GetBodyAndShape(false /*forceRebuild */, PhysScene.World, this, delegate(BulletBody pBody, BulletShape pShape) | ||
1600 | { | ||
1601 | // Called if the current prim body is about to be destroyed. | ||
1602 | // Remove all the physical dependencies on the old body. | ||
1603 | // (Maybe someday make the changing of BSShape an event to be subscribed to by BSLinkset, ...) | ||
1604 | // Note: this virtual function is overloaded by BSPrimLinkable to remove linkset constraints. | ||
1605 | RemoveDependencies(); | ||
1606 | }); | ||
1607 | |||
1608 | // Make sure the properties are set on the new object | ||
1609 | UpdatePhysicalParameters(); | ||
1610 | return; | ||
1611 | } | ||
1612 | |||
1613 | // Called at taint-time | ||
1614 | protected virtual void RemoveDependencies() | ||
1615 | { | ||
1616 | PhysicalActors.RemoveDependencies(); | ||
1617 | } | ||
1618 | |||
1619 | #region Extension | ||
1620 | public override object Extension(string pFunct, params object[] pParams) | ||
1621 | { | ||
1622 | DetailLog("{0} BSPrim.Extension,op={1}", LocalID, pFunct); | ||
1623 | object ret = null; | ||
1624 | switch (pFunct) | ||
1625 | { | ||
1626 | case ExtendedPhysics.PhysFunctAxisLockLimits: | ||
1627 | ret = SetAxisLockLimitsExtension(pParams); | ||
1628 | break; | ||
1629 | default: | ||
1630 | ret = base.Extension(pFunct, pParams); | ||
1631 | break; | ||
1632 | } | ||
1633 | return ret; | ||
1634 | } | ||
1635 | |||
1636 | private void InitializeAxisActor() | ||
1637 | { | ||
1638 | EnableActor(LockedAngularAxis != LockedAxisFree || LockedLinearAxis != LockedAxisFree, | ||
1639 | LockedAxisActorName, delegate() | ||
1640 | { | ||
1641 | return new BSActorLockAxis(PhysScene, this, LockedAxisActorName); | ||
1642 | }); | ||
1643 | |||
1644 | // Update parameters so the new actor's Refresh() action is called at the right time. | ||
1645 | PhysScene.TaintedObject(LocalID, "BSPrim.LockAxis", delegate() | ||
1646 | { | ||
1647 | UpdatePhysicalParameters(); | ||
1648 | }); | ||
1649 | } | ||
1650 | |||
1651 | // Passed an array of an array of parameters, set the axis locking. | ||
1652 | // This expects an int (PHYS_AXIS_*) followed by none or two limit floats | ||
1653 | // followed by another int and floats, etc. | ||
1654 | private object SetAxisLockLimitsExtension(object[] pParams) | ||
1655 | { | ||
1656 | DetailLog("{0} SetAxisLockLimitsExtension. parmlen={1}", LocalID, pParams.GetLength(0)); | ||
1657 | object ret = null; | ||
1658 | try | ||
1659 | { | ||
1660 | if (pParams.GetLength(0) > 1) | ||
1661 | { | ||
1662 | int index = 2; | ||
1663 | while (index < pParams.GetLength(0)) | ||
1664 | { | ||
1665 | var funct = pParams[index]; | ||
1666 | DetailLog("{0} SetAxisLockLimitsExtension. op={1}, index={2}", LocalID, funct, index); | ||
1667 | if (funct is Int32 || funct is Int64) | ||
1668 | { | ||
1669 | switch ((int)funct) | ||
1670 | { | ||
1671 | // Those that take no parameters | ||
1672 | case ExtendedPhysics.PHYS_AXIS_LOCK_LINEAR: | ||
1673 | case ExtendedPhysics.PHYS_AXIS_LOCK_LINEAR_X: | ||
1674 | case ExtendedPhysics.PHYS_AXIS_LOCK_LINEAR_Y: | ||
1675 | case ExtendedPhysics.PHYS_AXIS_LOCK_LINEAR_Z: | ||
1676 | case ExtendedPhysics.PHYS_AXIS_LOCK_ANGULAR: | ||
1677 | case ExtendedPhysics.PHYS_AXIS_LOCK_ANGULAR_X: | ||
1678 | case ExtendedPhysics.PHYS_AXIS_LOCK_ANGULAR_Y: | ||
1679 | case ExtendedPhysics.PHYS_AXIS_LOCK_ANGULAR_Z: | ||
1680 | case ExtendedPhysics.PHYS_AXIS_UNLOCK_LINEAR: | ||
1681 | case ExtendedPhysics.PHYS_AXIS_UNLOCK_LINEAR_X: | ||
1682 | case ExtendedPhysics.PHYS_AXIS_UNLOCK_LINEAR_Y: | ||
1683 | case ExtendedPhysics.PHYS_AXIS_UNLOCK_LINEAR_Z: | ||
1684 | case ExtendedPhysics.PHYS_AXIS_UNLOCK_ANGULAR: | ||
1685 | case ExtendedPhysics.PHYS_AXIS_UNLOCK_ANGULAR_X: | ||
1686 | case ExtendedPhysics.PHYS_AXIS_UNLOCK_ANGULAR_Y: | ||
1687 | case ExtendedPhysics.PHYS_AXIS_UNLOCK_ANGULAR_Z: | ||
1688 | case ExtendedPhysics.PHYS_AXIS_UNLOCK: | ||
1689 | ApplyAxisLimits((int)funct, 0f, 0f); | ||
1690 | index += 1; | ||
1691 | break; | ||
1692 | // Those that take two parameters (the limits) | ||
1693 | case ExtendedPhysics.PHYS_AXIS_LIMIT_LINEAR_X: | ||
1694 | case ExtendedPhysics.PHYS_AXIS_LIMIT_LINEAR_Y: | ||
1695 | case ExtendedPhysics.PHYS_AXIS_LIMIT_LINEAR_Z: | ||
1696 | case ExtendedPhysics.PHYS_AXIS_LIMIT_ANGULAR_X: | ||
1697 | case ExtendedPhysics.PHYS_AXIS_LIMIT_ANGULAR_Y: | ||
1698 | case ExtendedPhysics.PHYS_AXIS_LIMIT_ANGULAR_Z: | ||
1699 | ApplyAxisLimits((int)funct, (float)pParams[index + 1], (float)pParams[index + 2]); | ||
1700 | index += 3; | ||
1701 | break; | ||
1702 | default: | ||
1703 | m_log.WarnFormat("{0} SetSxisLockLimitsExtension. Unknown op={1}", LogHeader, funct); | ||
1704 | index += 1; | ||
1705 | break; | ||
1706 | } | ||
1707 | } | ||
1708 | } | ||
1709 | InitializeAxisActor(); | ||
1710 | ret = (object)index; | ||
1711 | } | ||
1712 | } | ||
1713 | catch (Exception e) | ||
1714 | { | ||
1715 | m_log.WarnFormat("{0} SetSxisLockLimitsExtension exception in object {1}: {2}", LogHeader, this.Name, e); | ||
1716 | ret = null; | ||
1717 | } | ||
1718 | return ret; // not implemented yet | ||
1719 | } | ||
1720 | |||
1721 | // Set the locking parameters. | ||
1722 | // If an axis is locked, the limits for the axis are set to zero, | ||
1723 | // If the axis is being constrained, the high and low value are passed and set. | ||
1724 | // When done here, LockedXXXAxis flags are set and LockedXXXAxixLow/High are set to the range. | ||
1725 | protected void ApplyAxisLimits(int funct, float low, float high) | ||
1726 | { | ||
1727 | DetailLog("{0} ApplyAxisLimits. op={1}, low={2}, high={3}", LocalID, funct, low, high); | ||
1728 | float linearMax = 23000f; | ||
1729 | float angularMax = (float)Math.PI; | ||
1730 | |||
1731 | switch (funct) | ||
1732 | { | ||
1733 | case ExtendedPhysics.PHYS_AXIS_LOCK_LINEAR: | ||
1734 | this.LockedLinearAxis = new OMV.Vector3(LockedAxis, LockedAxis, LockedAxis); | ||
1735 | this.LockedLinearAxisLow = OMV.Vector3.Zero; | ||
1736 | this.LockedLinearAxisHigh = OMV.Vector3.Zero; | ||
1737 | break; | ||
1738 | case ExtendedPhysics.PHYS_AXIS_LOCK_LINEAR_X: | ||
1739 | this.LockedLinearAxis.X = LockedAxis; | ||
1740 | this.LockedLinearAxisLow.X = 0f; | ||
1741 | this.LockedLinearAxisHigh.X = 0f; | ||
1742 | break; | ||
1743 | case ExtendedPhysics.PHYS_AXIS_LIMIT_LINEAR_X: | ||
1744 | this.LockedLinearAxis.X = LockedAxis; | ||
1745 | this.LockedLinearAxisLow.X = Util.Clip(low, -linearMax, linearMax); | ||
1746 | this.LockedLinearAxisHigh.X = Util.Clip(high, -linearMax, linearMax); | ||
1747 | break; | ||
1748 | case ExtendedPhysics.PHYS_AXIS_LOCK_LINEAR_Y: | ||
1749 | this.LockedLinearAxis.Y = LockedAxis; | ||
1750 | this.LockedLinearAxisLow.Y = 0f; | ||
1751 | this.LockedLinearAxisHigh.Y = 0f; | ||
1752 | break; | ||
1753 | case ExtendedPhysics.PHYS_AXIS_LIMIT_LINEAR_Y: | ||
1754 | this.LockedLinearAxis.Y = LockedAxis; | ||
1755 | this.LockedLinearAxisLow.Y = Util.Clip(low, -linearMax, linearMax); | ||
1756 | this.LockedLinearAxisHigh.Y = Util.Clip(high, -linearMax, linearMax); | ||
1757 | break; | ||
1758 | case ExtendedPhysics.PHYS_AXIS_LOCK_LINEAR_Z: | ||
1759 | this.LockedLinearAxis.Z = LockedAxis; | ||
1760 | this.LockedLinearAxisLow.Z = 0f; | ||
1761 | this.LockedLinearAxisHigh.Z = 0f; | ||
1762 | break; | ||
1763 | case ExtendedPhysics.PHYS_AXIS_LIMIT_LINEAR_Z: | ||
1764 | this.LockedLinearAxis.Z = LockedAxis; | ||
1765 | this.LockedLinearAxisLow.Z = Util.Clip(low, -linearMax, linearMax); | ||
1766 | this.LockedLinearAxisHigh.Z = Util.Clip(high, -linearMax, linearMax); | ||
1767 | break; | ||
1768 | case ExtendedPhysics.PHYS_AXIS_LOCK_ANGULAR: | ||
1769 | this.LockedAngularAxis = new OMV.Vector3(LockedAxis, LockedAxis, LockedAxis); | ||
1770 | this.LockedAngularAxisLow = OMV.Vector3.Zero; | ||
1771 | this.LockedAngularAxisHigh = OMV.Vector3.Zero; | ||
1772 | break; | ||
1773 | case ExtendedPhysics.PHYS_AXIS_LOCK_ANGULAR_X: | ||
1774 | this.LockedAngularAxis.X = LockedAxis; | ||
1775 | this.LockedAngularAxisLow.X = 0; | ||
1776 | this.LockedAngularAxisHigh.X = 0; | ||
1777 | break; | ||
1778 | case ExtendedPhysics.PHYS_AXIS_LIMIT_ANGULAR_X: | ||
1779 | this.LockedAngularAxis.X = LockedAxis; | ||
1780 | this.LockedAngularAxisLow.X = Util.Clip(low, -angularMax, angularMax); | ||
1781 | this.LockedAngularAxisHigh.X = Util.Clip(high, -angularMax, angularMax); | ||
1782 | break; | ||
1783 | case ExtendedPhysics.PHYS_AXIS_LOCK_ANGULAR_Y: | ||
1784 | this.LockedAngularAxis.Y = LockedAxis; | ||
1785 | this.LockedAngularAxisLow.Y = 0; | ||
1786 | this.LockedAngularAxisHigh.Y = 0; | ||
1787 | break; | ||
1788 | case ExtendedPhysics.PHYS_AXIS_LIMIT_ANGULAR_Y: | ||
1789 | this.LockedAngularAxis.Y = LockedAxis; | ||
1790 | this.LockedAngularAxisLow.Y = Util.Clip(low, -angularMax, angularMax); | ||
1791 | this.LockedAngularAxisHigh.Y = Util.Clip(high, -angularMax, angularMax); | ||
1792 | break; | ||
1793 | case ExtendedPhysics.PHYS_AXIS_LOCK_ANGULAR_Z: | ||
1794 | this.LockedAngularAxis.Z = LockedAxis; | ||
1795 | this.LockedAngularAxisLow.Z = 0; | ||
1796 | this.LockedAngularAxisHigh.Z = 0; | ||
1797 | break; | ||
1798 | case ExtendedPhysics.PHYS_AXIS_LIMIT_ANGULAR_Z: | ||
1799 | this.LockedAngularAxis.Z = LockedAxis; | ||
1800 | this.LockedAngularAxisLow.Z = Util.Clip(low, -angularMax, angularMax); | ||
1801 | this.LockedAngularAxisHigh.Z = Util.Clip(high, -angularMax, angularMax); | ||
1802 | break; | ||
1803 | case ExtendedPhysics.PHYS_AXIS_UNLOCK_LINEAR: | ||
1804 | this.LockedLinearAxis = LockedAxisFree; | ||
1805 | this.LockedLinearAxisLow = new OMV.Vector3(-linearMax, -linearMax, -linearMax); | ||
1806 | this.LockedLinearAxisHigh = new OMV.Vector3(linearMax, linearMax, linearMax); | ||
1807 | break; | ||
1808 | case ExtendedPhysics.PHYS_AXIS_UNLOCK_LINEAR_X: | ||
1809 | this.LockedLinearAxis.X = FreeAxis; | ||
1810 | this.LockedLinearAxisLow.X = -linearMax; | ||
1811 | this.LockedLinearAxisHigh.X = linearMax; | ||
1812 | break; | ||
1813 | case ExtendedPhysics.PHYS_AXIS_UNLOCK_LINEAR_Y: | ||
1814 | this.LockedLinearAxis.Y = FreeAxis; | ||
1815 | this.LockedLinearAxisLow.Y = -linearMax; | ||
1816 | this.LockedLinearAxisHigh.Y = linearMax; | ||
1817 | break; | ||
1818 | case ExtendedPhysics.PHYS_AXIS_UNLOCK_LINEAR_Z: | ||
1819 | this.LockedLinearAxis.Z = FreeAxis; | ||
1820 | this.LockedLinearAxisLow.Z = -linearMax; | ||
1821 | this.LockedLinearAxisHigh.Z = linearMax; | ||
1822 | break; | ||
1823 | case ExtendedPhysics.PHYS_AXIS_UNLOCK_ANGULAR: | ||
1824 | this.LockedAngularAxis = LockedAxisFree; | ||
1825 | this.LockedAngularAxisLow = new OMV.Vector3(-angularMax, -angularMax, -angularMax); | ||
1826 | this.LockedAngularAxisHigh = new OMV.Vector3(angularMax, angularMax, angularMax); | ||
1827 | break; | ||
1828 | case ExtendedPhysics.PHYS_AXIS_UNLOCK_ANGULAR_X: | ||
1829 | this.LockedAngularAxis.X = FreeAxis; | ||
1830 | this.LockedAngularAxisLow.X = -angularMax; | ||
1831 | this.LockedAngularAxisHigh.X = angularMax; | ||
1832 | break; | ||
1833 | case ExtendedPhysics.PHYS_AXIS_UNLOCK_ANGULAR_Y: | ||
1834 | this.LockedAngularAxis.Y = FreeAxis; | ||
1835 | this.LockedAngularAxisLow.Y = -angularMax; | ||
1836 | this.LockedAngularAxisHigh.Y = angularMax; | ||
1837 | break; | ||
1838 | case ExtendedPhysics.PHYS_AXIS_UNLOCK_ANGULAR_Z: | ||
1839 | this.LockedAngularAxis.Z = FreeAxis; | ||
1840 | this.LockedAngularAxisLow.Z = -angularMax; | ||
1841 | this.LockedAngularAxisHigh.Z = angularMax; | ||
1842 | break; | ||
1843 | case ExtendedPhysics.PHYS_AXIS_UNLOCK: | ||
1844 | ApplyAxisLimits(ExtendedPhysics.PHYS_AXIS_UNLOCK_LINEAR, 0f, 0f); | ||
1845 | ApplyAxisLimits(ExtendedPhysics.PHYS_AXIS_UNLOCK_ANGULAR, 0f, 0f); | ||
1846 | break; | ||
1847 | default: | ||
1848 | break; | ||
1849 | } | ||
1850 | return; | ||
1851 | } | ||
1852 | #endregion // Extension | ||
1853 | |||
1854 | // The physics engine says that properties have updated. Update same and inform | ||
1855 | // the world that things have changed. | ||
1856 | // NOTE: BSPrim.UpdateProperties is overloaded by BSPrimLinkable which modifies updates from root and children prims. | ||
1857 | // NOTE: BSPrim.UpdateProperties is overloaded by BSPrimDisplaced which handles mapping physical position to simulator position. | ||
1858 | public override void UpdateProperties(EntityProperties entprop) | ||
1859 | { | ||
1860 | // Let anyone (like the actors) modify the updated properties before they are pushed into the object and the simulator. | ||
1861 | TriggerPreUpdatePropertyAction(ref entprop); | ||
1862 | |||
1863 | // DetailLog("{0},BSPrim.UpdateProperties,entry,entprop={1}", LocalID, entprop); // DEBUG DEBUG | ||
1864 | |||
1865 | // Assign directly to the local variables so the normal set actions do not happen | ||
1866 | RawPosition = entprop.Position; | ||
1867 | RawOrientation = entprop.Rotation; | ||
1868 | // DEBUG DEBUG DEBUG -- smooth velocity changes a bit. The simulator seems to be | ||
1869 | // very sensitive to velocity changes. | ||
1870 | if (entprop.Velocity == OMV.Vector3.Zero || !entprop.Velocity.ApproxEquals(RawVelocity, BSParam.UpdateVelocityChangeThreshold)) | ||
1871 | RawVelocity = entprop.Velocity; | ||
1872 | _acceleration = entprop.Acceleration; | ||
1873 | _rotationalVelocity = entprop.RotationalVelocity; | ||
1874 | |||
1875 | // DetailLog("{0},BSPrim.UpdateProperties,afterAssign,entprop={1}", LocalID, entprop); // DEBUG DEBUG | ||
1876 | |||
1877 | // The sanity check can change the velocity and/or position. | ||
1878 | if (PositionSanityCheck(true /* inTaintTime */ )) | ||
1879 | { | ||
1880 | entprop.Position = RawPosition; | ||
1881 | entprop.Velocity = RawVelocity; | ||
1882 | entprop.RotationalVelocity = _rotationalVelocity; | ||
1883 | entprop.Acceleration = _acceleration; | ||
1884 | } | ||
1885 | |||
1886 | OMV.Vector3 direction = OMV.Vector3.UnitX * RawOrientation; // DEBUG DEBUG DEBUG | ||
1887 | DetailLog("{0},BSPrim.UpdateProperties,call,entProp={1},dir={2}", LocalID, entprop, direction); | ||
1888 | |||
1889 | // remember the current and last set values | ||
1890 | LastEntityProperties = CurrentEntityProperties; | ||
1891 | CurrentEntityProperties = entprop; | ||
1892 | |||
1893 | PhysScene.PostUpdate(this); | ||
1894 | } | ||
1895 | } | ||
1896 | } | ||