diff options
Diffstat (limited to 'OpenSim/Region/PhysicsModules/BulletS/BSCharacter.cs')
-rw-r--r-- | OpenSim/Region/PhysicsModules/BulletS/BSCharacter.cs | 813 |
1 files changed, 813 insertions, 0 deletions
diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSCharacter.cs b/OpenSim/Region/PhysicsModules/BulletS/BSCharacter.cs new file mode 100644 index 0000000..9c3f160 --- /dev/null +++ b/OpenSim/Region/PhysicsModules/BulletS/BSCharacter.cs | |||
@@ -0,0 +1,813 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyrightD | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSimulator Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | using System; | ||
28 | using System.Collections.Generic; | ||
29 | using System.Reflection; | ||
30 | using log4net; | ||
31 | using OMV = OpenMetaverse; | ||
32 | using OpenSim.Framework; | ||
33 | using OpenSim.Region.Physics.Manager; | ||
34 | |||
35 | namespace OpenSim.Region.Physics.BulletSPlugin | ||
36 | { | ||
37 | public sealed class BSCharacter : BSPhysObject | ||
38 | { | ||
39 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
40 | private static readonly string LogHeader = "[BULLETS CHAR]"; | ||
41 | |||
42 | // private bool _stopped; | ||
43 | private OMV.Vector3 _size; | ||
44 | private bool _grabbed; | ||
45 | private bool _selected; | ||
46 | private float _mass; | ||
47 | private float _avatarVolume; | ||
48 | private float _collisionScore; | ||
49 | private OMV.Vector3 _acceleration; | ||
50 | private int _physicsActorType; | ||
51 | private bool _isPhysical; | ||
52 | private bool _flying; | ||
53 | private bool _setAlwaysRun; | ||
54 | private bool _throttleUpdates; | ||
55 | private bool _floatOnWater; | ||
56 | private OMV.Vector3 _rotationalVelocity; | ||
57 | private bool _kinematic; | ||
58 | private float _buoyancy; | ||
59 | |||
60 | private BSActorAvatarMove m_moveActor; | ||
61 | private const string AvatarMoveActorName = "BSCharacter.AvatarMove"; | ||
62 | |||
63 | private OMV.Vector3 _PIDTarget; | ||
64 | private float _PIDTau; | ||
65 | |||
66 | // public override OMV.Vector3 RawVelocity | ||
67 | // { get { return base.RawVelocity; } | ||
68 | // set { | ||
69 | // if (value != base.RawVelocity) | ||
70 | // Util.PrintCallStack(); | ||
71 | // Console.WriteLine("Set rawvel to {0}", value); | ||
72 | // base.RawVelocity = value; } | ||
73 | // } | ||
74 | |||
75 | // Avatars are always complete (in the physics engine sense) | ||
76 | public override bool IsIncomplete { get { return false; } } | ||
77 | |||
78 | public BSCharacter( | ||
79 | uint localID, String avName, BSScene parent_scene, OMV.Vector3 pos, OMV.Vector3 vel, OMV.Vector3 size, bool isFlying) | ||
80 | |||
81 | : base(parent_scene, localID, avName, "BSCharacter") | ||
82 | { | ||
83 | _physicsActorType = (int)ActorTypes.Agent; | ||
84 | RawPosition = pos; | ||
85 | |||
86 | _flying = isFlying; | ||
87 | RawOrientation = OMV.Quaternion.Identity; | ||
88 | RawVelocity = vel; | ||
89 | _buoyancy = ComputeBuoyancyFromFlying(isFlying); | ||
90 | Friction = BSParam.AvatarStandingFriction; | ||
91 | Density = BSParam.AvatarDensity; | ||
92 | |||
93 | // Old versions of ScenePresence passed only the height. If width and/or depth are zero, | ||
94 | // replace with the default values. | ||
95 | _size = size; | ||
96 | if (_size.X == 0f) _size.X = BSParam.AvatarCapsuleDepth; | ||
97 | if (_size.Y == 0f) _size.Y = BSParam.AvatarCapsuleWidth; | ||
98 | |||
99 | // The dimensions of the physical capsule are kept in the scale. | ||
100 | // Physics creates a unit capsule which is scaled by the physics engine. | ||
101 | Scale = ComputeAvatarScale(_size); | ||
102 | // set _avatarVolume and _mass based on capsule size, _density and Scale | ||
103 | ComputeAvatarVolumeAndMass(); | ||
104 | |||
105 | DetailLog( | ||
106 | "{0},BSCharacter.create,call,size={1},scale={2},density={3},volume={4},mass={5},pos={6},vel={7}", | ||
107 | LocalID, _size, Scale, Density, _avatarVolume, RawMass, pos, vel); | ||
108 | |||
109 | // do actual creation in taint time | ||
110 | PhysScene.TaintedObject(LocalID, "BSCharacter.create", delegate() | ||
111 | { | ||
112 | DetailLog("{0},BSCharacter.create,taint", LocalID); | ||
113 | |||
114 | // New body and shape into PhysBody and PhysShape | ||
115 | PhysScene.Shapes.GetBodyAndShape(true, PhysScene.World, this); | ||
116 | |||
117 | // The avatar's movement is controlled by this motor that speeds up and slows down | ||
118 | // the avatar seeking to reach the motor's target speed. | ||
119 | // This motor runs as a prestep action for the avatar so it will keep the avatar | ||
120 | // standing as well as moving. Destruction of the avatar will destroy the pre-step action. | ||
121 | m_moveActor = new BSActorAvatarMove(PhysScene, this, AvatarMoveActorName); | ||
122 | PhysicalActors.Add(AvatarMoveActorName, m_moveActor); | ||
123 | |||
124 | SetPhysicalProperties(); | ||
125 | |||
126 | IsInitialized = true; | ||
127 | }); | ||
128 | return; | ||
129 | } | ||
130 | |||
131 | // called when this character is being destroyed and the resources should be released | ||
132 | public override void Destroy() | ||
133 | { | ||
134 | IsInitialized = false; | ||
135 | |||
136 | base.Destroy(); | ||
137 | |||
138 | DetailLog("{0},BSCharacter.Destroy", LocalID); | ||
139 | PhysScene.TaintedObject(LocalID, "BSCharacter.destroy", delegate() | ||
140 | { | ||
141 | PhysScene.Shapes.DereferenceBody(PhysBody, null /* bodyCallback */); | ||
142 | PhysBody.Clear(); | ||
143 | PhysShape.Dereference(PhysScene); | ||
144 | PhysShape = new BSShapeNull(); | ||
145 | }); | ||
146 | } | ||
147 | |||
148 | private void SetPhysicalProperties() | ||
149 | { | ||
150 | PhysScene.PE.RemoveObjectFromWorld(PhysScene.World, PhysBody); | ||
151 | |||
152 | ForcePosition = RawPosition; | ||
153 | |||
154 | // Set the velocity | ||
155 | if (m_moveActor != null) | ||
156 | m_moveActor.SetVelocityAndTarget(RawVelocity, RawVelocity, false); | ||
157 | |||
158 | ForceVelocity = RawVelocity; | ||
159 | TargetVelocity = RawVelocity; | ||
160 | |||
161 | // This will enable or disable the flying buoyancy of the avatar. | ||
162 | // Needs to be reset especially when an avatar is recreated after crossing a region boundry. | ||
163 | Flying = _flying; | ||
164 | |||
165 | PhysScene.PE.SetRestitution(PhysBody, BSParam.AvatarRestitution); | ||
166 | PhysScene.PE.SetMargin(PhysShape.physShapeInfo, PhysScene.Params.collisionMargin); | ||
167 | PhysScene.PE.SetLocalScaling(PhysShape.physShapeInfo, Scale); | ||
168 | PhysScene.PE.SetContactProcessingThreshold(PhysBody, BSParam.ContactProcessingThreshold); | ||
169 | if (BSParam.CcdMotionThreshold > 0f) | ||
170 | { | ||
171 | PhysScene.PE.SetCcdMotionThreshold(PhysBody, BSParam.CcdMotionThreshold); | ||
172 | PhysScene.PE.SetCcdSweptSphereRadius(PhysBody, BSParam.CcdSweptSphereRadius); | ||
173 | } | ||
174 | |||
175 | UpdatePhysicalMassProperties(RawMass, false); | ||
176 | |||
177 | // Make so capsule does not fall over | ||
178 | PhysScene.PE.SetAngularFactorV(PhysBody, OMV.Vector3.Zero); | ||
179 | |||
180 | // The avatar mover sets some parameters. | ||
181 | PhysicalActors.Refresh(); | ||
182 | |||
183 | PhysScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.CF_CHARACTER_OBJECT); | ||
184 | |||
185 | PhysScene.PE.AddObjectToWorld(PhysScene.World, PhysBody); | ||
186 | |||
187 | // PhysicsScene.PE.ForceActivationState(PhysBody, ActivationState.ACTIVE_TAG); | ||
188 | PhysScene.PE.ForceActivationState(PhysBody, ActivationState.DISABLE_DEACTIVATION); | ||
189 | PhysScene.PE.UpdateSingleAabb(PhysScene.World, PhysBody); | ||
190 | |||
191 | // Do this after the object has been added to the world | ||
192 | if (BSParam.AvatarToAvatarCollisionsByDefault) | ||
193 | PhysBody.collisionType = CollisionType.Avatar; | ||
194 | else | ||
195 | PhysBody.collisionType = CollisionType.PhantomToOthersAvatar; | ||
196 | |||
197 | PhysBody.ApplyCollisionMask(PhysScene); | ||
198 | } | ||
199 | |||
200 | public override void RequestPhysicsterseUpdate() | ||
201 | { | ||
202 | base.RequestPhysicsterseUpdate(); | ||
203 | } | ||
204 | |||
205 | // No one calls this method so I don't know what it could possibly mean | ||
206 | public override bool Stopped { get { return false; } } | ||
207 | |||
208 | public override OMV.Vector3 Size { | ||
209 | get | ||
210 | { | ||
211 | // Avatar capsule size is kept in the scale parameter. | ||
212 | return _size; | ||
213 | } | ||
214 | |||
215 | set { | ||
216 | // This is how much the avatar size is changing. Positive means getting bigger. | ||
217 | // The avatar altitude must be adjusted for this change. | ||
218 | float heightChange = value.Z - _size.Z; | ||
219 | |||
220 | _size = value; | ||
221 | // Old versions of ScenePresence passed only the height. If width and/or depth are zero, | ||
222 | // replace with the default values. | ||
223 | if (_size.X == 0f) _size.X = BSParam.AvatarCapsuleDepth; | ||
224 | if (_size.Y == 0f) _size.Y = BSParam.AvatarCapsuleWidth; | ||
225 | |||
226 | Scale = ComputeAvatarScale(_size); | ||
227 | ComputeAvatarVolumeAndMass(); | ||
228 | DetailLog("{0},BSCharacter.setSize,call,size={1},scale={2},density={3},volume={4},mass={5}", | ||
229 | LocalID, _size, Scale, Density, _avatarVolume, RawMass); | ||
230 | |||
231 | PhysScene.TaintedObject(LocalID, "BSCharacter.setSize", delegate() | ||
232 | { | ||
233 | if (PhysBody.HasPhysicalBody && PhysShape.physShapeInfo.HasPhysicalShape) | ||
234 | { | ||
235 | PhysScene.PE.SetLocalScaling(PhysShape.physShapeInfo, Scale); | ||
236 | UpdatePhysicalMassProperties(RawMass, true); | ||
237 | |||
238 | // Adjust the avatar's position to account for the increase/decrease in size | ||
239 | ForcePosition = new OMV.Vector3(RawPosition.X, RawPosition.Y, RawPosition.Z + heightChange / 2f); | ||
240 | |||
241 | // Make sure this change appears as a property update event | ||
242 | PhysScene.PE.PushUpdate(PhysBody); | ||
243 | } | ||
244 | }); | ||
245 | |||
246 | } | ||
247 | } | ||
248 | |||
249 | public override PrimitiveBaseShape Shape | ||
250 | { | ||
251 | set { BaseShape = value; } | ||
252 | } | ||
253 | |||
254 | public override bool Grabbed { | ||
255 | set { _grabbed = value; } | ||
256 | } | ||
257 | public override bool Selected { | ||
258 | set { _selected = value; } | ||
259 | } | ||
260 | public override bool IsSelected | ||
261 | { | ||
262 | get { return _selected; } | ||
263 | } | ||
264 | public override void CrossingFailure() { return; } | ||
265 | public override void link(PhysicsActor obj) { return; } | ||
266 | public override void delink() { return; } | ||
267 | |||
268 | // Set motion values to zero. | ||
269 | // Do it to the properties so the values get set in the physics engine. | ||
270 | // Push the setting of the values to the viewer. | ||
271 | // Called at taint time! | ||
272 | public override void ZeroMotion(bool inTaintTime) | ||
273 | { | ||
274 | RawVelocity = OMV.Vector3.Zero; | ||
275 | _acceleration = OMV.Vector3.Zero; | ||
276 | _rotationalVelocity = OMV.Vector3.Zero; | ||
277 | |||
278 | // Zero some other properties directly into the physics engine | ||
279 | PhysScene.TaintedObject(inTaintTime, LocalID, "BSCharacter.ZeroMotion", delegate() | ||
280 | { | ||
281 | if (PhysBody.HasPhysicalBody) | ||
282 | PhysScene.PE.ClearAllForces(PhysBody); | ||
283 | }); | ||
284 | } | ||
285 | |||
286 | public override void ZeroAngularMotion(bool inTaintTime) | ||
287 | { | ||
288 | _rotationalVelocity = OMV.Vector3.Zero; | ||
289 | |||
290 | PhysScene.TaintedObject(inTaintTime, LocalID, "BSCharacter.ZeroMotion", delegate() | ||
291 | { | ||
292 | if (PhysBody.HasPhysicalBody) | ||
293 | { | ||
294 | PhysScene.PE.SetInterpolationAngularVelocity(PhysBody, OMV.Vector3.Zero); | ||
295 | PhysScene.PE.SetAngularVelocity(PhysBody, OMV.Vector3.Zero); | ||
296 | // The next also get rid of applied linear force but the linear velocity is untouched. | ||
297 | PhysScene.PE.ClearForces(PhysBody); | ||
298 | } | ||
299 | }); | ||
300 | } | ||
301 | |||
302 | |||
303 | public override void LockAngularMotion(OMV.Vector3 axis) { return; } | ||
304 | |||
305 | public override OMV.Vector3 Position { | ||
306 | get { | ||
307 | // Don't refetch the position because this function is called a zillion times | ||
308 | // RawPosition = PhysicsScene.PE.GetObjectPosition(Scene.World, LocalID); | ||
309 | return RawPosition; | ||
310 | } | ||
311 | set { | ||
312 | RawPosition = value; | ||
313 | |||
314 | PhysScene.TaintedObject(LocalID, "BSCharacter.setPosition", delegate() | ||
315 | { | ||
316 | DetailLog("{0},BSCharacter.SetPosition,taint,pos={1},orient={2}", LocalID, RawPosition, RawOrientation); | ||
317 | PositionSanityCheck(); | ||
318 | ForcePosition = RawPosition; | ||
319 | }); | ||
320 | } | ||
321 | } | ||
322 | public override OMV.Vector3 ForcePosition { | ||
323 | get { | ||
324 | RawPosition = PhysScene.PE.GetPosition(PhysBody); | ||
325 | return RawPosition; | ||
326 | } | ||
327 | set { | ||
328 | RawPosition = value; | ||
329 | if (PhysBody.HasPhysicalBody) | ||
330 | { | ||
331 | PhysScene.PE.SetTranslation(PhysBody, RawPosition, RawOrientation); | ||
332 | } | ||
333 | } | ||
334 | } | ||
335 | |||
336 | |||
337 | // Check that the current position is sane and, if not, modify the position to make it so. | ||
338 | // Check for being below terrain or on water. | ||
339 | // Returns 'true' of the position was made sane by some action. | ||
340 | private bool PositionSanityCheck() | ||
341 | { | ||
342 | bool ret = false; | ||
343 | |||
344 | // TODO: check for out of bounds | ||
345 | if (!PhysScene.TerrainManager.IsWithinKnownTerrain(RawPosition)) | ||
346 | { | ||
347 | // The character is out of the known/simulated area. | ||
348 | // Force the avatar position to be within known. ScenePresence will use the position | ||
349 | // plus the velocity to decide if the avatar is moving out of the region. | ||
350 | RawPosition = PhysScene.TerrainManager.ClampPositionIntoKnownTerrain(RawPosition); | ||
351 | DetailLog("{0},BSCharacter.PositionSanityCheck,notWithinKnownTerrain,clampedPos={1}", LocalID, RawPosition); | ||
352 | return true; | ||
353 | } | ||
354 | |||
355 | // If below the ground, move the avatar up | ||
356 | float terrainHeight = PhysScene.TerrainManager.GetTerrainHeightAtXYZ(RawPosition); | ||
357 | if (Position.Z < terrainHeight) | ||
358 | { | ||
359 | DetailLog("{0},BSCharacter.PositionSanityCheck,adjustForUnderGround,pos={1},terrain={2}", LocalID, RawPosition, terrainHeight); | ||
360 | RawPosition = new OMV.Vector3(RawPosition.X, RawPosition.Y, terrainHeight + BSParam.AvatarBelowGroundUpCorrectionMeters); | ||
361 | ret = true; | ||
362 | } | ||
363 | if ((CurrentCollisionFlags & CollisionFlags.BS_FLOATS_ON_WATER) != 0) | ||
364 | { | ||
365 | float waterHeight = PhysScene.TerrainManager.GetWaterLevelAtXYZ(RawPosition); | ||
366 | if (Position.Z < waterHeight) | ||
367 | { | ||
368 | RawPosition = new OMV.Vector3(RawPosition.X, RawPosition.Y, waterHeight); | ||
369 | ret = true; | ||
370 | } | ||
371 | } | ||
372 | |||
373 | return ret; | ||
374 | } | ||
375 | |||
376 | // A version of the sanity check that also makes sure a new position value is | ||
377 | // pushed back to the physics engine. This routine would be used by anyone | ||
378 | // who is not already pushing the value. | ||
379 | private bool PositionSanityCheck(bool inTaintTime) | ||
380 | { | ||
381 | bool ret = false; | ||
382 | if (PositionSanityCheck()) | ||
383 | { | ||
384 | // The new position value must be pushed into the physics engine but we can't | ||
385 | // just assign to "Position" because of potential call loops. | ||
386 | PhysScene.TaintedObject(inTaintTime, LocalID, "BSCharacter.PositionSanityCheck", delegate() | ||
387 | { | ||
388 | DetailLog("{0},BSCharacter.PositionSanityCheck,taint,pos={1},orient={2}", LocalID, RawPosition, RawOrientation); | ||
389 | ForcePosition = RawPosition; | ||
390 | }); | ||
391 | ret = true; | ||
392 | } | ||
393 | return ret; | ||
394 | } | ||
395 | |||
396 | public override float Mass { get { return _mass; } } | ||
397 | |||
398 | // used when we only want this prim's mass and not the linkset thing | ||
399 | public override float RawMass { | ||
400 | get {return _mass; } | ||
401 | } | ||
402 | public override void UpdatePhysicalMassProperties(float physMass, bool inWorld) | ||
403 | { | ||
404 | OMV.Vector3 localInertia = PhysScene.PE.CalculateLocalInertia(PhysShape.physShapeInfo, physMass); | ||
405 | PhysScene.PE.SetMassProps(PhysBody, physMass, localInertia); | ||
406 | } | ||
407 | |||
408 | public override OMV.Vector3 Force { | ||
409 | get { return RawForce; } | ||
410 | set { | ||
411 | RawForce = value; | ||
412 | // m_log.DebugFormat("{0}: Force = {1}", LogHeader, _force); | ||
413 | PhysScene.TaintedObject(LocalID, "BSCharacter.SetForce", delegate() | ||
414 | { | ||
415 | DetailLog("{0},BSCharacter.setForce,taint,force={1}", LocalID, RawForce); | ||
416 | if (PhysBody.HasPhysicalBody) | ||
417 | PhysScene.PE.SetObjectForce(PhysBody, RawForce); | ||
418 | }); | ||
419 | } | ||
420 | } | ||
421 | |||
422 | // Avatars don't do vehicles | ||
423 | public override int VehicleType { get { return (int)Vehicle.TYPE_NONE; } set { return; } } | ||
424 | public override void VehicleFloatParam(int param, float value) { } | ||
425 | public override void VehicleVectorParam(int param, OMV.Vector3 value) {} | ||
426 | public override void VehicleRotationParam(int param, OMV.Quaternion rotation) { } | ||
427 | public override void VehicleFlags(int param, bool remove) { } | ||
428 | |||
429 | // Allows the detection of collisions with inherently non-physical prims. see llVolumeDetect for more | ||
430 | public override void SetVolumeDetect(int param) { return; } | ||
431 | public override bool IsVolumeDetect { get { return false; } } | ||
432 | |||
433 | public override OMV.Vector3 GeometricCenter { get { return OMV.Vector3.Zero; } } | ||
434 | public override OMV.Vector3 CenterOfMass { get { return OMV.Vector3.Zero; } } | ||
435 | |||
436 | // Sets the target in the motor. This starts the changing of the avatar's velocity. | ||
437 | public override OMV.Vector3 TargetVelocity | ||
438 | { | ||
439 | get | ||
440 | { | ||
441 | return base.m_targetVelocity; | ||
442 | } | ||
443 | set | ||
444 | { | ||
445 | DetailLog("{0},BSCharacter.setTargetVelocity,call,vel={1}", LocalID, value); | ||
446 | m_targetVelocity = value; | ||
447 | OMV.Vector3 targetVel = value; | ||
448 | if (_setAlwaysRun && !_flying) | ||
449 | targetVel *= new OMV.Vector3(BSParam.AvatarAlwaysRunFactor, BSParam.AvatarAlwaysRunFactor, 1f); | ||
450 | |||
451 | if (m_moveActor != null) | ||
452 | m_moveActor.SetVelocityAndTarget(RawVelocity, targetVel, false /* inTaintTime */); | ||
453 | } | ||
454 | } | ||
455 | // Directly setting velocity means this is what the user really wants now. | ||
456 | public override OMV.Vector3 Velocity { | ||
457 | get { return RawVelocity; } | ||
458 | set { | ||
459 | RawVelocity = value; | ||
460 | OMV.Vector3 vel = RawVelocity; | ||
461 | |||
462 | DetailLog("{0}: set Velocity = {1}", LocalID, value); | ||
463 | |||
464 | PhysScene.TaintedObject(LocalID, "BSCharacter.setVelocity", delegate() | ||
465 | { | ||
466 | if (m_moveActor != null) | ||
467 | m_moveActor.SetVelocityAndTarget(vel, vel, true /* inTaintTime */); | ||
468 | |||
469 | DetailLog("{0},BSCharacter.setVelocity,taint,vel={1}", LocalID, vel); | ||
470 | ForceVelocity = vel; | ||
471 | }); | ||
472 | } | ||
473 | } | ||
474 | |||
475 | public override OMV.Vector3 ForceVelocity { | ||
476 | get { return RawVelocity; } | ||
477 | set { | ||
478 | PhysScene.AssertInTaintTime("BSCharacter.ForceVelocity"); | ||
479 | // Util.PrintCallStack(); | ||
480 | DetailLog("{0}: set ForceVelocity = {1}", LocalID, value); | ||
481 | |||
482 | RawVelocity = value; | ||
483 | PhysScene.PE.SetLinearVelocity(PhysBody, RawVelocity); | ||
484 | PhysScene.PE.Activate(PhysBody, true); | ||
485 | } | ||
486 | } | ||
487 | |||
488 | public override OMV.Vector3 Torque { | ||
489 | get { return RawTorque; } | ||
490 | set { RawTorque = value; | ||
491 | } | ||
492 | } | ||
493 | |||
494 | public override float CollisionScore { | ||
495 | get { return _collisionScore; } | ||
496 | set { _collisionScore = value; | ||
497 | } | ||
498 | } | ||
499 | public override OMV.Vector3 Acceleration { | ||
500 | get { return _acceleration; } | ||
501 | set { _acceleration = value; } | ||
502 | } | ||
503 | public override OMV.Quaternion Orientation { | ||
504 | get { return RawOrientation; } | ||
505 | set { | ||
506 | // Orientation is set zillions of times when an avatar is walking. It's like | ||
507 | // the viewer doesn't trust us. | ||
508 | if (RawOrientation != value) | ||
509 | { | ||
510 | RawOrientation = value; | ||
511 | PhysScene.TaintedObject(LocalID, "BSCharacter.setOrientation", delegate() | ||
512 | { | ||
513 | // Bullet assumes we know what we are doing when forcing orientation | ||
514 | // so it lets us go against all the rules and just compensates for them later. | ||
515 | // This forces rotation to be only around the Z axis and doesn't change any of the other axis. | ||
516 | // This keeps us from flipping the capsule over which the veiwer does not understand. | ||
517 | float oRoll, oPitch, oYaw; | ||
518 | RawOrientation.GetEulerAngles(out oRoll, out oPitch, out oYaw); | ||
519 | OMV.Quaternion trimmedOrientation = OMV.Quaternion.CreateFromEulers(0f, 0f, oYaw); | ||
520 | // DetailLog("{0},BSCharacter.setOrientation,taint,val={1},valDir={2},conv={3},convDir={4}", | ||
521 | // LocalID, RawOrientation, OMV.Vector3.UnitX * RawOrientation, | ||
522 | // trimmedOrientation, OMV.Vector3.UnitX * trimmedOrientation); | ||
523 | ForceOrientation = trimmedOrientation; | ||
524 | }); | ||
525 | } | ||
526 | } | ||
527 | } | ||
528 | // Go directly to Bullet to get/set the value. | ||
529 | public override OMV.Quaternion ForceOrientation | ||
530 | { | ||
531 | get | ||
532 | { | ||
533 | RawOrientation = PhysScene.PE.GetOrientation(PhysBody); | ||
534 | return RawOrientation; | ||
535 | } | ||
536 | set | ||
537 | { | ||
538 | RawOrientation = value; | ||
539 | if (PhysBody.HasPhysicalBody) | ||
540 | { | ||
541 | // RawPosition = PhysicsScene.PE.GetPosition(BSBody); | ||
542 | PhysScene.PE.SetTranslation(PhysBody, RawPosition, RawOrientation); | ||
543 | } | ||
544 | } | ||
545 | } | ||
546 | public override int PhysicsActorType { | ||
547 | get { return _physicsActorType; } | ||
548 | set { _physicsActorType = value; | ||
549 | } | ||
550 | } | ||
551 | public override bool IsPhysical { | ||
552 | get { return _isPhysical; } | ||
553 | set { _isPhysical = value; | ||
554 | } | ||
555 | } | ||
556 | public override bool IsSolid { | ||
557 | get { return true; } | ||
558 | } | ||
559 | public override bool IsStatic { | ||
560 | get { return false; } | ||
561 | } | ||
562 | public override bool IsPhysicallyActive { | ||
563 | get { return true; } | ||
564 | } | ||
565 | public override bool Flying { | ||
566 | get { return _flying; } | ||
567 | set { | ||
568 | _flying = value; | ||
569 | |||
570 | // simulate flying by changing the effect of gravity | ||
571 | Buoyancy = ComputeBuoyancyFromFlying(_flying); | ||
572 | } | ||
573 | } | ||
574 | // Flying is implimented by changing the avatar's buoyancy. | ||
575 | // Would this be done better with a vehicle type? | ||
576 | private float ComputeBuoyancyFromFlying(bool ifFlying) { | ||
577 | return ifFlying ? 1f : 0f; | ||
578 | } | ||
579 | public override bool | ||
580 | SetAlwaysRun { | ||
581 | get { return _setAlwaysRun; } | ||
582 | set { _setAlwaysRun = value; } | ||
583 | } | ||
584 | public override bool ThrottleUpdates { | ||
585 | get { return _throttleUpdates; } | ||
586 | set { _throttleUpdates = value; } | ||
587 | } | ||
588 | public override bool FloatOnWater { | ||
589 | set { | ||
590 | _floatOnWater = value; | ||
591 | PhysScene.TaintedObject(LocalID, "BSCharacter.setFloatOnWater", delegate() | ||
592 | { | ||
593 | if (PhysBody.HasPhysicalBody) | ||
594 | { | ||
595 | if (_floatOnWater) | ||
596 | CurrentCollisionFlags = PhysScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.BS_FLOATS_ON_WATER); | ||
597 | else | ||
598 | CurrentCollisionFlags = PhysScene.PE.RemoveFromCollisionFlags(PhysBody, CollisionFlags.BS_FLOATS_ON_WATER); | ||
599 | } | ||
600 | }); | ||
601 | } | ||
602 | } | ||
603 | public override OMV.Vector3 RotationalVelocity { | ||
604 | get { return _rotationalVelocity; } | ||
605 | set { _rotationalVelocity = value; } | ||
606 | } | ||
607 | public override OMV.Vector3 ForceRotationalVelocity { | ||
608 | get { return _rotationalVelocity; } | ||
609 | set { _rotationalVelocity = value; } | ||
610 | } | ||
611 | public override bool Kinematic { | ||
612 | get { return _kinematic; } | ||
613 | set { _kinematic = value; } | ||
614 | } | ||
615 | // neg=fall quickly, 0=1g, 1=0g, pos=float up | ||
616 | public override float Buoyancy { | ||
617 | get { return _buoyancy; } | ||
618 | set { _buoyancy = value; | ||
619 | PhysScene.TaintedObject(LocalID, "BSCharacter.setBuoyancy", delegate() | ||
620 | { | ||
621 | DetailLog("{0},BSCharacter.setBuoyancy,taint,buoy={1}", LocalID, _buoyancy); | ||
622 | ForceBuoyancy = _buoyancy; | ||
623 | }); | ||
624 | } | ||
625 | } | ||
626 | public override float ForceBuoyancy { | ||
627 | get { return _buoyancy; } | ||
628 | set { | ||
629 | PhysScene.AssertInTaintTime("BSCharacter.ForceBuoyancy"); | ||
630 | |||
631 | _buoyancy = value; | ||
632 | DetailLog("{0},BSCharacter.setForceBuoyancy,taint,buoy={1}", LocalID, _buoyancy); | ||
633 | // Buoyancy is faked by changing the gravity applied to the object | ||
634 | float grav = BSParam.Gravity * (1f - _buoyancy); | ||
635 | Gravity = new OMV.Vector3(0f, 0f, grav); | ||
636 | if (PhysBody.HasPhysicalBody) | ||
637 | PhysScene.PE.SetGravity(PhysBody, Gravity); | ||
638 | } | ||
639 | } | ||
640 | |||
641 | // Used for MoveTo | ||
642 | public override OMV.Vector3 PIDTarget { | ||
643 | set { _PIDTarget = value; } | ||
644 | } | ||
645 | |||
646 | public override bool PIDActive { get; set; } | ||
647 | |||
648 | public override float PIDTau { | ||
649 | set { _PIDTau = value; } | ||
650 | } | ||
651 | |||
652 | public override void AddForce(OMV.Vector3 force, bool pushforce) | ||
653 | { | ||
654 | // Since this force is being applied in only one step, make this a force per second. | ||
655 | OMV.Vector3 addForce = force / PhysScene.LastTimeStep; | ||
656 | AddForce(addForce, pushforce, false); | ||
657 | } | ||
658 | public override void AddForce(OMV.Vector3 force, bool pushforce, bool inTaintTime) { | ||
659 | if (force.IsFinite()) | ||
660 | { | ||
661 | OMV.Vector3 addForce = Util.ClampV(force, BSParam.MaxAddForceMagnitude); | ||
662 | // DetailLog("{0},BSCharacter.addForce,call,force={1}", LocalID, addForce); | ||
663 | |||
664 | PhysScene.TaintedObject(inTaintTime, LocalID, "BSCharacter.AddForce", delegate() | ||
665 | { | ||
666 | // Bullet adds this central force to the total force for this tick | ||
667 | // DetailLog("{0},BSCharacter.addForce,taint,force={1}", LocalID, addForce); | ||
668 | if (PhysBody.HasPhysicalBody) | ||
669 | { | ||
670 | PhysScene.PE.ApplyCentralForce(PhysBody, addForce); | ||
671 | } | ||
672 | }); | ||
673 | } | ||
674 | else | ||
675 | { | ||
676 | m_log.WarnFormat("{0}: Got a NaN force applied to a character. LocalID={1}", LogHeader, LocalID); | ||
677 | return; | ||
678 | } | ||
679 | } | ||
680 | |||
681 | public override void AddAngularForce(OMV.Vector3 force, bool pushforce, bool inTaintTime) { | ||
682 | } | ||
683 | public override void SetMomentum(OMV.Vector3 momentum) { | ||
684 | } | ||
685 | |||
686 | private OMV.Vector3 ComputeAvatarScale(OMV.Vector3 size) | ||
687 | { | ||
688 | OMV.Vector3 newScale = size; | ||
689 | |||
690 | // Bullet's capsule total height is the "passed height + radius * 2"; | ||
691 | // The base capsule is 1 unit in diameter and 2 units in height (passed radius=0.5, passed height = 1) | ||
692 | // The number we pass in for 'scaling' is the multiplier to get that base | ||
693 | // shape to be the size desired. | ||
694 | // So, when creating the scale for the avatar height, we take the passed height | ||
695 | // (size.Z) and remove the caps. | ||
696 | // An oddity of the Bullet capsule implementation is that it presumes the Y | ||
697 | // dimension is the radius of the capsule. Even though some of the code allows | ||
698 | // for a asymmetrical capsule, other parts of the code presume it is cylindrical. | ||
699 | |||
700 | // Scale is multiplier of radius with one of "0.5" | ||
701 | |||
702 | float heightAdjust = BSParam.AvatarHeightMidFudge; | ||
703 | if (BSParam.AvatarHeightLowFudge != 0f || BSParam.AvatarHeightHighFudge != 0f) | ||
704 | { | ||
705 | const float AVATAR_LOW = 1.1f; | ||
706 | const float AVATAR_MID = 1.775f; // 1.87f | ||
707 | const float AVATAR_HI = 2.45f; | ||
708 | // An avatar is between 1.1 and 2.45 meters. Midpoint is 1.775m. | ||
709 | float midHeightOffset = size.Z - AVATAR_MID; | ||
710 | if (midHeightOffset < 0f) | ||
711 | { | ||
712 | // Small avatar. Add the adjustment based on the distance from midheight | ||
713 | heightAdjust += ((-1f * midHeightOffset) / (AVATAR_MID - AVATAR_LOW)) * BSParam.AvatarHeightLowFudge; | ||
714 | } | ||
715 | else | ||
716 | { | ||
717 | // Large avatar. Add the adjustment based on the distance from midheight | ||
718 | heightAdjust += ((midHeightOffset) / (AVATAR_HI - AVATAR_MID)) * BSParam.AvatarHeightHighFudge; | ||
719 | } | ||
720 | } | ||
721 | if (BSParam.AvatarShape == BSShapeCollection.AvatarShapeCapsule) | ||
722 | { | ||
723 | newScale.X = size.X / 2f; | ||
724 | newScale.Y = size.Y / 2f; | ||
725 | // The total scale height is the central cylindar plus the caps on the two ends. | ||
726 | newScale.Z = (size.Z + (Math.Min(size.X, size.Y) * 2) + heightAdjust) / 2f; | ||
727 | } | ||
728 | else | ||
729 | { | ||
730 | newScale.Z = size.Z + heightAdjust; | ||
731 | } | ||
732 | // m_log.DebugFormat("{0} ComputeAvatarScale: size={1},adj={2},scale={3}", LogHeader, size, heightAdjust, newScale); | ||
733 | |||
734 | // If smaller than the endcaps, just fake like we're almost that small | ||
735 | if (newScale.Z < 0) | ||
736 | newScale.Z = 0.1f; | ||
737 | |||
738 | DetailLog("{0},BSCharacter.ComputerAvatarScale,size={1},lowF={2},midF={3},hiF={4},adj={5},newScale={6}", | ||
739 | LocalID, size, BSParam.AvatarHeightLowFudge, BSParam.AvatarHeightMidFudge, BSParam.AvatarHeightHighFudge, heightAdjust, newScale); | ||
740 | |||
741 | return newScale; | ||
742 | } | ||
743 | |||
744 | // set _avatarVolume and _mass based on capsule size, _density and Scale | ||
745 | private void ComputeAvatarVolumeAndMass() | ||
746 | { | ||
747 | _avatarVolume = (float)( | ||
748 | Math.PI | ||
749 | * Size.X / 2f | ||
750 | * Size.Y / 2f // the area of capsule cylinder | ||
751 | * Size.Z // times height of capsule cylinder | ||
752 | + 1.33333333f | ||
753 | * Math.PI | ||
754 | * Size.X / 2f | ||
755 | * Math.Min(Size.X, Size.Y) / 2 | ||
756 | * Size.Y / 2f // plus the volume of the capsule end caps | ||
757 | ); | ||
758 | _mass = Density * BSParam.DensityScaleFactor * _avatarVolume; | ||
759 | } | ||
760 | |||
761 | // The physics engine says that properties have updated. Update same and inform | ||
762 | // the world that things have changed. | ||
763 | public override void UpdateProperties(EntityProperties entprop) | ||
764 | { | ||
765 | // Let anyone (like the actors) modify the updated properties before they are pushed into the object and the simulator. | ||
766 | TriggerPreUpdatePropertyAction(ref entprop); | ||
767 | |||
768 | RawPosition = entprop.Position; | ||
769 | RawOrientation = entprop.Rotation; | ||
770 | |||
771 | // Smooth velocity. OpenSimulator is VERY sensitive to changes in velocity of the avatar | ||
772 | // and will send agent updates to the clients if velocity changes by more than | ||
773 | // 0.001m/s. Bullet introduces a lot of jitter in the velocity which causes many | ||
774 | // extra updates. | ||
775 | // | ||
776 | // XXX: Contrary to the above comment, setting an update threshold here above 0.4 actually introduces jitter to | ||
777 | // avatar movement rather than removes it. The larger the threshold, the bigger the jitter. | ||
778 | // This is most noticeable in level flight and can be seen with | ||
779 | // the "show updates" option in a viewer. With an update threshold, the RawVelocity cycles between a lower | ||
780 | // bound and an upper bound, where the difference between the two is enough to trigger a large delta v update | ||
781 | // and subsequently trigger an update in ScenePresence.SendTerseUpdateToAllClients(). The cause of this cycle (feedback?) | ||
782 | // has not yet been identified. | ||
783 | // | ||
784 | // If there is a threshold below 0.4 or no threshold check at all (as in ODE), then RawVelocity stays constant and extra | ||
785 | // updates are not triggered in ScenePresence.SendTerseUpdateToAllClients(). | ||
786 | // if (!entprop.Velocity.ApproxEquals(RawVelocity, 0.1f)) | ||
787 | RawVelocity = entprop.Velocity; | ||
788 | |||
789 | _acceleration = entprop.Acceleration; | ||
790 | _rotationalVelocity = entprop.RotationalVelocity; | ||
791 | |||
792 | // Do some sanity checking for the avatar. Make sure it's above ground and inbounds. | ||
793 | if (PositionSanityCheck(true)) | ||
794 | { | ||
795 | DetailLog("{0},BSCharacter.UpdateProperties,updatePosForSanity,pos={1}", LocalID, RawPosition); | ||
796 | entprop.Position = RawPosition; | ||
797 | } | ||
798 | |||
799 | // remember the current and last set values | ||
800 | LastEntityProperties = CurrentEntityProperties; | ||
801 | CurrentEntityProperties = entprop; | ||
802 | |||
803 | // Tell the linkset about value changes | ||
804 | // Linkset.UpdateProperties(UpdatedProperties.EntPropUpdates, this); | ||
805 | |||
806 | // Avatars don't report their changes the usual way. Changes are checked for in the heartbeat loop. | ||
807 | // PhysScene.PostUpdate(this); | ||
808 | |||
809 | DetailLog("{0},BSCharacter.UpdateProperties,call,pos={1},orient={2},vel={3},accel={4},rotVel={5}", | ||
810 | LocalID, RawPosition, RawOrientation, RawVelocity, _acceleration, _rotationalVelocity); | ||
811 | } | ||
812 | } | ||
813 | } | ||