aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs')
-rw-r--r--OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs1267
1 files changed, 1267 insertions, 0 deletions
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs
new file mode 100644
index 0000000..cc414e9
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs
@@ -0,0 +1,1267 @@
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 */
27using System;
28using System.Reflection;
29using System.Collections.Generic;
30using System.Xml;
31using log4net;
32using OMV = OpenMetaverse;
33using OpenSim.Framework;
34using OpenSim.Region.Physics.Manager;
35using OpenSim.Region.Physics.ConvexDecompositionDotNet;
36
37namespace OpenSim.Region.Physics.BulletSPlugin
38{
39 [Serializable]
40public sealed class BSPrim : PhysicsActor
41{
42 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
43 private static readonly string LogHeader = "[BULLETS PRIM]";
44
45 private IMesh _mesh;
46 private PrimitiveBaseShape _pbs;
47 private ShapeData.PhysicsShapeType _shapeType;
48 private ulong _hullKey;
49 private List<ConvexResult> _hulls;
50
51 private BSScene _scene;
52 private String _avName;
53 private uint _localID = 0;
54
55 private OMV.Vector3 _size;
56 private OMV.Vector3 _scale;
57 private bool _stopped;
58 private bool _grabbed;
59 private bool _isSelected;
60 private bool _isVolumeDetect;
61 private OMV.Vector3 _position;
62 private float _mass;
63 private float _density;
64 private OMV.Vector3 _force;
65 private OMV.Vector3 _velocity;
66 private OMV.Vector3 _torque;
67 private float _collisionScore;
68 private OMV.Vector3 _acceleration;
69 private OMV.Quaternion _orientation;
70 private int _physicsActorType;
71 private bool _isPhysical;
72 private bool _flying;
73 private float _friction;
74 private float _restitution;
75 private bool _setAlwaysRun;
76 private bool _throttleUpdates;
77 private bool _isColliding;
78 private bool _collidingGround;
79 private bool _collidingObj;
80 private bool _floatOnWater;
81 private OMV.Vector3 _rotationalVelocity;
82 private bool _kinematic;
83 private float _buoyancy;
84 private OMV.Vector3 _angularVelocity;
85
86 private List<BSPrim> _childrenPrims;
87 private BSPrim _parentPrim;
88
89 private int _subscribedEventsMs = 0;
90 private int _lastCollisionTime = 0;
91 long _collidingStep;
92 long _collidingGroundStep;
93
94 private BSDynamics _vehicle;
95
96 private OMV.Vector3 _PIDTarget;
97 private bool _usePID;
98 private float _PIDTau;
99 private bool _useHoverPID;
100 private float _PIDHoverHeight;
101 private PIDHoverType _PIDHoverType;
102 private float _PIDHoverTao;
103
104 public BSPrim(uint localID, String primName, BSScene parent_scene, OMV.Vector3 pos, OMV.Vector3 size,
105 OMV.Quaternion rotation, PrimitiveBaseShape pbs, bool pisPhysical)
106 {
107 // m_log.DebugFormat("{0}: BSPrim creation of {1}, id={2}", LogHeader, primName, localID);
108 _localID = localID;
109 _avName = primName;
110 _scene = parent_scene;
111 _position = pos;
112 _size = size;
113 _scale = new OMV.Vector3(1f, 1f, 1f); // the scale will be set by CreateGeom depending on object type
114 _orientation = rotation;
115 _buoyancy = 1f;
116 _velocity = OMV.Vector3.Zero;
117 _rotationalVelocity = OMV.Vector3.Zero;
118 _angularVelocity = OMV.Vector3.Zero;
119 _hullKey = 0;
120 _pbs = pbs;
121 _isPhysical = pisPhysical;
122 _isVolumeDetect = false;
123 _subscribedEventsMs = 0;
124 _friction = _scene.Params.defaultFriction; // TODO: compute based on object material
125 _density = _scene.Params.defaultDensity; // TODO: compute based on object material
126 _restitution = _scene.Params.defaultRestitution;
127 _parentPrim = null; // not a child or a parent
128 _vehicle = new BSDynamics(this); // add vehicleness
129 _childrenPrims = new List<BSPrim>();
130 if (_isPhysical)
131 _mass = CalculateMass();
132 else
133 _mass = 0f;
134 // do the actual object creation at taint time
135 _scene.TaintedObject(delegate()
136 {
137 RecreateGeomAndObject();
138 });
139 }
140
141 // called when this prim is being destroyed and we should free all the resources
142 public void Destroy()
143 {
144 // m_log.DebugFormat("{0}: Destroy", LogHeader);
145 // Undo any vehicle properties
146 _vehicle.ProcessTypeChange(Vehicle.TYPE_NONE);
147 _scene.RemoveVehiclePrim(this); // just to make sure
148 _scene.TaintedObject(delegate()
149 {
150 BulletSimAPI.DestroyObject(_scene.WorldID, _localID);
151 });
152 }
153
154 public override bool Stopped {
155 get { return _stopped; }
156 }
157 public override OMV.Vector3 Size {
158 get { return _size; }
159 set {
160 _size = value;
161 _scene.TaintedObject(delegate()
162 {
163 if (_isPhysical) _mass = CalculateMass(); // changing size changes the mass
164 BulletSimAPI.SetObjectScaleMass(_scene.WorldID, _localID, _scale, _mass, _isPhysical);
165 RecreateGeomAndObject();
166 });
167 }
168 }
169 public override PrimitiveBaseShape Shape {
170 set {
171 _pbs = value;
172 _scene.TaintedObject(delegate()
173 {
174 if (_isPhysical) _mass = CalculateMass(); // changing the shape changes the mass
175 RecreateGeomAndObject();
176 });
177 }
178 }
179 public override uint LocalID {
180 set { _localID = value; }
181 get { return _localID; }
182 }
183 public override bool Grabbed {
184 set { _grabbed = value;
185 }
186 }
187 public override bool Selected {
188 set {
189 _isSelected = value;
190 _scene.TaintedObject(delegate()
191 {
192 SetObjectDynamic();
193 });
194 }
195 }
196 public override void CrossingFailure() { return; }
197
198 // link me to the specified parent
199 public override void link(PhysicsActor obj) {
200 BSPrim parent = (BSPrim)obj;
201 // m_log.DebugFormat("{0}: link {1}/{2} to {3}", LogHeader, _avName, _localID, obj.LocalID);
202 // TODO: decide if this parent checking needs to happen at taint time
203 if (_parentPrim == null)
204 {
205 if (parent != null)
206 {
207 // I don't have a parent so I am joining a linkset
208 parent.AddChildToLinkset(this);
209 }
210 }
211 else
212 {
213 // I already have a parent, is parenting changing?
214 if (parent != _parentPrim)
215 {
216 if (parent == null)
217 {
218 // we are being removed from a linkset
219 _parentPrim.RemoveChildFromLinkset(this);
220 }
221 else
222 {
223 // asking to reparent a prim should not happen
224 m_log.ErrorFormat("{0}: Reparenting a prim. ", LogHeader);
225 }
226 }
227 }
228 return;
229 }
230
231 // delink me from my linkset
232 public override void delink() {
233 // TODO: decide if this parent checking needs to happen at taint time
234 // Race condition here: if link() and delink() in same simulation tick, the delink will not happen
235 // m_log.DebugFormat("{0}: delink {1}/{2}", LogHeader, _avName, _localID);
236 if (_parentPrim != null)
237 {
238 _parentPrim.RemoveChildFromLinkset(this);
239 }
240 return;
241 }
242
243 // I am the root of a linkset and a new child is being added
244 public void AddChildToLinkset(BSPrim pchild)
245 {
246 BSPrim child = pchild;
247 _scene.TaintedObject(delegate()
248 {
249 if (!_childrenPrims.Contains(child))
250 {
251 _childrenPrims.Add(child);
252 child.ParentPrim = this; // the child has gained a parent
253 RecreateGeomAndObject(); // rebuild my shape with the new child added
254 }
255 });
256 return;
257 }
258
259 // I am the root of a linkset and one of my children is being removed.
260 // Safe to call even if the child is not really in my linkset.
261 public void RemoveChildFromLinkset(BSPrim pchild)
262 {
263 BSPrim child = pchild;
264 _scene.TaintedObject(delegate()
265 {
266 if (_childrenPrims.Contains(child))
267 {
268 BulletSimAPI.RemoveConstraint(_scene.WorldID, child.LocalID, this.LocalID);
269 _childrenPrims.Remove(child);
270 child.ParentPrim = null; // the child has lost its parent
271 RecreateGeomAndObject(); // rebuild my shape with the child removed
272 }
273 else
274 {
275 m_log.ErrorFormat("{0}: Asked to remove child from linkset that was not in linkset");
276 }
277 });
278 return;
279 }
280
281 public BSPrim ParentPrim
282 {
283 set { _parentPrim = value; }
284 }
285
286 public ulong HullKey
287 {
288 get { return _hullKey; }
289 }
290
291 // return true if we are the root of a linkset (there are children to manage)
292 public bool IsRootOfLinkset
293 {
294 get { return (_parentPrim == null && _childrenPrims.Count != 0); }
295 }
296
297 // Set motion values to zero.
298 // Do it to the properties so the values get set in the physics engine.
299 // Push the setting of the values to the viewer.
300 private void ZeroMotion()
301 {
302 Velocity = OMV.Vector3.Zero;
303 _acceleration = OMV.Vector3.Zero;
304 RotationalVelocity = OMV.Vector3.Zero;
305 base.RequestPhysicsterseUpdate();
306 }
307
308 public override void LockAngularMotion(OMV.Vector3 axis) { return; }
309
310 public override OMV.Vector3 Position {
311 get {
312 // don't do the following GetObjectPosition because this function is called a zillion times
313 // _position = BulletSimAPI.GetObjectPosition(_scene.WorldID, _localID);
314 return _position;
315 }
316 set {
317 _position = value;
318 _scene.TaintedObject(delegate()
319 {
320 BulletSimAPI.SetObjectTranslation(_scene.WorldID, _localID, _position, _orientation);
321 // m_log.DebugFormat("{0}: setPosition: id={1}, position={2}", LogHeader, _localID, _position);
322 });
323 }
324 }
325 public override float Mass {
326 get { return _mass; }
327 }
328 public override OMV.Vector3 Force {
329 get { return _force; }
330 set {
331 _force = value;
332 _scene.TaintedObject(delegate()
333 {
334 BulletSimAPI.SetObjectForce(_scene.WorldID, _localID, _force);
335 });
336 }
337 }
338
339 public override int VehicleType {
340 get {
341 return (int)_vehicle.Type; // if we are a vehicle, return that type
342 }
343 set {
344 Vehicle type = (Vehicle)value;
345 _vehicle.ProcessTypeChange(type);
346 _scene.TaintedObject(delegate()
347 {
348 if (type == Vehicle.TYPE_NONE)
349 {
350 _scene.RemoveVehiclePrim(this);
351 }
352 else
353 {
354 // make it so the scene will call us each tick to do vehicle things
355 _scene.AddVehiclePrim(this);
356 }
357 return;
358 });
359 }
360 }
361 public override void VehicleFloatParam(int param, float value)
362 {
363 _vehicle.ProcessFloatVehicleParam((Vehicle)param, value);
364 }
365 public override void VehicleVectorParam(int param, OMV.Vector3 value)
366 {
367 _vehicle.ProcessVectorVehicleParam((Vehicle)param, value);
368 }
369 public override void VehicleRotationParam(int param, OMV.Quaternion rotation)
370 {
371 _vehicle.ProcessRotationVehicleParam((Vehicle)param, rotation);
372 }
373 public override void VehicleFlags(int param, bool remove)
374 {
375 _vehicle.ProcessVehicleFlags(param, remove);
376 }
377 // Called each simulation step to advance vehicle characteristics
378 public void StepVehicle(float timeStep)
379 {
380 _vehicle.Step(timeStep, _scene);
381 }
382
383 // Allows the detection of collisions with inherently non-physical prims. see llVolumeDetect for more
384 public override void SetVolumeDetect(int param) {
385 bool newValue = (param != 0);
386 if (_isVolumeDetect != newValue)
387 {
388 _isVolumeDetect = newValue;
389 _scene.TaintedObject(delegate()
390 {
391 SetObjectDynamic();
392 });
393 }
394 return;
395 }
396
397 public override OMV.Vector3 GeometricCenter { get { return OMV.Vector3.Zero; } }
398 public override OMV.Vector3 CenterOfMass { get { return OMV.Vector3.Zero; } }
399 public override OMV.Vector3 Velocity {
400 get { return _velocity; }
401 set { _velocity = value;
402 _scene.TaintedObject(delegate()
403 {
404 BulletSimAPI.SetObjectVelocity(_scene.WorldID, LocalID, _velocity);
405 });
406 }
407 }
408 public override OMV.Vector3 Torque {
409 get { return _torque; }
410 set { _torque = value;
411 }
412 }
413 public override float CollisionScore {
414 get { return _collisionScore; }
415 set { _collisionScore = value;
416 }
417 }
418 public override OMV.Vector3 Acceleration {
419 get { return _acceleration; }
420 }
421 public override OMV.Quaternion Orientation {
422 get { return _orientation; }
423 set {
424 _orientation = value;
425 // m_log.DebugFormat("{0}: set orientation: id={1}, ori={2}", LogHeader, LocalID, _orientation);
426 _scene.TaintedObject(delegate()
427 {
428 // _position = BulletSimAPI.GetObjectPosition(_scene.WorldID, _localID);
429 BulletSimAPI.SetObjectTranslation(_scene.WorldID, _localID, _position, _orientation);
430 });
431 }
432 }
433 public override int PhysicsActorType {
434 get { return _physicsActorType; }
435 set { _physicsActorType = value;
436 }
437 }
438 public override bool IsPhysical {
439 get { return _isPhysical; }
440 set {
441 _isPhysical = value;
442 _scene.TaintedObject(delegate()
443 {
444 SetObjectDynamic();
445 });
446 }
447 }
448
449 // An object is static (does not move) if selected or not physical
450 private bool IsStatic
451 {
452 get { return _isSelected || !IsPhysical; }
453 }
454
455 // An object is solid if it's not phantom and if it's not doing VolumeDetect
456 private bool IsSolid
457 {
458 get { return !IsPhantom && !_isVolumeDetect; }
459 }
460
461 // make gravity work if the object is physical and not selected
462 // no locking here because only called when it is safe
463 private void SetObjectDynamic()
464 {
465 // non-physical things work best with a mass of zero
466 _mass = IsStatic ? 0f : CalculateMass();
467 BulletSimAPI.SetObjectProperties(_scene.WorldID, LocalID, IsStatic, IsSolid, SubscribedEvents(), _mass);
468 // m_log.DebugFormat("{0}: ID={1}, SetObjectDynamic: IsStatic={2}, IsSolid={3}, mass={4}", LogHeader, _localID, IsStatic, IsSolid, _mass);
469 }
470
471 // prims don't fly
472 public override bool Flying {
473 get { return _flying; }
474 set { _flying = value; }
475 }
476 public override bool SetAlwaysRun {
477 get { return _setAlwaysRun; }
478 set { _setAlwaysRun = value; }
479 }
480 public override bool ThrottleUpdates {
481 get { return _throttleUpdates; }
482 set { _throttleUpdates = value; }
483 }
484 public override bool IsColliding {
485 get { return (_collidingStep == _scene.SimulationStep); }
486 set { _isColliding = value; }
487 }
488 public override bool CollidingGround {
489 get { return (_collidingGroundStep == _scene.SimulationStep); }
490 set { _collidingGround = value; }
491 }
492 public override bool CollidingObj {
493 get { return _collidingObj; }
494 set { _collidingObj = value; }
495 }
496 public bool IsPhantom {
497 get {
498 // SceneObjectPart removes phantom objects from the physics scene
499 // so, although we could implement touching and such, we never
500 // are invoked as a phantom object
501 return false;
502 }
503 }
504 public override bool FloatOnWater {
505 set { _floatOnWater = value; }
506 }
507 public override OMV.Vector3 RotationalVelocity {
508 get { return _rotationalVelocity; }
509 set { _rotationalVelocity = value;
510 // m_log.DebugFormat("{0}: RotationalVelocity={1}", LogHeader, _rotationalVelocity);
511 _scene.TaintedObject(delegate()
512 {
513 BulletSimAPI.SetObjectAngularVelocity(_scene.WorldID, LocalID, _rotationalVelocity);
514 });
515 }
516 }
517 public OMV.Vector3 AngularVelocity {
518 get { return _angularVelocity; }
519 set { _angularVelocity = value; }
520 }
521 public override bool Kinematic {
522 get { return _kinematic; }
523 set { _kinematic = value;
524 // m_log.DebugFormat("{0}: Kinematic={1}", LogHeader, _kinematic);
525 }
526 }
527 public override float Buoyancy {
528 get { return _buoyancy; }
529 set { _buoyancy = value;
530 _scene.TaintedObject(delegate()
531 {
532 BulletSimAPI.SetObjectBuoyancy(_scene.WorldID, _localID, _buoyancy);
533 });
534 }
535 }
536
537 // Used for MoveTo
538 public override OMV.Vector3 PIDTarget {
539 set { _PIDTarget = value; }
540 }
541 public override bool PIDActive {
542 set { _usePID = value; }
543 }
544 public override float PIDTau {
545 set { _PIDTau = value; }
546 }
547
548 // Used for llSetHoverHeight and maybe vehicle height
549 // Hover Height will override MoveTo target's Z
550 public override bool PIDHoverActive {
551 set { _useHoverPID = value; }
552 }
553 public override float PIDHoverHeight {
554 set { _PIDHoverHeight = value; }
555 }
556 public override PIDHoverType PIDHoverType {
557 set { _PIDHoverType = value; }
558 }
559 public override float PIDHoverTau {
560 set { _PIDHoverTao = value; }
561 }
562
563 // For RotLookAt
564 public override OMV.Quaternion APIDTarget { set { return; } }
565 public override bool APIDActive { set { return; } }
566 public override float APIDStrength { set { return; } }
567 public override float APIDDamping { set { return; } }
568
569 public override void AddForce(OMV.Vector3 force, bool pushforce) {
570 if (force.IsFinite())
571 {
572 _force.X += force.X;
573 _force.Y += force.Y;
574 _force.Z += force.Z;
575 }
576 else
577 {
578 m_log.WarnFormat("{0}: Got a NaN force applied to a Character", LogHeader);
579 }
580 _scene.TaintedObject(delegate()
581 {
582 BulletSimAPI.SetObjectForce(_scene.WorldID, _localID, _force);
583 });
584 }
585
586 public override void AddAngularForce(OMV.Vector3 force, bool pushforce) {
587 // m_log.DebugFormat("{0}: AddAngularForce. f={1}, push={2}", LogHeader, force, pushforce);
588 }
589 public override void SetMomentum(OMV.Vector3 momentum) {
590 }
591 public override void SubscribeEvents(int ms) {
592 _subscribedEventsMs = ms;
593 _lastCollisionTime = Util.EnvironmentTickCount() - _subscribedEventsMs; // make first collision happen
594 }
595 public override void UnSubscribeEvents() {
596 _subscribedEventsMs = 0;
597 }
598 public override bool SubscribedEvents() {
599 return (_subscribedEventsMs > 0);
600 }
601
602 #region Mass Calculation
603
604 private float CalculateMass()
605 {
606 float volume = _size.X * _size.Y * _size.Z; // default
607 float tmp;
608
609 float returnMass = 0;
610 float hollowAmount = (float)_pbs.ProfileHollow * 2.0e-5f;
611 float hollowVolume = hollowAmount * hollowAmount;
612
613 switch (_pbs.ProfileShape)
614 {
615 case ProfileShape.Square:
616 // default box
617
618 if (_pbs.PathCurve == (byte)Extrusion.Straight)
619 {
620 if (hollowAmount > 0.0)
621 {
622 switch (_pbs.HollowShape)
623 {
624 case HollowShape.Square:
625 case HollowShape.Same:
626 break;
627
628 case HollowShape.Circle:
629
630 hollowVolume *= 0.78539816339f;
631 break;
632
633 case HollowShape.Triangle:
634
635 hollowVolume *= (0.5f * .5f);
636 break;
637
638 default:
639 hollowVolume = 0;
640 break;
641 }
642 volume *= (1.0f - hollowVolume);
643 }
644 }
645
646 else if (_pbs.PathCurve == (byte)Extrusion.Curve1)
647 {
648 //a tube
649
650 volume *= 0.78539816339e-2f * (float)(200 - _pbs.PathScaleX);
651 tmp= 1.0f -2.0e-2f * (float)(200 - _pbs.PathScaleY);
652 volume -= volume*tmp*tmp;
653
654 if (hollowAmount > 0.0)
655 {
656 hollowVolume *= hollowAmount;
657
658 switch (_pbs.HollowShape)
659 {
660 case HollowShape.Square:
661 case HollowShape.Same:
662 break;
663
664 case HollowShape.Circle:
665 hollowVolume *= 0.78539816339f;;
666 break;
667
668 case HollowShape.Triangle:
669 hollowVolume *= 0.5f * 0.5f;
670 break;
671 default:
672 hollowVolume = 0;
673 break;
674 }
675 volume *= (1.0f - hollowVolume);
676 }
677 }
678
679 break;
680
681 case ProfileShape.Circle:
682
683 if (_pbs.PathCurve == (byte)Extrusion.Straight)
684 {
685 volume *= 0.78539816339f; // elipse base
686
687 if (hollowAmount > 0.0)
688 {
689 switch (_pbs.HollowShape)
690 {
691 case HollowShape.Same:
692 case HollowShape.Circle:
693 break;
694
695 case HollowShape.Square:
696 hollowVolume *= 0.5f * 2.5984480504799f;
697 break;
698
699 case HollowShape.Triangle:
700 hollowVolume *= .5f * 1.27323954473516f;
701 break;
702
703 default:
704 hollowVolume = 0;
705 break;
706 }
707 volume *= (1.0f - hollowVolume);
708 }
709 }
710
711 else if (_pbs.PathCurve == (byte)Extrusion.Curve1)
712 {
713 volume *= 0.61685027506808491367715568749226e-2f * (float)(200 - _pbs.PathScaleX);
714 tmp = 1.0f - .02f * (float)(200 - _pbs.PathScaleY);
715 volume *= (1.0f - tmp * tmp);
716
717 if (hollowAmount > 0.0)
718 {
719
720 // calculate the hollow volume by it's shape compared to the prim shape
721 hollowVolume *= hollowAmount;
722
723 switch (_pbs.HollowShape)
724 {
725 case HollowShape.Same:
726 case HollowShape.Circle:
727 break;
728
729 case HollowShape.Square:
730 hollowVolume *= 0.5f * 2.5984480504799f;
731 break;
732
733 case HollowShape.Triangle:
734 hollowVolume *= .5f * 1.27323954473516f;
735 break;
736
737 default:
738 hollowVolume = 0;
739 break;
740 }
741 volume *= (1.0f - hollowVolume);
742 }
743 }
744 break;
745
746 case ProfileShape.HalfCircle:
747 if (_pbs.PathCurve == (byte)Extrusion.Curve1)
748 {
749 volume *= 0.52359877559829887307710723054658f;
750 }
751 break;
752
753 case ProfileShape.EquilateralTriangle:
754
755 if (_pbs.PathCurve == (byte)Extrusion.Straight)
756 {
757 volume *= 0.32475953f;
758
759 if (hollowAmount > 0.0)
760 {
761
762 // calculate the hollow volume by it's shape compared to the prim shape
763 switch (_pbs.HollowShape)
764 {
765 case HollowShape.Same:
766 case HollowShape.Triangle:
767 hollowVolume *= .25f;
768 break;
769
770 case HollowShape.Square:
771 hollowVolume *= 0.499849f * 3.07920140172638f;
772 break;
773
774 case HollowShape.Circle:
775 // Hollow shape is a perfect cyllinder in respect to the cube's scale
776 // Cyllinder hollow volume calculation
777
778 hollowVolume *= 0.1963495f * 3.07920140172638f;
779 break;
780
781 default:
782 hollowVolume = 0;
783 break;
784 }
785 volume *= (1.0f - hollowVolume);
786 }
787 }
788 else if (_pbs.PathCurve == (byte)Extrusion.Curve1)
789 {
790 volume *= 0.32475953f;
791 volume *= 0.01f * (float)(200 - _pbs.PathScaleX);
792 tmp = 1.0f - .02f * (float)(200 - _pbs.PathScaleY);
793 volume *= (1.0f - tmp * tmp);
794
795 if (hollowAmount > 0.0)
796 {
797
798 hollowVolume *= hollowAmount;
799
800 switch (_pbs.HollowShape)
801 {
802 case HollowShape.Same:
803 case HollowShape.Triangle:
804 hollowVolume *= .25f;
805 break;
806
807 case HollowShape.Square:
808 hollowVolume *= 0.499849f * 3.07920140172638f;
809 break;
810
811 case HollowShape.Circle:
812
813 hollowVolume *= 0.1963495f * 3.07920140172638f;
814 break;
815
816 default:
817 hollowVolume = 0;
818 break;
819 }
820 volume *= (1.0f - hollowVolume);
821 }
822 }
823 break;
824
825 default:
826 break;
827 }
828
829
830
831 float taperX1;
832 float taperY1;
833 float taperX;
834 float taperY;
835 float pathBegin;
836 float pathEnd;
837 float profileBegin;
838 float profileEnd;
839
840 if (_pbs.PathCurve == (byte)Extrusion.Straight || _pbs.PathCurve == (byte)Extrusion.Flexible)
841 {
842 taperX1 = _pbs.PathScaleX * 0.01f;
843 if (taperX1 > 1.0f)
844 taperX1 = 2.0f - taperX1;
845 taperX = 1.0f - taperX1;
846
847 taperY1 = _pbs.PathScaleY * 0.01f;
848 if (taperY1 > 1.0f)
849 taperY1 = 2.0f - taperY1;
850 taperY = 1.0f - taperY1;
851 }
852 else
853 {
854 taperX = _pbs.PathTaperX * 0.01f;
855 if (taperX < 0.0f)
856 taperX = -taperX;
857 taperX1 = 1.0f - taperX;
858
859 taperY = _pbs.PathTaperY * 0.01f;
860 if (taperY < 0.0f)
861 taperY = -taperY;
862 taperY1 = 1.0f - taperY;
863
864 }
865
866
867 volume *= (taperX1 * taperY1 + 0.5f * (taperX1 * taperY + taperX * taperY1) + 0.3333333333f * taperX * taperY);
868
869 pathBegin = (float)_pbs.PathBegin * 2.0e-5f;
870 pathEnd = 1.0f - (float)_pbs.PathEnd * 2.0e-5f;
871 volume *= (pathEnd - pathBegin);
872
873 // this is crude aproximation
874 profileBegin = (float)_pbs.ProfileBegin * 2.0e-5f;
875 profileEnd = 1.0f - (float)_pbs.ProfileEnd * 2.0e-5f;
876 volume *= (profileEnd - profileBegin);
877
878 returnMass = _density * volume;
879
880 if (IsRootOfLinkset)
881 {
882 foreach (BSPrim prim in _childrenPrims)
883 {
884 returnMass += prim.CalculateMass();
885 }
886 }
887
888 if (returnMass <= 0)
889 returnMass = 0.0001f;
890
891 if (returnMass > _scene.MaximumObjectMass)
892 returnMass = _scene.MaximumObjectMass;
893
894 return returnMass;
895 }// end CalculateMass
896 #endregion Mass Calculation
897
898 // Create the geometry information in Bullet for later use
899 // No locking here because this is done when we know physics is not simulating
900 private void CreateGeom()
901 {
902 // Since we're recreating new, get rid of any previously generated shape
903 if (_hullKey != 0)
904 {
905 // m_log.DebugFormat("{0}: CreateGeom: deleting old hull. Key={1}", LogHeader, _hullKey);
906 BulletSimAPI.DestroyHull(_scene.WorldID, _hullKey);
907 _hullKey = 0;
908 _hulls.Clear();
909 }
910
911 if (_mesh == null)
912 {
913 // the mesher thought this was too simple to mesh. Use a native Bullet collision shape.
914 if (_pbs.ProfileShape == ProfileShape.HalfCircle && _pbs.PathCurve == (byte)Extrusion.Curve1)
915 {
916 if (_size.X == _size.Y && _size.Y == _size.Z && _size.X == _size.Z)
917 {
918 // m_log.DebugFormat("{0}: CreateGeom: mesh null. Defaulting to sphere of size {1}", LogHeader, _size);
919 _shapeType = ShapeData.PhysicsShapeType.SHAPE_SPHERE;
920 // Bullet native objects are scaled by the Bullet engine so pass the size in
921 _scale = _size;
922 }
923 }
924 else
925 {
926 // m_log.DebugFormat("{0}: CreateGeom: mesh null. Defaulting to box. lid={1}, size={2}", LogHeader, LocalID, _size);
927 _shapeType = ShapeData.PhysicsShapeType.SHAPE_BOX;
928 _scale = _size;
929 }
930 }
931 else
932 {
933 int[] indices = _mesh.getIndexListAsInt();
934 List<OMV.Vector3> vertices = _mesh.getVertexList();
935
936 //format conversion from IMesh format to DecompDesc format
937 List<int> convIndices = new List<int>();
938 List<float3> convVertices = new List<float3>();
939 for (int ii = 0; ii < indices.GetLength(0); ii++)
940 {
941 convIndices.Add(indices[ii]);
942 }
943 foreach (OMV.Vector3 vv in vertices)
944 {
945 convVertices.Add(new float3(vv.X, vv.Y, vv.Z));
946 }
947
948 // setup and do convex hull conversion
949 _hulls = new List<ConvexResult>();
950 DecompDesc dcomp = new DecompDesc();
951 dcomp.mIndices = convIndices;
952 dcomp.mVertices = convVertices;
953 ConvexBuilder convexBuilder = new ConvexBuilder(HullReturn);
954 // create the hull into the _hulls variable
955 convexBuilder.process(dcomp);
956
957 // Convert the vertices and indices for passing to unmanaged
958 // The hull information is passed as a large floating point array.
959 // The format is:
960 // convHulls[0] = number of hulls
961 // convHulls[1] = number of vertices in first hull
962 // convHulls[2] = hull centroid X coordinate
963 // convHulls[3] = hull centroid Y coordinate
964 // convHulls[4] = hull centroid Z coordinate
965 // convHulls[5] = first hull vertex X
966 // convHulls[6] = first hull vertex Y
967 // convHulls[7] = first hull vertex Z
968 // convHulls[8] = second hull vertex X
969 // ...
970 // convHulls[n] = number of vertices in second hull
971 // convHulls[n+1] = second hull centroid X coordinate
972 // ...
973 //
974 // TODO: is is very inefficient. Someday change the convex hull generator to return
975 // data structures that do not need to be converted in order to pass to Bullet.
976 // And maybe put the values directly into pinned memory rather than marshaling.
977 int hullCount = _hulls.Count;
978 int totalVertices = 1; // include one for the count of the hulls
979 foreach (ConvexResult cr in _hulls)
980 {
981 totalVertices += 4; // add four for the vertex count and centroid
982 totalVertices += cr.HullIndices.Count * 3; // we pass just triangles
983 }
984 float[] convHulls = new float[totalVertices];
985
986 convHulls[0] = (float)hullCount;
987 int jj = 1;
988 foreach (ConvexResult cr in _hulls)
989 {
990 // copy vertices for index access
991 float3[] verts = new float3[cr.HullVertices.Count];
992 int kk = 0;
993 foreach (float3 ff in cr.HullVertices)
994 {
995 verts[kk++] = ff;
996 }
997
998 // add to the array one hull's worth of data
999 convHulls[jj++] = cr.HullIndices.Count;
1000 convHulls[jj++] = 0f; // centroid x,y,z
1001 convHulls[jj++] = 0f;
1002 convHulls[jj++] = 0f;
1003 foreach (int ind in cr.HullIndices)
1004 {
1005 convHulls[jj++] = verts[ind].x;
1006 convHulls[jj++] = verts[ind].y;
1007 convHulls[jj++] = verts[ind].z;
1008 }
1009 }
1010
1011 // create the hull definition in Bullet
1012 _hullKey = (ulong)_pbs.GetHashCode();
1013 // m_log.DebugFormat("{0}: CreateGeom: calling CreateHull. lid={1}, key={2}, hulls={3}", LogHeader, _localID, _hullKey, hullCount);
1014 BulletSimAPI.CreateHull(_scene.WorldID, _hullKey, hullCount, convHulls);
1015 _shapeType = ShapeData.PhysicsShapeType.SHAPE_HULL;
1016 // meshes are already scaled by the meshmerizer
1017 _scale = new OMV.Vector3(1f, 1f, 1f);
1018 }
1019 return;
1020 }
1021
1022 // Callback from convex hull creater with a newly created hull.
1023 // Just add it to the collection of hulls for this shape.
1024 private void HullReturn(ConvexResult result)
1025 {
1026 _hulls.Add(result);
1027 return;
1028 }
1029
1030 // Create an object in Bullet
1031 // No locking here because this is done when the physics engine is not simulating
1032 private void CreateObject()
1033 {
1034 if (IsRootOfLinkset)
1035 {
1036 // Create a linkset around this object
1037 // CreateLinksetWithCompoundHull();
1038 CreateLinksetWithConstraints();
1039 }
1040 else
1041 {
1042 // simple object
1043 ShapeData shape;
1044 FillShapeInfo(out shape);
1045 BulletSimAPI.CreateObject(_scene.WorldID, shape);
1046 }
1047 }
1048
1049 // Create a linkset by creating a compound hull at the root prim that consists of all
1050 // the children.
1051 // NOTE: This does not allow proper collisions with the children prims so it is not a workable solution
1052 void CreateLinksetWithCompoundHull()
1053 {
1054 // If I am the root prim of a linkset, replace my physical shape with all the
1055 // pieces of the children.
1056 // All of the children should have called CreateGeom so they have a hull
1057 // in the physics engine already. Here we pull together all of those hulls
1058 // into one shape.
1059 int totalPrimsInLinkset = _childrenPrims.Count + 1;
1060 // m_log.DebugFormat("{0}: CreateLinkset. Root prim={1}, prims={2}", LogHeader, LocalID, totalPrimsInLinkset);
1061 ShapeData[] shapes = new ShapeData[totalPrimsInLinkset];
1062 FillShapeInfo(out shapes[0]);
1063 int ii = 1;
1064 foreach (BSPrim prim in _childrenPrims)
1065 {
1066 // m_log.DebugFormat("{0}: CreateLinkset: adding prim {1}", LogHeader, prim.LocalID);
1067 prim.FillShapeInfo(out shapes[ii]);
1068 ii++;
1069 }
1070 BulletSimAPI.CreateLinkset(_scene.WorldID, totalPrimsInLinkset, shapes);
1071 }
1072
1073 // Copy prim's info into the BulletSim shape description structure
1074 public void FillShapeInfo(out ShapeData shape)
1075 {
1076 shape.ID = _localID;
1077 shape.Type = _shapeType;
1078 shape.Position = _position;
1079 shape.Rotation = _orientation;
1080 shape.Velocity = _velocity;
1081 shape.Scale = _scale;
1082 shape.Mass = _isPhysical ? _mass : 0f;
1083 shape.Buoyancy = _buoyancy;
1084 shape.MeshKey = _hullKey;
1085 shape.Friction = _friction;
1086 shape.Restitution = _restitution;
1087 shape.Collidable = (!IsPhantom) ? ShapeData.numericTrue : ShapeData.numericFalse;
1088 shape.Static = _isPhysical ? ShapeData.numericFalse : ShapeData.numericTrue;
1089 }
1090
1091 // Create the linkset by putting constraints between the objects of the set so they cannot move
1092 // relative to each other.
1093 // TODO: make this more effeicient: a large linkset gets rebuilt over and over and prims are added
1094 void CreateLinksetWithConstraints()
1095 {
1096 // m_log.DebugFormat("{0}: CreateLinkset. Root prim={1}, prims={2}", LogHeader, LocalID, _childrenPrims.Count+1);
1097
1098 // remove any constraints that might be in place
1099 foreach (BSPrim prim in _childrenPrims)
1100 {
1101 // m_log.DebugFormat("{0}: CreateObject: RemoveConstraint between root prim {1} and child prim {2}", LogHeader, LocalID, prim.LocalID);
1102 BulletSimAPI.RemoveConstraint(_scene.WorldID, LocalID, prim.LocalID);
1103 }
1104 // create constraints between the root prim and each of the children
1105 foreach (BSPrim prim in _childrenPrims)
1106 {
1107 // m_log.DebugFormat("{0}: CreateObject: AddConstraint between root prim {1} and child prim {2}", LogHeader, LocalID, prim.LocalID);
1108
1109 // Zero motion for children so they don't interpolate
1110 prim.ZeroMotion();
1111
1112 // relative position normalized to the root prim
1113 OMV.Vector3 childRelativePosition = (prim._position - this._position) * OMV.Quaternion.Inverse(this._orientation);
1114
1115 // relative rotation of the child to the parent
1116 OMV.Quaternion relativeRotation = OMV.Quaternion.Inverse(prim._orientation) * this._orientation;
1117
1118 // this is a constraint that allows no freedom of movement between the two objects
1119 // http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=4818
1120 BulletSimAPI.AddConstraint(_scene.WorldID, LocalID, prim.LocalID,
1121 childRelativePosition,
1122 relativeRotation,
1123 OMV.Vector3.Zero,
1124 OMV.Quaternion.Identity,
1125 OMV.Vector3.Zero, OMV.Vector3.Zero,
1126 OMV.Vector3.Zero, OMV.Vector3.Zero);
1127 }
1128 }
1129
1130 // Rebuild the geometry and object.
1131 // This is called when the shape changes so we need to recreate the mesh/hull.
1132 // No locking here because this is done when the physics engine is not simulating
1133 private void RecreateGeomAndObject()
1134 {
1135 // If this object is complex or we are the root of a linkset, build a mesh.
1136 // The root of a linkset must be a mesh so we can create the linked compound object.
1137 // if (_scene.NeedsMeshing(_pbs) || IsRootOfLinkset )
1138 if (_scene.NeedsMeshing(_pbs)) // linksets with constraints don't need a root mesh
1139 {
1140 // m_log.DebugFormat("{0}: RecreateGeomAndObject: creating mesh", LogHeader);
1141 _mesh = _scene.mesher.CreateMesh(_avName, _pbs, _size, _scene.MeshLOD, _isPhysical);
1142 }
1143 else
1144 {
1145 // implement the shape with a Bullet native shape.
1146 _mesh = null;
1147 }
1148 CreateGeom();
1149 CreateObject();
1150 return;
1151 }
1152
1153 // The physics engine says that properties have updated. Update same and inform
1154 // the world that things have changed.
1155 // TODO: do we really need to check for changed? Maybe just copy values and call RequestPhysicsterseUpdate()
1156 enum UpdatedProperties {
1157 Position = 1 << 0,
1158 Rotation = 1 << 1,
1159 Velocity = 1 << 2,
1160 Acceleration = 1 << 3,
1161 RotationalVel = 1 << 4
1162 }
1163
1164 const float ROTATION_TOLERANCE = 0.01f;
1165 const float VELOCITY_TOLERANCE = 0.001f;
1166 const float POSITION_TOLERANCE = 0.05f;
1167 const float ACCELERATION_TOLERANCE = 0.01f;
1168 const float ROTATIONAL_VELOCITY_TOLERANCE = 0.01f;
1169 const bool SHOULD_DAMP_UPDATES = false;
1170
1171 public void UpdateProperties(EntityProperties entprop)
1172 {
1173 UpdatedProperties changed = 0;
1174 if (SHOULD_DAMP_UPDATES)
1175 {
1176 // assign to the local variables so the normal set action does not happen
1177 // if (_position != entprop.Position)
1178 if (!_position.ApproxEquals(entprop.Position, POSITION_TOLERANCE))
1179 {
1180 _position = entprop.Position;
1181 // m_log.DebugFormat("{0}: UpdateProperties: id={1}, pos = {2}", LogHeader, LocalID, _position);
1182 changed |= UpdatedProperties.Position;
1183 }
1184 // if (_orientation != entprop.Rotation)
1185 if (!_orientation.ApproxEquals(entprop.Rotation, ROTATION_TOLERANCE))
1186 {
1187 _orientation = entprop.Rotation;
1188 // m_log.DebugFormat("{0}: UpdateProperties: id={1}, rot = {2}", LogHeader, LocalID, _orientation);
1189 changed |= UpdatedProperties.Rotation;
1190 }
1191 // if (_velocity != entprop.Velocity)
1192 if (!_velocity.ApproxEquals(entprop.Velocity, VELOCITY_TOLERANCE))
1193 {
1194 _velocity = entprop.Velocity;
1195 // m_log.DebugFormat("{0}: UpdateProperties: velocity = {1}", LogHeader, _velocity);
1196 changed |= UpdatedProperties.Velocity;
1197 }
1198 // if (_acceleration != entprop.Acceleration)
1199 if (!_acceleration.ApproxEquals(entprop.Acceleration, ACCELERATION_TOLERANCE))
1200 {
1201 _acceleration = entprop.Acceleration;
1202 // m_log.DebugFormat("{0}: UpdateProperties: acceleration = {1}", LogHeader, _acceleration);
1203 changed |= UpdatedProperties.Acceleration;
1204 }
1205 // if (_rotationalVelocity != entprop.RotationalVelocity)
1206 if (!_rotationalVelocity.ApproxEquals(entprop.RotationalVelocity, ROTATIONAL_VELOCITY_TOLERANCE))
1207 {
1208 _rotationalVelocity = entprop.RotationalVelocity;
1209 // m_log.DebugFormat("{0}: UpdateProperties: rotationalVelocity = {1}", LogHeader, _rotationalVelocity);
1210 changed |= UpdatedProperties.RotationalVel;
1211 }
1212 if (changed != 0)
1213 {
1214 // m_log.DebugFormat("{0}: UpdateProperties: id={1}, c={2}, pos={3}, rot={4}", LogHeader, LocalID, changed, _position, _orientation);
1215 // Only update the position of single objects and linkset roots
1216 if (this._parentPrim == null)
1217 {
1218 // m_log.DebugFormat("{0}: RequestTerseUpdate. id={1}, ch={2}, pos={3}, rot={4}", LogHeader, LocalID, changed, _position, _orientation);
1219 base.RequestPhysicsterseUpdate();
1220 }
1221 }
1222 }
1223 else
1224 {
1225 // Don't check for damping here -- it's done in BulletSim and SceneObjectPart.
1226
1227 // Only updates only for individual prims and for the root object of a linkset.
1228 if (this._parentPrim == null)
1229 {
1230 // Assign to the local variables so the normal set action does not happen
1231 _position = entprop.Position;
1232 _orientation = entprop.Rotation;
1233 _velocity = entprop.Velocity;
1234 _acceleration = entprop.Acceleration;
1235 _rotationalVelocity = entprop.RotationalVelocity;
1236 // m_log.DebugFormat("{0}: RequestTerseUpdate. id={1}, ch={2}, pos={3}, rot={4}", LogHeader, LocalID, changed, _position, _orientation);
1237 base.RequestPhysicsterseUpdate();
1238 }
1239 }
1240 }
1241
1242 // I've collided with something
1243 public void Collide(uint collidingWith, ActorTypes type, OMV.Vector3 contactPoint, OMV.Vector3 contactNormal, float pentrationDepth)
1244 {
1245 // m_log.DebugFormat("{0}: Collide: ms={1}, id={2}, with={3}", LogHeader, _subscribedEventsMs, LocalID, collidingWith);
1246
1247 // The following lines make IsColliding() and IsCollidingGround() work
1248 _collidingStep = _scene.SimulationStep;
1249 if (collidingWith == BSScene.TERRAIN_ID || collidingWith == BSScene.GROUNDPLANE_ID)
1250 {
1251 _collidingGroundStep = _scene.SimulationStep;
1252 }
1253
1254 if (_subscribedEventsMs == 0) return; // nothing in the object is waiting for collision events
1255 // throttle the collisions to the number of milliseconds specified in the subscription
1256 int nowTime = _scene.SimulationNowTime;
1257 if (nowTime < (_lastCollisionTime + _subscribedEventsMs)) return;
1258 _lastCollisionTime = nowTime;
1259
1260 // create the event for the collision
1261 Dictionary<uint, ContactPoint> contactPoints = new Dictionary<uint, ContactPoint>();
1262 contactPoints.Add(collidingWith, new ContactPoint(contactPoint, contactNormal, pentrationDepth));
1263 CollisionEventUpdate args = new CollisionEventUpdate(LocalID, (int)type, 1, contactPoints);
1264 base.SendCollisionUpdate(args);
1265 }
1266}
1267}