diff options
author | teravus | 2012-12-23 15:21:25 -0500 |
---|---|---|
committer | teravus | 2012-12-23 15:21:25 -0500 |
commit | 92e4f9f412046f8f7926c99c9e56c3a8b6b2edbf (patch) | |
tree | eabcbb758a7512222e84cb51b51b6822cdbf561b /OpenSim/Region/Physics/BulletSNPlugin/BSPrim.cs | |
parent | Revert "Whitespace change to trigger bot" (diff) | |
download | opensim-SC_OLD-92e4f9f412046f8f7926c99c9e56c3a8b6b2edbf.zip opensim-SC_OLD-92e4f9f412046f8f7926c99c9e56c3a8b6b2edbf.tar.gz opensim-SC_OLD-92e4f9f412046f8f7926c99c9e56c3a8b6b2edbf.tar.bz2 opensim-SC_OLD-92e4f9f412046f8f7926c99c9e56c3a8b6b2edbf.tar.xz |
* Initial commit of BulletSimN (BulletSNPlugin). Purely C# implementation of BulletSim. This is designed to be /as close as possible/ to the BulletSim plugin while still being entirely in the managed space to make keeping it up to date easy as possible (no thinking work). This implementation is /slower/ then the c++ version just because it's fully managed, so it's not appropriate for huge sims, but it will run small ones OK. At the moment, it supports all known features of BulletSim. Think of it like.. POS but everything works. To use this plugin, set the physics plugin to BulletSimN.
Diffstat (limited to 'OpenSim/Region/Physics/BulletSNPlugin/BSPrim.cs')
-rw-r--r-- | OpenSim/Region/Physics/BulletSNPlugin/BSPrim.cs | 1467 |
1 files changed, 1467 insertions, 0 deletions
diff --git a/OpenSim/Region/Physics/BulletSNPlugin/BSPrim.cs b/OpenSim/Region/Physics/BulletSNPlugin/BSPrim.cs new file mode 100644 index 0000000..a889c24 --- /dev/null +++ b/OpenSim/Region/Physics/BulletSNPlugin/BSPrim.cs | |||
@@ -0,0 +1,1467 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyrightD | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSimulator Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System; | ||
29 | using System.Reflection; | ||
30 | using System.Collections.Generic; | ||
31 | using System.Xml; | ||
32 | using log4net; | ||
33 | using OMV = OpenMetaverse; | ||
34 | using OpenSim.Framework; | ||
35 | using OpenSim.Region.Physics.Manager; | ||
36 | using OpenSim.Region.Physics.ConvexDecompositionDotNet; | ||
37 | |||
38 | namespace OpenSim.Region.Physics.BulletSNPlugin | ||
39 | { | ||
40 | |||
41 | [Serializable] | ||
42 | public sealed class BSPrim : BSPhysObject | ||
43 | { | ||
44 | private 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 | private OMV.Vector3 _position; | ||
54 | private float _mass; // the mass of this object | ||
55 | private float _density; | ||
56 | private OMV.Vector3 _force; | ||
57 | private OMV.Vector3 _velocity; | ||
58 | private OMV.Vector3 _torque; | ||
59 | private float _collisionScore; | ||
60 | private OMV.Vector3 _acceleration; | ||
61 | private OMV.Quaternion _orientation; | ||
62 | private int _physicsActorType; | ||
63 | private bool _isPhysical; | ||
64 | private bool _flying; | ||
65 | private float _friction; | ||
66 | private float _restitution; | ||
67 | private bool _setAlwaysRun; | ||
68 | private bool _throttleUpdates; | ||
69 | private bool _isColliding; | ||
70 | private bool _collidingGround; | ||
71 | private bool _collidingObj; | ||
72 | private bool _floatOnWater; | ||
73 | private OMV.Vector3 _rotationalVelocity; | ||
74 | private bool _kinematic; | ||
75 | private float _buoyancy; | ||
76 | |||
77 | private BSDynamics _vehicle; | ||
78 | |||
79 | private OMV.Vector3 _PIDTarget; | ||
80 | private bool _usePID; | ||
81 | private float _PIDTau; | ||
82 | private bool _useHoverPID; | ||
83 | private float _PIDHoverHeight; | ||
84 | private PIDHoverType _PIDHoverType; | ||
85 | private float _PIDHoverTao; | ||
86 | |||
87 | public BSPrim(uint localID, String primName, BSScene parent_scene, OMV.Vector3 pos, OMV.Vector3 size, | ||
88 | OMV.Quaternion rotation, PrimitiveBaseShape pbs, bool pisPhysical) | ||
89 | : base(parent_scene, localID, primName, "BSPrim") | ||
90 | { | ||
91 | // m_log.DebugFormat("{0}: BSPrim creation of {1}, id={2}", LogHeader, primName, localID); | ||
92 | _physicsActorType = (int)ActorTypes.Prim; | ||
93 | _position = pos; | ||
94 | _size = size; | ||
95 | Scale = size; // prims are the size the user wants them to be (different for BSCharactes). | ||
96 | _orientation = rotation; | ||
97 | _buoyancy = 1f; | ||
98 | _velocity = OMV.Vector3.Zero; | ||
99 | _rotationalVelocity = OMV.Vector3.Zero; | ||
100 | BaseShape = pbs; | ||
101 | _isPhysical = pisPhysical; | ||
102 | _isVolumeDetect = false; | ||
103 | |||
104 | // Someday set default attributes based on the material but, for now, we don't know the prim material yet. | ||
105 | // MaterialAttributes primMat = BSMaterials.GetAttributes(Material, pisPhysical); | ||
106 | _density = PhysicsScene.Params.defaultDensity; | ||
107 | _friction = PhysicsScene.Params.defaultFriction; | ||
108 | _restitution = PhysicsScene.Params.defaultRestitution; | ||
109 | |||
110 | _vehicle = new BSDynamics(PhysicsScene, this); // add vehicleness | ||
111 | |||
112 | _mass = CalculateMass(); | ||
113 | |||
114 | // No body or shape yet | ||
115 | PhysBody = new BulletBody(LocalID); | ||
116 | PhysShape = new BulletShape(); | ||
117 | |||
118 | DetailLog("{0},BSPrim.constructor,call", LocalID); | ||
119 | // do the actual object creation at taint time | ||
120 | PhysicsScene.TaintedObject("BSPrim.create", delegate() | ||
121 | { | ||
122 | CreateGeomAndObject(true); | ||
123 | |||
124 | CurrentCollisionFlags = BulletSimAPI.GetCollisionFlags2(PhysBody.ptr); | ||
125 | }); | ||
126 | } | ||
127 | |||
128 | // called when this prim is being destroyed and we should free all the resources | ||
129 | public override void Destroy() | ||
130 | { | ||
131 | // m_log.DebugFormat("{0}: Destroy, id={1}", LogHeader, LocalID); | ||
132 | base.Destroy(); | ||
133 | |||
134 | // Undo any links between me and any other object | ||
135 | BSPhysObject parentBefore = Linkset.LinksetRoot; | ||
136 | int childrenBefore = Linkset.NumberOfChildren; | ||
137 | |||
138 | Linkset = Linkset.RemoveMeFromLinkset(this); | ||
139 | |||
140 | DetailLog("{0},BSPrim.Destroy,call,parentBefore={1},childrenBefore={2},parentAfter={3},childrenAfter={4}", | ||
141 | LocalID, parentBefore.LocalID, childrenBefore, Linkset.LinksetRoot.LocalID, Linkset.NumberOfChildren); | ||
142 | |||
143 | // Undo any vehicle properties | ||
144 | this.VehicleType = (int)Vehicle.TYPE_NONE; | ||
145 | |||
146 | PhysicsScene.TaintedObject("BSPrim.destroy", delegate() | ||
147 | { | ||
148 | DetailLog("{0},BSPrim.Destroy,taint,", LocalID); | ||
149 | // If there are physical body and shape, release my use of same. | ||
150 | PhysicsScene.Shapes.DereferenceBody(PhysBody, true, null); | ||
151 | PhysBody.Clear(); | ||
152 | PhysicsScene.Shapes.DereferenceShape(PhysShape, true, null); | ||
153 | PhysShape.Clear(); | ||
154 | }); | ||
155 | } | ||
156 | |||
157 | // No one uses this property. | ||
158 | public override bool Stopped { | ||
159 | get { return false; } | ||
160 | } | ||
161 | public override OMV.Vector3 Size { | ||
162 | get { return _size; } | ||
163 | set { | ||
164 | // We presume the scale and size are the same. If scale must be changed for | ||
165 | // the physical shape, that is done when the geometry is built. | ||
166 | _size = value; | ||
167 | Scale = _size; | ||
168 | ForceBodyShapeRebuild(false); | ||
169 | } | ||
170 | } | ||
171 | |||
172 | public override PrimitiveBaseShape Shape { | ||
173 | set { | ||
174 | BaseShape = value; | ||
175 | ForceBodyShapeRebuild(false); | ||
176 | } | ||
177 | } | ||
178 | // Whatever the linkset wants is what I want. | ||
179 | public override BSPhysicsShapeType PreferredPhysicalShape | ||
180 | { get { return Linkset.PreferredPhysicalShape(this); } } | ||
181 | |||
182 | public override bool ForceBodyShapeRebuild(bool inTaintTime) | ||
183 | { | ||
184 | LastAssetBuildFailed = false; | ||
185 | PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ForceBodyShapeRebuild", delegate() | ||
186 | { | ||
187 | _mass = CalculateMass(); // changing the shape changes the mass | ||
188 | CreateGeomAndObject(true); | ||
189 | }); | ||
190 | return true; | ||
191 | } | ||
192 | public override bool Grabbed { | ||
193 | set { _grabbed = value; | ||
194 | } | ||
195 | } | ||
196 | public override bool Selected { | ||
197 | set | ||
198 | { | ||
199 | if (value != _isSelected) | ||
200 | { | ||
201 | _isSelected = value; | ||
202 | PhysicsScene.TaintedObject("BSPrim.setSelected", delegate() | ||
203 | { | ||
204 | DetailLog("{0},BSPrim.selected,taint,selected={1}", LocalID, _isSelected); | ||
205 | SetObjectDynamic(false); | ||
206 | }); | ||
207 | } | ||
208 | } | ||
209 | } | ||
210 | public override void CrossingFailure() { return; } | ||
211 | |||
212 | // link me to the specified parent | ||
213 | public override void link(PhysicsActor obj) { | ||
214 | BSPrim parent = obj as BSPrim; | ||
215 | if (parent != null) | ||
216 | { | ||
217 | BSPhysObject parentBefore = Linkset.LinksetRoot; | ||
218 | int childrenBefore = Linkset.NumberOfChildren; | ||
219 | |||
220 | Linkset = parent.Linkset.AddMeToLinkset(this); | ||
221 | |||
222 | DetailLog("{0},BSPrim.link,call,parentBefore={1}, childrenBefore=={2}, parentAfter={3}, childrenAfter={4}", | ||
223 | LocalID, parentBefore.LocalID, childrenBefore, Linkset.LinksetRoot.LocalID, Linkset.NumberOfChildren); | ||
224 | } | ||
225 | return; | ||
226 | } | ||
227 | |||
228 | // delink me from my linkset | ||
229 | public override void delink() { | ||
230 | // TODO: decide if this parent checking needs to happen at taint time | ||
231 | // Race condition here: if link() and delink() in same simulation tick, the delink will not happen | ||
232 | |||
233 | BSPhysObject parentBefore = Linkset.LinksetRoot; | ||
234 | int childrenBefore = Linkset.NumberOfChildren; | ||
235 | |||
236 | Linkset = Linkset.RemoveMeFromLinkset(this); | ||
237 | |||
238 | DetailLog("{0},BSPrim.delink,parentBefore={1},childrenBefore={2},parentAfter={3},childrenAfter={4}, ", | ||
239 | LocalID, parentBefore.LocalID, childrenBefore, Linkset.LinksetRoot.LocalID, Linkset.NumberOfChildren); | ||
240 | return; | ||
241 | } | ||
242 | |||
243 | // Set motion values to zero. | ||
244 | // Do it to the properties so the values get set in the physics engine. | ||
245 | // Push the setting of the values to the viewer. | ||
246 | // Called at taint time! | ||
247 | public override void ZeroMotion(bool inTaintTime) | ||
248 | { | ||
249 | _velocity = OMV.Vector3.Zero; | ||
250 | _acceleration = OMV.Vector3.Zero; | ||
251 | _rotationalVelocity = OMV.Vector3.Zero; | ||
252 | |||
253 | // Zero some other properties in the physics engine | ||
254 | PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ZeroMotion", delegate() | ||
255 | { | ||
256 | if (PhysBody.HasPhysicalBody) | ||
257 | BulletSimAPI.ClearAllForces2(PhysBody.ptr); | ||
258 | }); | ||
259 | } | ||
260 | public override void ZeroAngularMotion(bool inTaintTime) | ||
261 | { | ||
262 | _rotationalVelocity = OMV.Vector3.Zero; | ||
263 | // Zero some other properties in the physics engine | ||
264 | PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ZeroMotion", delegate() | ||
265 | { | ||
266 | // DetailLog("{0},BSPrim.ZeroAngularMotion,call,rotVel={1}", LocalID, _rotationalVelocity); | ||
267 | if (PhysBody.HasPhysicalBody) | ||
268 | { | ||
269 | BulletSimAPI.SetInterpolationAngularVelocity2(PhysBody.ptr, _rotationalVelocity); | ||
270 | BulletSimAPI.SetAngularVelocity2(PhysBody.ptr, _rotationalVelocity); | ||
271 | } | ||
272 | }); | ||
273 | } | ||
274 | |||
275 | public override void LockAngularMotion(OMV.Vector3 axis) | ||
276 | { | ||
277 | DetailLog("{0},BSPrim.LockAngularMotion,call,axis={1}", LocalID, axis); | ||
278 | return; | ||
279 | } | ||
280 | |||
281 | public override OMV.Vector3 RawPosition | ||
282 | { | ||
283 | get { return _position; } | ||
284 | set { _position = value; } | ||
285 | } | ||
286 | public override OMV.Vector3 Position { | ||
287 | get { | ||
288 | /* NOTE: this refetch is not necessary. The simulator knows about linkset children | ||
289 | * and does not fetch this position info for children. Thus this is commented out. | ||
290 | // child prims move around based on their parent. Need to get the latest location | ||
291 | if (!Linkset.IsRoot(this)) | ||
292 | _position = Linkset.PositionGet(this); | ||
293 | */ | ||
294 | |||
295 | // don't do the GetObjectPosition for root elements because this function is called a zillion times. | ||
296 | // _position = BulletSimAPI.GetObjectPosition2(PhysicsScene.World.ptr, BSBody.ptr); | ||
297 | return _position; | ||
298 | } | ||
299 | set { | ||
300 | // If the position must be forced into the physics engine, use ForcePosition. | ||
301 | // All positions are given in world positions. | ||
302 | if (_position == value) | ||
303 | { | ||
304 | DetailLog("{0},BSPrim.setPosition,taint,positionNotChanging,pos={1},orient={2}", LocalID, _position, _orientation); | ||
305 | return; | ||
306 | } | ||
307 | _position = value; | ||
308 | PositionSanityCheck(false); | ||
309 | |||
310 | // A linkset might need to know if a component information changed. | ||
311 | Linkset.UpdateProperties(this, false); | ||
312 | |||
313 | PhysicsScene.TaintedObject("BSPrim.setPosition", delegate() | ||
314 | { | ||
315 | DetailLog("{0},BSPrim.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation); | ||
316 | ForcePosition = _position; | ||
317 | }); | ||
318 | } | ||
319 | } | ||
320 | public override OMV.Vector3 ForcePosition { | ||
321 | get { | ||
322 | _position = BulletSimAPI.GetPosition2(PhysBody.ptr); | ||
323 | return _position; | ||
324 | } | ||
325 | set { | ||
326 | _position = value; | ||
327 | if (PhysBody.HasPhysicalBody) | ||
328 | { | ||
329 | BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation); | ||
330 | ActivateIfPhysical(false); | ||
331 | } | ||
332 | } | ||
333 | } | ||
334 | |||
335 | // Check that the current position is sane and, if not, modify the position to make it so. | ||
336 | // Check for being below terrain and being out of bounds. | ||
337 | // Returns 'true' of the position was made sane by some action. | ||
338 | private bool PositionSanityCheck(bool inTaintTime) | ||
339 | { | ||
340 | bool ret = false; | ||
341 | |||
342 | if (!PhysicsScene.TerrainManager.IsWithinKnownTerrain(_position)) | ||
343 | { | ||
344 | // The physical object is out of the known/simulated area. | ||
345 | // Upper levels of code will handle the transition to other areas so, for | ||
346 | // the time, we just ignore the position. | ||
347 | return ret; | ||
348 | } | ||
349 | |||
350 | float terrainHeight = PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(_position); | ||
351 | OMV.Vector3 upForce = OMV.Vector3.Zero; | ||
352 | if (RawPosition.Z < terrainHeight) | ||
353 | { | ||
354 | DetailLog("{0},BSPrim.PositionAdjustUnderGround,call,pos={1},terrain={2}", LocalID, _position, terrainHeight); | ||
355 | float targetHeight = terrainHeight + (Size.Z / 2f); | ||
356 | // Upforce proportional to the distance away from the terrain. Correct the error in 1 sec. | ||
357 | upForce.Z = (terrainHeight - RawPosition.Z) * 1f; | ||
358 | ret = true; | ||
359 | } | ||
360 | |||
361 | if ((CurrentCollisionFlags & CollisionFlags.BS_FLOATS_ON_WATER) != 0) | ||
362 | { | ||
363 | float waterHeight = PhysicsScene.TerrainManager.GetWaterLevelAtXYZ(_position); | ||
364 | // TODO: a floating motor so object will bob in the water | ||
365 | if (Math.Abs(RawPosition.Z - waterHeight) > 0.1f) | ||
366 | { | ||
367 | // Upforce proportional to the distance away from the water. Correct the error in 1 sec. | ||
368 | upForce.Z = (waterHeight - RawPosition.Z) * 1f; | ||
369 | ret = true; | ||
370 | } | ||
371 | } | ||
372 | |||
373 | // The above code computes a force to apply to correct any out-of-bounds problems. Apply same. | ||
374 | // TODO: This should be intergrated with a geneal physics action mechanism. | ||
375 | // TODO: This should be moderated with PID'ness. | ||
376 | if (ret) | ||
377 | { | ||
378 | // Apply upforce and overcome gravity. | ||
379 | OMV.Vector3 correctionForce = upForce - PhysicsScene.DefaultGravity; | ||
380 | DetailLog("{0},BSPrim.PositionSanityCheck,applyForce,pos={1},upForce={2},correctionForce={3}", LocalID, _position, upForce, correctionForce); | ||
381 | AddForce(correctionForce, false, inTaintTime); | ||
382 | } | ||
383 | return ret; | ||
384 | } | ||
385 | |||
386 | // Return the effective mass of the object. | ||
387 | // If there are multiple items in the linkset, add them together for the root | ||
388 | public override float Mass | ||
389 | { | ||
390 | get | ||
391 | { | ||
392 | return Linkset.LinksetMass; | ||
393 | // return _mass; | ||
394 | } | ||
395 | } | ||
396 | |||
397 | // used when we only want this prim's mass and not the linkset thing | ||
398 | public override float RawMass { | ||
399 | get { return _mass; } | ||
400 | } | ||
401 | // Set the physical mass to the passed mass. | ||
402 | // Note that this does not change _mass! | ||
403 | public override void UpdatePhysicalMassProperties(float physMass) | ||
404 | { | ||
405 | if (IsStatic) | ||
406 | { | ||
407 | Inertia = OMV.Vector3.Zero; | ||
408 | BulletSimAPI.SetMassProps2(PhysBody.ptr, 0f, Inertia); | ||
409 | BulletSimAPI.UpdateInertiaTensor2(PhysBody.ptr); | ||
410 | } | ||
411 | else | ||
412 | { | ||
413 | Inertia = BulletSimAPI.CalculateLocalInertia2(PhysShape.ptr, physMass); | ||
414 | BulletSimAPI.SetMassProps2(PhysBody.ptr, physMass, Inertia); | ||
415 | BulletSimAPI.UpdateInertiaTensor2(PhysBody.ptr); | ||
416 | // center of mass is at the zero of the object | ||
417 | // DEBUG DEBUG BulletSimAPI.SetCenterOfMassByPosRot2(PhysBody.ptr, ForcePosition, ForceOrientation); | ||
418 | DetailLog("{0},BSPrim.UpdateMassProperties,mass={1},localInertia={2}", LocalID, physMass, Inertia); | ||
419 | } | ||
420 | } | ||
421 | |||
422 | // Is this used? | ||
423 | public override OMV.Vector3 CenterOfMass | ||
424 | { | ||
425 | get { return Linkset.CenterOfMass; } | ||
426 | } | ||
427 | |||
428 | // Is this used? | ||
429 | public override OMV.Vector3 GeometricCenter | ||
430 | { | ||
431 | get { return Linkset.GeometricCenter; } | ||
432 | } | ||
433 | |||
434 | public override OMV.Vector3 Force { | ||
435 | get { return _force; } | ||
436 | set { | ||
437 | _force = value; | ||
438 | if (_force != OMV.Vector3.Zero) | ||
439 | { | ||
440 | // If the force is non-zero, it must be reapplied each tick because | ||
441 | // Bullet clears the forces applied last frame. | ||
442 | RegisterPreStepAction("BSPrim.setForce", LocalID, | ||
443 | delegate(float timeStep) | ||
444 | { | ||
445 | DetailLog("{0},BSPrim.setForce,preStep,force={1}", LocalID, _force); | ||
446 | if (PhysBody.HasPhysicalBody) | ||
447 | { | ||
448 | BulletSimAPI.ApplyCentralForce2(PhysBody.ptr, _force); | ||
449 | ActivateIfPhysical(false); | ||
450 | } | ||
451 | } | ||
452 | ); | ||
453 | } | ||
454 | else | ||
455 | { | ||
456 | UnRegisterPreStepAction("BSPrim.setForce", LocalID); | ||
457 | } | ||
458 | } | ||
459 | } | ||
460 | |||
461 | public override int VehicleType { | ||
462 | get { | ||
463 | return (int)_vehicle.Type; // if we are a vehicle, return that type | ||
464 | } | ||
465 | set { | ||
466 | Vehicle type = (Vehicle)value; | ||
467 | |||
468 | PhysicsScene.TaintedObject("setVehicleType", delegate() | ||
469 | { | ||
470 | // Done at taint time so we're sure the physics engine is not using the variables | ||
471 | // Vehicle code changes the parameters for this vehicle type. | ||
472 | _vehicle.ProcessTypeChange(type); | ||
473 | ActivateIfPhysical(false); | ||
474 | |||
475 | // If an active vehicle, register the vehicle code to be called before each step | ||
476 | if (_vehicle.Type == Vehicle.TYPE_NONE) | ||
477 | UnRegisterPreStepAction("BSPrim.Vehicle", LocalID); | ||
478 | else | ||
479 | RegisterPreStepAction("BSPrim.Vehicle", LocalID, _vehicle.Step); | ||
480 | }); | ||
481 | } | ||
482 | } | ||
483 | public override void VehicleFloatParam(int param, float value) | ||
484 | { | ||
485 | PhysicsScene.TaintedObject("BSPrim.VehicleFloatParam", delegate() | ||
486 | { | ||
487 | _vehicle.ProcessFloatVehicleParam((Vehicle)param, value); | ||
488 | ActivateIfPhysical(false); | ||
489 | }); | ||
490 | } | ||
491 | public override void VehicleVectorParam(int param, OMV.Vector3 value) | ||
492 | { | ||
493 | PhysicsScene.TaintedObject("BSPrim.VehicleVectorParam", delegate() | ||
494 | { | ||
495 | _vehicle.ProcessVectorVehicleParam((Vehicle)param, value); | ||
496 | ActivateIfPhysical(false); | ||
497 | }); | ||
498 | } | ||
499 | public override void VehicleRotationParam(int param, OMV.Quaternion rotation) | ||
500 | { | ||
501 | PhysicsScene.TaintedObject("BSPrim.VehicleRotationParam", delegate() | ||
502 | { | ||
503 | _vehicle.ProcessRotationVehicleParam((Vehicle)param, rotation); | ||
504 | ActivateIfPhysical(false); | ||
505 | }); | ||
506 | } | ||
507 | public override void VehicleFlags(int param, bool remove) | ||
508 | { | ||
509 | PhysicsScene.TaintedObject("BSPrim.VehicleFlags", delegate() | ||
510 | { | ||
511 | _vehicle.ProcessVehicleFlags(param, remove); | ||
512 | }); | ||
513 | } | ||
514 | |||
515 | // Allows the detection of collisions with inherently non-physical prims. see llVolumeDetect for more | ||
516 | public override void SetVolumeDetect(int param) { | ||
517 | bool newValue = (param != 0); | ||
518 | if (_isVolumeDetect != newValue) | ||
519 | { | ||
520 | _isVolumeDetect = newValue; | ||
521 | PhysicsScene.TaintedObject("BSPrim.SetVolumeDetect", delegate() | ||
522 | { | ||
523 | // DetailLog("{0},setVolumeDetect,taint,volDetect={1}", LocalID, _isVolumeDetect); | ||
524 | SetObjectDynamic(true); | ||
525 | }); | ||
526 | } | ||
527 | return; | ||
528 | } | ||
529 | public override OMV.Vector3 Velocity { | ||
530 | get { return _velocity; } | ||
531 | set { | ||
532 | _velocity = value; | ||
533 | PhysicsScene.TaintedObject("BSPrim.setVelocity", delegate() | ||
534 | { | ||
535 | // DetailLog("{0},BSPrim.SetVelocity,taint,vel={1}", LocalID, _velocity); | ||
536 | ForceVelocity = _velocity; | ||
537 | }); | ||
538 | } | ||
539 | } | ||
540 | public override OMV.Vector3 ForceVelocity { | ||
541 | get { return _velocity; } | ||
542 | set { | ||
543 | PhysicsScene.AssertInTaintTime("BSPrim.ForceVelocity"); | ||
544 | |||
545 | _velocity = value; | ||
546 | if (PhysBody.HasPhysicalBody) | ||
547 | { | ||
548 | BulletSimAPI.SetLinearVelocity2(PhysBody.ptr, _velocity); | ||
549 | ActivateIfPhysical(false); | ||
550 | } | ||
551 | } | ||
552 | } | ||
553 | public override OMV.Vector3 Torque { | ||
554 | get { return _torque; } | ||
555 | set { | ||
556 | _torque = value; | ||
557 | if (_torque != OMV.Vector3.Zero) | ||
558 | { | ||
559 | // If the torque is non-zero, it must be reapplied each tick because | ||
560 | // Bullet clears the forces applied last frame. | ||
561 | RegisterPreStepAction("BSPrim.setTorque", LocalID, | ||
562 | delegate(float timeStep) | ||
563 | { | ||
564 | if (PhysBody.HasPhysicalBody) | ||
565 | AddAngularForce(_torque, false, true); | ||
566 | } | ||
567 | ); | ||
568 | } | ||
569 | else | ||
570 | { | ||
571 | UnRegisterPreStepAction("BSPrim.setTorque", LocalID); | ||
572 | } | ||
573 | // DetailLog("{0},BSPrim.SetTorque,call,torque={1}", LocalID, _torque); | ||
574 | } | ||
575 | } | ||
576 | public override float CollisionScore { | ||
577 | get { return _collisionScore; } | ||
578 | set { _collisionScore = value; | ||
579 | } | ||
580 | } | ||
581 | public override OMV.Vector3 Acceleration { | ||
582 | get { return _acceleration; } | ||
583 | set { _acceleration = value; } | ||
584 | } | ||
585 | public override OMV.Quaternion RawOrientation | ||
586 | { | ||
587 | get { return _orientation; } | ||
588 | set { _orientation = value; } | ||
589 | } | ||
590 | public override OMV.Quaternion Orientation { | ||
591 | get { | ||
592 | /* NOTE: this refetch is not necessary. The simulator knows about linkset children | ||
593 | * and does not fetch this position info for children. Thus this is commented out. | ||
594 | // Children move around because tied to parent. Get a fresh value. | ||
595 | if (!Linkset.IsRoot(this)) | ||
596 | { | ||
597 | _orientation = Linkset.OrientationGet(this); | ||
598 | } | ||
599 | */ | ||
600 | return _orientation; | ||
601 | } | ||
602 | set { | ||
603 | if (_orientation == value) | ||
604 | return; | ||
605 | _orientation = value; | ||
606 | |||
607 | // A linkset might need to know if a component information changed. | ||
608 | Linkset.UpdateProperties(this, false); | ||
609 | |||
610 | PhysicsScene.TaintedObject("BSPrim.setOrientation", delegate() | ||
611 | { | ||
612 | if (PhysBody.HasPhysicalBody) | ||
613 | { | ||
614 | // _position = BulletSimAPI.GetObjectPosition2(PhysicsScene.World.ptr, BSBody.ptr); | ||
615 | // DetailLog("{0},BSPrim.setOrientation,taint,pos={1},orient={2}", LocalID, _position, _orientation); | ||
616 | BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation); | ||
617 | } | ||
618 | }); | ||
619 | } | ||
620 | } | ||
621 | // Go directly to Bullet to get/set the value. | ||
622 | public override OMV.Quaternion ForceOrientation | ||
623 | { | ||
624 | get | ||
625 | { | ||
626 | _orientation = BulletSimAPI.GetOrientation2(PhysBody.ptr); | ||
627 | return _orientation; | ||
628 | } | ||
629 | set | ||
630 | { | ||
631 | _orientation = value; | ||
632 | BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation); | ||
633 | } | ||
634 | } | ||
635 | public override int PhysicsActorType { | ||
636 | get { return _physicsActorType; } | ||
637 | set { _physicsActorType = value; } | ||
638 | } | ||
639 | public override bool IsPhysical { | ||
640 | get { return _isPhysical; } | ||
641 | set { | ||
642 | if (_isPhysical != value) | ||
643 | { | ||
644 | _isPhysical = value; | ||
645 | PhysicsScene.TaintedObject("BSPrim.setIsPhysical", delegate() | ||
646 | { | ||
647 | // DetailLog("{0},setIsPhysical,taint,isPhys={1}", LocalID, _isPhysical); | ||
648 | SetObjectDynamic(true); | ||
649 | // whether phys-to-static or static-to-phys, the object is not moving. | ||
650 | ZeroMotion(true); | ||
651 | }); | ||
652 | } | ||
653 | } | ||
654 | } | ||
655 | |||
656 | // An object is static (does not move) if selected or not physical | ||
657 | public override bool IsStatic | ||
658 | { | ||
659 | get { return _isSelected || !IsPhysical; } | ||
660 | } | ||
661 | |||
662 | // An object is solid if it's not phantom and if it's not doing VolumeDetect | ||
663 | public override bool IsSolid | ||
664 | { | ||
665 | get { return !IsPhantom && !_isVolumeDetect; } | ||
666 | } | ||
667 | |||
668 | // Make gravity work if the object is physical and not selected | ||
669 | // Called at taint-time!! | ||
670 | private void SetObjectDynamic(bool forceRebuild) | ||
671 | { | ||
672 | // Recreate the physical object if necessary | ||
673 | CreateGeomAndObject(forceRebuild); | ||
674 | } | ||
675 | |||
676 | // Convert the simulator's physical properties into settings on BulletSim objects. | ||
677 | // There are four flags we're interested in: | ||
678 | // IsStatic: Object does not move, otherwise the object has mass and moves | ||
679 | // isSolid: other objects bounce off of this object | ||
680 | // isVolumeDetect: other objects pass through but can generate collisions | ||
681 | // collisionEvents: whether this object returns collision events | ||
682 | private void UpdatePhysicalParameters() | ||
683 | { | ||
684 | // DetailLog("{0},BSPrim.UpdatePhysicalParameters,entry,body={1},shape={2}", LocalID, BSBody, BSShape); | ||
685 | |||
686 | // Mangling all the physical properties requires the object not be in the physical world. | ||
687 | // This is a NOOP if the object is not in the world (BulletSim and Bullet ignore objects not found). | ||
688 | BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, PhysBody.ptr); | ||
689 | |||
690 | // Set up the object physicalness (does gravity and collisions move this object) | ||
691 | MakeDynamic(IsStatic); | ||
692 | |||
693 | // Update vehicle specific parameters (after MakeDynamic() so can change physical parameters) | ||
694 | _vehicle.Refresh(); | ||
695 | |||
696 | // Arrange for collision events if the simulator wants them | ||
697 | EnableCollisions(SubscribedEvents()); | ||
698 | |||
699 | // Make solid or not (do things bounce off or pass through this object). | ||
700 | MakeSolid(IsSolid); | ||
701 | |||
702 | BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, PhysBody.ptr, _position, _orientation); | ||
703 | |||
704 | // Rebuild its shape | ||
705 | BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, PhysBody.ptr); | ||
706 | |||
707 | // Collision filter can be set only when the object is in the world | ||
708 | PhysBody.ApplyCollisionMask(); | ||
709 | |||
710 | // Recompute any linkset parameters. | ||
711 | // When going from non-physical to physical, this re-enables the constraints that | ||
712 | // had been automatically disabled when the mass was set to zero. | ||
713 | // For compound based linksets, this enables and disables interactions of the children. | ||
714 | Linkset.Refresh(this); | ||
715 | |||
716 | DetailLog("{0},BSPrim.UpdatePhysicalParameters,taintExit,static={1},solid={2},mass={3},collide={4},cf={5:X},body={6},shape={7}", | ||
717 | LocalID, IsStatic, IsSolid, _mass, SubscribedEvents(), CurrentCollisionFlags, PhysBody, PhysShape); | ||
718 | } | ||
719 | |||
720 | // "Making dynamic" means changing to and from static. | ||
721 | // When static, gravity does not effect the object and it is fixed in space. | ||
722 | // When dynamic, the object can fall and be pushed by others. | ||
723 | // This is independent of its 'solidness' which controls what passes through | ||
724 | // this object and what interacts with it. | ||
725 | private void MakeDynamic(bool makeStatic) | ||
726 | { | ||
727 | if (makeStatic) | ||
728 | { | ||
729 | // Become a Bullet 'static' object type | ||
730 | CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.CF_STATIC_OBJECT); | ||
731 | // Stop all movement | ||
732 | ZeroMotion(true); | ||
733 | |||
734 | // Set various physical properties so other object interact properly | ||
735 | MaterialAttributes matAttrib = BSMaterials.GetAttributes(Material, false); | ||
736 | BulletSimAPI.SetFriction2(PhysBody.ptr, matAttrib.friction); | ||
737 | BulletSimAPI.SetRestitution2(PhysBody.ptr, matAttrib.restitution); | ||
738 | |||
739 | // Mass is zero which disables a bunch of physics stuff in Bullet | ||
740 | UpdatePhysicalMassProperties(0f); | ||
741 | // Set collision detection parameters | ||
742 | if (BSParam.CcdMotionThreshold > 0f) | ||
743 | { | ||
744 | BulletSimAPI.SetCcdMotionThreshold2(PhysBody.ptr, BSParam.CcdMotionThreshold); | ||
745 | BulletSimAPI.SetCcdSweptSphereRadius2(PhysBody.ptr, BSParam.CcdSweptSphereRadius); | ||
746 | } | ||
747 | |||
748 | // The activation state is 'disabled' so Bullet will not try to act on it. | ||
749 | // BulletSimAPI.ForceActivationState2(PhysBody.ptr, ActivationState.DISABLE_SIMULATION); | ||
750 | // Start it out sleeping and physical actions could wake it up. | ||
751 | BulletSimAPI.ForceActivationState2(PhysBody.ptr, ActivationState.ISLAND_SLEEPING); | ||
752 | |||
753 | // This collides like a static object | ||
754 | PhysBody.collisionType = CollisionType.Static; | ||
755 | |||
756 | // There can be special things needed for implementing linksets | ||
757 | Linkset.MakeStatic(this); | ||
758 | } | ||
759 | else | ||
760 | { | ||
761 | // Not a Bullet static object | ||
762 | CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(PhysBody.ptr, CollisionFlags.CF_STATIC_OBJECT); | ||
763 | |||
764 | // Set various physical properties so other object interact properly | ||
765 | MaterialAttributes matAttrib = BSMaterials.GetAttributes(Material, true); | ||
766 | BulletSimAPI.SetFriction2(PhysBody.ptr, matAttrib.friction); | ||
767 | BulletSimAPI.SetRestitution2(PhysBody.ptr, matAttrib.restitution); | ||
768 | |||
769 | // per http://www.bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=3382 | ||
770 | // Since this can be called multiple times, only zero forces when becoming physical | ||
771 | // BulletSimAPI.ClearAllForces2(BSBody.ptr); | ||
772 | |||
773 | // For good measure, make sure the transform is set through to the motion state | ||
774 | BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation); | ||
775 | |||
776 | // Center of mass is at the center of the object | ||
777 | // DEBUG DEBUG BulletSimAPI.SetCenterOfMassByPosRot2(Linkset.LinksetRoot.PhysBody.ptr, _position, _orientation); | ||
778 | |||
779 | // A dynamic object has mass | ||
780 | UpdatePhysicalMassProperties(RawMass); | ||
781 | |||
782 | // Set collision detection parameters | ||
783 | if (BSParam.CcdMotionThreshold > 0f) | ||
784 | { | ||
785 | BulletSimAPI.SetCcdMotionThreshold2(PhysBody.ptr, BSParam.CcdMotionThreshold); | ||
786 | BulletSimAPI.SetCcdSweptSphereRadius2(PhysBody.ptr, BSParam.CcdSweptSphereRadius); | ||
787 | } | ||
788 | |||
789 | // Various values for simulation limits | ||
790 | BulletSimAPI.SetDamping2(PhysBody.ptr, BSParam.LinearDamping, BSParam.AngularDamping); | ||
791 | BulletSimAPI.SetDeactivationTime2(PhysBody.ptr, BSParam.DeactivationTime); | ||
792 | BulletSimAPI.SetSleepingThresholds2(PhysBody.ptr, BSParam.LinearSleepingThreshold, BSParam.AngularSleepingThreshold); | ||
793 | BulletSimAPI.SetContactProcessingThreshold2(PhysBody.ptr, BSParam.ContactProcessingThreshold); | ||
794 | |||
795 | // This collides like an object. | ||
796 | PhysBody.collisionType = CollisionType.Dynamic; | ||
797 | |||
798 | // Force activation of the object so Bullet will act on it. | ||
799 | // Must do the ForceActivationState2() to overcome the DISABLE_SIMULATION from static objects. | ||
800 | BulletSimAPI.ForceActivationState2(PhysBody.ptr, ActivationState.ACTIVE_TAG); | ||
801 | |||
802 | // There might be special things needed for implementing linksets. | ||
803 | Linkset.MakeDynamic(this); | ||
804 | } | ||
805 | } | ||
806 | |||
807 | // "Making solid" means that other object will not pass through this object. | ||
808 | // To make transparent, we create a Bullet ghost object. | ||
809 | // Note: This expects to be called from the UpdatePhysicalParameters() routine as | ||
810 | // the functions after this one set up the state of a possibly newly created collision body. | ||
811 | private void MakeSolid(bool makeSolid) | ||
812 | { | ||
813 | CollisionObjectTypes bodyType = (CollisionObjectTypes)BulletSimAPI.GetBodyType2(PhysBody.ptr); | ||
814 | if (makeSolid) | ||
815 | { | ||
816 | // Verify the previous code created the correct shape for this type of thing. | ||
817 | if ((bodyType & CollisionObjectTypes.CO_RIGID_BODY) == 0) | ||
818 | { | ||
819 | m_log.ErrorFormat("{0} MakeSolid: physical body of wrong type for solidity. id={1}, type={2}", LogHeader, LocalID, bodyType); | ||
820 | } | ||
821 | CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(PhysBody.ptr, CollisionFlags.CF_NO_CONTACT_RESPONSE); | ||
822 | } | ||
823 | else | ||
824 | { | ||
825 | if ((bodyType & CollisionObjectTypes.CO_GHOST_OBJECT) == 0) | ||
826 | { | ||
827 | m_log.ErrorFormat("{0} MakeSolid: physical body of wrong type for non-solidness. id={1}, type={2}", LogHeader, LocalID, bodyType); | ||
828 | } | ||
829 | CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.CF_NO_CONTACT_RESPONSE); | ||
830 | |||
831 | // Change collision info from a static object to a ghosty collision object | ||
832 | PhysBody.collisionType = CollisionType.VolumeDetect; | ||
833 | } | ||
834 | } | ||
835 | |||
836 | // Enable physical actions. Bullet will keep sleeping non-moving physical objects so | ||
837 | // they need waking up when parameters are changed. | ||
838 | // Called in taint-time!! | ||
839 | private void ActivateIfPhysical(bool forceIt) | ||
840 | { | ||
841 | if (IsPhysical && PhysBody.HasPhysicalBody) | ||
842 | BulletSimAPI.Activate2(PhysBody.ptr, forceIt); | ||
843 | } | ||
844 | |||
845 | // Turn on or off the flag controlling whether collision events are returned to the simulator. | ||
846 | private void EnableCollisions(bool wantsCollisionEvents) | ||
847 | { | ||
848 | if (wantsCollisionEvents) | ||
849 | { | ||
850 | CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); | ||
851 | } | ||
852 | else | ||
853 | { | ||
854 | CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); | ||
855 | } | ||
856 | } | ||
857 | |||
858 | // prims don't fly | ||
859 | public override bool Flying { | ||
860 | get { return _flying; } | ||
861 | set { | ||
862 | _flying = value; | ||
863 | } | ||
864 | } | ||
865 | public override bool SetAlwaysRun { | ||
866 | get { return _setAlwaysRun; } | ||
867 | set { _setAlwaysRun = value; } | ||
868 | } | ||
869 | public override bool ThrottleUpdates { | ||
870 | get { return _throttleUpdates; } | ||
871 | set { _throttleUpdates = value; } | ||
872 | } | ||
873 | public override bool IsColliding { | ||
874 | get { return (CollidingStep == PhysicsScene.SimulationStep); } | ||
875 | set { _isColliding = value; } | ||
876 | } | ||
877 | public override bool CollidingGround { | ||
878 | get { return (CollidingGroundStep == PhysicsScene.SimulationStep); } | ||
879 | set { _collidingGround = value; } | ||
880 | } | ||
881 | public override bool CollidingObj { | ||
882 | get { return _collidingObj; } | ||
883 | set { _collidingObj = value; } | ||
884 | } | ||
885 | public bool IsPhantom { | ||
886 | get { | ||
887 | // SceneObjectPart removes phantom objects from the physics scene | ||
888 | // so, although we could implement touching and such, we never | ||
889 | // are invoked as a phantom object | ||
890 | return false; | ||
891 | } | ||
892 | } | ||
893 | public override bool FloatOnWater { | ||
894 | set { | ||
895 | _floatOnWater = value; | ||
896 | PhysicsScene.TaintedObject("BSPrim.setFloatOnWater", delegate() | ||
897 | { | ||
898 | if (_floatOnWater) | ||
899 | CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_FLOATS_ON_WATER); | ||
900 | else | ||
901 | CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_FLOATS_ON_WATER); | ||
902 | }); | ||
903 | } | ||
904 | } | ||
905 | public override OMV.Vector3 RotationalVelocity { | ||
906 | get { | ||
907 | return _rotationalVelocity; | ||
908 | } | ||
909 | set { | ||
910 | _rotationalVelocity = value; | ||
911 | // m_log.DebugFormat("{0}: RotationalVelocity={1}", LogHeader, _rotationalVelocity); | ||
912 | PhysicsScene.TaintedObject("BSPrim.setRotationalVelocity", delegate() | ||
913 | { | ||
914 | DetailLog("{0},BSPrim.SetRotationalVel,taint,rotvel={1}", LocalID, _rotationalVelocity); | ||
915 | ForceRotationalVelocity = _rotationalVelocity; | ||
916 | }); | ||
917 | } | ||
918 | } | ||
919 | public override OMV.Vector3 ForceRotationalVelocity { | ||
920 | get { | ||
921 | return _rotationalVelocity; | ||
922 | } | ||
923 | set { | ||
924 | _rotationalVelocity = value; | ||
925 | if (PhysBody.HasPhysicalBody) | ||
926 | { | ||
927 | BulletSimAPI.SetAngularVelocity2(PhysBody.ptr, _rotationalVelocity); | ||
928 | ActivateIfPhysical(false); | ||
929 | } | ||
930 | } | ||
931 | } | ||
932 | public override bool Kinematic { | ||
933 | get { return _kinematic; } | ||
934 | set { _kinematic = value; | ||
935 | // m_log.DebugFormat("{0}: Kinematic={1}", LogHeader, _kinematic); | ||
936 | } | ||
937 | } | ||
938 | public override float Buoyancy { | ||
939 | get { return _buoyancy; } | ||
940 | set { | ||
941 | _buoyancy = value; | ||
942 | PhysicsScene.TaintedObject("BSPrim.setBuoyancy", delegate() | ||
943 | { | ||
944 | ForceBuoyancy = _buoyancy; | ||
945 | }); | ||
946 | } | ||
947 | } | ||
948 | public override float ForceBuoyancy { | ||
949 | get { return _buoyancy; } | ||
950 | set { | ||
951 | _buoyancy = value; | ||
952 | // DetailLog("{0},BSPrim.setForceBuoyancy,taint,buoy={1}", LocalID, _buoyancy); | ||
953 | // Buoyancy is faked by changing the gravity applied to the object | ||
954 | if (PhysBody.HasPhysicalBody) | ||
955 | { | ||
956 | float grav = PhysicsScene.Params.gravity * (1f - _buoyancy); | ||
957 | BulletSimAPI.SetGravity2(PhysBody.ptr, new OMV.Vector3(0f, 0f, grav)); | ||
958 | ActivateIfPhysical(false); | ||
959 | } | ||
960 | } | ||
961 | } | ||
962 | |||
963 | // Used for MoveTo | ||
964 | public override OMV.Vector3 PIDTarget { | ||
965 | set { _PIDTarget = value; } | ||
966 | } | ||
967 | public override bool PIDActive { | ||
968 | set { _usePID = value; } | ||
969 | } | ||
970 | public override float PIDTau { | ||
971 | set { _PIDTau = value; } | ||
972 | } | ||
973 | |||
974 | // Used for llSetHoverHeight and maybe vehicle height | ||
975 | // Hover Height will override MoveTo target's Z | ||
976 | public override bool PIDHoverActive { | ||
977 | set { _useHoverPID = value; } | ||
978 | } | ||
979 | public override float PIDHoverHeight { | ||
980 | set { _PIDHoverHeight = value; } | ||
981 | } | ||
982 | public override PIDHoverType PIDHoverType { | ||
983 | set { _PIDHoverType = value; } | ||
984 | } | ||
985 | public override float PIDHoverTau { | ||
986 | set { _PIDHoverTao = value; } | ||
987 | } | ||
988 | |||
989 | // For RotLookAt | ||
990 | public override OMV.Quaternion APIDTarget { set { return; } } | ||
991 | public override bool APIDActive { set { return; } } | ||
992 | public override float APIDStrength { set { return; } } | ||
993 | public override float APIDDamping { set { return; } } | ||
994 | |||
995 | public override void AddForce(OMV.Vector3 force, bool pushforce) { | ||
996 | AddForce(force, pushforce, false); | ||
997 | } | ||
998 | // Applying a force just adds this to the total force on the object. | ||
999 | // This added force will only last the next simulation tick. | ||
1000 | public void AddForce(OMV.Vector3 force, bool pushforce, bool inTaintTime) { | ||
1001 | // for an object, doesn't matter if force is a pushforce or not | ||
1002 | if (force.IsFinite()) | ||
1003 | { | ||
1004 | OMV.Vector3 addForce = force; | ||
1005 | DetailLog("{0},BSPrim.addForce,call,force={1}", LocalID, addForce); | ||
1006 | PhysicsScene.TaintedObject(inTaintTime, "BSPrim.AddForce", delegate() | ||
1007 | { | ||
1008 | // Bullet adds this central force to the total force for this tick | ||
1009 | DetailLog("{0},BSPrim.addForce,taint,force={1}", LocalID, addForce); | ||
1010 | if (PhysBody.HasPhysicalBody) | ||
1011 | { | ||
1012 | BulletSimAPI.ApplyCentralForce2(PhysBody.ptr, addForce); | ||
1013 | ActivateIfPhysical(false); | ||
1014 | } | ||
1015 | }); | ||
1016 | } | ||
1017 | else | ||
1018 | { | ||
1019 | m_log.WarnFormat("{0}: Got a NaN force applied to a prim. LocalID={1}", LogHeader, LocalID); | ||
1020 | return; | ||
1021 | } | ||
1022 | } | ||
1023 | |||
1024 | public override void AddAngularForce(OMV.Vector3 force, bool pushforce) { | ||
1025 | AddAngularForce(force, pushforce, false); | ||
1026 | } | ||
1027 | public void AddAngularForce(OMV.Vector3 force, bool pushforce, bool inTaintTime) | ||
1028 | { | ||
1029 | if (force.IsFinite()) | ||
1030 | { | ||
1031 | OMV.Vector3 angForce = force; | ||
1032 | PhysicsScene.TaintedObject(inTaintTime, "BSPrim.AddAngularForce", delegate() | ||
1033 | { | ||
1034 | if (PhysBody.HasPhysicalBody) | ||
1035 | { | ||
1036 | BulletSimAPI.ApplyTorque2(PhysBody.ptr, angForce); | ||
1037 | ActivateIfPhysical(false); | ||
1038 | } | ||
1039 | }); | ||
1040 | } | ||
1041 | else | ||
1042 | { | ||
1043 | m_log.WarnFormat("{0}: Got a NaN force applied to a prim. LocalID={1}", LogHeader, LocalID); | ||
1044 | return; | ||
1045 | } | ||
1046 | } | ||
1047 | |||
1048 | // A torque impulse. | ||
1049 | // ApplyTorqueImpulse adds torque directly to the angularVelocity. | ||
1050 | // AddAngularForce accumulates the force and applied it to the angular velocity all at once. | ||
1051 | // Computed as: angularVelocity += impulse * inertia; | ||
1052 | public void ApplyTorqueImpulse(OMV.Vector3 impulse, bool inTaintTime) | ||
1053 | { | ||
1054 | OMV.Vector3 applyImpulse = impulse; | ||
1055 | PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ApplyTorqueImpulse", delegate() | ||
1056 | { | ||
1057 | if (PhysBody.HasPhysicalBody) | ||
1058 | { | ||
1059 | BulletSimAPI.ApplyTorqueImpulse2(PhysBody.ptr, applyImpulse); | ||
1060 | ActivateIfPhysical(false); | ||
1061 | } | ||
1062 | }); | ||
1063 | } | ||
1064 | |||
1065 | public override void SetMomentum(OMV.Vector3 momentum) { | ||
1066 | // DetailLog("{0},BSPrim.SetMomentum,call,mom={1}", LocalID, momentum); | ||
1067 | } | ||
1068 | #region Mass Calculation | ||
1069 | |||
1070 | private float CalculateMass() | ||
1071 | { | ||
1072 | float volume = _size.X * _size.Y * _size.Z; // default | ||
1073 | float tmp; | ||
1074 | |||
1075 | float returnMass = 0; | ||
1076 | float hollowAmount = (float)BaseShape.ProfileHollow * 2.0e-5f; | ||
1077 | float hollowVolume = hollowAmount * hollowAmount; | ||
1078 | |||
1079 | switch (BaseShape.ProfileShape) | ||
1080 | { | ||
1081 | case ProfileShape.Square: | ||
1082 | // default box | ||
1083 | |||
1084 | if (BaseShape.PathCurve == (byte)Extrusion.Straight) | ||
1085 | { | ||
1086 | if (hollowAmount > 0.0) | ||
1087 | { | ||
1088 | switch (BaseShape.HollowShape) | ||
1089 | { | ||
1090 | case HollowShape.Square: | ||
1091 | case HollowShape.Same: | ||
1092 | break; | ||
1093 | |||
1094 | case HollowShape.Circle: | ||
1095 | |||
1096 | hollowVolume *= 0.78539816339f; | ||
1097 | break; | ||
1098 | |||
1099 | case HollowShape.Triangle: | ||
1100 | |||
1101 | hollowVolume *= (0.5f * .5f); | ||
1102 | break; | ||
1103 | |||
1104 | default: | ||
1105 | hollowVolume = 0; | ||
1106 | break; | ||
1107 | } | ||
1108 | volume *= (1.0f - hollowVolume); | ||
1109 | } | ||
1110 | } | ||
1111 | |||
1112 | else if (BaseShape.PathCurve == (byte)Extrusion.Curve1) | ||
1113 | { | ||
1114 | //a tube | ||
1115 | |||
1116 | volume *= 0.78539816339e-2f * (float)(200 - BaseShape.PathScaleX); | ||
1117 | tmp= 1.0f -2.0e-2f * (float)(200 - BaseShape.PathScaleY); | ||
1118 | volume -= volume*tmp*tmp; | ||
1119 | |||
1120 | if (hollowAmount > 0.0) | ||
1121 | { | ||
1122 | hollowVolume *= hollowAmount; | ||
1123 | |||
1124 | switch (BaseShape.HollowShape) | ||
1125 | { | ||
1126 | case HollowShape.Square: | ||
1127 | case HollowShape.Same: | ||
1128 | break; | ||
1129 | |||
1130 | case HollowShape.Circle: | ||
1131 | hollowVolume *= 0.78539816339f;; | ||
1132 | break; | ||
1133 | |||
1134 | case HollowShape.Triangle: | ||
1135 | hollowVolume *= 0.5f * 0.5f; | ||
1136 | break; | ||
1137 | default: | ||
1138 | hollowVolume = 0; | ||
1139 | break; | ||
1140 | } | ||
1141 | volume *= (1.0f - hollowVolume); | ||
1142 | } | ||
1143 | } | ||
1144 | |||
1145 | break; | ||
1146 | |||
1147 | case ProfileShape.Circle: | ||
1148 | |||
1149 | if (BaseShape.PathCurve == (byte)Extrusion.Straight) | ||
1150 | { | ||
1151 | volume *= 0.78539816339f; // elipse base | ||
1152 | |||
1153 | if (hollowAmount > 0.0) | ||
1154 | { | ||
1155 | switch (BaseShape.HollowShape) | ||
1156 | { | ||
1157 | case HollowShape.Same: | ||
1158 | case HollowShape.Circle: | ||
1159 | break; | ||
1160 | |||
1161 | case HollowShape.Square: | ||
1162 | hollowVolume *= 0.5f * 2.5984480504799f; | ||
1163 | break; | ||
1164 | |||
1165 | case HollowShape.Triangle: | ||
1166 | hollowVolume *= .5f * 1.27323954473516f; | ||
1167 | break; | ||
1168 | |||
1169 | default: | ||
1170 | hollowVolume = 0; | ||
1171 | break; | ||
1172 | } | ||
1173 | volume *= (1.0f - hollowVolume); | ||
1174 | } | ||
1175 | } | ||
1176 | |||
1177 | else if (BaseShape.PathCurve == (byte)Extrusion.Curve1) | ||
1178 | { | ||
1179 | volume *= 0.61685027506808491367715568749226e-2f * (float)(200 - BaseShape.PathScaleX); | ||
1180 | tmp = 1.0f - .02f * (float)(200 - BaseShape.PathScaleY); | ||
1181 | volume *= (1.0f - tmp * tmp); | ||
1182 | |||
1183 | if (hollowAmount > 0.0) | ||
1184 | { | ||
1185 | |||
1186 | // calculate the hollow volume by it's shape compared to the prim shape | ||
1187 | hollowVolume *= hollowAmount; | ||
1188 | |||
1189 | switch (BaseShape.HollowShape) | ||
1190 | { | ||
1191 | case HollowShape.Same: | ||
1192 | case HollowShape.Circle: | ||
1193 | break; | ||
1194 | |||
1195 | case HollowShape.Square: | ||
1196 | hollowVolume *= 0.5f * 2.5984480504799f; | ||
1197 | break; | ||
1198 | |||
1199 | case HollowShape.Triangle: | ||
1200 | hollowVolume *= .5f * 1.27323954473516f; | ||
1201 | break; | ||
1202 | |||
1203 | default: | ||
1204 | hollowVolume = 0; | ||
1205 | break; | ||
1206 | } | ||
1207 | volume *= (1.0f - hollowVolume); | ||
1208 | } | ||
1209 | } | ||
1210 | break; | ||
1211 | |||
1212 | case ProfileShape.HalfCircle: | ||
1213 | if (BaseShape.PathCurve == (byte)Extrusion.Curve1) | ||
1214 | { | ||
1215 | volume *= 0.52359877559829887307710723054658f; | ||
1216 | } | ||
1217 | break; | ||
1218 | |||
1219 | case ProfileShape.EquilateralTriangle: | ||
1220 | |||
1221 | if (BaseShape.PathCurve == (byte)Extrusion.Straight) | ||
1222 | { | ||
1223 | volume *= 0.32475953f; | ||
1224 | |||
1225 | if (hollowAmount > 0.0) | ||
1226 | { | ||
1227 | |||
1228 | // calculate the hollow volume by it's shape compared to the prim shape | ||
1229 | switch (BaseShape.HollowShape) | ||
1230 | { | ||
1231 | case HollowShape.Same: | ||
1232 | case HollowShape.Triangle: | ||
1233 | hollowVolume *= .25f; | ||
1234 | break; | ||
1235 | |||
1236 | case HollowShape.Square: | ||
1237 | hollowVolume *= 0.499849f * 3.07920140172638f; | ||
1238 | break; | ||
1239 | |||
1240 | case HollowShape.Circle: | ||
1241 | // Hollow shape is a perfect cyllinder in respect to the cube's scale | ||
1242 | // Cyllinder hollow volume calculation | ||
1243 | |||
1244 | hollowVolume *= 0.1963495f * 3.07920140172638f; | ||
1245 | break; | ||
1246 | |||
1247 | default: | ||
1248 | hollowVolume = 0; | ||
1249 | break; | ||
1250 | } | ||
1251 | volume *= (1.0f - hollowVolume); | ||
1252 | } | ||
1253 | } | ||
1254 | else if (BaseShape.PathCurve == (byte)Extrusion.Curve1) | ||
1255 | { | ||
1256 | volume *= 0.32475953f; | ||
1257 | volume *= 0.01f * (float)(200 - BaseShape.PathScaleX); | ||
1258 | tmp = 1.0f - .02f * (float)(200 - BaseShape.PathScaleY); | ||
1259 | volume *= (1.0f - tmp * tmp); | ||
1260 | |||
1261 | if (hollowAmount > 0.0) | ||
1262 | { | ||
1263 | |||
1264 | hollowVolume *= hollowAmount; | ||
1265 | |||
1266 | switch (BaseShape.HollowShape) | ||
1267 | { | ||
1268 | case HollowShape.Same: | ||
1269 | case HollowShape.Triangle: | ||
1270 | hollowVolume *= .25f; | ||
1271 | break; | ||
1272 | |||
1273 | case HollowShape.Square: | ||
1274 | hollowVolume *= 0.499849f * 3.07920140172638f; | ||
1275 | break; | ||
1276 | |||
1277 | case HollowShape.Circle: | ||
1278 | |||
1279 | hollowVolume *= 0.1963495f * 3.07920140172638f; | ||
1280 | break; | ||
1281 | |||
1282 | default: | ||
1283 | hollowVolume = 0; | ||
1284 | break; | ||
1285 | } | ||
1286 | volume *= (1.0f - hollowVolume); | ||
1287 | } | ||
1288 | } | ||
1289 | break; | ||
1290 | |||
1291 | default: | ||
1292 | break; | ||
1293 | } | ||
1294 | |||
1295 | |||
1296 | |||
1297 | float taperX1; | ||
1298 | float taperY1; | ||
1299 | float taperX; | ||
1300 | float taperY; | ||
1301 | float pathBegin; | ||
1302 | float pathEnd; | ||
1303 | float profileBegin; | ||
1304 | float profileEnd; | ||
1305 | |||
1306 | if (BaseShape.PathCurve == (byte)Extrusion.Straight || BaseShape.PathCurve == (byte)Extrusion.Flexible) | ||
1307 | { | ||
1308 | taperX1 = BaseShape.PathScaleX * 0.01f; | ||
1309 | if (taperX1 > 1.0f) | ||
1310 | taperX1 = 2.0f - taperX1; | ||
1311 | taperX = 1.0f - taperX1; | ||
1312 | |||
1313 | taperY1 = BaseShape.PathScaleY * 0.01f; | ||
1314 | if (taperY1 > 1.0f) | ||
1315 | taperY1 = 2.0f - taperY1; | ||
1316 | taperY = 1.0f - taperY1; | ||
1317 | } | ||
1318 | else | ||
1319 | { | ||
1320 | taperX = BaseShape.PathTaperX * 0.01f; | ||
1321 | if (taperX < 0.0f) | ||
1322 | taperX = -taperX; | ||
1323 | taperX1 = 1.0f - taperX; | ||
1324 | |||
1325 | taperY = BaseShape.PathTaperY * 0.01f; | ||
1326 | if (taperY < 0.0f) | ||
1327 | taperY = -taperY; | ||
1328 | taperY1 = 1.0f - taperY; | ||
1329 | |||
1330 | } | ||
1331 | |||
1332 | |||
1333 | volume *= (taperX1 * taperY1 + 0.5f * (taperX1 * taperY + taperX * taperY1) + 0.3333333333f * taperX * taperY); | ||
1334 | |||
1335 | pathBegin = (float)BaseShape.PathBegin * 2.0e-5f; | ||
1336 | pathEnd = 1.0f - (float)BaseShape.PathEnd * 2.0e-5f; | ||
1337 | volume *= (pathEnd - pathBegin); | ||
1338 | |||
1339 | // this is crude aproximation | ||
1340 | profileBegin = (float)BaseShape.ProfileBegin * 2.0e-5f; | ||
1341 | profileEnd = 1.0f - (float)BaseShape.ProfileEnd * 2.0e-5f; | ||
1342 | volume *= (profileEnd - profileBegin); | ||
1343 | |||
1344 | returnMass = _density * volume; | ||
1345 | |||
1346 | /* Comment out code that computes the mass of the linkset. That is done in the Linkset class. | ||
1347 | if (IsRootOfLinkset) | ||
1348 | { | ||
1349 | foreach (BSPrim prim in _childrenPrims) | ||
1350 | { | ||
1351 | returnMass += prim.CalculateMass(); | ||
1352 | } | ||
1353 | } | ||
1354 | */ | ||
1355 | |||
1356 | returnMass = Util.Clamp(returnMass, BSParam.MinimumObjectMass, BSParam.MaximumObjectMass); | ||
1357 | |||
1358 | return returnMass; | ||
1359 | }// end CalculateMass | ||
1360 | #endregion Mass Calculation | ||
1361 | |||
1362 | // Rebuild the geometry and object. | ||
1363 | // This is called when the shape changes so we need to recreate the mesh/hull. | ||
1364 | // Called at taint-time!!! | ||
1365 | public void CreateGeomAndObject(bool forceRebuild) | ||
1366 | { | ||
1367 | // If this prim is part of a linkset, we must remove and restore the physical | ||
1368 | // links if the body is rebuilt. | ||
1369 | bool needToRestoreLinkset = false; | ||
1370 | bool needToRestoreVehicle = false; | ||
1371 | |||
1372 | // Create the correct physical representation for this type of object. | ||
1373 | // Updates PhysBody and PhysShape with the new information. | ||
1374 | // Ignore 'forceRebuild'. This routine makes the right choices and changes of necessary. | ||
1375 | PhysicsScene.Shapes.GetBodyAndShape(false, PhysicsScene.World, this, null, delegate(BulletBody dBody) | ||
1376 | { | ||
1377 | // Called if the current prim body is about to be destroyed. | ||
1378 | // Remove all the physical dependencies on the old body. | ||
1379 | // (Maybe someday make the changing of BSShape an event to be subscribed to by BSLinkset, ...) | ||
1380 | needToRestoreLinkset = Linkset.RemoveBodyDependencies(this); | ||
1381 | needToRestoreVehicle = _vehicle.RemoveBodyDependencies(this); | ||
1382 | }); | ||
1383 | |||
1384 | if (needToRestoreLinkset) | ||
1385 | { | ||
1386 | // If physical body dependencies were removed, restore them | ||
1387 | Linkset.RestoreBodyDependencies(this); | ||
1388 | } | ||
1389 | if (needToRestoreVehicle) | ||
1390 | { | ||
1391 | // If physical body dependencies were removed, restore them | ||
1392 | _vehicle.RestoreBodyDependencies(this); | ||
1393 | } | ||
1394 | |||
1395 | // Make sure the properties are set on the new object | ||
1396 | UpdatePhysicalParameters(); | ||
1397 | return; | ||
1398 | } | ||
1399 | |||
1400 | // The physics engine says that properties have updated. Update same and inform | ||
1401 | // the world that things have changed. | ||
1402 | // TODO: do we really need to check for changed? Maybe just copy values and call RequestPhysicsterseUpdate() | ||
1403 | enum UpdatedProperties { | ||
1404 | Position = 1 << 0, | ||
1405 | Rotation = 1 << 1, | ||
1406 | Velocity = 1 << 2, | ||
1407 | Acceleration = 1 << 3, | ||
1408 | RotationalVel = 1 << 4 | ||
1409 | } | ||
1410 | |||
1411 | const float ROTATION_TOLERANCE = 0.01f; | ||
1412 | const float VELOCITY_TOLERANCE = 0.001f; | ||
1413 | const float POSITION_TOLERANCE = 0.05f; | ||
1414 | const float ACCELERATION_TOLERANCE = 0.01f; | ||
1415 | const float ROTATIONAL_VELOCITY_TOLERANCE = 0.01f; | ||
1416 | |||
1417 | public override void UpdateProperties(EntityProperties entprop) | ||
1418 | { | ||
1419 | // Updates only for individual prims and for the root object of a linkset. | ||
1420 | if (Linkset.IsRoot(this)) | ||
1421 | { | ||
1422 | // A temporary kludge to suppress the rotational effects introduced on vehicles by Bullet | ||
1423 | // TODO: handle physics introduced by Bullet with computed vehicle physics. | ||
1424 | if (_vehicle.IsActive) | ||
1425 | { | ||
1426 | entprop.RotationalVelocity = OMV.Vector3.Zero; | ||
1427 | } | ||
1428 | |||
1429 | // Assign directly to the local variables so the normal set action does not happen | ||
1430 | _position = entprop.Position; | ||
1431 | _orientation = entprop.Rotation; | ||
1432 | _velocity = entprop.Velocity; | ||
1433 | _acceleration = entprop.Acceleration; | ||
1434 | _rotationalVelocity = entprop.RotationalVelocity; | ||
1435 | |||
1436 | // The sanity check can change the velocity and/or position. | ||
1437 | if (IsPhysical && PositionSanityCheck(true)) | ||
1438 | { | ||
1439 | entprop.Position = _position; | ||
1440 | entprop.Velocity = _velocity; | ||
1441 | } | ||
1442 | |||
1443 | OMV.Vector3 direction = OMV.Vector3.UnitX * _orientation; // DEBUG DEBUG DEBUG | ||
1444 | DetailLog("{0},BSPrim.UpdateProperties,call,pos={1},orient={2},dir={3},vel={4},rotVel={5}", | ||
1445 | LocalID, _position, _orientation, direction, _velocity, _rotationalVelocity); | ||
1446 | |||
1447 | // remember the current and last set values | ||
1448 | LastEntityProperties = CurrentEntityProperties; | ||
1449 | CurrentEntityProperties = entprop; | ||
1450 | |||
1451 | base.RequestPhysicsterseUpdate(); | ||
1452 | } | ||
1453 | /* | ||
1454 | else | ||
1455 | { | ||
1456 | // For debugging, report the movement of children | ||
1457 | DetailLog("{0},BSPrim.UpdateProperties,child,pos={1},orient={2},vel={3},accel={4},rotVel={5}", | ||
1458 | LocalID, entprop.Position, entprop.Rotation, entprop.Velocity, | ||
1459 | entprop.Acceleration, entprop.RotationalVelocity); | ||
1460 | } | ||
1461 | */ | ||
1462 | |||
1463 | // The linkset implimentation might want to know about this. | ||
1464 | Linkset.UpdateProperties(this, true); | ||
1465 | } | ||
1466 | } | ||
1467 | } | ||