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