diff options
Diffstat (limited to 'OpenSim/Region/Physics/BulletSPlugin')
-rw-r--r-- | OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs | 451 | ||||
-rw-r--r-- | OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs | 951 | ||||
-rw-r--r-- | OpenSim/Region/Physics/BulletSPlugin/BSPlugin.cs | 68 | ||||
-rw-r--r-- | OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs | 1357 | ||||
-rw-r--r-- | OpenSim/Region/Physics/BulletSPlugin/BSScene.cs | 860 | ||||
-rw-r--r-- | OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs | 257 |
6 files changed, 3944 insertions, 0 deletions
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs new file mode 100644 index 0000000..682eb80 --- /dev/null +++ b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs | |||
@@ -0,0 +1,451 @@ | |||
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 OpenMetaverse; | ||
32 | using OpenSim.Framework; | ||
33 | using OpenSim.Region.Physics.Manager; | ||
34 | |||
35 | namespace OpenSim.Region.Physics.BulletSPlugin | ||
36 | { | ||
37 | public class BSCharacter : PhysicsActor | ||
38 | { | ||
39 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
40 | private static readonly string LogHeader = "[BULLETS CHAR]"; | ||
41 | |||
42 | private BSScene _scene; | ||
43 | private String _avName; | ||
44 | private bool _stopped; | ||
45 | private Vector3 _size; | ||
46 | private Vector3 _scale; | ||
47 | private PrimitiveBaseShape _pbs; | ||
48 | private uint _localID = 0; | ||
49 | private bool _grabbed; | ||
50 | private bool _selected; | ||
51 | private Vector3 _position; | ||
52 | private float _mass; | ||
53 | public float _density; | ||
54 | public float _avatarVolume; | ||
55 | private Vector3 _force; | ||
56 | private Vector3 _velocity; | ||
57 | private Vector3 _torque; | ||
58 | private float _collisionScore; | ||
59 | private Vector3 _acceleration; | ||
60 | private Quaternion _orientation; | ||
61 | private int _physicsActorType; | ||
62 | private bool _isPhysical; | ||
63 | private bool _flying; | ||
64 | private bool _setAlwaysRun; | ||
65 | private bool _throttleUpdates; | ||
66 | private bool _isColliding; | ||
67 | private long _collidingStep; | ||
68 | private bool _collidingGround; | ||
69 | private long _collidingGroundStep; | ||
70 | private bool _collidingObj; | ||
71 | private bool _floatOnWater; | ||
72 | private Vector3 _rotationalVelocity; | ||
73 | private bool _kinematic; | ||
74 | private float _buoyancy; | ||
75 | |||
76 | private int _subscribedEventsMs = 0; | ||
77 | private int _lastCollisionTime = 0; | ||
78 | |||
79 | private 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 BSCharacter(uint localID, String avName, BSScene parent_scene, Vector3 pos, Vector3 size, bool isFlying) | ||
88 | { | ||
89 | _localID = localID; | ||
90 | _avName = avName; | ||
91 | _scene = parent_scene; | ||
92 | _position = pos; | ||
93 | _size = size; | ||
94 | _flying = isFlying; | ||
95 | _orientation = Quaternion.Identity; | ||
96 | _velocity = Vector3.Zero; | ||
97 | _buoyancy = isFlying ? 1f : 0f; | ||
98 | _scale = new Vector3(1f, 1f, 1f); | ||
99 | _density = _scene.Params.avatarDensity; | ||
100 | ComputeAvatarVolumeAndMass(); // set _avatarVolume and _mass based on capsule size, _density and _scale | ||
101 | |||
102 | ShapeData shapeData = new ShapeData(); | ||
103 | shapeData.ID = _localID; | ||
104 | shapeData.Type = ShapeData.PhysicsShapeType.SHAPE_AVATAR; | ||
105 | shapeData.Position = _position; | ||
106 | shapeData.Rotation = _orientation; | ||
107 | shapeData.Velocity = _velocity; | ||
108 | shapeData.Scale = _scale; | ||
109 | shapeData.Mass = _mass; | ||
110 | shapeData.Buoyancy = _buoyancy; | ||
111 | shapeData.Static = ShapeData.numericFalse; | ||
112 | shapeData.Friction = _scene.Params.avatarFriction; | ||
113 | shapeData.Restitution = _scene.Params.defaultRestitution; | ||
114 | |||
115 | // do actual create at taint time | ||
116 | _scene.TaintedObject(delegate() | ||
117 | { | ||
118 | BulletSimAPI.CreateObject(parent_scene.WorldID, shapeData); | ||
119 | }); | ||
120 | |||
121 | return; | ||
122 | } | ||
123 | |||
124 | // called when this character is being destroyed and the resources should be released | ||
125 | public void Destroy() | ||
126 | { | ||
127 | _scene.TaintedObject(delegate() | ||
128 | { | ||
129 | BulletSimAPI.DestroyObject(_scene.WorldID, _localID); | ||
130 | }); | ||
131 | } | ||
132 | |||
133 | public override void RequestPhysicsterseUpdate() | ||
134 | { | ||
135 | base.RequestPhysicsterseUpdate(); | ||
136 | } | ||
137 | |||
138 | public override bool Stopped { | ||
139 | get { return _stopped; } | ||
140 | } | ||
141 | public override Vector3 Size { | ||
142 | get { return _size; } | ||
143 | set { _size = value; | ||
144 | } | ||
145 | } | ||
146 | public override PrimitiveBaseShape Shape { | ||
147 | set { _pbs = value; | ||
148 | } | ||
149 | } | ||
150 | public override uint LocalID { | ||
151 | set { _localID = value; | ||
152 | } | ||
153 | get { return _localID; } | ||
154 | } | ||
155 | public override bool Grabbed { | ||
156 | set { _grabbed = value; | ||
157 | } | ||
158 | } | ||
159 | public override bool Selected { | ||
160 | set { _selected = value; | ||
161 | } | ||
162 | } | ||
163 | public override void CrossingFailure() { return; } | ||
164 | public override void link(PhysicsActor obj) { return; } | ||
165 | public override void delink() { return; } | ||
166 | public override void LockAngularMotion(Vector3 axis) { return; } | ||
167 | |||
168 | public override Vector3 Position { | ||
169 | get { | ||
170 | // _position = BulletSimAPI.GetObjectPosition(_scene.WorldID, _localID); | ||
171 | return _position; | ||
172 | } | ||
173 | set { | ||
174 | _position = value; | ||
175 | _scene.TaintedObject(delegate() | ||
176 | { | ||
177 | BulletSimAPI.SetObjectTranslation(_scene.WorldID, _localID, _position, _orientation); | ||
178 | }); | ||
179 | } | ||
180 | } | ||
181 | public override float Mass { | ||
182 | get { | ||
183 | return _mass; | ||
184 | } | ||
185 | } | ||
186 | public override Vector3 Force { | ||
187 | get { return _force; } | ||
188 | set { | ||
189 | _force = value; | ||
190 | // m_log.DebugFormat("{0}: Force = {1}", LogHeader, _force); | ||
191 | _scene.TaintedObject(delegate() | ||
192 | { | ||
193 | BulletSimAPI.SetObjectForce(_scene.WorldID, _localID, _force); | ||
194 | }); | ||
195 | } | ||
196 | } | ||
197 | |||
198 | public override int VehicleType { | ||
199 | get { return 0; } | ||
200 | set { return; } | ||
201 | } | ||
202 | public override void VehicleFloatParam(int param, float value) { } | ||
203 | public override void VehicleVectorParam(int param, Vector3 value) {} | ||
204 | public override void VehicleRotationParam(int param, Quaternion rotation) { } | ||
205 | public override void VehicleFlags(int param, bool remove) { } | ||
206 | |||
207 | // Allows the detection of collisions with inherently non-physical prims. see llVolumeDetect for more | ||
208 | public override void SetVolumeDetect(int param) { return; } | ||
209 | |||
210 | public override Vector3 GeometricCenter { get { return Vector3.Zero; } } | ||
211 | public override Vector3 CenterOfMass { get { return Vector3.Zero; } } | ||
212 | public override Vector3 Velocity { | ||
213 | get { return _velocity; } | ||
214 | set { | ||
215 | _velocity = value; | ||
216 | // m_log.DebugFormat("{0}: set velocity = {1}", LogHeader, _velocity); | ||
217 | _scene.TaintedObject(delegate() | ||
218 | { | ||
219 | BulletSimAPI.SetObjectVelocity(_scene.WorldID, _localID, _velocity); | ||
220 | }); | ||
221 | } | ||
222 | } | ||
223 | public override Vector3 Torque { | ||
224 | get { return _torque; } | ||
225 | set { _torque = value; | ||
226 | } | ||
227 | } | ||
228 | public override float CollisionScore { | ||
229 | get { return _collisionScore; } | ||
230 | set { _collisionScore = value; | ||
231 | } | ||
232 | } | ||
233 | public override Vector3 Acceleration { | ||
234 | get { return _acceleration; } | ||
235 | } | ||
236 | public override Quaternion Orientation { | ||
237 | get { return _orientation; } | ||
238 | set { | ||
239 | _orientation = value; | ||
240 | // m_log.DebugFormat("{0}: set orientation to {1}", LogHeader, _orientation); | ||
241 | _scene.TaintedObject(delegate() | ||
242 | { | ||
243 | // _position = BulletSimAPI.GetObjectPosition(_scene.WorldID, _localID); | ||
244 | BulletSimAPI.SetObjectTranslation(_scene.WorldID, _localID, _position, _orientation); | ||
245 | }); | ||
246 | } | ||
247 | } | ||
248 | public override int PhysicsActorType { | ||
249 | get { return _physicsActorType; } | ||
250 | set { _physicsActorType = value; | ||
251 | } | ||
252 | } | ||
253 | public override bool IsPhysical { | ||
254 | get { return _isPhysical; } | ||
255 | set { _isPhysical = value; | ||
256 | } | ||
257 | } | ||
258 | public override bool Flying { | ||
259 | get { return _flying; } | ||
260 | set { | ||
261 | _flying = value; | ||
262 | _scene.TaintedObject(delegate() | ||
263 | { | ||
264 | // simulate flying by changing the effect of gravity | ||
265 | BulletSimAPI.SetObjectBuoyancy(_scene.WorldID, LocalID, _flying ? 1f : 0f); | ||
266 | }); | ||
267 | } | ||
268 | } | ||
269 | public override bool | ||
270 | SetAlwaysRun { | ||
271 | get { return _setAlwaysRun; } | ||
272 | set { _setAlwaysRun = value; } | ||
273 | } | ||
274 | public override bool ThrottleUpdates { | ||
275 | get { return _throttleUpdates; } | ||
276 | set { _throttleUpdates = value; } | ||
277 | } | ||
278 | public override bool IsColliding { | ||
279 | get { return (_collidingStep == _scene.SimulationStep); } | ||
280 | set { _isColliding = value; } | ||
281 | } | ||
282 | public override bool CollidingGround { | ||
283 | get { return (_collidingGroundStep == _scene.SimulationStep); } | ||
284 | set { _collidingGround = value; } | ||
285 | } | ||
286 | public override bool CollidingObj { | ||
287 | get { return _collidingObj; } | ||
288 | set { _collidingObj = value; } | ||
289 | } | ||
290 | public override bool FloatOnWater { | ||
291 | set { _floatOnWater = value; } | ||
292 | } | ||
293 | public override Vector3 RotationalVelocity { | ||
294 | get { return _rotationalVelocity; } | ||
295 | set { _rotationalVelocity = value; } | ||
296 | } | ||
297 | public override bool Kinematic { | ||
298 | get { return _kinematic; } | ||
299 | set { _kinematic = value; } | ||
300 | } | ||
301 | public override float Buoyancy { | ||
302 | get { return _buoyancy; } | ||
303 | set { _buoyancy = value; | ||
304 | _scene.TaintedObject(delegate() | ||
305 | { | ||
306 | BulletSimAPI.SetObjectBuoyancy(_scene.WorldID, LocalID, _buoyancy); | ||
307 | }); | ||
308 | } | ||
309 | } | ||
310 | |||
311 | // Used for MoveTo | ||
312 | public override Vector3 PIDTarget { | ||
313 | set { _PIDTarget = value; } | ||
314 | } | ||
315 | public override bool PIDActive { | ||
316 | set { _usePID = value; } | ||
317 | } | ||
318 | public override float PIDTau { | ||
319 | set { _PIDTau = value; } | ||
320 | } | ||
321 | |||
322 | // Used for llSetHoverHeight and maybe vehicle height | ||
323 | // Hover Height will override MoveTo target's Z | ||
324 | public override bool PIDHoverActive { | ||
325 | set { _useHoverPID = value; } | ||
326 | } | ||
327 | public override float PIDHoverHeight { | ||
328 | set { _PIDHoverHeight = value; } | ||
329 | } | ||
330 | public override PIDHoverType PIDHoverType { | ||
331 | set { _PIDHoverType = value; } | ||
332 | } | ||
333 | public override float PIDHoverTau { | ||
334 | set { _PIDHoverTao = value; } | ||
335 | } | ||
336 | |||
337 | // For RotLookAt | ||
338 | public override Quaternion APIDTarget { set { return; } } | ||
339 | public override bool APIDActive { set { return; } } | ||
340 | public override float APIDStrength { set { return; } } | ||
341 | public override float APIDDamping { set { return; } } | ||
342 | |||
343 | public override void AddForce(Vector3 force, bool pushforce) { | ||
344 | if (force.IsFinite()) | ||
345 | { | ||
346 | _force.X += force.X; | ||
347 | _force.Y += force.Y; | ||
348 | _force.Z += force.Z; | ||
349 | // m_log.DebugFormat("{0}: AddForce. adding={1}, newForce={2}", LogHeader, force, _force); | ||
350 | _scene.TaintedObject(delegate() | ||
351 | { | ||
352 | BulletSimAPI.SetObjectForce(_scene.WorldID, _localID, _force); | ||
353 | }); | ||
354 | } | ||
355 | else | ||
356 | { | ||
357 | m_log.WarnFormat("{0}: Got a NaN force applied to a Character", LogHeader); | ||
358 | } | ||
359 | //m_lastUpdateSent = false; | ||
360 | } | ||
361 | public override void AddAngularForce(Vector3 force, bool pushforce) { | ||
362 | } | ||
363 | public override void SetMomentum(Vector3 momentum) { | ||
364 | } | ||
365 | public override void SubscribeEvents(int ms) { | ||
366 | _subscribedEventsMs = ms; | ||
367 | _lastCollisionTime = Util.EnvironmentTickCount() - _subscribedEventsMs; // make first collision happen | ||
368 | } | ||
369 | public override void UnSubscribeEvents() { | ||
370 | _subscribedEventsMs = 0; | ||
371 | } | ||
372 | public override bool SubscribedEvents() { | ||
373 | return (_subscribedEventsMs > 0); | ||
374 | } | ||
375 | |||
376 | // set _avatarVolume and _mass based on capsule size, _density and _scale | ||
377 | private void ComputeAvatarVolumeAndMass() | ||
378 | { | ||
379 | _avatarVolume = (float)( | ||
380 | Math.PI | ||
381 | * _scene.Params.avatarCapsuleRadius * _scale.X | ||
382 | * _scene.Params.avatarCapsuleRadius * _scale.Y | ||
383 | * _scene.Params.avatarCapsuleHeight * _scale.Z); | ||
384 | _mass = _density * _avatarVolume; | ||
385 | } | ||
386 | |||
387 | // The physics engine says that properties have updated. Update same and inform | ||
388 | // the world that things have changed. | ||
389 | public void UpdateProperties(EntityProperties entprop) | ||
390 | { | ||
391 | bool changed = false; | ||
392 | // we assign to the local variables so the normal set action does not happen | ||
393 | if (_position != entprop.Position) | ||
394 | { | ||
395 | _position = entprop.Position; | ||
396 | changed = true; | ||
397 | } | ||
398 | if (_orientation != entprop.Rotation) | ||
399 | { | ||
400 | _orientation = entprop.Rotation; | ||
401 | changed = true; | ||
402 | } | ||
403 | if (_velocity != entprop.Velocity) | ||
404 | { | ||
405 | _velocity = entprop.Velocity; | ||
406 | changed = true; | ||
407 | } | ||
408 | if (_acceleration != entprop.Acceleration) | ||
409 | { | ||
410 | _acceleration = entprop.Acceleration; | ||
411 | changed = true; | ||
412 | } | ||
413 | if (_rotationalVelocity != entprop.RotationalVelocity) | ||
414 | { | ||
415 | _rotationalVelocity = entprop.RotationalVelocity; | ||
416 | changed = true; | ||
417 | } | ||
418 | if (changed) | ||
419 | { | ||
420 | // m_log.DebugFormat("{0}: UpdateProperties: id={1}, c={2}, pos={3}, rot={4}", LogHeader, LocalID, changed, _position, _orientation); | ||
421 | // Avatar movement is not done by generating this event. There is a system that | ||
422 | // checks for avatar updates each heartbeat loop. | ||
423 | // base.RequestPhysicsterseUpdate(); | ||
424 | } | ||
425 | } | ||
426 | |||
427 | public void Collide(uint collidingWith, ActorTypes type, Vector3 contactPoint, Vector3 contactNormal, float pentrationDepth) | ||
428 | { | ||
429 | // m_log.DebugFormat("{0}: Collide: ms={1}, id={2}, with={3}", LogHeader, _subscribedEventsMs, LocalID, collidingWith); | ||
430 | |||
431 | // The following makes IsColliding() and IsCollidingGround() work | ||
432 | _collidingStep = _scene.SimulationStep; | ||
433 | if (collidingWith == BSScene.TERRAIN_ID || collidingWith == BSScene.GROUNDPLANE_ID) | ||
434 | { | ||
435 | _collidingGroundStep = _scene.SimulationStep; | ||
436 | } | ||
437 | |||
438 | // throttle collisions to the rate specified in the subscription | ||
439 | if (_subscribedEventsMs == 0) return; // don't want collisions | ||
440 | int nowTime = _scene.SimulationNowTime; | ||
441 | if (nowTime < (_lastCollisionTime + _subscribedEventsMs)) return; | ||
442 | _lastCollisionTime = nowTime; | ||
443 | |||
444 | Dictionary<uint, ContactPoint> contactPoints = new Dictionary<uint, ContactPoint>(); | ||
445 | contactPoints.Add(collidingWith, new ContactPoint(contactPoint, contactNormal, pentrationDepth)); | ||
446 | CollisionEventUpdate args = new CollisionEventUpdate(LocalID, (int)type, 1, contactPoints); | ||
447 | base.SendCollisionUpdate(args); | ||
448 | } | ||
449 | |||
450 | } | ||
451 | } | ||
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs new file mode 100644 index 0000000..046726d --- /dev/null +++ b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs | |||
@@ -0,0 +1,951 @@ | |||
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 copyright | ||
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 | /* RA: June 14, 2011. Copied from ODEDynamics.cs and converted to | ||
29 | * call the BulletSim system. | ||
30 | */ | ||
31 | /* Revised Aug, Sept 2009 by Kitto Flora. ODEDynamics.cs replaces | ||
32 | * ODEVehicleSettings.cs. It and ODEPrim.cs are re-organised: | ||
33 | * ODEPrim.cs contains methods dealing with Prim editing, Prim | ||
34 | * characteristics and Kinetic motion. | ||
35 | * ODEDynamics.cs contains methods dealing with Prim Physical motion | ||
36 | * (dynamics) and the associated settings. Old Linear and angular | ||
37 | * motors for dynamic motion have been replace with MoveLinear() | ||
38 | * and MoveAngular(); 'Physical' is used only to switch ODE dynamic | ||
39 | * simualtion on/off; VEHICAL_TYPE_NONE/VEHICAL_TYPE_<other> is to | ||
40 | * switch between 'VEHICLE' parameter use and general dynamics | ||
41 | * settings use. | ||
42 | */ | ||
43 | |||
44 | using System; | ||
45 | using System.Collections.Generic; | ||
46 | using System.Reflection; | ||
47 | using System.Runtime.InteropServices; | ||
48 | using log4net; | ||
49 | using OpenMetaverse; | ||
50 | using OpenSim.Framework; | ||
51 | using OpenSim.Region.Physics.Manager; | ||
52 | |||
53 | namespace OpenSim.Region.Physics.BulletSPlugin | ||
54 | { | ||
55 | public class BSDynamics | ||
56 | { | ||
57 | private int frcount = 0; // Used to limit dynamics debug output to | ||
58 | // every 100th frame | ||
59 | |||
60 | // private BSScene m_parentScene = null; | ||
61 | private BSPrim m_prim; // the prim this dynamic controller belongs to | ||
62 | |||
63 | // Vehicle properties | ||
64 | private Vehicle m_type = Vehicle.TYPE_NONE; // If a 'VEHICLE', and what kind | ||
65 | public Vehicle Type | ||
66 | { | ||
67 | get { return m_type; } | ||
68 | } | ||
69 | // private Quaternion m_referenceFrame = Quaternion.Identity; // Axis modifier | ||
70 | private VehicleFlag m_flags = (VehicleFlag) 0; // Boolean settings: | ||
71 | // HOVER_TERRAIN_ONLY | ||
72 | // HOVER_GLOBAL_HEIGHT | ||
73 | // NO_DEFLECTION_UP | ||
74 | // HOVER_WATER_ONLY | ||
75 | // HOVER_UP_ONLY | ||
76 | // LIMIT_MOTOR_UP | ||
77 | // LIMIT_ROLL_ONLY | ||
78 | private VehicleFlag m_Hoverflags = (VehicleFlag)0; | ||
79 | private Vector3 m_BlockingEndPoint = Vector3.Zero; | ||
80 | private Quaternion m_RollreferenceFrame = Quaternion.Identity; | ||
81 | // Linear properties | ||
82 | private Vector3 m_linearMotorDirection = Vector3.Zero; // velocity requested by LSL, decayed by time | ||
83 | private Vector3 m_linearMotorDirectionLASTSET = Vector3.Zero; // velocity requested by LSL | ||
84 | private Vector3 m_dir = Vector3.Zero; // velocity applied to body | ||
85 | private Vector3 m_linearFrictionTimescale = Vector3.Zero; | ||
86 | private float m_linearMotorDecayTimescale = 0; | ||
87 | private float m_linearMotorTimescale = 0; | ||
88 | private Vector3 m_lastLinearVelocityVector = Vector3.Zero; | ||
89 | private Vector3 m_lastPositionVector = Vector3.Zero; | ||
90 | // private bool m_LinearMotorSetLastFrame = false; | ||
91 | // private Vector3 m_linearMotorOffset = Vector3.Zero; | ||
92 | |||
93 | //Angular properties | ||
94 | private Vector3 m_angularMotorDirection = Vector3.Zero; // angular velocity requested by LSL motor | ||
95 | private int m_angularMotorApply = 0; // application frame counter | ||
96 | private Vector3 m_angularMotorVelocity = Vector3.Zero; // current angular motor velocity | ||
97 | private float m_angularMotorTimescale = 0; // motor angular velocity ramp up rate | ||
98 | private float m_angularMotorDecayTimescale = 0; // motor angular velocity decay rate | ||
99 | private Vector3 m_angularFrictionTimescale = Vector3.Zero; // body angular velocity decay rate | ||
100 | private Vector3 m_lastAngularVelocity = Vector3.Zero; // what was last applied to body | ||
101 | // private Vector3 m_lastVertAttractor = Vector3.Zero; // what VA was last applied to body | ||
102 | |||
103 | //Deflection properties | ||
104 | // private float m_angularDeflectionEfficiency = 0; | ||
105 | // private float m_angularDeflectionTimescale = 0; | ||
106 | // private float m_linearDeflectionEfficiency = 0; | ||
107 | // private float m_linearDeflectionTimescale = 0; | ||
108 | |||
109 | //Banking properties | ||
110 | // private float m_bankingEfficiency = 0; | ||
111 | // private float m_bankingMix = 0; | ||
112 | // private float m_bankingTimescale = 0; | ||
113 | |||
114 | //Hover and Buoyancy properties | ||
115 | private float m_VhoverHeight = 0f; | ||
116 | // private float m_VhoverEfficiency = 0f; | ||
117 | private float m_VhoverTimescale = 0f; | ||
118 | private float m_VhoverTargetHeight = -1.0f; // if <0 then no hover, else its the current target height | ||
119 | private float m_VehicleBuoyancy = 0f; //KF: m_VehicleBuoyancy is set by VEHICLE_BUOYANCY for a vehicle. | ||
120 | // Modifies gravity. Slider between -1 (double-gravity) and 1 (full anti-gravity) | ||
121 | // KF: So far I have found no good method to combine a script-requested .Z velocity and gravity. | ||
122 | // Therefore only m_VehicleBuoyancy=1 (0g) will use the script-requested .Z velocity. | ||
123 | |||
124 | //Attractor properties | ||
125 | private float m_verticalAttractionEfficiency = 1.0f; // damped | ||
126 | private float m_verticalAttractionTimescale = 500f; // Timescale > 300 means no vert attractor. | ||
127 | |||
128 | public BSDynamics(BSPrim myPrim) | ||
129 | { | ||
130 | m_prim = myPrim; | ||
131 | m_type = Vehicle.TYPE_NONE; | ||
132 | } | ||
133 | |||
134 | internal void ProcessFloatVehicleParam(Vehicle pParam, float pValue) | ||
135 | { | ||
136 | switch (pParam) | ||
137 | { | ||
138 | case Vehicle.ANGULAR_DEFLECTION_EFFICIENCY: | ||
139 | if (pValue < 0.01f) pValue = 0.01f; | ||
140 | // m_angularDeflectionEfficiency = pValue; | ||
141 | break; | ||
142 | case Vehicle.ANGULAR_DEFLECTION_TIMESCALE: | ||
143 | if (pValue < 0.01f) pValue = 0.01f; | ||
144 | // m_angularDeflectionTimescale = pValue; | ||
145 | break; | ||
146 | case Vehicle.ANGULAR_MOTOR_DECAY_TIMESCALE: | ||
147 | if (pValue < 0.01f) pValue = 0.01f; | ||
148 | m_angularMotorDecayTimescale = pValue; | ||
149 | break; | ||
150 | case Vehicle.ANGULAR_MOTOR_TIMESCALE: | ||
151 | if (pValue < 0.01f) pValue = 0.01f; | ||
152 | m_angularMotorTimescale = pValue; | ||
153 | break; | ||
154 | case Vehicle.BANKING_EFFICIENCY: | ||
155 | if (pValue < 0.01f) pValue = 0.01f; | ||
156 | // m_bankingEfficiency = pValue; | ||
157 | break; | ||
158 | case Vehicle.BANKING_MIX: | ||
159 | if (pValue < 0.01f) pValue = 0.01f; | ||
160 | // m_bankingMix = pValue; | ||
161 | break; | ||
162 | case Vehicle.BANKING_TIMESCALE: | ||
163 | if (pValue < 0.01f) pValue = 0.01f; | ||
164 | // m_bankingTimescale = pValue; | ||
165 | break; | ||
166 | case Vehicle.BUOYANCY: | ||
167 | if (pValue < -1f) pValue = -1f; | ||
168 | if (pValue > 1f) pValue = 1f; | ||
169 | m_VehicleBuoyancy = pValue; | ||
170 | break; | ||
171 | // case Vehicle.HOVER_EFFICIENCY: | ||
172 | // if (pValue < 0f) pValue = 0f; | ||
173 | // if (pValue > 1f) pValue = 1f; | ||
174 | // m_VhoverEfficiency = pValue; | ||
175 | // break; | ||
176 | case Vehicle.HOVER_HEIGHT: | ||
177 | m_VhoverHeight = pValue; | ||
178 | break; | ||
179 | case Vehicle.HOVER_TIMESCALE: | ||
180 | if (pValue < 0.01f) pValue = 0.01f; | ||
181 | m_VhoverTimescale = pValue; | ||
182 | break; | ||
183 | case Vehicle.LINEAR_DEFLECTION_EFFICIENCY: | ||
184 | if (pValue < 0.01f) pValue = 0.01f; | ||
185 | // m_linearDeflectionEfficiency = pValue; | ||
186 | break; | ||
187 | case Vehicle.LINEAR_DEFLECTION_TIMESCALE: | ||
188 | if (pValue < 0.01f) pValue = 0.01f; | ||
189 | // m_linearDeflectionTimescale = pValue; | ||
190 | break; | ||
191 | case Vehicle.LINEAR_MOTOR_DECAY_TIMESCALE: | ||
192 | if (pValue < 0.01f) pValue = 0.01f; | ||
193 | m_linearMotorDecayTimescale = pValue; | ||
194 | break; | ||
195 | case Vehicle.LINEAR_MOTOR_TIMESCALE: | ||
196 | if (pValue < 0.01f) pValue = 0.01f; | ||
197 | m_linearMotorTimescale = pValue; | ||
198 | break; | ||
199 | case Vehicle.VERTICAL_ATTRACTION_EFFICIENCY: | ||
200 | if (pValue < 0.1f) pValue = 0.1f; // Less goes unstable | ||
201 | if (pValue > 1.0f) pValue = 1.0f; | ||
202 | m_verticalAttractionEfficiency = pValue; | ||
203 | break; | ||
204 | case Vehicle.VERTICAL_ATTRACTION_TIMESCALE: | ||
205 | if (pValue < 0.01f) pValue = 0.01f; | ||
206 | m_verticalAttractionTimescale = pValue; | ||
207 | break; | ||
208 | |||
209 | // These are vector properties but the engine lets you use a single float value to | ||
210 | // set all of the components to the same value | ||
211 | case Vehicle.ANGULAR_FRICTION_TIMESCALE: | ||
212 | m_angularFrictionTimescale = new Vector3(pValue, pValue, pValue); | ||
213 | break; | ||
214 | case Vehicle.ANGULAR_MOTOR_DIRECTION: | ||
215 | m_angularMotorDirection = new Vector3(pValue, pValue, pValue); | ||
216 | m_angularMotorApply = 10; | ||
217 | break; | ||
218 | case Vehicle.LINEAR_FRICTION_TIMESCALE: | ||
219 | m_linearFrictionTimescale = new Vector3(pValue, pValue, pValue); | ||
220 | break; | ||
221 | case Vehicle.LINEAR_MOTOR_DIRECTION: | ||
222 | m_linearMotorDirection = new Vector3(pValue, pValue, pValue); | ||
223 | m_linearMotorDirectionLASTSET = new Vector3(pValue, pValue, pValue); | ||
224 | break; | ||
225 | case Vehicle.LINEAR_MOTOR_OFFSET: | ||
226 | // m_linearMotorOffset = new Vector3(pValue, pValue, pValue); | ||
227 | break; | ||
228 | |||
229 | } | ||
230 | }//end ProcessFloatVehicleParam | ||
231 | |||
232 | internal void ProcessVectorVehicleParam(Vehicle pParam, Vector3 pValue) | ||
233 | { | ||
234 | switch (pParam) | ||
235 | { | ||
236 | case Vehicle.ANGULAR_FRICTION_TIMESCALE: | ||
237 | m_angularFrictionTimescale = new Vector3(pValue.X, pValue.Y, pValue.Z); | ||
238 | break; | ||
239 | case Vehicle.ANGULAR_MOTOR_DIRECTION: | ||
240 | m_angularMotorDirection = new Vector3(pValue.X, pValue.Y, pValue.Z); | ||
241 | // Limit requested angular speed to 2 rps= 4 pi rads/sec | ||
242 | if (m_angularMotorDirection.X > 12.56f) m_angularMotorDirection.X = 12.56f; | ||
243 | if (m_angularMotorDirection.X < - 12.56f) m_angularMotorDirection.X = - 12.56f; | ||
244 | if (m_angularMotorDirection.Y > 12.56f) m_angularMotorDirection.Y = 12.56f; | ||
245 | if (m_angularMotorDirection.Y < - 12.56f) m_angularMotorDirection.Y = - 12.56f; | ||
246 | if (m_angularMotorDirection.Z > 12.56f) m_angularMotorDirection.Z = 12.56f; | ||
247 | if (m_angularMotorDirection.Z < - 12.56f) m_angularMotorDirection.Z = - 12.56f; | ||
248 | m_angularMotorApply = 10; | ||
249 | break; | ||
250 | case Vehicle.LINEAR_FRICTION_TIMESCALE: | ||
251 | m_linearFrictionTimescale = new Vector3(pValue.X, pValue.Y, pValue.Z); | ||
252 | break; | ||
253 | case Vehicle.LINEAR_MOTOR_DIRECTION: | ||
254 | m_linearMotorDirection = new Vector3(pValue.X, pValue.Y, pValue.Z); | ||
255 | m_linearMotorDirectionLASTSET = new Vector3(pValue.X, pValue.Y, pValue.Z); | ||
256 | break; | ||
257 | case Vehicle.LINEAR_MOTOR_OFFSET: | ||
258 | // m_linearMotorOffset = new Vector3(pValue.X, pValue.Y, pValue.Z); | ||
259 | break; | ||
260 | case Vehicle.BLOCK_EXIT: | ||
261 | m_BlockingEndPoint = new Vector3(pValue.X, pValue.Y, pValue.Z); | ||
262 | break; | ||
263 | } | ||
264 | }//end ProcessVectorVehicleParam | ||
265 | |||
266 | internal void ProcessRotationVehicleParam(Vehicle pParam, Quaternion pValue) | ||
267 | { | ||
268 | switch (pParam) | ||
269 | { | ||
270 | case Vehicle.REFERENCE_FRAME: | ||
271 | // m_referenceFrame = pValue; | ||
272 | break; | ||
273 | case Vehicle.ROLL_FRAME: | ||
274 | m_RollreferenceFrame = pValue; | ||
275 | break; | ||
276 | } | ||
277 | }//end ProcessRotationVehicleParam | ||
278 | |||
279 | internal void ProcessVehicleFlags(int pParam, bool remove) | ||
280 | { | ||
281 | if (remove) | ||
282 | { | ||
283 | if (pParam == -1) | ||
284 | { | ||
285 | m_flags = (VehicleFlag)0; | ||
286 | m_Hoverflags = (VehicleFlag)0; | ||
287 | return; | ||
288 | } | ||
289 | if ((pParam & (int)VehicleFlag.HOVER_GLOBAL_HEIGHT) == (int)VehicleFlag.HOVER_GLOBAL_HEIGHT) | ||
290 | { | ||
291 | if ((m_Hoverflags & VehicleFlag.HOVER_GLOBAL_HEIGHT) != (VehicleFlag)0) | ||
292 | m_Hoverflags &= ~(VehicleFlag.HOVER_GLOBAL_HEIGHT); | ||
293 | } | ||
294 | if ((pParam & (int)VehicleFlag.HOVER_TERRAIN_ONLY) == (int)VehicleFlag.HOVER_TERRAIN_ONLY) | ||
295 | { | ||
296 | if ((m_Hoverflags & VehicleFlag.HOVER_TERRAIN_ONLY) != (VehicleFlag)0) | ||
297 | m_Hoverflags &= ~(VehicleFlag.HOVER_TERRAIN_ONLY); | ||
298 | } | ||
299 | if ((pParam & (int)VehicleFlag.HOVER_UP_ONLY) == (int)VehicleFlag.HOVER_UP_ONLY) | ||
300 | { | ||
301 | if ((m_Hoverflags & VehicleFlag.HOVER_UP_ONLY) != (VehicleFlag)0) | ||
302 | m_Hoverflags &= ~(VehicleFlag.HOVER_UP_ONLY); | ||
303 | } | ||
304 | if ((pParam & (int)VehicleFlag.HOVER_WATER_ONLY) == (int)VehicleFlag.HOVER_WATER_ONLY) | ||
305 | { | ||
306 | if ((m_Hoverflags & VehicleFlag.HOVER_WATER_ONLY) != (VehicleFlag)0) | ||
307 | m_Hoverflags &= ~(VehicleFlag.HOVER_WATER_ONLY); | ||
308 | } | ||
309 | if ((pParam & (int)VehicleFlag.LIMIT_MOTOR_UP) == (int)VehicleFlag.LIMIT_MOTOR_UP) | ||
310 | { | ||
311 | if ((m_flags & VehicleFlag.LIMIT_MOTOR_UP) != (VehicleFlag)0) | ||
312 | m_flags &= ~(VehicleFlag.LIMIT_MOTOR_UP); | ||
313 | } | ||
314 | if ((pParam & (int)VehicleFlag.LIMIT_ROLL_ONLY) == (int)VehicleFlag.LIMIT_ROLL_ONLY) | ||
315 | { | ||
316 | if ((m_flags & VehicleFlag.LIMIT_ROLL_ONLY) != (VehicleFlag)0) | ||
317 | m_flags &= ~(VehicleFlag.LIMIT_ROLL_ONLY); | ||
318 | } | ||
319 | if ((pParam & (int)VehicleFlag.MOUSELOOK_BANK) == (int)VehicleFlag.MOUSELOOK_BANK) | ||
320 | { | ||
321 | if ((m_flags & VehicleFlag.MOUSELOOK_BANK) != (VehicleFlag)0) | ||
322 | m_flags &= ~(VehicleFlag.MOUSELOOK_BANK); | ||
323 | } | ||
324 | if ((pParam & (int)VehicleFlag.MOUSELOOK_STEER) == (int)VehicleFlag.MOUSELOOK_STEER) | ||
325 | { | ||
326 | if ((m_flags & VehicleFlag.MOUSELOOK_STEER) != (VehicleFlag)0) | ||
327 | m_flags &= ~(VehicleFlag.MOUSELOOK_STEER); | ||
328 | } | ||
329 | if ((pParam & (int)VehicleFlag.NO_DEFLECTION_UP) == (int)VehicleFlag.NO_DEFLECTION_UP) | ||
330 | { | ||
331 | if ((m_flags & VehicleFlag.NO_DEFLECTION_UP) != (VehicleFlag)0) | ||
332 | m_flags &= ~(VehicleFlag.NO_DEFLECTION_UP); | ||
333 | } | ||
334 | if ((pParam & (int)VehicleFlag.CAMERA_DECOUPLED) == (int)VehicleFlag.CAMERA_DECOUPLED) | ||
335 | { | ||
336 | if ((m_flags & VehicleFlag.CAMERA_DECOUPLED) != (VehicleFlag)0) | ||
337 | m_flags &= ~(VehicleFlag.CAMERA_DECOUPLED); | ||
338 | } | ||
339 | if ((pParam & (int)VehicleFlag.NO_X) == (int)VehicleFlag.NO_X) | ||
340 | { | ||
341 | if ((m_flags & VehicleFlag.NO_X) != (VehicleFlag)0) | ||
342 | m_flags &= ~(VehicleFlag.NO_X); | ||
343 | } | ||
344 | if ((pParam & (int)VehicleFlag.NO_Y) == (int)VehicleFlag.NO_Y) | ||
345 | { | ||
346 | if ((m_flags & VehicleFlag.NO_Y) != (VehicleFlag)0) | ||
347 | m_flags &= ~(VehicleFlag.NO_Y); | ||
348 | } | ||
349 | if ((pParam & (int)VehicleFlag.NO_Z) == (int)VehicleFlag.NO_Z) | ||
350 | { | ||
351 | if ((m_flags & VehicleFlag.NO_Z) != (VehicleFlag)0) | ||
352 | m_flags &= ~(VehicleFlag.NO_Z); | ||
353 | } | ||
354 | if ((pParam & (int)VehicleFlag.LOCK_HOVER_HEIGHT) == (int)VehicleFlag.LOCK_HOVER_HEIGHT) | ||
355 | { | ||
356 | if ((m_Hoverflags & VehicleFlag.LOCK_HOVER_HEIGHT) != (VehicleFlag)0) | ||
357 | m_Hoverflags &= ~(VehicleFlag.LOCK_HOVER_HEIGHT); | ||
358 | } | ||
359 | if ((pParam & (int)VehicleFlag.NO_DEFLECTION) == (int)VehicleFlag.NO_DEFLECTION) | ||
360 | { | ||
361 | if ((m_flags & VehicleFlag.NO_DEFLECTION) != (VehicleFlag)0) | ||
362 | m_flags &= ~(VehicleFlag.NO_DEFLECTION); | ||
363 | } | ||
364 | if ((pParam & (int)VehicleFlag.LOCK_ROTATION) == (int)VehicleFlag.LOCK_ROTATION) | ||
365 | { | ||
366 | if ((m_flags & VehicleFlag.LOCK_ROTATION) != (VehicleFlag)0) | ||
367 | m_flags &= ~(VehicleFlag.LOCK_ROTATION); | ||
368 | } | ||
369 | } | ||
370 | else | ||
371 | { | ||
372 | if ((pParam & (int)VehicleFlag.HOVER_GLOBAL_HEIGHT) == (int)VehicleFlag.HOVER_GLOBAL_HEIGHT) | ||
373 | { | ||
374 | m_Hoverflags |= (VehicleFlag.HOVER_GLOBAL_HEIGHT | m_flags); | ||
375 | } | ||
376 | if ((pParam & (int)VehicleFlag.HOVER_TERRAIN_ONLY) == (int)VehicleFlag.HOVER_TERRAIN_ONLY) | ||
377 | { | ||
378 | m_Hoverflags |= (VehicleFlag.HOVER_TERRAIN_ONLY | m_flags); | ||
379 | } | ||
380 | if ((pParam & (int)VehicleFlag.HOVER_UP_ONLY) == (int)VehicleFlag.HOVER_UP_ONLY) | ||
381 | { | ||
382 | m_Hoverflags |= (VehicleFlag.HOVER_UP_ONLY | m_flags); | ||
383 | } | ||
384 | if ((pParam & (int)VehicleFlag.HOVER_WATER_ONLY) == (int)VehicleFlag.HOVER_WATER_ONLY) | ||
385 | { | ||
386 | m_Hoverflags |= (VehicleFlag.HOVER_WATER_ONLY | m_flags); | ||
387 | } | ||
388 | if ((pParam & (int)VehicleFlag.LIMIT_MOTOR_UP) == (int)VehicleFlag.LIMIT_MOTOR_UP) | ||
389 | { | ||
390 | m_flags |= (VehicleFlag.LIMIT_MOTOR_UP | m_flags); | ||
391 | } | ||
392 | if ((pParam & (int)VehicleFlag.MOUSELOOK_BANK) == (int)VehicleFlag.MOUSELOOK_BANK) | ||
393 | { | ||
394 | m_flags |= (VehicleFlag.MOUSELOOK_BANK | m_flags); | ||
395 | } | ||
396 | if ((pParam & (int)VehicleFlag.MOUSELOOK_STEER) == (int)VehicleFlag.MOUSELOOK_STEER) | ||
397 | { | ||
398 | m_flags |= (VehicleFlag.MOUSELOOK_STEER | m_flags); | ||
399 | } | ||
400 | if ((pParam & (int)VehicleFlag.NO_DEFLECTION_UP) == (int)VehicleFlag.NO_DEFLECTION_UP) | ||
401 | { | ||
402 | m_flags |= (VehicleFlag.NO_DEFLECTION_UP | m_flags); | ||
403 | } | ||
404 | if ((pParam & (int)VehicleFlag.CAMERA_DECOUPLED) == (int)VehicleFlag.CAMERA_DECOUPLED) | ||
405 | { | ||
406 | m_flags |= (VehicleFlag.CAMERA_DECOUPLED | m_flags); | ||
407 | } | ||
408 | if ((pParam & (int)VehicleFlag.NO_X) == (int)VehicleFlag.NO_X) | ||
409 | { | ||
410 | m_flags |= (VehicleFlag.NO_X); | ||
411 | } | ||
412 | if ((pParam & (int)VehicleFlag.NO_Y) == (int)VehicleFlag.NO_Y) | ||
413 | { | ||
414 | m_flags |= (VehicleFlag.NO_Y); | ||
415 | } | ||
416 | if ((pParam & (int)VehicleFlag.NO_Z) == (int)VehicleFlag.NO_Z) | ||
417 | { | ||
418 | m_flags |= (VehicleFlag.NO_Z); | ||
419 | } | ||
420 | if ((pParam & (int)VehicleFlag.LOCK_HOVER_HEIGHT) == (int)VehicleFlag.LOCK_HOVER_HEIGHT) | ||
421 | { | ||
422 | m_Hoverflags |= (VehicleFlag.LOCK_HOVER_HEIGHT); | ||
423 | } | ||
424 | if ((pParam & (int)VehicleFlag.NO_DEFLECTION) == (int)VehicleFlag.NO_DEFLECTION) | ||
425 | { | ||
426 | m_flags |= (VehicleFlag.NO_DEFLECTION); | ||
427 | } | ||
428 | if ((pParam & (int)VehicleFlag.LOCK_ROTATION) == (int)VehicleFlag.LOCK_ROTATION) | ||
429 | { | ||
430 | m_flags |= (VehicleFlag.LOCK_ROTATION); | ||
431 | } | ||
432 | } | ||
433 | }//end ProcessVehicleFlags | ||
434 | |||
435 | internal void ProcessTypeChange(Vehicle pType) | ||
436 | { | ||
437 | // Set Defaults For Type | ||
438 | m_type = pType; | ||
439 | switch (pType) | ||
440 | { | ||
441 | case Vehicle.TYPE_NONE: | ||
442 | m_linearFrictionTimescale = new Vector3(0, 0, 0); | ||
443 | m_angularFrictionTimescale = new Vector3(0, 0, 0); | ||
444 | m_linearMotorDirection = Vector3.Zero; | ||
445 | m_linearMotorTimescale = 0; | ||
446 | m_linearMotorDecayTimescale = 0; | ||
447 | m_angularMotorDirection = Vector3.Zero; | ||
448 | m_angularMotorTimescale = 0; | ||
449 | m_angularMotorDecayTimescale = 0; | ||
450 | m_VhoverHeight = 0; | ||
451 | m_VhoverTimescale = 0; | ||
452 | m_VehicleBuoyancy = 0; | ||
453 | m_flags = (VehicleFlag)0; | ||
454 | break; | ||
455 | |||
456 | case Vehicle.TYPE_SLED: | ||
457 | m_linearFrictionTimescale = new Vector3(30, 1, 1000); | ||
458 | m_angularFrictionTimescale = new Vector3(1000, 1000, 1000); | ||
459 | m_linearMotorDirection = Vector3.Zero; | ||
460 | m_linearMotorTimescale = 1000; | ||
461 | m_linearMotorDecayTimescale = 120; | ||
462 | m_angularMotorDirection = Vector3.Zero; | ||
463 | m_angularMotorTimescale = 1000; | ||
464 | m_angularMotorDecayTimescale = 120; | ||
465 | m_VhoverHeight = 0; | ||
466 | // m_VhoverEfficiency = 1; | ||
467 | m_VhoverTimescale = 10; | ||
468 | m_VehicleBuoyancy = 0; | ||
469 | // m_linearDeflectionEfficiency = 1; | ||
470 | // m_linearDeflectionTimescale = 1; | ||
471 | // m_angularDeflectionEfficiency = 1; | ||
472 | // m_angularDeflectionTimescale = 1000; | ||
473 | // m_bankingEfficiency = 0; | ||
474 | // m_bankingMix = 1; | ||
475 | // m_bankingTimescale = 10; | ||
476 | // m_referenceFrame = Quaternion.Identity; | ||
477 | m_Hoverflags &= | ||
478 | ~(VehicleFlag.HOVER_WATER_ONLY | VehicleFlag.HOVER_TERRAIN_ONLY | | ||
479 | VehicleFlag.HOVER_GLOBAL_HEIGHT | VehicleFlag.HOVER_UP_ONLY); | ||
480 | m_flags |= (VehicleFlag.NO_DEFLECTION_UP | VehicleFlag.LIMIT_ROLL_ONLY | VehicleFlag.LIMIT_MOTOR_UP); | ||
481 | break; | ||
482 | case Vehicle.TYPE_CAR: | ||
483 | m_linearFrictionTimescale = new Vector3(100, 2, 1000); | ||
484 | m_angularFrictionTimescale = new Vector3(1000, 1000, 1000); | ||
485 | m_linearMotorDirection = Vector3.Zero; | ||
486 | m_linearMotorTimescale = 1; | ||
487 | m_linearMotorDecayTimescale = 60; | ||
488 | m_angularMotorDirection = Vector3.Zero; | ||
489 | m_angularMotorTimescale = 1; | ||
490 | m_angularMotorDecayTimescale = 0.8f; | ||
491 | m_VhoverHeight = 0; | ||
492 | // m_VhoverEfficiency = 0; | ||
493 | m_VhoverTimescale = 1000; | ||
494 | m_VehicleBuoyancy = 0; | ||
495 | // // m_linearDeflectionEfficiency = 1; | ||
496 | // // m_linearDeflectionTimescale = 2; | ||
497 | // // m_angularDeflectionEfficiency = 0; | ||
498 | // m_angularDeflectionTimescale = 10; | ||
499 | m_verticalAttractionEfficiency = 1f; | ||
500 | m_verticalAttractionTimescale = 10f; | ||
501 | // m_bankingEfficiency = -0.2f; | ||
502 | // m_bankingMix = 1; | ||
503 | // m_bankingTimescale = 1; | ||
504 | // m_referenceFrame = Quaternion.Identity; | ||
505 | m_Hoverflags &= ~(VehicleFlag.HOVER_WATER_ONLY | VehicleFlag.HOVER_TERRAIN_ONLY | VehicleFlag.HOVER_GLOBAL_HEIGHT); | ||
506 | m_flags |= (VehicleFlag.NO_DEFLECTION_UP | VehicleFlag.LIMIT_ROLL_ONLY | | ||
507 | VehicleFlag.LIMIT_MOTOR_UP); | ||
508 | m_Hoverflags |= (VehicleFlag.HOVER_UP_ONLY); | ||
509 | break; | ||
510 | case Vehicle.TYPE_BOAT: | ||
511 | m_linearFrictionTimescale = new Vector3(10, 3, 2); | ||
512 | m_angularFrictionTimescale = new Vector3(10,10,10); | ||
513 | m_linearMotorDirection = Vector3.Zero; | ||
514 | m_linearMotorTimescale = 5; | ||
515 | m_linearMotorDecayTimescale = 60; | ||
516 | m_angularMotorDirection = Vector3.Zero; | ||
517 | m_angularMotorTimescale = 4; | ||
518 | m_angularMotorDecayTimescale = 4; | ||
519 | m_VhoverHeight = 0; | ||
520 | // m_VhoverEfficiency = 0.5f; | ||
521 | m_VhoverTimescale = 2; | ||
522 | m_VehicleBuoyancy = 1; | ||
523 | // m_linearDeflectionEfficiency = 0.5f; | ||
524 | // m_linearDeflectionTimescale = 3; | ||
525 | // m_angularDeflectionEfficiency = 0.5f; | ||
526 | // m_angularDeflectionTimescale = 5; | ||
527 | m_verticalAttractionEfficiency = 0.5f; | ||
528 | m_verticalAttractionTimescale = 5f; | ||
529 | // m_bankingEfficiency = -0.3f; | ||
530 | // m_bankingMix = 0.8f; | ||
531 | // m_bankingTimescale = 1; | ||
532 | // m_referenceFrame = Quaternion.Identity; | ||
533 | m_Hoverflags &= ~(VehicleFlag.HOVER_TERRAIN_ONLY | | ||
534 | VehicleFlag.HOVER_GLOBAL_HEIGHT | VehicleFlag.HOVER_UP_ONLY); | ||
535 | m_flags &= ~(VehicleFlag.LIMIT_ROLL_ONLY); | ||
536 | m_flags |= (VehicleFlag.NO_DEFLECTION_UP | | ||
537 | VehicleFlag.LIMIT_MOTOR_UP); | ||
538 | m_Hoverflags |= (VehicleFlag.HOVER_WATER_ONLY); | ||
539 | break; | ||
540 | case Vehicle.TYPE_AIRPLANE: | ||
541 | m_linearFrictionTimescale = new Vector3(200, 10, 5); | ||
542 | m_angularFrictionTimescale = new Vector3(20, 20, 20); | ||
543 | m_linearMotorDirection = Vector3.Zero; | ||
544 | m_linearMotorTimescale = 2; | ||
545 | m_linearMotorDecayTimescale = 60; | ||
546 | m_angularMotorDirection = Vector3.Zero; | ||
547 | m_angularMotorTimescale = 4; | ||
548 | m_angularMotorDecayTimescale = 4; | ||
549 | m_VhoverHeight = 0; | ||
550 | // m_VhoverEfficiency = 0.5f; | ||
551 | m_VhoverTimescale = 1000; | ||
552 | m_VehicleBuoyancy = 0; | ||
553 | // m_linearDeflectionEfficiency = 0.5f; | ||
554 | // m_linearDeflectionTimescale = 3; | ||
555 | // m_angularDeflectionEfficiency = 1; | ||
556 | // m_angularDeflectionTimescale = 2; | ||
557 | m_verticalAttractionEfficiency = 0.9f; | ||
558 | m_verticalAttractionTimescale = 2f; | ||
559 | // m_bankingEfficiency = 1; | ||
560 | // m_bankingMix = 0.7f; | ||
561 | // m_bankingTimescale = 2; | ||
562 | // m_referenceFrame = Quaternion.Identity; | ||
563 | m_Hoverflags &= ~(VehicleFlag.HOVER_WATER_ONLY | VehicleFlag.HOVER_TERRAIN_ONLY | | ||
564 | VehicleFlag.HOVER_GLOBAL_HEIGHT | VehicleFlag.HOVER_UP_ONLY); | ||
565 | m_flags &= ~(VehicleFlag.NO_DEFLECTION_UP | VehicleFlag.LIMIT_MOTOR_UP); | ||
566 | m_flags |= (VehicleFlag.LIMIT_ROLL_ONLY); | ||
567 | break; | ||
568 | case Vehicle.TYPE_BALLOON: | ||
569 | m_linearFrictionTimescale = new Vector3(5, 5, 5); | ||
570 | m_angularFrictionTimescale = new Vector3(10, 10, 10); | ||
571 | m_linearMotorDirection = Vector3.Zero; | ||
572 | m_linearMotorTimescale = 5; | ||
573 | m_linearMotorDecayTimescale = 60; | ||
574 | m_angularMotorDirection = Vector3.Zero; | ||
575 | m_angularMotorTimescale = 6; | ||
576 | m_angularMotorDecayTimescale = 10; | ||
577 | m_VhoverHeight = 5; | ||
578 | // m_VhoverEfficiency = 0.8f; | ||
579 | m_VhoverTimescale = 10; | ||
580 | m_VehicleBuoyancy = 1; | ||
581 | // m_linearDeflectionEfficiency = 0; | ||
582 | // m_linearDeflectionTimescale = 5; | ||
583 | // m_angularDeflectionEfficiency = 0; | ||
584 | // m_angularDeflectionTimescale = 5; | ||
585 | m_verticalAttractionEfficiency = 1f; | ||
586 | m_verticalAttractionTimescale = 100f; | ||
587 | // m_bankingEfficiency = 0; | ||
588 | // m_bankingMix = 0.7f; | ||
589 | // m_bankingTimescale = 5; | ||
590 | // m_referenceFrame = Quaternion.Identity; | ||
591 | m_Hoverflags &= ~(VehicleFlag.HOVER_WATER_ONLY | VehicleFlag.HOVER_TERRAIN_ONLY | | ||
592 | VehicleFlag.HOVER_UP_ONLY); | ||
593 | m_flags &= ~(VehicleFlag.NO_DEFLECTION_UP | VehicleFlag.LIMIT_MOTOR_UP); | ||
594 | m_flags |= (VehicleFlag.LIMIT_ROLL_ONLY); | ||
595 | m_Hoverflags |= (VehicleFlag.HOVER_GLOBAL_HEIGHT); | ||
596 | break; | ||
597 | |||
598 | } | ||
599 | }//end SetDefaultsForType | ||
600 | |||
601 | internal void Step(float pTimestep, BSScene pParentScene) | ||
602 | { | ||
603 | if (m_type == Vehicle.TYPE_NONE) return; | ||
604 | |||
605 | frcount++; // used to limit debug comment output | ||
606 | if (frcount > 100) | ||
607 | frcount = 0; | ||
608 | |||
609 | MoveLinear(pTimestep, pParentScene); | ||
610 | MoveAngular(pTimestep); | ||
611 | LimitRotation(pTimestep); | ||
612 | }// end Step | ||
613 | |||
614 | private void MoveLinear(float pTimestep, BSScene _pParentScene) | ||
615 | { | ||
616 | if (!m_linearMotorDirection.ApproxEquals(Vector3.Zero, 0.01f)) // requested m_linearMotorDirection is significant | ||
617 | { | ||
618 | // add drive to body | ||
619 | Vector3 addAmount = m_linearMotorDirection/(m_linearMotorTimescale/pTimestep); | ||
620 | m_lastLinearVelocityVector += (addAmount*10); // lastLinearVelocityVector is the current body velocity vector? | ||
621 | |||
622 | // This will work temporarily, but we really need to compare speed on an axis | ||
623 | // KF: Limit body velocity to applied velocity? | ||
624 | if (Math.Abs(m_lastLinearVelocityVector.X) > Math.Abs(m_linearMotorDirectionLASTSET.X)) | ||
625 | m_lastLinearVelocityVector.X = m_linearMotorDirectionLASTSET.X; | ||
626 | if (Math.Abs(m_lastLinearVelocityVector.Y) > Math.Abs(m_linearMotorDirectionLASTSET.Y)) | ||
627 | m_lastLinearVelocityVector.Y = m_linearMotorDirectionLASTSET.Y; | ||
628 | if (Math.Abs(m_lastLinearVelocityVector.Z) > Math.Abs(m_linearMotorDirectionLASTSET.Z)) | ||
629 | m_lastLinearVelocityVector.Z = m_linearMotorDirectionLASTSET.Z; | ||
630 | |||
631 | // decay applied velocity | ||
632 | Vector3 decayfraction = ((Vector3.One/(m_linearMotorDecayTimescale/pTimestep))); | ||
633 | //Console.WriteLine("decay: " + decayfraction); | ||
634 | m_linearMotorDirection -= m_linearMotorDirection * decayfraction * 0.5f; | ||
635 | //Console.WriteLine("actual: " + m_linearMotorDirection); | ||
636 | } | ||
637 | else | ||
638 | { // requested is not significant | ||
639 | // if what remains of applied is small, zero it. | ||
640 | if (m_lastLinearVelocityVector.ApproxEquals(Vector3.Zero, 0.01f)) | ||
641 | m_lastLinearVelocityVector = Vector3.Zero; | ||
642 | } | ||
643 | |||
644 | // convert requested object velocity to world-referenced vector | ||
645 | m_dir = m_lastLinearVelocityVector; | ||
646 | Quaternion rot = m_prim.Orientation; | ||
647 | Quaternion rotq = new Quaternion(rot.X, rot.Y, rot.Z, rot.W); // rotq = rotation of object | ||
648 | m_dir *= rotq; // apply obj rotation to velocity vector | ||
649 | |||
650 | // add Gravity andBuoyancy | ||
651 | // KF: So far I have found no good method to combine a script-requested | ||
652 | // .Z velocity and gravity. Therefore only 0g will used script-requested | ||
653 | // .Z velocity. >0g (m_VehicleBuoyancy < 1) will used modified gravity only. | ||
654 | Vector3 grav = Vector3.Zero; | ||
655 | // There is some gravity, make a gravity force vector | ||
656 | // that is applied after object velocity. | ||
657 | float objMass = m_prim.Mass; | ||
658 | // m_VehicleBuoyancy: -1=2g; 0=1g; 1=0g; | ||
659 | grav.Z = _pParentScene.DefaultGravity.Z * objMass * (1f - m_VehicleBuoyancy); | ||
660 | // Preserve the current Z velocity | ||
661 | Vector3 vel_now = m_prim.Velocity; | ||
662 | m_dir.Z = vel_now.Z; // Preserve the accumulated falling velocity | ||
663 | |||
664 | Vector3 pos = m_prim.Position; | ||
665 | // Vector3 accel = new Vector3(-(m_dir.X - m_lastLinearVelocityVector.X / 0.1f), -(m_dir.Y - m_lastLinearVelocityVector.Y / 0.1f), m_dir.Z - m_lastLinearVelocityVector.Z / 0.1f); | ||
666 | Vector3 posChange = new Vector3(); | ||
667 | posChange.X = pos.X - m_lastPositionVector.X; | ||
668 | posChange.Y = pos.Y - m_lastPositionVector.Y; | ||
669 | posChange.Z = pos.Z - m_lastPositionVector.Z; | ||
670 | double Zchange = Math.Abs(posChange.Z); | ||
671 | if (m_BlockingEndPoint != Vector3.Zero) | ||
672 | { | ||
673 | if (pos.X >= (m_BlockingEndPoint.X - (float)1)) | ||
674 | { | ||
675 | pos.X -= posChange.X + 1; | ||
676 | m_prim.Position = pos; | ||
677 | } | ||
678 | if (pos.Y >= (m_BlockingEndPoint.Y - (float)1)) | ||
679 | { | ||
680 | pos.Y -= posChange.Y + 1; | ||
681 | m_prim.Position = pos; | ||
682 | } | ||
683 | if (pos.Z >= (m_BlockingEndPoint.Z - (float)1)) | ||
684 | { | ||
685 | pos.Z -= posChange.Z + 1; | ||
686 | m_prim.Position = pos; | ||
687 | } | ||
688 | if (pos.X <= 0) | ||
689 | { | ||
690 | pos.X += posChange.X + 1; | ||
691 | m_prim.Position = pos; | ||
692 | } | ||
693 | if (pos.Y <= 0) | ||
694 | { | ||
695 | pos.Y += posChange.Y + 1; | ||
696 | m_prim.Position = pos; | ||
697 | } | ||
698 | } | ||
699 | if (pos.Z < _pParentScene.GetTerrainHeightAtXY(pos.X, pos.Y)) | ||
700 | { | ||
701 | pos.Z = _pParentScene.GetTerrainHeightAtXY(pos.X, pos.Y) + 2; | ||
702 | m_prim.Position = pos; | ||
703 | } | ||
704 | |||
705 | // Check if hovering | ||
706 | if ((m_Hoverflags & (VehicleFlag.HOVER_WATER_ONLY | VehicleFlag.HOVER_TERRAIN_ONLY | VehicleFlag.HOVER_GLOBAL_HEIGHT)) != 0) | ||
707 | { | ||
708 | // We should hover, get the target height | ||
709 | if ((m_Hoverflags & VehicleFlag.HOVER_WATER_ONLY) != 0) | ||
710 | { | ||
711 | m_VhoverTargetHeight = _pParentScene.GetWaterLevel() + m_VhoverHeight; | ||
712 | } | ||
713 | if ((m_Hoverflags & VehicleFlag.HOVER_TERRAIN_ONLY) != 0) | ||
714 | { | ||
715 | m_VhoverTargetHeight = _pParentScene.GetTerrainHeightAtXY(pos.X, pos.Y) + m_VhoverHeight; | ||
716 | } | ||
717 | if ((m_Hoverflags & VehicleFlag.HOVER_GLOBAL_HEIGHT) != 0) | ||
718 | { | ||
719 | m_VhoverTargetHeight = m_VhoverHeight; | ||
720 | } | ||
721 | |||
722 | if ((m_Hoverflags & VehicleFlag.HOVER_UP_ONLY) != 0) | ||
723 | { | ||
724 | // If body is aready heigher, use its height as target height | ||
725 | if (pos.Z > m_VhoverTargetHeight) m_VhoverTargetHeight = pos.Z; | ||
726 | } | ||
727 | if ((m_Hoverflags & VehicleFlag.LOCK_HOVER_HEIGHT) != 0) | ||
728 | { | ||
729 | if ((pos.Z - m_VhoverTargetHeight) > .2 || (pos.Z - m_VhoverTargetHeight) < -.2) | ||
730 | { | ||
731 | m_prim.Position = pos; | ||
732 | } | ||
733 | } | ||
734 | else | ||
735 | { | ||
736 | float herr0 = pos.Z - m_VhoverTargetHeight; | ||
737 | // Replace Vertical speed with correction figure if significant | ||
738 | if (Math.Abs(herr0) > 0.01f) | ||
739 | { | ||
740 | m_dir.Z = -((herr0 * pTimestep * 50.0f) / m_VhoverTimescale); | ||
741 | //KF: m_VhoverEfficiency is not yet implemented | ||
742 | } | ||
743 | else | ||
744 | { | ||
745 | m_dir.Z = 0f; | ||
746 | } | ||
747 | } | ||
748 | |||
749 | // m_VhoverEfficiency = 0f; // 0=boucy, 1=Crit.damped | ||
750 | // m_VhoverTimescale = 0f; // time to acheive height | ||
751 | // pTimestep is time since last frame,in secs | ||
752 | } | ||
753 | |||
754 | if ((m_flags & (VehicleFlag.LIMIT_MOTOR_UP)) != 0) | ||
755 | { | ||
756 | //Start Experimental Values | ||
757 | if (Zchange > .3) | ||
758 | { | ||
759 | grav.Z = (float)(grav.Z * 3); | ||
760 | } | ||
761 | if (Zchange > .15) | ||
762 | { | ||
763 | grav.Z = (float)(grav.Z * 2); | ||
764 | } | ||
765 | if (Zchange > .75) | ||
766 | { | ||
767 | grav.Z = (float)(grav.Z * 1.5); | ||
768 | } | ||
769 | if (Zchange > .05) | ||
770 | { | ||
771 | grav.Z = (float)(grav.Z * 1.25); | ||
772 | } | ||
773 | if (Zchange > .025) | ||
774 | { | ||
775 | grav.Z = (float)(grav.Z * 1.125); | ||
776 | } | ||
777 | float terraintemp = _pParentScene.GetTerrainHeightAtXY(pos.X, pos.Y); | ||
778 | float postemp = (pos.Z - terraintemp); | ||
779 | if (postemp > 2.5f) | ||
780 | { | ||
781 | grav.Z = (float)(grav.Z * 1.037125); | ||
782 | } | ||
783 | //End Experimental Values | ||
784 | } | ||
785 | if ((m_flags & (VehicleFlag.NO_X)) != 0) | ||
786 | { | ||
787 | m_dir.X = 0; | ||
788 | } | ||
789 | if ((m_flags & (VehicleFlag.NO_Y)) != 0) | ||
790 | { | ||
791 | m_dir.Y = 0; | ||
792 | } | ||
793 | if ((m_flags & (VehicleFlag.NO_Z)) != 0) | ||
794 | { | ||
795 | m_dir.Z = 0; | ||
796 | } | ||
797 | |||
798 | m_lastPositionVector = m_prim.Position; | ||
799 | |||
800 | // Apply velocity | ||
801 | m_prim.Velocity = m_dir; | ||
802 | // apply gravity force | ||
803 | m_prim.Force = grav; | ||
804 | |||
805 | |||
806 | // apply friction | ||
807 | Vector3 decayamount = Vector3.One / (m_linearFrictionTimescale / pTimestep); | ||
808 | m_lastLinearVelocityVector -= m_lastLinearVelocityVector * decayamount; | ||
809 | } // end MoveLinear() | ||
810 | |||
811 | private void MoveAngular(float pTimestep) | ||
812 | { | ||
813 | /* | ||
814 | private Vector3 m_angularMotorDirection = Vector3.Zero; // angular velocity requested by LSL motor | ||
815 | private int m_angularMotorApply = 0; // application frame counter | ||
816 | private float m_angularMotorVelocity = 0; // current angular motor velocity (ramps up and down) | ||
817 | private float m_angularMotorTimescale = 0; // motor angular velocity ramp up rate | ||
818 | private float m_angularMotorDecayTimescale = 0; // motor angular velocity decay rate | ||
819 | private Vector3 m_angularFrictionTimescale = Vector3.Zero; // body angular velocity decay rate | ||
820 | private Vector3 m_lastAngularVelocity = Vector3.Zero; // what was last applied to body | ||
821 | */ | ||
822 | |||
823 | // Get what the body is doing, this includes 'external' influences | ||
824 | Vector3 angularVelocity = m_prim.AngularVelocity; | ||
825 | // Vector3 angularVelocity = Vector3.Zero; | ||
826 | |||
827 | if (m_angularMotorApply > 0) | ||
828 | { | ||
829 | // ramp up to new value | ||
830 | // current velocity += error / (time to get there / step interval) | ||
831 | // requested speed - last motor speed | ||
832 | m_angularMotorVelocity.X += (m_angularMotorDirection.X - m_angularMotorVelocity.X) / (m_angularMotorTimescale / pTimestep); | ||
833 | m_angularMotorVelocity.Y += (m_angularMotorDirection.Y - m_angularMotorVelocity.Y) / (m_angularMotorTimescale / pTimestep); | ||
834 | m_angularMotorVelocity.Z += (m_angularMotorDirection.Z - m_angularMotorVelocity.Z) / (m_angularMotorTimescale / pTimestep); | ||
835 | |||
836 | m_angularMotorApply--; // This is done so that if script request rate is less than phys frame rate the expected | ||
837 | // velocity may still be acheived. | ||
838 | } | ||
839 | else | ||
840 | { | ||
841 | // no motor recently applied, keep the body velocity | ||
842 | /* m_angularMotorVelocity.X = angularVelocity.X; | ||
843 | m_angularMotorVelocity.Y = angularVelocity.Y; | ||
844 | m_angularMotorVelocity.Z = angularVelocity.Z; */ | ||
845 | |||
846 | // and decay the velocity | ||
847 | m_angularMotorVelocity -= m_angularMotorVelocity / (m_angularMotorDecayTimescale / pTimestep); | ||
848 | } // end motor section | ||
849 | |||
850 | // Vertical attractor section | ||
851 | Vector3 vertattr = Vector3.Zero; | ||
852 | |||
853 | if (m_verticalAttractionTimescale < 300) | ||
854 | { | ||
855 | float VAservo = 0.2f / (m_verticalAttractionTimescale * pTimestep); | ||
856 | // get present body rotation | ||
857 | Quaternion rotq = m_prim.Orientation; | ||
858 | // make a vector pointing up | ||
859 | Vector3 verterr = Vector3.Zero; | ||
860 | verterr.Z = 1.0f; | ||
861 | // rotate it to Body Angle | ||
862 | verterr = verterr * rotq; | ||
863 | // verterr.X and .Y are the World error ammounts. They are 0 when there is no error (Vehicle Body is 'vertical'), and .Z will be 1. | ||
864 | // As the body leans to its side |.X| will increase to 1 and .Z fall to 0. As body inverts |.X| will fall and .Z will go | ||
865 | // negative. Similar for tilt and |.Y|. .X and .Y must be modulated to prevent a stable inverted body. | ||
866 | if (verterr.Z < 0.0f) | ||
867 | { | ||
868 | verterr.X = 2.0f - verterr.X; | ||
869 | verterr.Y = 2.0f - verterr.Y; | ||
870 | } | ||
871 | // Error is 0 (no error) to +/- 2 (max error) | ||
872 | // scale it by VAservo | ||
873 | verterr = verterr * VAservo; | ||
874 | //if (frcount == 0) Console.WriteLine("VAerr=" + verterr); | ||
875 | |||
876 | // As the body rotates around the X axis, then verterr.Y increases; Rotated around Y then .X increases, so | ||
877 | // Change Body angular velocity X based on Y, and Y based on X. Z is not changed. | ||
878 | vertattr.X = verterr.Y; | ||
879 | vertattr.Y = - verterr.X; | ||
880 | vertattr.Z = 0f; | ||
881 | |||
882 | // scaling appears better usingsquare-law | ||
883 | float bounce = 1.0f - (m_verticalAttractionEfficiency * m_verticalAttractionEfficiency); | ||
884 | vertattr.X += bounce * angularVelocity.X; | ||
885 | vertattr.Y += bounce * angularVelocity.Y; | ||
886 | |||
887 | } // else vertical attractor is off | ||
888 | |||
889 | // m_lastVertAttractor = vertattr; | ||
890 | |||
891 | // Bank section tba | ||
892 | // Deflection section tba | ||
893 | |||
894 | // Sum velocities | ||
895 | m_lastAngularVelocity = m_angularMotorVelocity + vertattr; // + bank + deflection | ||
896 | |||
897 | if ((m_flags & (VehicleFlag.NO_DEFLECTION_UP)) != 0) | ||
898 | { | ||
899 | m_lastAngularVelocity.X = 0; | ||
900 | m_lastAngularVelocity.Y = 0; | ||
901 | } | ||
902 | |||
903 | if (m_lastAngularVelocity.ApproxEquals(Vector3.Zero, 0.01f)) | ||
904 | { | ||
905 | m_lastAngularVelocity = Vector3.Zero; // Reduce small value to zero. | ||
906 | } | ||
907 | |||
908 | // apply friction | ||
909 | Vector3 decayamount = Vector3.One / (m_angularFrictionTimescale / pTimestep); | ||
910 | m_lastAngularVelocity -= m_lastAngularVelocity * decayamount; | ||
911 | |||
912 | // Apply to the body | ||
913 | m_prim.AngularVelocity = m_lastAngularVelocity; | ||
914 | |||
915 | } //end MoveAngular | ||
916 | internal void LimitRotation(float timestep) | ||
917 | { | ||
918 | Quaternion rotq = m_prim.Orientation; // rotq = rotation of object | ||
919 | Quaternion m_rot = rotq; | ||
920 | bool changed = false; | ||
921 | if (m_RollreferenceFrame != Quaternion.Identity) | ||
922 | { | ||
923 | if (rotq.X >= m_RollreferenceFrame.X) | ||
924 | { | ||
925 | m_rot.X = rotq.X - (m_RollreferenceFrame.X / 2); | ||
926 | } | ||
927 | if (rotq.Y >= m_RollreferenceFrame.Y) | ||
928 | { | ||
929 | m_rot.Y = rotq.Y - (m_RollreferenceFrame.Y / 2); | ||
930 | } | ||
931 | if (rotq.X <= -m_RollreferenceFrame.X) | ||
932 | { | ||
933 | m_rot.X = rotq.X + (m_RollreferenceFrame.X / 2); | ||
934 | } | ||
935 | if (rotq.Y <= -m_RollreferenceFrame.Y) | ||
936 | { | ||
937 | m_rot.Y = rotq.Y + (m_RollreferenceFrame.Y / 2); | ||
938 | } | ||
939 | changed = true; | ||
940 | } | ||
941 | if ((m_flags & VehicleFlag.LOCK_ROTATION) != 0) | ||
942 | { | ||
943 | m_rot.X = 0; | ||
944 | m_rot.Y = 0; | ||
945 | changed = true; | ||
946 | } | ||
947 | if (changed) | ||
948 | m_prim.Orientation = m_rot; | ||
949 | } | ||
950 | } | ||
951 | } | ||
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPlugin.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPlugin.cs new file mode 100644 index 0000000..61be56d --- /dev/null +++ b/OpenSim/Region/Physics/BulletSPlugin/BSPlugin.cs | |||
@@ -0,0 +1,68 @@ | |||
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 OpenSim.Framework; | ||
30 | using OpenSim.Region.Physics.Manager; | ||
31 | using OpenMetaverse; | ||
32 | |||
33 | namespace OpenSim.Region.Physics.BulletSPlugin | ||
34 | { | ||
35 | public class BSPlugin : IPhysicsPlugin | ||
36 | { | ||
37 | //private static readonly log4net.ILog m_log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); | ||
38 | |||
39 | private BSScene _mScene; | ||
40 | |||
41 | public BSPlugin() | ||
42 | { | ||
43 | } | ||
44 | |||
45 | public bool Init() | ||
46 | { | ||
47 | return true; | ||
48 | } | ||
49 | |||
50 | public PhysicsScene GetScene(String sceneIdentifier) | ||
51 | { | ||
52 | if (_mScene == null) | ||
53 | { | ||
54 | _mScene = new BSScene(sceneIdentifier); | ||
55 | } | ||
56 | return (_mScene); | ||
57 | } | ||
58 | |||
59 | public string GetName() | ||
60 | { | ||
61 | return ("BulletSim"); | ||
62 | } | ||
63 | |||
64 | public void Dispose() | ||
65 | { | ||
66 | } | ||
67 | } | ||
68 | } | ||
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs new file mode 100644 index 0000000..bb8d601 --- /dev/null +++ b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs | |||
@@ -0,0 +1,1357 @@ | |||
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.Reflection; | ||
29 | using System.Collections.Generic; | ||
30 | using System.Xml; | ||
31 | using log4net; | ||
32 | using OMV = OpenMetaverse; | ||
33 | using OpenSim.Framework; | ||
34 | using OpenSim.Region.Physics.Manager; | ||
35 | using OpenSim.Region.Physics.ConvexDecompositionDotNet; | ||
36 | |||
37 | namespace OpenSim.Region.Physics.BulletSPlugin | ||
38 | { | ||
39 | [Serializable] | ||
40 | public sealed class BSPrim : PhysicsActor | ||
41 | { | ||
42 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
43 | private static readonly string LogHeader = "[BULLETS PRIM]"; | ||
44 | |||
45 | private IMesh _mesh; | ||
46 | private PrimitiveBaseShape _pbs; | ||
47 | private ShapeData.PhysicsShapeType _shapeType; | ||
48 | private ulong _meshKey; | ||
49 | private ulong _hullKey; | ||
50 | private List<ConvexResult> _hulls; | ||
51 | |||
52 | private BSScene _scene; | ||
53 | private String _avName; | ||
54 | private uint _localID = 0; | ||
55 | |||
56 | // _size is what the user passed. _scale is what we pass to the physics engine with the mesh. | ||
57 | // Often _scale is unity because the meshmerizer will apply _size when creating the mesh. | ||
58 | private OMV.Vector3 _size; // the multiplier for each mesh dimension as passed by the user | ||
59 | private OMV.Vector3 _scale; // the multiplier for each mesh dimension for the mesh as created by the meshmerizer | ||
60 | |||
61 | private bool _stopped; | ||
62 | private bool _grabbed; | ||
63 | private bool _isSelected; | ||
64 | private bool _isVolumeDetect; | ||
65 | private OMV.Vector3 _position; | ||
66 | private float _mass; | ||
67 | private float _density; | ||
68 | private OMV.Vector3 _force; | ||
69 | private OMV.Vector3 _velocity; | ||
70 | private OMV.Vector3 _torque; | ||
71 | private float _collisionScore; | ||
72 | private OMV.Vector3 _acceleration; | ||
73 | private OMV.Quaternion _orientation; | ||
74 | private int _physicsActorType; | ||
75 | private bool _isPhysical; | ||
76 | private bool _flying; | ||
77 | private float _friction; | ||
78 | private float _restitution; | ||
79 | private bool _setAlwaysRun; | ||
80 | private bool _throttleUpdates; | ||
81 | private bool _isColliding; | ||
82 | private bool _collidingGround; | ||
83 | private bool _collidingObj; | ||
84 | private bool _floatOnWater; | ||
85 | private OMV.Vector3 _rotationalVelocity; | ||
86 | private bool _kinematic; | ||
87 | private float _buoyancy; | ||
88 | private OMV.Vector3 _angularVelocity; | ||
89 | |||
90 | private List<BSPrim> _childrenPrims; | ||
91 | private BSPrim _parentPrim; | ||
92 | |||
93 | private int _subscribedEventsMs = 0; | ||
94 | private int _lastCollisionTime = 0; | ||
95 | long _collidingStep; | ||
96 | long _collidingGroundStep; | ||
97 | |||
98 | private BSDynamics _vehicle; | ||
99 | |||
100 | private OMV.Vector3 _PIDTarget; | ||
101 | private bool _usePID; | ||
102 | private float _PIDTau; | ||
103 | private bool _useHoverPID; | ||
104 | private float _PIDHoverHeight; | ||
105 | private PIDHoverType _PIDHoverType; | ||
106 | private float _PIDHoverTao; | ||
107 | |||
108 | public BSPrim(uint localID, String primName, BSScene parent_scene, OMV.Vector3 pos, OMV.Vector3 size, | ||
109 | OMV.Quaternion rotation, PrimitiveBaseShape pbs, bool pisPhysical) | ||
110 | { | ||
111 | // m_log.DebugFormat("{0}: BSPrim creation of {1}, id={2}", LogHeader, primName, localID); | ||
112 | _localID = localID; | ||
113 | _avName = primName; | ||
114 | _scene = parent_scene; | ||
115 | _position = pos; | ||
116 | _size = size; | ||
117 | _scale = new OMV.Vector3(1f, 1f, 1f); // the scale will be set by CreateGeom depending on object type | ||
118 | _orientation = rotation; | ||
119 | _buoyancy = 1f; | ||
120 | _velocity = OMV.Vector3.Zero; | ||
121 | _rotationalVelocity = OMV.Vector3.Zero; | ||
122 | _angularVelocity = OMV.Vector3.Zero; | ||
123 | _hullKey = 0; | ||
124 | _meshKey = 0; | ||
125 | _pbs = pbs; | ||
126 | _isPhysical = pisPhysical; | ||
127 | _isVolumeDetect = false; | ||
128 | _subscribedEventsMs = 0; | ||
129 | _friction = _scene.Params.defaultFriction; // TODO: compute based on object material | ||
130 | _density = _scene.Params.defaultDensity; // TODO: compute based on object material | ||
131 | _restitution = _scene.Params.defaultRestitution; | ||
132 | _parentPrim = null; // not a child or a parent | ||
133 | _vehicle = new BSDynamics(this); // add vehicleness | ||
134 | _childrenPrims = new List<BSPrim>(); | ||
135 | if (_isPhysical) | ||
136 | _mass = CalculateMass(); | ||
137 | else | ||
138 | _mass = 0f; | ||
139 | // do the actual object creation at taint time | ||
140 | _scene.TaintedObject(delegate() | ||
141 | { | ||
142 | RecreateGeomAndObject(); | ||
143 | }); | ||
144 | } | ||
145 | |||
146 | // called when this prim is being destroyed and we should free all the resources | ||
147 | public void Destroy() | ||
148 | { | ||
149 | // m_log.DebugFormat("{0}: Destroy", LogHeader); | ||
150 | // Undo any vehicle properties | ||
151 | _vehicle.ProcessTypeChange(Vehicle.TYPE_NONE); | ||
152 | _scene.RemoveVehiclePrim(this); // just to make sure | ||
153 | _scene.TaintedObject(delegate() | ||
154 | { | ||
155 | // everything in the C# world will get garbage collected. Tell the C++ world to free stuff. | ||
156 | BulletSimAPI.DestroyObject(_scene.WorldID, _localID); | ||
157 | }); | ||
158 | } | ||
159 | |||
160 | public override bool Stopped { | ||
161 | get { return _stopped; } | ||
162 | } | ||
163 | public override OMV.Vector3 Size { | ||
164 | get { return _size; } | ||
165 | set { | ||
166 | _size = value; | ||
167 | _scene.TaintedObject(delegate() | ||
168 | { | ||
169 | if (_isPhysical) _mass = CalculateMass(); // changing size changes the mass | ||
170 | BulletSimAPI.SetObjectScaleMass(_scene.WorldID, _localID, _scale, _mass, _isPhysical); | ||
171 | RecreateGeomAndObject(); | ||
172 | }); | ||
173 | } | ||
174 | } | ||
175 | public override PrimitiveBaseShape Shape { | ||
176 | set { | ||
177 | _pbs = value; | ||
178 | _scene.TaintedObject(delegate() | ||
179 | { | ||
180 | if (_isPhysical) _mass = CalculateMass(); // changing the shape changes the mass | ||
181 | RecreateGeomAndObject(); | ||
182 | }); | ||
183 | } | ||
184 | } | ||
185 | public override uint LocalID { | ||
186 | set { _localID = value; } | ||
187 | get { return _localID; } | ||
188 | } | ||
189 | public override bool Grabbed { | ||
190 | set { _grabbed = value; | ||
191 | } | ||
192 | } | ||
193 | public override bool Selected { | ||
194 | set { | ||
195 | _isSelected = value; | ||
196 | _scene.TaintedObject(delegate() | ||
197 | { | ||
198 | SetObjectDynamic(); | ||
199 | }); | ||
200 | } | ||
201 | } | ||
202 | public override void CrossingFailure() { return; } | ||
203 | |||
204 | // link me to the specified parent | ||
205 | public override void link(PhysicsActor obj) { | ||
206 | BSPrim parent = (BSPrim)obj; | ||
207 | // m_log.DebugFormat("{0}: link {1}/{2} to {3}", LogHeader, _avName, _localID, obj.LocalID); | ||
208 | // TODO: decide if this parent checking needs to happen at taint time | ||
209 | if (_parentPrim == null) | ||
210 | { | ||
211 | if (parent != null) | ||
212 | { | ||
213 | // I don't have a parent so I am joining a linkset | ||
214 | parent.AddChildToLinkset(this); | ||
215 | } | ||
216 | } | ||
217 | else | ||
218 | { | ||
219 | // I already have a parent, is parenting changing? | ||
220 | if (parent != _parentPrim) | ||
221 | { | ||
222 | if (parent == null) | ||
223 | { | ||
224 | // we are being removed from a linkset | ||
225 | _parentPrim.RemoveChildFromLinkset(this); | ||
226 | } | ||
227 | else | ||
228 | { | ||
229 | // asking to reparent a prim should not happen | ||
230 | m_log.ErrorFormat("{0}: Reparenting a prim. ", LogHeader); | ||
231 | } | ||
232 | } | ||
233 | } | ||
234 | return; | ||
235 | } | ||
236 | |||
237 | // delink me from my linkset | ||
238 | public override void delink() { | ||
239 | // TODO: decide if this parent checking needs to happen at taint time | ||
240 | // Race condition here: if link() and delink() in same simulation tick, the delink will not happen | ||
241 | // m_log.DebugFormat("{0}: delink {1}/{2}", LogHeader, _avName, _localID); | ||
242 | if (_parentPrim != null) | ||
243 | { | ||
244 | _parentPrim.RemoveChildFromLinkset(this); | ||
245 | } | ||
246 | return; | ||
247 | } | ||
248 | |||
249 | // I am the root of a linkset and a new child is being added | ||
250 | public void AddChildToLinkset(BSPrim pchild) | ||
251 | { | ||
252 | BSPrim child = pchild; | ||
253 | _scene.TaintedObject(delegate() | ||
254 | { | ||
255 | if (!_childrenPrims.Contains(child)) | ||
256 | { | ||
257 | _childrenPrims.Add(child); | ||
258 | child.ParentPrim = this; // the child has gained a parent | ||
259 | RecreateGeomAndObject(); // rebuild my shape with the new child added | ||
260 | } | ||
261 | }); | ||
262 | return; | ||
263 | } | ||
264 | |||
265 | // I am the root of a linkset and one of my children is being removed. | ||
266 | // Safe to call even if the child is not really in my linkset. | ||
267 | public void RemoveChildFromLinkset(BSPrim pchild) | ||
268 | { | ||
269 | BSPrim child = pchild; | ||
270 | _scene.TaintedObject(delegate() | ||
271 | { | ||
272 | if (_childrenPrims.Contains(child)) | ||
273 | { | ||
274 | BulletSimAPI.RemoveConstraint(_scene.WorldID, child.LocalID, this.LocalID); | ||
275 | _childrenPrims.Remove(child); | ||
276 | child.ParentPrim = null; // the child has lost its parent | ||
277 | RecreateGeomAndObject(); // rebuild my shape with the child removed | ||
278 | } | ||
279 | else | ||
280 | { | ||
281 | m_log.ErrorFormat("{0}: Asked to remove child from linkset that was not in linkset"); | ||
282 | } | ||
283 | }); | ||
284 | return; | ||
285 | } | ||
286 | |||
287 | public BSPrim ParentPrim | ||
288 | { | ||
289 | set { _parentPrim = value; } | ||
290 | } | ||
291 | |||
292 | // return true if we are the root of a linkset (there are children to manage) | ||
293 | public bool IsRootOfLinkset | ||
294 | { | ||
295 | get { return (_parentPrim == null && _childrenPrims.Count != 0); } | ||
296 | } | ||
297 | |||
298 | // Set motion values to zero. | ||
299 | // Do it to the properties so the values get set in the physics engine. | ||
300 | // Push the setting of the values to the viewer. | ||
301 | private void ZeroMotion() | ||
302 | { | ||
303 | Velocity = OMV.Vector3.Zero; | ||
304 | _acceleration = OMV.Vector3.Zero; | ||
305 | RotationalVelocity = OMV.Vector3.Zero; | ||
306 | base.RequestPhysicsterseUpdate(); | ||
307 | } | ||
308 | |||
309 | public override void LockAngularMotion(OMV.Vector3 axis) { return; } | ||
310 | |||
311 | public override OMV.Vector3 Position { | ||
312 | get { | ||
313 | // don't do the following GetObjectPosition because this function is called a zillion times | ||
314 | // _position = BulletSimAPI.GetObjectPosition(_scene.WorldID, _localID); | ||
315 | return _position; | ||
316 | } | ||
317 | set { | ||
318 | _position = value; | ||
319 | _scene.TaintedObject(delegate() | ||
320 | { | ||
321 | BulletSimAPI.SetObjectTranslation(_scene.WorldID, _localID, _position, _orientation); | ||
322 | // m_log.DebugFormat("{0}: setPosition: id={1}, position={2}", LogHeader, _localID, _position); | ||
323 | }); | ||
324 | } | ||
325 | } | ||
326 | public override float Mass { | ||
327 | get { return _mass; } | ||
328 | } | ||
329 | public override OMV.Vector3 Force { | ||
330 | get { return _force; } | ||
331 | set { | ||
332 | _force = value; | ||
333 | _scene.TaintedObject(delegate() | ||
334 | { | ||
335 | BulletSimAPI.SetObjectForce(_scene.WorldID, _localID, _force); | ||
336 | }); | ||
337 | } | ||
338 | } | ||
339 | |||
340 | public override int VehicleType { | ||
341 | get { | ||
342 | return (int)_vehicle.Type; // if we are a vehicle, return that type | ||
343 | } | ||
344 | set { | ||
345 | Vehicle type = (Vehicle)value; | ||
346 | _vehicle.ProcessTypeChange(type); | ||
347 | _scene.TaintedObject(delegate() | ||
348 | { | ||
349 | if (type == Vehicle.TYPE_NONE) | ||
350 | { | ||
351 | _scene.RemoveVehiclePrim(this); | ||
352 | } | ||
353 | else | ||
354 | { | ||
355 | // make it so the scene will call us each tick to do vehicle things | ||
356 | _scene.AddVehiclePrim(this); | ||
357 | } | ||
358 | return; | ||
359 | }); | ||
360 | } | ||
361 | } | ||
362 | public override void VehicleFloatParam(int param, float value) | ||
363 | { | ||
364 | _vehicle.ProcessFloatVehicleParam((Vehicle)param, value); | ||
365 | } | ||
366 | public override void VehicleVectorParam(int param, OMV.Vector3 value) | ||
367 | { | ||
368 | _vehicle.ProcessVectorVehicleParam((Vehicle)param, value); | ||
369 | } | ||
370 | public override void VehicleRotationParam(int param, OMV.Quaternion rotation) | ||
371 | { | ||
372 | _vehicle.ProcessRotationVehicleParam((Vehicle)param, rotation); | ||
373 | } | ||
374 | public override void VehicleFlags(int param, bool remove) | ||
375 | { | ||
376 | _vehicle.ProcessVehicleFlags(param, remove); | ||
377 | } | ||
378 | // Called each simulation step to advance vehicle characteristics | ||
379 | public void StepVehicle(float timeStep) | ||
380 | { | ||
381 | _vehicle.Step(timeStep, _scene); | ||
382 | } | ||
383 | |||
384 | // Allows the detection of collisions with inherently non-physical prims. see llVolumeDetect for more | ||
385 | public override void SetVolumeDetect(int param) { | ||
386 | bool newValue = (param != 0); | ||
387 | if (_isVolumeDetect != newValue) | ||
388 | { | ||
389 | _isVolumeDetect = newValue; | ||
390 | _scene.TaintedObject(delegate() | ||
391 | { | ||
392 | SetObjectDynamic(); | ||
393 | }); | ||
394 | } | ||
395 | return; | ||
396 | } | ||
397 | |||
398 | public override OMV.Vector3 GeometricCenter { get { return OMV.Vector3.Zero; } } | ||
399 | public override OMV.Vector3 CenterOfMass { get { return OMV.Vector3.Zero; } } | ||
400 | public override OMV.Vector3 Velocity { | ||
401 | get { return _velocity; } | ||
402 | set { _velocity = value; | ||
403 | _scene.TaintedObject(delegate() | ||
404 | { | ||
405 | BulletSimAPI.SetObjectVelocity(_scene.WorldID, LocalID, _velocity); | ||
406 | }); | ||
407 | } | ||
408 | } | ||
409 | public override OMV.Vector3 Torque { | ||
410 | get { return _torque; } | ||
411 | set { _torque = value; | ||
412 | } | ||
413 | } | ||
414 | public override float CollisionScore { | ||
415 | get { return _collisionScore; } | ||
416 | set { _collisionScore = value; | ||
417 | } | ||
418 | } | ||
419 | public override OMV.Vector3 Acceleration { | ||
420 | get { return _acceleration; } | ||
421 | } | ||
422 | public override OMV.Quaternion Orientation { | ||
423 | get { return _orientation; } | ||
424 | set { | ||
425 | _orientation = value; | ||
426 | // m_log.DebugFormat("{0}: set orientation: id={1}, ori={2}", LogHeader, LocalID, _orientation); | ||
427 | _scene.TaintedObject(delegate() | ||
428 | { | ||
429 | // _position = BulletSimAPI.GetObjectPosition(_scene.WorldID, _localID); | ||
430 | BulletSimAPI.SetObjectTranslation(_scene.WorldID, _localID, _position, _orientation); | ||
431 | }); | ||
432 | } | ||
433 | } | ||
434 | public override int PhysicsActorType { | ||
435 | get { return _physicsActorType; } | ||
436 | set { _physicsActorType = value; | ||
437 | } | ||
438 | } | ||
439 | public override bool IsPhysical { | ||
440 | get { return _isPhysical; } | ||
441 | set { | ||
442 | _isPhysical = value; | ||
443 | _scene.TaintedObject(delegate() | ||
444 | { | ||
445 | SetObjectDynamic(); | ||
446 | }); | ||
447 | } | ||
448 | } | ||
449 | |||
450 | // An object is static (does not move) if selected or not physical | ||
451 | private bool IsStatic | ||
452 | { | ||
453 | get { return _isSelected || !IsPhysical; } | ||
454 | } | ||
455 | |||
456 | // An object is solid if it's not phantom and if it's not doing VolumeDetect | ||
457 | private bool IsSolid | ||
458 | { | ||
459 | get { return !IsPhantom && !_isVolumeDetect; } | ||
460 | } | ||
461 | |||
462 | // make gravity work if the object is physical and not selected | ||
463 | // no locking here because only called when it is safe | ||
464 | private void SetObjectDynamic() | ||
465 | { | ||
466 | // m_log.DebugFormat("{0}: ID={1}, SetObjectDynamic: IsStatic={2}, IsSolid={3}", LogHeader, _localID, IsStatic, IsSolid); | ||
467 | // non-physical things work best with a mass of zero | ||
468 | if (IsStatic) | ||
469 | { | ||
470 | _mass = 0f; | ||
471 | } | ||
472 | else | ||
473 | { | ||
474 | _mass = CalculateMass(); | ||
475 | // If it's dynamic, make sure the hull has been created for it | ||
476 | // This shouldn't do much work if the object had previously been built | ||
477 | RecreateGeomAndObject(); | ||
478 | |||
479 | } | ||
480 | BulletSimAPI.SetObjectProperties(_scene.WorldID, LocalID, IsStatic, IsSolid, SubscribedEvents(), _mass); | ||
481 | } | ||
482 | |||
483 | // prims don't fly | ||
484 | public override bool Flying { | ||
485 | get { return _flying; } | ||
486 | set { _flying = value; } | ||
487 | } | ||
488 | public override bool SetAlwaysRun { | ||
489 | get { return _setAlwaysRun; } | ||
490 | set { _setAlwaysRun = value; } | ||
491 | } | ||
492 | public override bool ThrottleUpdates { | ||
493 | get { return _throttleUpdates; } | ||
494 | set { _throttleUpdates = value; } | ||
495 | } | ||
496 | public override bool IsColliding { | ||
497 | get { return (_collidingStep == _scene.SimulationStep); } | ||
498 | set { _isColliding = value; } | ||
499 | } | ||
500 | public override bool CollidingGround { | ||
501 | get { return (_collidingGroundStep == _scene.SimulationStep); } | ||
502 | set { _collidingGround = value; } | ||
503 | } | ||
504 | public override bool CollidingObj { | ||
505 | get { return _collidingObj; } | ||
506 | set { _collidingObj = value; } | ||
507 | } | ||
508 | public bool IsPhantom { | ||
509 | get { | ||
510 | // SceneObjectPart removes phantom objects from the physics scene | ||
511 | // so, although we could implement touching and such, we never | ||
512 | // are invoked as a phantom object | ||
513 | return false; | ||
514 | } | ||
515 | } | ||
516 | public override bool FloatOnWater { | ||
517 | set { _floatOnWater = value; } | ||
518 | } | ||
519 | public override OMV.Vector3 RotationalVelocity { | ||
520 | get { return _rotationalVelocity; } | ||
521 | set { _rotationalVelocity = value; | ||
522 | // m_log.DebugFormat("{0}: RotationalVelocity={1}", LogHeader, _rotationalVelocity); | ||
523 | _scene.TaintedObject(delegate() | ||
524 | { | ||
525 | BulletSimAPI.SetObjectAngularVelocity(_scene.WorldID, LocalID, _rotationalVelocity); | ||
526 | }); | ||
527 | } | ||
528 | } | ||
529 | public OMV.Vector3 AngularVelocity { | ||
530 | get { return _angularVelocity; } | ||
531 | set { _angularVelocity = value; } | ||
532 | } | ||
533 | public override bool Kinematic { | ||
534 | get { return _kinematic; } | ||
535 | set { _kinematic = value; | ||
536 | // m_log.DebugFormat("{0}: Kinematic={1}", LogHeader, _kinematic); | ||
537 | } | ||
538 | } | ||
539 | public override float Buoyancy { | ||
540 | get { return _buoyancy; } | ||
541 | set { _buoyancy = value; | ||
542 | _scene.TaintedObject(delegate() | ||
543 | { | ||
544 | BulletSimAPI.SetObjectBuoyancy(_scene.WorldID, _localID, _buoyancy); | ||
545 | }); | ||
546 | } | ||
547 | } | ||
548 | |||
549 | // Used for MoveTo | ||
550 | public override OMV.Vector3 PIDTarget { | ||
551 | set { _PIDTarget = value; } | ||
552 | } | ||
553 | public override bool PIDActive { | ||
554 | set { _usePID = value; } | ||
555 | } | ||
556 | public override float PIDTau { | ||
557 | set { _PIDTau = value; } | ||
558 | } | ||
559 | |||
560 | // Used for llSetHoverHeight and maybe vehicle height | ||
561 | // Hover Height will override MoveTo target's Z | ||
562 | public override bool PIDHoverActive { | ||
563 | set { _useHoverPID = value; } | ||
564 | } | ||
565 | public override float PIDHoverHeight { | ||
566 | set { _PIDHoverHeight = value; } | ||
567 | } | ||
568 | public override PIDHoverType PIDHoverType { | ||
569 | set { _PIDHoverType = value; } | ||
570 | } | ||
571 | public override float PIDHoverTau { | ||
572 | set { _PIDHoverTao = value; } | ||
573 | } | ||
574 | |||
575 | // For RotLookAt | ||
576 | public override OMV.Quaternion APIDTarget { set { return; } } | ||
577 | public override bool APIDActive { set { return; } } | ||
578 | public override float APIDStrength { set { return; } } | ||
579 | public override float APIDDamping { set { return; } } | ||
580 | |||
581 | public override void AddForce(OMV.Vector3 force, bool pushforce) { | ||
582 | if (force.IsFinite()) | ||
583 | { | ||
584 | _force.X += force.X; | ||
585 | _force.Y += force.Y; | ||
586 | _force.Z += force.Z; | ||
587 | } | ||
588 | else | ||
589 | { | ||
590 | m_log.WarnFormat("{0}: Got a NaN force applied to a Character", LogHeader); | ||
591 | } | ||
592 | _scene.TaintedObject(delegate() | ||
593 | { | ||
594 | BulletSimAPI.SetObjectForce(_scene.WorldID, _localID, _force); | ||
595 | }); | ||
596 | } | ||
597 | |||
598 | public override void AddAngularForce(OMV.Vector3 force, bool pushforce) { | ||
599 | // m_log.DebugFormat("{0}: AddAngularForce. f={1}, push={2}", LogHeader, force, pushforce); | ||
600 | } | ||
601 | public override void SetMomentum(OMV.Vector3 momentum) { | ||
602 | } | ||
603 | public override void SubscribeEvents(int ms) { | ||
604 | _subscribedEventsMs = ms; | ||
605 | _lastCollisionTime = Util.EnvironmentTickCount() - _subscribedEventsMs; // make first collision happen | ||
606 | } | ||
607 | public override void UnSubscribeEvents() { | ||
608 | _subscribedEventsMs = 0; | ||
609 | } | ||
610 | public override bool SubscribedEvents() { | ||
611 | return (_subscribedEventsMs > 0); | ||
612 | } | ||
613 | |||
614 | #region Mass Calculation | ||
615 | |||
616 | private float CalculateMass() | ||
617 | { | ||
618 | float volume = _size.X * _size.Y * _size.Z; // default | ||
619 | float tmp; | ||
620 | |||
621 | float returnMass = 0; | ||
622 | float hollowAmount = (float)_pbs.ProfileHollow * 2.0e-5f; | ||
623 | float hollowVolume = hollowAmount * hollowAmount; | ||
624 | |||
625 | switch (_pbs.ProfileShape) | ||
626 | { | ||
627 | case ProfileShape.Square: | ||
628 | // default box | ||
629 | |||
630 | if (_pbs.PathCurve == (byte)Extrusion.Straight) | ||
631 | { | ||
632 | if (hollowAmount > 0.0) | ||
633 | { | ||
634 | switch (_pbs.HollowShape) | ||
635 | { | ||
636 | case HollowShape.Square: | ||
637 | case HollowShape.Same: | ||
638 | break; | ||
639 | |||
640 | case HollowShape.Circle: | ||
641 | |||
642 | hollowVolume *= 0.78539816339f; | ||
643 | break; | ||
644 | |||
645 | case HollowShape.Triangle: | ||
646 | |||
647 | hollowVolume *= (0.5f * .5f); | ||
648 | break; | ||
649 | |||
650 | default: | ||
651 | hollowVolume = 0; | ||
652 | break; | ||
653 | } | ||
654 | volume *= (1.0f - hollowVolume); | ||
655 | } | ||
656 | } | ||
657 | |||
658 | else if (_pbs.PathCurve == (byte)Extrusion.Curve1) | ||
659 | { | ||
660 | //a tube | ||
661 | |||
662 | volume *= 0.78539816339e-2f * (float)(200 - _pbs.PathScaleX); | ||
663 | tmp= 1.0f -2.0e-2f * (float)(200 - _pbs.PathScaleY); | ||
664 | volume -= volume*tmp*tmp; | ||
665 | |||
666 | if (hollowAmount > 0.0) | ||
667 | { | ||
668 | hollowVolume *= hollowAmount; | ||
669 | |||
670 | switch (_pbs.HollowShape) | ||
671 | { | ||
672 | case HollowShape.Square: | ||
673 | case HollowShape.Same: | ||
674 | break; | ||
675 | |||
676 | case HollowShape.Circle: | ||
677 | hollowVolume *= 0.78539816339f;; | ||
678 | break; | ||
679 | |||
680 | case HollowShape.Triangle: | ||
681 | hollowVolume *= 0.5f * 0.5f; | ||
682 | break; | ||
683 | default: | ||
684 | hollowVolume = 0; | ||
685 | break; | ||
686 | } | ||
687 | volume *= (1.0f - hollowVolume); | ||
688 | } | ||
689 | } | ||
690 | |||
691 | break; | ||
692 | |||
693 | case ProfileShape.Circle: | ||
694 | |||
695 | if (_pbs.PathCurve == (byte)Extrusion.Straight) | ||
696 | { | ||
697 | volume *= 0.78539816339f; // elipse base | ||
698 | |||
699 | if (hollowAmount > 0.0) | ||
700 | { | ||
701 | switch (_pbs.HollowShape) | ||
702 | { | ||
703 | case HollowShape.Same: | ||
704 | case HollowShape.Circle: | ||
705 | break; | ||
706 | |||
707 | case HollowShape.Square: | ||
708 | hollowVolume *= 0.5f * 2.5984480504799f; | ||
709 | break; | ||
710 | |||
711 | case HollowShape.Triangle: | ||
712 | hollowVolume *= .5f * 1.27323954473516f; | ||
713 | break; | ||
714 | |||
715 | default: | ||
716 | hollowVolume = 0; | ||
717 | break; | ||
718 | } | ||
719 | volume *= (1.0f - hollowVolume); | ||
720 | } | ||
721 | } | ||
722 | |||
723 | else if (_pbs.PathCurve == (byte)Extrusion.Curve1) | ||
724 | { | ||
725 | volume *= 0.61685027506808491367715568749226e-2f * (float)(200 - _pbs.PathScaleX); | ||
726 | tmp = 1.0f - .02f * (float)(200 - _pbs.PathScaleY); | ||
727 | volume *= (1.0f - tmp * tmp); | ||
728 | |||
729 | if (hollowAmount > 0.0) | ||
730 | { | ||
731 | |||
732 | // calculate the hollow volume by it's shape compared to the prim shape | ||
733 | hollowVolume *= hollowAmount; | ||
734 | |||
735 | switch (_pbs.HollowShape) | ||
736 | { | ||
737 | case HollowShape.Same: | ||
738 | case HollowShape.Circle: | ||
739 | break; | ||
740 | |||
741 | case HollowShape.Square: | ||
742 | hollowVolume *= 0.5f * 2.5984480504799f; | ||
743 | break; | ||
744 | |||
745 | case HollowShape.Triangle: | ||
746 | hollowVolume *= .5f * 1.27323954473516f; | ||
747 | break; | ||
748 | |||
749 | default: | ||
750 | hollowVolume = 0; | ||
751 | break; | ||
752 | } | ||
753 | volume *= (1.0f - hollowVolume); | ||
754 | } | ||
755 | } | ||
756 | break; | ||
757 | |||
758 | case ProfileShape.HalfCircle: | ||
759 | if (_pbs.PathCurve == (byte)Extrusion.Curve1) | ||
760 | { | ||
761 | volume *= 0.52359877559829887307710723054658f; | ||
762 | } | ||
763 | break; | ||
764 | |||
765 | case ProfileShape.EquilateralTriangle: | ||
766 | |||
767 | if (_pbs.PathCurve == (byte)Extrusion.Straight) | ||
768 | { | ||
769 | volume *= 0.32475953f; | ||
770 | |||
771 | if (hollowAmount > 0.0) | ||
772 | { | ||
773 | |||
774 | // calculate the hollow volume by it's shape compared to the prim shape | ||
775 | switch (_pbs.HollowShape) | ||
776 | { | ||
777 | case HollowShape.Same: | ||
778 | case HollowShape.Triangle: | ||
779 | hollowVolume *= .25f; | ||
780 | break; | ||
781 | |||
782 | case HollowShape.Square: | ||
783 | hollowVolume *= 0.499849f * 3.07920140172638f; | ||
784 | break; | ||
785 | |||
786 | case HollowShape.Circle: | ||
787 | // Hollow shape is a perfect cyllinder in respect to the cube's scale | ||
788 | // Cyllinder hollow volume calculation | ||
789 | |||
790 | hollowVolume *= 0.1963495f * 3.07920140172638f; | ||
791 | break; | ||
792 | |||
793 | default: | ||
794 | hollowVolume = 0; | ||
795 | break; | ||
796 | } | ||
797 | volume *= (1.0f - hollowVolume); | ||
798 | } | ||
799 | } | ||
800 | else if (_pbs.PathCurve == (byte)Extrusion.Curve1) | ||
801 | { | ||
802 | volume *= 0.32475953f; | ||
803 | volume *= 0.01f * (float)(200 - _pbs.PathScaleX); | ||
804 | tmp = 1.0f - .02f * (float)(200 - _pbs.PathScaleY); | ||
805 | volume *= (1.0f - tmp * tmp); | ||
806 | |||
807 | if (hollowAmount > 0.0) | ||
808 | { | ||
809 | |||
810 | hollowVolume *= hollowAmount; | ||
811 | |||
812 | switch (_pbs.HollowShape) | ||
813 | { | ||
814 | case HollowShape.Same: | ||
815 | case HollowShape.Triangle: | ||
816 | hollowVolume *= .25f; | ||
817 | break; | ||
818 | |||
819 | case HollowShape.Square: | ||
820 | hollowVolume *= 0.499849f * 3.07920140172638f; | ||
821 | break; | ||
822 | |||
823 | case HollowShape.Circle: | ||
824 | |||
825 | hollowVolume *= 0.1963495f * 3.07920140172638f; | ||
826 | break; | ||
827 | |||
828 | default: | ||
829 | hollowVolume = 0; | ||
830 | break; | ||
831 | } | ||
832 | volume *= (1.0f - hollowVolume); | ||
833 | } | ||
834 | } | ||
835 | break; | ||
836 | |||
837 | default: | ||
838 | break; | ||
839 | } | ||
840 | |||
841 | |||
842 | |||
843 | float taperX1; | ||
844 | float taperY1; | ||
845 | float taperX; | ||
846 | float taperY; | ||
847 | float pathBegin; | ||
848 | float pathEnd; | ||
849 | float profileBegin; | ||
850 | float profileEnd; | ||
851 | |||
852 | if (_pbs.PathCurve == (byte)Extrusion.Straight || _pbs.PathCurve == (byte)Extrusion.Flexible) | ||
853 | { | ||
854 | taperX1 = _pbs.PathScaleX * 0.01f; | ||
855 | if (taperX1 > 1.0f) | ||
856 | taperX1 = 2.0f - taperX1; | ||
857 | taperX = 1.0f - taperX1; | ||
858 | |||
859 | taperY1 = _pbs.PathScaleY * 0.01f; | ||
860 | if (taperY1 > 1.0f) | ||
861 | taperY1 = 2.0f - taperY1; | ||
862 | taperY = 1.0f - taperY1; | ||
863 | } | ||
864 | else | ||
865 | { | ||
866 | taperX = _pbs.PathTaperX * 0.01f; | ||
867 | if (taperX < 0.0f) | ||
868 | taperX = -taperX; | ||
869 | taperX1 = 1.0f - taperX; | ||
870 | |||
871 | taperY = _pbs.PathTaperY * 0.01f; | ||
872 | if (taperY < 0.0f) | ||
873 | taperY = -taperY; | ||
874 | taperY1 = 1.0f - taperY; | ||
875 | |||
876 | } | ||
877 | |||
878 | |||
879 | volume *= (taperX1 * taperY1 + 0.5f * (taperX1 * taperY + taperX * taperY1) + 0.3333333333f * taperX * taperY); | ||
880 | |||
881 | pathBegin = (float)_pbs.PathBegin * 2.0e-5f; | ||
882 | pathEnd = 1.0f - (float)_pbs.PathEnd * 2.0e-5f; | ||
883 | volume *= (pathEnd - pathBegin); | ||
884 | |||
885 | // this is crude aproximation | ||
886 | profileBegin = (float)_pbs.ProfileBegin * 2.0e-5f; | ||
887 | profileEnd = 1.0f - (float)_pbs.ProfileEnd * 2.0e-5f; | ||
888 | volume *= (profileEnd - profileBegin); | ||
889 | |||
890 | returnMass = _density * volume; | ||
891 | |||
892 | if (IsRootOfLinkset) | ||
893 | { | ||
894 | foreach (BSPrim prim in _childrenPrims) | ||
895 | { | ||
896 | returnMass += prim.CalculateMass(); | ||
897 | } | ||
898 | } | ||
899 | |||
900 | if (returnMass <= 0) | ||
901 | returnMass = 0.0001f; | ||
902 | |||
903 | if (returnMass > _scene.MaximumObjectMass) | ||
904 | returnMass = _scene.MaximumObjectMass; | ||
905 | |||
906 | return returnMass; | ||
907 | }// end CalculateMass | ||
908 | #endregion Mass Calculation | ||
909 | |||
910 | // Create the geometry information in Bullet for later use | ||
911 | // The objects needs a hull if it's physical otherwise a mesh is enough | ||
912 | // No locking here because this is done when we know physics is not simulating | ||
913 | // if 'forceRebuild' is true, the geometry is rebuilt. Otherwise a previously built version is used | ||
914 | private void CreateGeom(bool forceRebuild) | ||
915 | { | ||
916 | // the mesher thought this was too simple to mesh. Use a native Bullet collision shape. | ||
917 | if (!_scene.NeedsMeshing(_pbs)) | ||
918 | { | ||
919 | if (_pbs.ProfileShape == ProfileShape.HalfCircle && _pbs.PathCurve == (byte)Extrusion.Curve1) | ||
920 | { | ||
921 | if (_size.X == _size.Y && _size.Y == _size.Z && _size.X == _size.Z) | ||
922 | { | ||
923 | // m_log.DebugFormat("{0}: CreateGeom: Defaulting to sphere of size {1}", LogHeader, _size); | ||
924 | _shapeType = ShapeData.PhysicsShapeType.SHAPE_SPHERE; | ||
925 | // Bullet native objects are scaled by the Bullet engine so pass the size in | ||
926 | _scale = _size; | ||
927 | } | ||
928 | } | ||
929 | else | ||
930 | { | ||
931 | // m_log.DebugFormat("{0}: CreateGeom: Defaulting to box. lid={1}, size={2}", LogHeader, LocalID, _size); | ||
932 | _shapeType = ShapeData.PhysicsShapeType.SHAPE_BOX; | ||
933 | _scale = _size; | ||
934 | } | ||
935 | } | ||
936 | else | ||
937 | { | ||
938 | if (IsPhysical) | ||
939 | { | ||
940 | if (forceRebuild || _hullKey == 0) | ||
941 | { | ||
942 | // physical objects require a hull for interaction. | ||
943 | // This will create the mesh if it doesn't already exist | ||
944 | CreateGeomHull(); | ||
945 | } | ||
946 | } | ||
947 | else | ||
948 | { | ||
949 | if (forceRebuild || _meshKey == 0) | ||
950 | { | ||
951 | // Static (non-physical) objects only need a mesh for bumping into | ||
952 | CreateGeomMesh(); | ||
953 | } | ||
954 | } | ||
955 | } | ||
956 | } | ||
957 | |||
958 | // No locking here because this is done when we know physics is not simulating | ||
959 | private void CreateGeomMesh() | ||
960 | { | ||
961 | float lod = _pbs.SculptEntry ? _scene.SculptLOD : _scene.MeshLOD; | ||
962 | ulong newMeshKey = (ulong)_pbs.GetMeshKey(_size, lod); | ||
963 | // m_log.DebugFormat("{0}: CreateGeomMesh: lID={1}, oldKey={2}, newKey={3}", LogHeader, _localID, _meshKey, newMeshKey); | ||
964 | |||
965 | // if this new shape is the same as last time, don't recreate the mesh | ||
966 | if (_meshKey == newMeshKey) return; | ||
967 | |||
968 | // Since we're recreating new, get rid of any previously generated shape | ||
969 | if (_meshKey != 0) | ||
970 | { | ||
971 | // m_log.DebugFormat("{0}: CreateGeom: deleting old mesh. lID={1}, Key={2}", LogHeader, _localID, _meshKey); | ||
972 | BulletSimAPI.DestroyMesh(_scene.WorldID, _meshKey); | ||
973 | _mesh = null; | ||
974 | _meshKey = 0; | ||
975 | } | ||
976 | |||
977 | _meshKey = newMeshKey; | ||
978 | // always pass false for physicalness as this creates some sort of bounding box which we don't need | ||
979 | _mesh = _scene.mesher.CreateMesh(_avName, _pbs, _size, lod, false); | ||
980 | |||
981 | int[] indices = _mesh.getIndexListAsInt(); | ||
982 | List<OMV.Vector3> vertices = _mesh.getVertexList(); | ||
983 | |||
984 | float[] verticesAsFloats = new float[vertices.Count * 3]; | ||
985 | int vi = 0; | ||
986 | foreach (OMV.Vector3 vv in vertices) | ||
987 | { | ||
988 | // m_log.DebugFormat("{0}: {1}: <{2:0.00}, {3:0.00}, {4:0.00}>", LogHeader, vi / 3, vv.X, vv.Y, vv.Z); | ||
989 | verticesAsFloats[vi++] = vv.X; | ||
990 | verticesAsFloats[vi++] = vv.Y; | ||
991 | verticesAsFloats[vi++] = vv.Z; | ||
992 | } | ||
993 | |||
994 | // m_log.DebugFormat("{0}: CreateGeomMesh: calling CreateMesh. lid={1}, key={2}, indices={3}, vertices={4}", | ||
995 | // LogHeader, _localID, _meshKey, indices.Length, vertices.Count); | ||
996 | BulletSimAPI.CreateMesh(_scene.WorldID, _meshKey, indices.GetLength(0), indices, | ||
997 | vertices.Count, verticesAsFloats); | ||
998 | |||
999 | _shapeType = ShapeData.PhysicsShapeType.SHAPE_MESH; | ||
1000 | // meshes are already scaled by the meshmerizer | ||
1001 | _scale = new OMV.Vector3(1f, 1f, 1f); | ||
1002 | return; | ||
1003 | } | ||
1004 | |||
1005 | // No locking here because this is done when we know physics is not simulating | ||
1006 | private void CreateGeomHull() | ||
1007 | { | ||
1008 | float lod = _pbs.SculptEntry ? _scene.SculptLOD : _scene.MeshLOD; | ||
1009 | ulong newHullKey = (ulong)_pbs.GetMeshKey(_size, lod); | ||
1010 | // m_log.DebugFormat("{0}: CreateGeomHull: lID={1}, oldKey={2}, newKey={3}", LogHeader, _localID, _hullKey, newHullKey); | ||
1011 | |||
1012 | // if the hull hasn't changed, don't rebuild it | ||
1013 | if (newHullKey == _hullKey) return; | ||
1014 | |||
1015 | // Since we're recreating new, get rid of any previously generated shape | ||
1016 | if (_hullKey != 0) | ||
1017 | { | ||
1018 | // m_log.DebugFormat("{0}: CreateGeom: deleting old hull. Key={1}", LogHeader, _hullKey); | ||
1019 | BulletSimAPI.DestroyHull(_scene.WorldID, _hullKey); | ||
1020 | _hullKey = 0; | ||
1021 | _hulls.Clear(); | ||
1022 | BulletSimAPI.DestroyMesh(_scene.WorldID, _meshKey); | ||
1023 | _mesh = null; // the mesh cannot match either | ||
1024 | _meshKey = 0; | ||
1025 | } | ||
1026 | |||
1027 | _hullKey = newHullKey; | ||
1028 | if (_meshKey != _hullKey) | ||
1029 | { | ||
1030 | // if the underlying mesh has changed, rebuild it | ||
1031 | CreateGeomMesh(); | ||
1032 | } | ||
1033 | |||
1034 | int[] indices = _mesh.getIndexListAsInt(); | ||
1035 | List<OMV.Vector3> vertices = _mesh.getVertexList(); | ||
1036 | |||
1037 | //format conversion from IMesh format to DecompDesc format | ||
1038 | List<int> convIndices = new List<int>(); | ||
1039 | List<float3> convVertices = new List<float3>(); | ||
1040 | for (int ii = 0; ii < indices.GetLength(0); ii++) | ||
1041 | { | ||
1042 | convIndices.Add(indices[ii]); | ||
1043 | } | ||
1044 | foreach (OMV.Vector3 vv in vertices) | ||
1045 | { | ||
1046 | convVertices.Add(new float3(vv.X, vv.Y, vv.Z)); | ||
1047 | } | ||
1048 | |||
1049 | // setup and do convex hull conversion | ||
1050 | _hulls = new List<ConvexResult>(); | ||
1051 | DecompDesc dcomp = new DecompDesc(); | ||
1052 | dcomp.mIndices = convIndices; | ||
1053 | dcomp.mVertices = convVertices; | ||
1054 | ConvexBuilder convexBuilder = new ConvexBuilder(HullReturn); | ||
1055 | // create the hull into the _hulls variable | ||
1056 | convexBuilder.process(dcomp); | ||
1057 | |||
1058 | // Convert the vertices and indices for passing to unmanaged | ||
1059 | // The hull information is passed as a large floating point array. | ||
1060 | // The format is: | ||
1061 | // convHulls[0] = number of hulls | ||
1062 | // convHulls[1] = number of vertices in first hull | ||
1063 | // convHulls[2] = hull centroid X coordinate | ||
1064 | // convHulls[3] = hull centroid Y coordinate | ||
1065 | // convHulls[4] = hull centroid Z coordinate | ||
1066 | // convHulls[5] = first hull vertex X | ||
1067 | // convHulls[6] = first hull vertex Y | ||
1068 | // convHulls[7] = first hull vertex Z | ||
1069 | // convHulls[8] = second hull vertex X | ||
1070 | // ... | ||
1071 | // convHulls[n] = number of vertices in second hull | ||
1072 | // convHulls[n+1] = second hull centroid X coordinate | ||
1073 | // ... | ||
1074 | // | ||
1075 | // TODO: is is very inefficient. Someday change the convex hull generator to return | ||
1076 | // data structures that do not need to be converted in order to pass to Bullet. | ||
1077 | // And maybe put the values directly into pinned memory rather than marshaling. | ||
1078 | int hullCount = _hulls.Count; | ||
1079 | int totalVertices = 1; // include one for the count of the hulls | ||
1080 | foreach (ConvexResult cr in _hulls) | ||
1081 | { | ||
1082 | totalVertices += 4; // add four for the vertex count and centroid | ||
1083 | totalVertices += cr.HullIndices.Count * 3; // we pass just triangles | ||
1084 | } | ||
1085 | float[] convHulls = new float[totalVertices]; | ||
1086 | |||
1087 | convHulls[0] = (float)hullCount; | ||
1088 | int jj = 1; | ||
1089 | foreach (ConvexResult cr in _hulls) | ||
1090 | { | ||
1091 | // copy vertices for index access | ||
1092 | float3[] verts = new float3[cr.HullVertices.Count]; | ||
1093 | int kk = 0; | ||
1094 | foreach (float3 ff in cr.HullVertices) | ||
1095 | { | ||
1096 | verts[kk++] = ff; | ||
1097 | } | ||
1098 | |||
1099 | // add to the array one hull's worth of data | ||
1100 | convHulls[jj++] = cr.HullIndices.Count; | ||
1101 | convHulls[jj++] = 0f; // centroid x,y,z | ||
1102 | convHulls[jj++] = 0f; | ||
1103 | convHulls[jj++] = 0f; | ||
1104 | foreach (int ind in cr.HullIndices) | ||
1105 | { | ||
1106 | convHulls[jj++] = verts[ind].x; | ||
1107 | convHulls[jj++] = verts[ind].y; | ||
1108 | convHulls[jj++] = verts[ind].z; | ||
1109 | } | ||
1110 | } | ||
1111 | |||
1112 | // create the hull definition in Bullet | ||
1113 | // m_log.DebugFormat("{0}: CreateGeom: calling CreateHull. lid={1}, key={2}, hulls={3}", LogHeader, _localID, _hullKey, hullCount); | ||
1114 | BulletSimAPI.CreateHull(_scene.WorldID, _hullKey, hullCount, convHulls); | ||
1115 | _shapeType = ShapeData.PhysicsShapeType.SHAPE_HULL; | ||
1116 | // meshes are already scaled by the meshmerizer | ||
1117 | _scale = new OMV.Vector3(1f, 1f, 1f); | ||
1118 | return; | ||
1119 | } | ||
1120 | |||
1121 | // Callback from convex hull creater with a newly created hull. | ||
1122 | // Just add it to the collection of hulls for this shape. | ||
1123 | private void HullReturn(ConvexResult result) | ||
1124 | { | ||
1125 | _hulls.Add(result); | ||
1126 | return; | ||
1127 | } | ||
1128 | |||
1129 | // Create an object in Bullet | ||
1130 | // No locking here because this is done when the physics engine is not simulating | ||
1131 | private void CreateObject() | ||
1132 | { | ||
1133 | if (IsRootOfLinkset) | ||
1134 | { | ||
1135 | // Create a linkset around this object | ||
1136 | // CreateLinksetWithCompoundHull(); | ||
1137 | CreateLinksetWithConstraints(); | ||
1138 | } | ||
1139 | else | ||
1140 | { | ||
1141 | // simple object | ||
1142 | // the mesh or hull must have already been created in Bullet | ||
1143 | ShapeData shape; | ||
1144 | FillShapeInfo(out shape); | ||
1145 | // m_log.DebugFormat("{0}: CreateObject: lID={1}, shape={2}", LogHeader, _localID, shape.Type); | ||
1146 | BulletSimAPI.CreateObject(_scene.WorldID, shape); | ||
1147 | } | ||
1148 | } | ||
1149 | |||
1150 | // Create a linkset by creating a compound hull at the root prim that consists of all | ||
1151 | // the children. | ||
1152 | // NOTE: This does not allow proper collisions with the children prims so it is not a workable solution | ||
1153 | void CreateLinksetWithCompoundHull() | ||
1154 | { | ||
1155 | // If I am the root prim of a linkset, replace my physical shape with all the | ||
1156 | // pieces of the children. | ||
1157 | // All of the children should have called CreateGeom so they have a hull | ||
1158 | // in the physics engine already. Here we pull together all of those hulls | ||
1159 | // into one shape. | ||
1160 | int totalPrimsInLinkset = _childrenPrims.Count + 1; | ||
1161 | // m_log.DebugFormat("{0}: CreateLinkset. Root prim={1}, prims={2}", LogHeader, LocalID, totalPrimsInLinkset); | ||
1162 | ShapeData[] shapes = new ShapeData[totalPrimsInLinkset]; | ||
1163 | FillShapeInfo(out shapes[0]); | ||
1164 | int ii = 1; | ||
1165 | foreach (BSPrim prim in _childrenPrims) | ||
1166 | { | ||
1167 | // m_log.DebugFormat("{0}: CreateLinkset: adding prim {1}", LogHeader, prim.LocalID); | ||
1168 | prim.FillShapeInfo(out shapes[ii]); | ||
1169 | ii++; | ||
1170 | } | ||
1171 | BulletSimAPI.CreateLinkset(_scene.WorldID, totalPrimsInLinkset, shapes); | ||
1172 | } | ||
1173 | |||
1174 | // Copy prim's info into the BulletSim shape description structure | ||
1175 | public void FillShapeInfo(out ShapeData shape) | ||
1176 | { | ||
1177 | shape.ID = _localID; | ||
1178 | shape.Type = _shapeType; | ||
1179 | shape.Position = _position; | ||
1180 | shape.Rotation = _orientation; | ||
1181 | shape.Velocity = _velocity; | ||
1182 | shape.Scale = _scale; | ||
1183 | shape.Mass = _isPhysical ? _mass : 0f; | ||
1184 | shape.Buoyancy = _buoyancy; | ||
1185 | shape.HullKey = _hullKey; | ||
1186 | shape.MeshKey = _meshKey; | ||
1187 | shape.Friction = _friction; | ||
1188 | shape.Restitution = _restitution; | ||
1189 | shape.Collidable = (!IsPhantom) ? ShapeData.numericTrue : ShapeData.numericFalse; | ||
1190 | shape.Static = _isPhysical ? ShapeData.numericFalse : ShapeData.numericTrue; | ||
1191 | } | ||
1192 | |||
1193 | // Create the linkset by putting constraints between the objects of the set so they cannot move | ||
1194 | // relative to each other. | ||
1195 | // TODO: make this more effeicient: a large linkset gets rebuilt over and over and prims are added | ||
1196 | void CreateLinksetWithConstraints() | ||
1197 | { | ||
1198 | // m_log.DebugFormat("{0}: CreateLinkset. Root prim={1}, prims={2}", LogHeader, LocalID, _childrenPrims.Count+1); | ||
1199 | |||
1200 | // remove any constraints that might be in place | ||
1201 | foreach (BSPrim prim in _childrenPrims) | ||
1202 | { | ||
1203 | // m_log.DebugFormat("{0}: CreateLinkset: RemoveConstraint between root prim {1} and child prim {2}", LogHeader, LocalID, prim.LocalID); | ||
1204 | BulletSimAPI.RemoveConstraint(_scene.WorldID, LocalID, prim.LocalID); | ||
1205 | } | ||
1206 | // create constraints between the root prim and each of the children | ||
1207 | foreach (BSPrim prim in _childrenPrims) | ||
1208 | { | ||
1209 | // m_log.DebugFormat("{0}: CreateLinkset: AddConstraint between root prim {1} and child prim {2}", LogHeader, LocalID, prim.LocalID); | ||
1210 | |||
1211 | // Zero motion for children so they don't interpolate | ||
1212 | prim.ZeroMotion(); | ||
1213 | |||
1214 | // relative position normalized to the root prim | ||
1215 | OMV.Vector3 childRelativePosition = (prim._position - this._position) * OMV.Quaternion.Inverse(this._orientation); | ||
1216 | |||
1217 | // relative rotation of the child to the parent | ||
1218 | OMV.Quaternion relativeRotation = OMV.Quaternion.Inverse(prim._orientation) * this._orientation; | ||
1219 | |||
1220 | // this is a constraint that allows no freedom of movement between the two objects | ||
1221 | // http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=4818 | ||
1222 | BulletSimAPI.AddConstraint(_scene.WorldID, LocalID, prim.LocalID, | ||
1223 | childRelativePosition, | ||
1224 | relativeRotation, | ||
1225 | OMV.Vector3.Zero, | ||
1226 | OMV.Quaternion.Identity, | ||
1227 | OMV.Vector3.Zero, OMV.Vector3.Zero, | ||
1228 | OMV.Vector3.Zero, OMV.Vector3.Zero); | ||
1229 | } | ||
1230 | } | ||
1231 | |||
1232 | // Rebuild the geometry and object. | ||
1233 | // This is called when the shape changes so we need to recreate the mesh/hull. | ||
1234 | // No locking here because this is done when the physics engine is not simulating | ||
1235 | private void RecreateGeomAndObject() | ||
1236 | { | ||
1237 | // m_log.DebugFormat("{0}: RecreateGeomAndObject. lID={1}", LogHeader, _localID); | ||
1238 | CreateGeom(true); | ||
1239 | CreateObject(); | ||
1240 | return; | ||
1241 | } | ||
1242 | |||
1243 | // The physics engine says that properties have updated. Update same and inform | ||
1244 | // the world that things have changed. | ||
1245 | // TODO: do we really need to check for changed? Maybe just copy values and call RequestPhysicsterseUpdate() | ||
1246 | enum UpdatedProperties { | ||
1247 | Position = 1 << 0, | ||
1248 | Rotation = 1 << 1, | ||
1249 | Velocity = 1 << 2, | ||
1250 | Acceleration = 1 << 3, | ||
1251 | RotationalVel = 1 << 4 | ||
1252 | } | ||
1253 | |||
1254 | const float ROTATION_TOLERANCE = 0.01f; | ||
1255 | const float VELOCITY_TOLERANCE = 0.001f; | ||
1256 | const float POSITION_TOLERANCE = 0.05f; | ||
1257 | const float ACCELERATION_TOLERANCE = 0.01f; | ||
1258 | const float ROTATIONAL_VELOCITY_TOLERANCE = 0.01f; | ||
1259 | const bool SHOULD_DAMP_UPDATES = false; | ||
1260 | |||
1261 | public void UpdateProperties(EntityProperties entprop) | ||
1262 | { | ||
1263 | UpdatedProperties changed = 0; | ||
1264 | if (SHOULD_DAMP_UPDATES) | ||
1265 | { | ||
1266 | // assign to the local variables so the normal set action does not happen | ||
1267 | // if (_position != entprop.Position) | ||
1268 | if (!_position.ApproxEquals(entprop.Position, POSITION_TOLERANCE)) | ||
1269 | { | ||
1270 | _position = entprop.Position; | ||
1271 | // m_log.DebugFormat("{0}: UpdateProperties: id={1}, pos = {2}", LogHeader, LocalID, _position); | ||
1272 | changed |= UpdatedProperties.Position; | ||
1273 | } | ||
1274 | // if (_orientation != entprop.Rotation) | ||
1275 | if (!_orientation.ApproxEquals(entprop.Rotation, ROTATION_TOLERANCE)) | ||
1276 | { | ||
1277 | _orientation = entprop.Rotation; | ||
1278 | // m_log.DebugFormat("{0}: UpdateProperties: id={1}, rot = {2}", LogHeader, LocalID, _orientation); | ||
1279 | changed |= UpdatedProperties.Rotation; | ||
1280 | } | ||
1281 | // if (_velocity != entprop.Velocity) | ||
1282 | if (!_velocity.ApproxEquals(entprop.Velocity, VELOCITY_TOLERANCE)) | ||
1283 | { | ||
1284 | _velocity = entprop.Velocity; | ||
1285 | // m_log.DebugFormat("{0}: UpdateProperties: velocity = {1}", LogHeader, _velocity); | ||
1286 | changed |= UpdatedProperties.Velocity; | ||
1287 | } | ||
1288 | // if (_acceleration != entprop.Acceleration) | ||
1289 | if (!_acceleration.ApproxEquals(entprop.Acceleration, ACCELERATION_TOLERANCE)) | ||
1290 | { | ||
1291 | _acceleration = entprop.Acceleration; | ||
1292 | // m_log.DebugFormat("{0}: UpdateProperties: acceleration = {1}", LogHeader, _acceleration); | ||
1293 | changed |= UpdatedProperties.Acceleration; | ||
1294 | } | ||
1295 | // if (_rotationalVelocity != entprop.RotationalVelocity) | ||
1296 | if (!_rotationalVelocity.ApproxEquals(entprop.RotationalVelocity, ROTATIONAL_VELOCITY_TOLERANCE)) | ||
1297 | { | ||
1298 | _rotationalVelocity = entprop.RotationalVelocity; | ||
1299 | // m_log.DebugFormat("{0}: UpdateProperties: rotationalVelocity = {1}", LogHeader, _rotationalVelocity); | ||
1300 | changed |= UpdatedProperties.RotationalVel; | ||
1301 | } | ||
1302 | if (changed != 0) | ||
1303 | { | ||
1304 | // m_log.DebugFormat("{0}: UpdateProperties: id={1}, c={2}, pos={3}, rot={4}", LogHeader, LocalID, changed, _position, _orientation); | ||
1305 | // Only update the position of single objects and linkset roots | ||
1306 | if (this._parentPrim == null) | ||
1307 | { | ||
1308 | // m_log.DebugFormat("{0}: RequestTerseUpdate. id={1}, ch={2}, pos={3}, rot={4}", LogHeader, LocalID, changed, _position, _orientation); | ||
1309 | base.RequestPhysicsterseUpdate(); | ||
1310 | } | ||
1311 | } | ||
1312 | } | ||
1313 | else | ||
1314 | { | ||
1315 | // Don't check for damping here -- it's done in BulletSim and SceneObjectPart. | ||
1316 | |||
1317 | // Only updates only for individual prims and for the root object of a linkset. | ||
1318 | if (this._parentPrim == null) | ||
1319 | { | ||
1320 | // Assign to the local variables so the normal set action does not happen | ||
1321 | _position = entprop.Position; | ||
1322 | _orientation = entprop.Rotation; | ||
1323 | _velocity = entprop.Velocity; | ||
1324 | _acceleration = entprop.Acceleration; | ||
1325 | _rotationalVelocity = entprop.RotationalVelocity; | ||
1326 | // m_log.DebugFormat("{0}: RequestTerseUpdate. id={1}, ch={2}, pos={3}, rot={4}", LogHeader, LocalID, changed, _position, _orientation); | ||
1327 | base.RequestPhysicsterseUpdate(); | ||
1328 | } | ||
1329 | } | ||
1330 | } | ||
1331 | |||
1332 | // I've collided with something | ||
1333 | public void Collide(uint collidingWith, ActorTypes type, OMV.Vector3 contactPoint, OMV.Vector3 contactNormal, float pentrationDepth) | ||
1334 | { | ||
1335 | // m_log.DebugFormat("{0}: Collide: ms={1}, id={2}, with={3}", LogHeader, _subscribedEventsMs, LocalID, collidingWith); | ||
1336 | |||
1337 | // The following lines make IsColliding() and IsCollidingGround() work | ||
1338 | _collidingStep = _scene.SimulationStep; | ||
1339 | if (collidingWith == BSScene.TERRAIN_ID || collidingWith == BSScene.GROUNDPLANE_ID) | ||
1340 | { | ||
1341 | _collidingGroundStep = _scene.SimulationStep; | ||
1342 | } | ||
1343 | |||
1344 | if (_subscribedEventsMs == 0) return; // nothing in the object is waiting for collision events | ||
1345 | // throttle the collisions to the number of milliseconds specified in the subscription | ||
1346 | int nowTime = _scene.SimulationNowTime; | ||
1347 | if (nowTime < (_lastCollisionTime + _subscribedEventsMs)) return; | ||
1348 | _lastCollisionTime = nowTime; | ||
1349 | |||
1350 | // create the event for the collision | ||
1351 | Dictionary<uint, ContactPoint> contactPoints = new Dictionary<uint, ContactPoint>(); | ||
1352 | contactPoints.Add(collidingWith, new ContactPoint(contactPoint, contactNormal, pentrationDepth)); | ||
1353 | CollisionEventUpdate args = new CollisionEventUpdate(LocalID, (int)type, 1, contactPoints); | ||
1354 | base.SendCollisionUpdate(args); | ||
1355 | } | ||
1356 | } | ||
1357 | } | ||
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs new file mode 100644 index 0000000..7704002 --- /dev/null +++ b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs | |||
@@ -0,0 +1,860 @@ | |||
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.Runtime.InteropServices; | ||
30 | using System.Text; | ||
31 | using System.Threading; | ||
32 | using Nini.Config; | ||
33 | using log4net; | ||
34 | using OpenSim.Framework; | ||
35 | using OpenSim.Region.Physics.Manager; | ||
36 | using OpenMetaverse; | ||
37 | using OpenSim.Region.Framework; | ||
38 | |||
39 | // TODOs for BulletSim (for BSScene, BSPrim, BSCharacter and BulletSim) | ||
40 | // Adjust character capsule size when height is adjusted (ScenePresence.SetHeight) | ||
41 | // Test sculpties | ||
42 | // Compute physics FPS reasonably | ||
43 | // Based on material, set density and friction | ||
44 | // More efficient memory usage in passing hull information from BSPrim to BulletSim | ||
45 | // Four states of prim: Physical, regular, phantom and selected. Are we modeling these correctly? | ||
46 | // In SL one can set both physical and phantom (gravity, does not effect others, makes collisions with ground) | ||
47 | // At the moment, physical and phantom causes object to drop through the terrain | ||
48 | // Should prim.link() and prim.delink() membership checking happen at taint time? | ||
49 | // Mesh sharing. Use meshHash to tell if we already have a hull of that shape and only create once | ||
50 | // Do attachments need to be handled separately? Need collision events. Do not collide with VolumeDetect | ||
51 | // Implement the genCollisions feature in BulletSim::SetObjectProperties (don't pass up unneeded collisions) | ||
52 | // Implement LockAngularMotion | ||
53 | // Decide if clearing forces is the right thing to do when setting position (BulletSim::SetObjectTranslation) | ||
54 | // Does NeedsMeshing() really need to exclude all the different shapes? | ||
55 | // | ||
56 | namespace OpenSim.Region.Physics.BulletSPlugin | ||
57 | { | ||
58 | public class BSScene : PhysicsScene, IPhysicsParameters | ||
59 | { | ||
60 | private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); | ||
61 | private static readonly string LogHeader = "[BULLETS SCENE]"; | ||
62 | |||
63 | public string BulletSimVersion = "?"; | ||
64 | |||
65 | private Dictionary<uint, BSCharacter> m_avatars = new Dictionary<uint, BSCharacter>(); | ||
66 | private Dictionary<uint, BSPrim> m_prims = new Dictionary<uint, BSPrim>(); | ||
67 | private List<BSPrim> m_vehicles = new List<BSPrim>(); | ||
68 | private float[] m_heightMap; | ||
69 | private float m_waterLevel; | ||
70 | private uint m_worldID; | ||
71 | public uint WorldID { get { return m_worldID; } } | ||
72 | |||
73 | private bool m_initialized = false; | ||
74 | |||
75 | public IMesher mesher; | ||
76 | private float m_meshLOD; | ||
77 | public float MeshLOD | ||
78 | { | ||
79 | get { return m_meshLOD; } | ||
80 | } | ||
81 | private float m_sculptLOD; | ||
82 | public float SculptLOD | ||
83 | { | ||
84 | get { return m_sculptLOD; } | ||
85 | } | ||
86 | |||
87 | private int m_maxSubSteps; | ||
88 | private float m_fixedTimeStep; | ||
89 | private long m_simulationStep = 0; | ||
90 | public long SimulationStep { get { return m_simulationStep; } } | ||
91 | |||
92 | // A value of the time now so all the collision and update routines do not have to get their own | ||
93 | // Set to 'now' just before all the prims and actors are called for collisions and updates | ||
94 | private int m_simulationNowTime; | ||
95 | public int SimulationNowTime { get { return m_simulationNowTime; } } | ||
96 | |||
97 | private int m_maxCollisionsPerFrame; | ||
98 | private CollisionDesc[] m_collisionArray; | ||
99 | private GCHandle m_collisionArrayPinnedHandle; | ||
100 | |||
101 | private int m_maxUpdatesPerFrame; | ||
102 | private EntityProperties[] m_updateArray; | ||
103 | private GCHandle m_updateArrayPinnedHandle; | ||
104 | |||
105 | private bool _meshSculptedPrim = true; // cause scuplted prims to get meshed | ||
106 | private bool _forceSimplePrimMeshing = false; // if a cube or sphere, let Bullet do internal shapes | ||
107 | |||
108 | public const uint TERRAIN_ID = 0; // OpenSim senses terrain with a localID of zero | ||
109 | public const uint GROUNDPLANE_ID = 1; | ||
110 | |||
111 | public ConfigurationParameters Params | ||
112 | { | ||
113 | get { return m_params[0]; } | ||
114 | } | ||
115 | public Vector3 DefaultGravity | ||
116 | { | ||
117 | get { return new Vector3(0f, 0f, Params.gravity); } | ||
118 | } | ||
119 | |||
120 | private float m_maximumObjectMass; | ||
121 | public float MaximumObjectMass | ||
122 | { | ||
123 | get { return m_maximumObjectMass; } | ||
124 | } | ||
125 | |||
126 | public delegate void TaintCallback(); | ||
127 | private List<TaintCallback> _taintedObjects; | ||
128 | private Object _taintLock = new Object(); | ||
129 | |||
130 | // A pointer to an instance if this structure is passed to the C++ code | ||
131 | ConfigurationParameters[] m_params; | ||
132 | GCHandle m_paramsHandle; | ||
133 | |||
134 | private BulletSimAPI.DebugLogCallback m_DebugLogCallbackHandle; | ||
135 | |||
136 | public BSScene(string identifier) | ||
137 | { | ||
138 | m_initialized = false; | ||
139 | } | ||
140 | |||
141 | public override void Initialise(IMesher meshmerizer, IConfigSource config) | ||
142 | { | ||
143 | // Allocate pinned memory to pass parameters. | ||
144 | m_params = new ConfigurationParameters[1]; | ||
145 | m_paramsHandle = GCHandle.Alloc(m_params, GCHandleType.Pinned); | ||
146 | |||
147 | // Set default values for physics parameters plus any overrides from the ini file | ||
148 | GetInitialParameterValues(config); | ||
149 | |||
150 | // allocate more pinned memory close to the above in an attempt to get the memory all together | ||
151 | m_collisionArray = new CollisionDesc[m_maxCollisionsPerFrame]; | ||
152 | m_collisionArrayPinnedHandle = GCHandle.Alloc(m_collisionArray, GCHandleType.Pinned); | ||
153 | m_updateArray = new EntityProperties[m_maxUpdatesPerFrame]; | ||
154 | m_updateArrayPinnedHandle = GCHandle.Alloc(m_updateArray, GCHandleType.Pinned); | ||
155 | |||
156 | // Get the version of the DLL | ||
157 | // TODO: this doesn't work yet. Something wrong with marshaling the returned string. | ||
158 | // BulletSimVersion = BulletSimAPI.GetVersion(); | ||
159 | // m_log.WarnFormat("{0}: BulletSim.dll version='{1}'", LogHeader, BulletSimVersion); | ||
160 | |||
161 | // if Debug, enable logging from the unmanaged code | ||
162 | if (m_log.IsDebugEnabled) | ||
163 | { | ||
164 | m_log.DebugFormat("{0}: Initialize: Setting debug callback for unmanaged code", LogHeader); | ||
165 | m_DebugLogCallbackHandle = new BulletSimAPI.DebugLogCallback(BulletLogger); | ||
166 | BulletSimAPI.SetDebugLogCallback(m_DebugLogCallbackHandle); | ||
167 | } | ||
168 | |||
169 | _taintedObjects = new List<TaintCallback>(); | ||
170 | |||
171 | mesher = meshmerizer; | ||
172 | // The bounding box for the simulated world | ||
173 | Vector3 worldExtent = new Vector3(Constants.RegionSize, Constants.RegionSize, 4096f); | ||
174 | |||
175 | // m_log.DebugFormat("{0}: Initialize: Calling BulletSimAPI.Initialize.", LogHeader); | ||
176 | m_worldID = BulletSimAPI.Initialize(worldExtent, m_paramsHandle.AddrOfPinnedObject(), | ||
177 | m_maxCollisionsPerFrame, m_collisionArrayPinnedHandle.AddrOfPinnedObject(), | ||
178 | m_maxUpdatesPerFrame, m_updateArrayPinnedHandle.AddrOfPinnedObject()); | ||
179 | |||
180 | m_initialized = true; | ||
181 | } | ||
182 | |||
183 | // All default parameter values are set here. There should be no values set in the | ||
184 | // variable definitions. | ||
185 | private void GetInitialParameterValues(IConfigSource config) | ||
186 | { | ||
187 | ConfigurationParameters parms = new ConfigurationParameters(); | ||
188 | |||
189 | _meshSculptedPrim = true; // mesh sculpted prims | ||
190 | _forceSimplePrimMeshing = false; // use complex meshing if called for | ||
191 | |||
192 | m_meshLOD = 8f; | ||
193 | m_sculptLOD = 32f; | ||
194 | |||
195 | m_maxSubSteps = 10; | ||
196 | m_fixedTimeStep = 1f / 60f; | ||
197 | m_maxCollisionsPerFrame = 2048; | ||
198 | m_maxUpdatesPerFrame = 2048; | ||
199 | m_maximumObjectMass = 10000.01f; | ||
200 | |||
201 | parms.defaultFriction = 0.5f; | ||
202 | parms.defaultDensity = 10.000006836f; // Aluminum g/cm3 | ||
203 | parms.defaultRestitution = 0f; | ||
204 | parms.collisionMargin = 0.0f; | ||
205 | parms.gravity = -9.80665f; | ||
206 | |||
207 | parms.linearDamping = 0.0f; | ||
208 | parms.angularDamping = 0.0f; | ||
209 | parms.deactivationTime = 0.2f; | ||
210 | parms.linearSleepingThreshold = 0.8f; | ||
211 | parms.angularSleepingThreshold = 1.0f; | ||
212 | parms.ccdMotionThreshold = 0.5f; // set to zero to disable | ||
213 | parms.ccdSweptSphereRadius = 0.2f; | ||
214 | |||
215 | parms.terrainFriction = 0.5f; | ||
216 | parms.terrainHitFraction = 0.8f; | ||
217 | parms.terrainRestitution = 0f; | ||
218 | parms.avatarFriction = 0.0f; | ||
219 | parms.avatarDensity = 60f; | ||
220 | parms.avatarCapsuleRadius = 0.37f; | ||
221 | parms.avatarCapsuleHeight = 1.5f; // 2.140599f | ||
222 | |||
223 | if (config != null) | ||
224 | { | ||
225 | // If there are specifications in the ini file, use those values | ||
226 | // WHEN ADDING OR UPDATING THIS SECTION, BE SURE TO UPDATE OpenSimDefaults.ini | ||
227 | // ALSO REMEMBER TO UPDATE THE RUNTIME SETTING OF THE PARAMETERS. | ||
228 | IConfig pConfig = config.Configs["BulletSim"]; | ||
229 | if (pConfig != null) | ||
230 | { | ||
231 | _meshSculptedPrim = pConfig.GetBoolean("MeshSculptedPrim", _meshSculptedPrim); | ||
232 | _forceSimplePrimMeshing = pConfig.GetBoolean("ForceSimplePrimMeshing", _forceSimplePrimMeshing); | ||
233 | |||
234 | m_meshLOD = pConfig.GetFloat("MeshLevelOfDetail", m_meshLOD); | ||
235 | m_sculptLOD = pConfig.GetFloat("SculptLevelOfDetail", m_sculptLOD); | ||
236 | |||
237 | m_maxSubSteps = pConfig.GetInt("MaxSubSteps", m_maxSubSteps); | ||
238 | m_fixedTimeStep = pConfig.GetFloat("FixedTimeStep", m_fixedTimeStep); | ||
239 | m_maxCollisionsPerFrame = pConfig.GetInt("MaxCollisionsPerFrame", m_maxCollisionsPerFrame); | ||
240 | m_maxUpdatesPerFrame = pConfig.GetInt("MaxUpdatesPerFrame", m_maxUpdatesPerFrame); | ||
241 | m_maximumObjectMass = pConfig.GetFloat("MaxObjectMass", m_maximumObjectMass); | ||
242 | |||
243 | parms.defaultFriction = pConfig.GetFloat("DefaultFriction", parms.defaultFriction); | ||
244 | parms.defaultDensity = pConfig.GetFloat("DefaultDensity", parms.defaultDensity); | ||
245 | parms.defaultRestitution = pConfig.GetFloat("DefaultRestitution", parms.defaultRestitution); | ||
246 | parms.collisionMargin = pConfig.GetFloat("CollisionMargin", parms.collisionMargin); | ||
247 | parms.gravity = pConfig.GetFloat("Gravity", parms.gravity); | ||
248 | |||
249 | parms.linearDamping = pConfig.GetFloat("LinearDamping", parms.linearDamping); | ||
250 | parms.angularDamping = pConfig.GetFloat("AngularDamping", parms.angularDamping); | ||
251 | parms.deactivationTime = pConfig.GetFloat("DeactivationTime", parms.deactivationTime); | ||
252 | parms.linearSleepingThreshold = pConfig.GetFloat("LinearSleepingThreshold", parms.linearSleepingThreshold); | ||
253 | parms.angularSleepingThreshold = pConfig.GetFloat("AngularSleepingThreshold", parms.angularSleepingThreshold); | ||
254 | parms.ccdMotionThreshold = pConfig.GetFloat("CcdMotionThreshold", parms.ccdMotionThreshold); | ||
255 | parms.ccdSweptSphereRadius = pConfig.GetFloat("CcdSweptSphereRadius", parms.ccdSweptSphereRadius); | ||
256 | |||
257 | parms.terrainFriction = pConfig.GetFloat("TerrainFriction", parms.terrainFriction); | ||
258 | parms.terrainHitFraction = pConfig.GetFloat("TerrainHitFraction", parms.terrainHitFraction); | ||
259 | parms.terrainRestitution = pConfig.GetFloat("TerrainRestitution", parms.terrainRestitution); | ||
260 | parms.avatarFriction = pConfig.GetFloat("AvatarFriction", parms.avatarFriction); | ||
261 | parms.avatarDensity = pConfig.GetFloat("AvatarDensity", parms.avatarDensity); | ||
262 | parms.avatarCapsuleRadius = pConfig.GetFloat("AvatarCapsuleRadius", parms.avatarCapsuleRadius); | ||
263 | parms.avatarCapsuleHeight = pConfig.GetFloat("AvatarCapsuleHeight", parms.avatarCapsuleHeight); | ||
264 | } | ||
265 | } | ||
266 | m_params[0] = parms; | ||
267 | } | ||
268 | |||
269 | // Called directly from unmanaged code so don't do much | ||
270 | private void BulletLogger(string msg) | ||
271 | { | ||
272 | m_log.Debug("[BULLETS UNMANAGED]:" + msg); | ||
273 | } | ||
274 | |||
275 | public override PhysicsActor AddAvatar(string avName, Vector3 position, Vector3 size, bool isFlying) | ||
276 | { | ||
277 | m_log.ErrorFormat("{0}: CALL TO AddAvatar in BSScene. NOT IMPLEMENTED", LogHeader); | ||
278 | return null; | ||
279 | } | ||
280 | |||
281 | public override PhysicsActor AddAvatar(uint localID, string avName, Vector3 position, Vector3 size, bool isFlying) | ||
282 | { | ||
283 | // m_log.DebugFormat("{0}: AddAvatar: {1}", LogHeader, avName); | ||
284 | BSCharacter actor = new BSCharacter(localID, avName, this, position, size, isFlying); | ||
285 | lock (m_avatars) m_avatars.Add(localID, actor); | ||
286 | return actor; | ||
287 | } | ||
288 | |||
289 | public override void RemoveAvatar(PhysicsActor actor) | ||
290 | { | ||
291 | // m_log.DebugFormat("{0}: RemoveAvatar", LogHeader); | ||
292 | if (actor is BSCharacter) | ||
293 | { | ||
294 | ((BSCharacter)actor).Destroy(); | ||
295 | } | ||
296 | try | ||
297 | { | ||
298 | lock (m_avatars) m_avatars.Remove(actor.LocalID); | ||
299 | } | ||
300 | catch (Exception e) | ||
301 | { | ||
302 | m_log.WarnFormat("{0}: Attempt to remove avatar that is not in physics scene: {1}", LogHeader, e); | ||
303 | } | ||
304 | } | ||
305 | |||
306 | public override void RemovePrim(PhysicsActor prim) | ||
307 | { | ||
308 | // m_log.DebugFormat("{0}: RemovePrim", LogHeader); | ||
309 | if (prim is BSPrim) | ||
310 | { | ||
311 | ((BSPrim)prim).Destroy(); | ||
312 | } | ||
313 | try | ||
314 | { | ||
315 | lock (m_prims) m_prims.Remove(prim.LocalID); | ||
316 | } | ||
317 | catch (Exception e) | ||
318 | { | ||
319 | m_log.WarnFormat("{0}: Attempt to remove prim that is not in physics scene: {1}", LogHeader, e); | ||
320 | } | ||
321 | } | ||
322 | |||
323 | public override PhysicsActor AddPrimShape(string primName, PrimitiveBaseShape pbs, Vector3 position, | ||
324 | Vector3 size, Quaternion rotation, bool isPhysical, uint localID) | ||
325 | { | ||
326 | // m_log.DebugFormat("{0}: AddPrimShape2: {1}", LogHeader, primName); | ||
327 | BSPrim prim = new BSPrim(localID, primName, this, position, size, rotation, pbs, isPhysical); | ||
328 | lock (m_prims) m_prims.Add(localID, prim); | ||
329 | return prim; | ||
330 | } | ||
331 | |||
332 | // This is a call from the simulator saying that some physical property has been updated. | ||
333 | // The BulletSim driver senses the changing of relevant properties so this taint | ||
334 | // information call is not needed. | ||
335 | public override void AddPhysicsActorTaint(PhysicsActor prim) { } | ||
336 | |||
337 | // Simulate one timestep | ||
338 | public override float Simulate(float timeStep) | ||
339 | { | ||
340 | int updatedEntityCount; | ||
341 | IntPtr updatedEntitiesPtr; | ||
342 | int collidersCount; | ||
343 | IntPtr collidersPtr; | ||
344 | |||
345 | // prevent simulation until we've been initialized | ||
346 | if (!m_initialized) return 10.0f; | ||
347 | |||
348 | // update the prim states while we know the physics engine is not busy | ||
349 | ProcessTaints(); | ||
350 | |||
351 | // Some of the prims operate with special vehicle properties | ||
352 | ProcessVehicles(timeStep); | ||
353 | ProcessTaints(); // the vehicles might have added taints | ||
354 | |||
355 | // step the physical world one interval | ||
356 | m_simulationStep++; | ||
357 | int numSubSteps = BulletSimAPI.PhysicsStep(m_worldID, timeStep, m_maxSubSteps, m_fixedTimeStep, | ||
358 | out updatedEntityCount, out updatedEntitiesPtr, out collidersCount, out collidersPtr); | ||
359 | |||
360 | // Don't have to use the pointers passed back since we know it is the same pinned memory we passed in | ||
361 | |||
362 | // Get a value for 'now' so all the collision and update routines don't have to get their own | ||
363 | m_simulationNowTime = Util.EnvironmentTickCount(); | ||
364 | |||
365 | // If there were collisions, process them by sending the event to the prim. | ||
366 | // Collisions must be processed before updates. | ||
367 | if (collidersCount > 0) | ||
368 | { | ||
369 | for (int ii = 0; ii < collidersCount; ii++) | ||
370 | { | ||
371 | uint cA = m_collisionArray[ii].aID; | ||
372 | uint cB = m_collisionArray[ii].bID; | ||
373 | Vector3 point = m_collisionArray[ii].point; | ||
374 | Vector3 normal = m_collisionArray[ii].normal; | ||
375 | SendCollision(cA, cB, point, normal, 0.01f); | ||
376 | SendCollision(cB, cA, point, -normal, 0.01f); | ||
377 | } | ||
378 | } | ||
379 | |||
380 | // If any of the objects had updated properties, tell the object it has been changed by the physics engine | ||
381 | if (updatedEntityCount > 0) | ||
382 | { | ||
383 | for (int ii = 0; ii < updatedEntityCount; ii++) | ||
384 | { | ||
385 | EntityProperties entprop = m_updateArray[ii]; | ||
386 | // m_log.DebugFormat("{0}: entprop[{1}]: id={2}, pos={3}", LogHeader, ii, entprop.ID, entprop.Position); | ||
387 | BSCharacter actor; | ||
388 | if (m_avatars.TryGetValue(entprop.ID, out actor)) | ||
389 | { | ||
390 | actor.UpdateProperties(entprop); | ||
391 | continue; | ||
392 | } | ||
393 | BSPrim prim; | ||
394 | if (m_prims.TryGetValue(entprop.ID, out prim)) | ||
395 | { | ||
396 | prim.UpdateProperties(entprop); | ||
397 | } | ||
398 | } | ||
399 | } | ||
400 | |||
401 | // TODO: FIX THIS: fps calculation wrong. This calculation always returns about 1 in normal operation. | ||
402 | return timeStep / (numSubSteps * m_fixedTimeStep) * 1000f; | ||
403 | } | ||
404 | |||
405 | // Something has collided | ||
406 | private void SendCollision(uint localID, uint collidingWith, Vector3 collidePoint, Vector3 collideNormal, float penitration) | ||
407 | { | ||
408 | if (localID == TERRAIN_ID || localID == GROUNDPLANE_ID) | ||
409 | { | ||
410 | return; // don't send collisions to the terrain | ||
411 | } | ||
412 | |||
413 | ActorTypes type = ActorTypes.Prim; | ||
414 | if (collidingWith == TERRAIN_ID || collidingWith == GROUNDPLANE_ID) | ||
415 | type = ActorTypes.Ground; | ||
416 | else if (m_avatars.ContainsKey(collidingWith)) | ||
417 | type = ActorTypes.Agent; | ||
418 | |||
419 | BSPrim prim; | ||
420 | if (m_prims.TryGetValue(localID, out prim)) { | ||
421 | prim.Collide(collidingWith, type, collidePoint, collideNormal, penitration); | ||
422 | return; | ||
423 | } | ||
424 | BSCharacter actor; | ||
425 | if (m_avatars.TryGetValue(localID, out actor)) { | ||
426 | actor.Collide(collidingWith, type, collidePoint, collideNormal, penitration); | ||
427 | return; | ||
428 | } | ||
429 | return; | ||
430 | } | ||
431 | |||
432 | public override void GetResults() { } | ||
433 | |||
434 | public override void SetTerrain(float[] heightMap) { | ||
435 | m_heightMap = heightMap; | ||
436 | this.TaintedObject(delegate() | ||
437 | { | ||
438 | BulletSimAPI.SetHeightmap(m_worldID, m_heightMap); | ||
439 | }); | ||
440 | } | ||
441 | |||
442 | public float GetTerrainHeightAtXY(float tX, float tY) | ||
443 | { | ||
444 | return m_heightMap[((int)tX) * Constants.RegionSize + ((int)tY)]; | ||
445 | } | ||
446 | |||
447 | public override void SetWaterLevel(float baseheight) | ||
448 | { | ||
449 | m_waterLevel = baseheight; | ||
450 | } | ||
451 | public float GetWaterLevel() | ||
452 | { | ||
453 | return m_waterLevel; | ||
454 | } | ||
455 | |||
456 | public override void DeleteTerrain() | ||
457 | { | ||
458 | m_log.DebugFormat("{0}: DeleteTerrain()", LogHeader); | ||
459 | } | ||
460 | |||
461 | public override void Dispose() | ||
462 | { | ||
463 | m_log.DebugFormat("{0}: Dispose()", LogHeader); | ||
464 | } | ||
465 | |||
466 | public override Dictionary<uint, float> GetTopColliders() | ||
467 | { | ||
468 | return new Dictionary<uint, float>(); | ||
469 | } | ||
470 | |||
471 | public override bool IsThreaded { get { return false; } } | ||
472 | |||
473 | /// <summary> | ||
474 | /// Routine to figure out if we need to mesh this prim with our mesher | ||
475 | /// </summary> | ||
476 | /// <param name="pbs"></param> | ||
477 | /// <returns>true if the prim needs meshing</returns> | ||
478 | public bool NeedsMeshing(PrimitiveBaseShape pbs) | ||
479 | { | ||
480 | // most of this is redundant now as the mesher will return null if it cant mesh a prim | ||
481 | // but we still need to check for sculptie meshing being enabled so this is the most | ||
482 | // convenient place to do it for now... | ||
483 | |||
484 | // int iPropertiesNotSupportedDefault = 0; | ||
485 | |||
486 | if (pbs.SculptEntry && !_meshSculptedPrim) | ||
487 | { | ||
488 | // Render sculpties as boxes | ||
489 | return false; | ||
490 | } | ||
491 | |||
492 | // if it's a standard box or sphere with no cuts, hollows, twist or top shear, return false since Bullet | ||
493 | // can use an internal representation for the prim | ||
494 | if (!_forceSimplePrimMeshing) | ||
495 | { | ||
496 | if ((pbs.ProfileShape == ProfileShape.Square && pbs.PathCurve == (byte)Extrusion.Straight) | ||
497 | || (pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte)Extrusion.Curve1 | ||
498 | && pbs.Scale.X == pbs.Scale.Y && pbs.Scale.Y == pbs.Scale.Z)) | ||
499 | { | ||
500 | |||
501 | if (pbs.ProfileBegin == 0 && pbs.ProfileEnd == 0 | ||
502 | && pbs.ProfileHollow == 0 | ||
503 | && pbs.PathTwist == 0 && pbs.PathTwistBegin == 0 | ||
504 | && pbs.PathBegin == 0 && pbs.PathEnd == 0 | ||
505 | && pbs.PathTaperX == 0 && pbs.PathTaperY == 0 | ||
506 | && pbs.PathScaleX == 100 && pbs.PathScaleY == 100 | ||
507 | && pbs.PathShearX == 0 && pbs.PathShearY == 0) | ||
508 | { | ||
509 | return false; | ||
510 | } | ||
511 | } | ||
512 | } | ||
513 | |||
514 | /* TODO: verify that the mesher will now do all these shapes | ||
515 | if (pbs.ProfileHollow != 0) | ||
516 | iPropertiesNotSupportedDefault++; | ||
517 | |||
518 | if ((pbs.PathBegin != 0) || pbs.PathEnd != 0) | ||
519 | iPropertiesNotSupportedDefault++; | ||
520 | |||
521 | if ((pbs.PathTwistBegin != 0) || (pbs.PathTwist != 0)) | ||
522 | iPropertiesNotSupportedDefault++; | ||
523 | |||
524 | if ((pbs.ProfileBegin != 0) || pbs.ProfileEnd != 0) | ||
525 | iPropertiesNotSupportedDefault++; | ||
526 | |||
527 | if ((pbs.PathScaleX != 100) || (pbs.PathScaleY != 100)) | ||
528 | iPropertiesNotSupportedDefault++; | ||
529 | |||
530 | if ((pbs.PathShearX != 0) || (pbs.PathShearY != 0)) | ||
531 | iPropertiesNotSupportedDefault++; | ||
532 | |||
533 | if (pbs.ProfileShape == ProfileShape.Circle && pbs.PathCurve == (byte)Extrusion.Straight) | ||
534 | iPropertiesNotSupportedDefault++; | ||
535 | |||
536 | if (pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte)Extrusion.Curve1 && (pbs.Scale.X != pbs.Scale.Y || pbs.Scale.Y != pbs.Scale.Z || pbs.Scale.Z != pbs.Scale.X)) | ||
537 | iPropertiesNotSupportedDefault++; | ||
538 | |||
539 | if (pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte) Extrusion.Curve1) | ||
540 | iPropertiesNotSupportedDefault++; | ||
541 | |||
542 | // test for torus | ||
543 | if ((pbs.ProfileCurve & 0x07) == (byte)ProfileShape.Square) | ||
544 | { | ||
545 | if (pbs.PathCurve == (byte)Extrusion.Curve1) | ||
546 | { | ||
547 | iPropertiesNotSupportedDefault++; | ||
548 | } | ||
549 | } | ||
550 | else if ((pbs.ProfileCurve & 0x07) == (byte)ProfileShape.Circle) | ||
551 | { | ||
552 | if (pbs.PathCurve == (byte)Extrusion.Straight) | ||
553 | { | ||
554 | iPropertiesNotSupportedDefault++; | ||
555 | } | ||
556 | // ProfileCurve seems to combine hole shape and profile curve so we need to only compare against the lower 3 bits | ||
557 | else if (pbs.PathCurve == (byte)Extrusion.Curve1) | ||
558 | { | ||
559 | iPropertiesNotSupportedDefault++; | ||
560 | } | ||
561 | } | ||
562 | else if ((pbs.ProfileCurve & 0x07) == (byte)ProfileShape.HalfCircle) | ||
563 | { | ||
564 | if (pbs.PathCurve == (byte)Extrusion.Curve1 || pbs.PathCurve == (byte)Extrusion.Curve2) | ||
565 | { | ||
566 | iPropertiesNotSupportedDefault++; | ||
567 | } | ||
568 | } | ||
569 | else if ((pbs.ProfileCurve & 0x07) == (byte)ProfileShape.EquilateralTriangle) | ||
570 | { | ||
571 | if (pbs.PathCurve == (byte)Extrusion.Straight) | ||
572 | { | ||
573 | iPropertiesNotSupportedDefault++; | ||
574 | } | ||
575 | else if (pbs.PathCurve == (byte)Extrusion.Curve1) | ||
576 | { | ||
577 | iPropertiesNotSupportedDefault++; | ||
578 | } | ||
579 | } | ||
580 | if (iPropertiesNotSupportedDefault == 0) | ||
581 | { | ||
582 | return false; | ||
583 | } | ||
584 | */ | ||
585 | return true; | ||
586 | } | ||
587 | |||
588 | // The calls to the PhysicsActors can't directly call into the physics engine | ||
589 | // because it might be busy. We we delay changes to a known time. | ||
590 | // We rely on C#'s closure to save and restore the context for the delegate. | ||
591 | public void TaintedObject(TaintCallback callback) | ||
592 | { | ||
593 | lock (_taintLock) | ||
594 | _taintedObjects.Add(callback); | ||
595 | return; | ||
596 | } | ||
597 | |||
598 | // When someone tries to change a property on a BSPrim or BSCharacter, the object queues | ||
599 | // a callback into itself to do the actual property change. That callback is called | ||
600 | // here just before the physics engine is called to step the simulation. | ||
601 | public void ProcessTaints() | ||
602 | { | ||
603 | if (_taintedObjects.Count > 0) // save allocating new list if there is nothing to process | ||
604 | { | ||
605 | // swizzle a new list into the list location so we can process what's there | ||
606 | List<TaintCallback> oldList; | ||
607 | lock (_taintLock) | ||
608 | { | ||
609 | oldList = _taintedObjects; | ||
610 | _taintedObjects = new List<TaintCallback>(); | ||
611 | } | ||
612 | |||
613 | foreach (TaintCallback callback in oldList) | ||
614 | { | ||
615 | try | ||
616 | { | ||
617 | callback(); | ||
618 | } | ||
619 | catch (Exception e) | ||
620 | { | ||
621 | m_log.ErrorFormat("{0}: ProcessTaints: Exception: {1}", LogHeader, e); | ||
622 | } | ||
623 | } | ||
624 | oldList.Clear(); | ||
625 | } | ||
626 | } | ||
627 | |||
628 | #region Vehicles | ||
629 | // Make so the scene will call this prim for vehicle actions each tick. | ||
630 | // Safe to call if prim is already in the vehicle list. | ||
631 | public void AddVehiclePrim(BSPrim vehicle) | ||
632 | { | ||
633 | lock (m_vehicles) | ||
634 | { | ||
635 | if (!m_vehicles.Contains(vehicle)) | ||
636 | { | ||
637 | m_vehicles.Add(vehicle); | ||
638 | } | ||
639 | } | ||
640 | } | ||
641 | |||
642 | // Remove a prim from our list of vehicles. | ||
643 | // Safe to call if the prim is not in the vehicle list. | ||
644 | public void RemoveVehiclePrim(BSPrim vehicle) | ||
645 | { | ||
646 | lock (m_vehicles) | ||
647 | { | ||
648 | if (m_vehicles.Contains(vehicle)) | ||
649 | { | ||
650 | m_vehicles.Remove(vehicle); | ||
651 | } | ||
652 | } | ||
653 | } | ||
654 | |||
655 | // Some prims have extra vehicle actions | ||
656 | // no locking because only called when physics engine is not busy | ||
657 | private void ProcessVehicles(float timeStep) | ||
658 | { | ||
659 | foreach (BSPrim prim in m_vehicles) | ||
660 | { | ||
661 | prim.StepVehicle(timeStep); | ||
662 | } | ||
663 | } | ||
664 | #endregion Vehicles | ||
665 | |||
666 | #region Runtime settable parameters | ||
667 | public static PhysParameterEntry[] SettableParameters = new PhysParameterEntry[] | ||
668 | { | ||
669 | new PhysParameterEntry("MeshLOD", "Level of detail to render meshes (32, 16, 8 or 4. 32=most detailed)"), | ||
670 | new PhysParameterEntry("SculptLOD", "Level of detail to render sculpties (32, 16, 8 or 4. 32=most detailed)"), | ||
671 | new PhysParameterEntry("MaxSubStep", "In simulation step, maximum number of substeps"), | ||
672 | new PhysParameterEntry("FixedTimeStep", "In simulation step, seconds of one substep (1/60)"), | ||
673 | new PhysParameterEntry("MaxObjectMass", "Maximum object mass (10000.01)"), | ||
674 | |||
675 | new PhysParameterEntry("DefaultFriction", "Friction factor used on new objects"), | ||
676 | new PhysParameterEntry("DefaultDensity", "Density for new objects" ), | ||
677 | new PhysParameterEntry("DefaultRestitution", "Bouncyness of an object" ), | ||
678 | // new PhysParameterEntry("CollisionMargin", "Margin around objects before collisions are calculated (must be zero!!)" ), | ||
679 | new PhysParameterEntry("Gravity", "Vertical force of gravity (negative means down)" ), | ||
680 | |||
681 | new PhysParameterEntry("LinearDamping", "Factor to damp linear movement per second (0.0 - 1.0)" ), | ||
682 | new PhysParameterEntry("AngularDamping", "Factor to damp angular movement per second (0.0 - 1.0)" ), | ||
683 | new PhysParameterEntry("DeactivationTime", "Seconds before considering an object potentially static" ), | ||
684 | new PhysParameterEntry("LinearSleepingThreshold", "Seconds to measure linear movement before considering static" ), | ||
685 | new PhysParameterEntry("AngularSleepingThreshold", "Seconds to measure angular movement before considering static" ), | ||
686 | // new PhysParameterEntry("CcdMotionThreshold", "" ), | ||
687 | // new PhysParameterEntry("CcdSweptSphereRadius", "" ), | ||
688 | |||
689 | new PhysParameterEntry("TerrainFriction", "Factor to reduce movement against terrain surface" ), | ||
690 | new PhysParameterEntry("TerrainHitFraction", "Distance to measure hit collisions" ), | ||
691 | new PhysParameterEntry("TerrainRestitution", "Bouncyness" ), | ||
692 | new PhysParameterEntry("AvatarFriction", "Factor to reduce movement against an avatar. Changed on avatar recreation." ), | ||
693 | new PhysParameterEntry("AvatarDensity", "Density of an avatar. Changed on avatar recreation." ), | ||
694 | new PhysParameterEntry("AvatarRestitution", "Bouncyness. Changed on avatar recreation." ), | ||
695 | new PhysParameterEntry("AvatarCapsuleRadius", "Radius of space around an avatar" ), | ||
696 | new PhysParameterEntry("AvatarCapsuleHeight", "Default height of space around avatar" ) | ||
697 | }; | ||
698 | |||
699 | #region IPhysicsParameters | ||
700 | // Get the list of parameters this physics engine supports | ||
701 | public PhysParameterEntry[] GetParameterList() | ||
702 | { | ||
703 | return SettableParameters; | ||
704 | } | ||
705 | |||
706 | // Set parameter on a specific or all instances. | ||
707 | // Return 'false' if not able to set the parameter. | ||
708 | // Setting the value in the m_params block will change the value the physics engine | ||
709 | // will use the next time since it's pinned and shared memory. | ||
710 | // Some of the values require calling into the physics engine to get the new | ||
711 | // value activated ('terrainFriction' for instance). | ||
712 | public bool SetPhysicsParameter(string parm, float val, uint localID) | ||
713 | { | ||
714 | bool ret = true; | ||
715 | string lparm = parm.ToLower(); | ||
716 | switch (lparm) | ||
717 | { | ||
718 | case "meshlod": m_meshLOD = (int)val; break; | ||
719 | case "sculptlod": m_sculptLOD = (int)val; break; | ||
720 | case "maxsubstep": m_maxSubSteps = (int)val; break; | ||
721 | case "fixedtimestep": m_fixedTimeStep = val; break; | ||
722 | case "maxobjectmass": m_maximumObjectMass = val; break; | ||
723 | |||
724 | case "defaultfriction": m_params[0].defaultFriction = val; break; | ||
725 | case "defaultdensity": m_params[0].defaultDensity = val; break; | ||
726 | case "defaultrestitution": m_params[0].defaultRestitution = val; break; | ||
727 | case "collisionmargin": m_params[0].collisionMargin = val; break; | ||
728 | case "gravity": m_params[0].gravity = val; TaintedUpdateParameter(lparm, PhysParameterEntry.APPLY_TO_NONE, val); break; | ||
729 | |||
730 | case "lineardamping": UpdateParameterPrims(ref m_params[0].linearDamping, lparm, localID, val); break; | ||
731 | case "angulardamping": UpdateParameterPrims(ref m_params[0].angularDamping, lparm, localID, val); break; | ||
732 | case "deactivationtime": UpdateParameterPrims(ref m_params[0].deactivationTime, lparm, localID, val); break; | ||
733 | case "linearsleepingthreshold": UpdateParameterPrims(ref m_params[0].linearSleepingThreshold, lparm, localID, val); break; | ||
734 | case "angularsleepingthreshold": UpdateParameterPrims(ref m_params[0].angularDamping, lparm, localID, val); break; | ||
735 | case "ccdmotionthreshold": UpdateParameterPrims(ref m_params[0].ccdMotionThreshold, lparm, localID, val); break; | ||
736 | case "ccdsweptsphereradius": UpdateParameterPrims(ref m_params[0].ccdSweptSphereRadius, lparm, localID, val); break; | ||
737 | |||
738 | // set a terrain physical feature and cause terrain to be recalculated | ||
739 | case "terrainfriction": m_params[0].terrainFriction = val; TaintedUpdateParameter("terrain", 0, val); break; | ||
740 | case "terrainhitfraction": m_params[0].terrainHitFraction = val; TaintedUpdateParameter("terrain", 0, val); break; | ||
741 | case "terrainrestitution": m_params[0].terrainRestitution = val; TaintedUpdateParameter("terrain", 0, val); break; | ||
742 | // set an avatar physical feature and cause avatar(s) to be recalculated | ||
743 | case "avatarfriction": UpdateParameterAvatars(ref m_params[0].avatarFriction, "avatar", localID, val); break; | ||
744 | case "avatardensity": UpdateParameterAvatars(ref m_params[0].avatarDensity, "avatar", localID, val); break; | ||
745 | case "avatarrestitution": UpdateParameterAvatars(ref m_params[0].avatarRestitution, "avatar", localID, val); break; | ||
746 | case "avatarcapsuleradius": UpdateParameterAvatars(ref m_params[0].avatarCapsuleRadius, "avatar", localID, val); break; | ||
747 | case "avatarcapsuleheight": UpdateParameterAvatars(ref m_params[0].avatarCapsuleHeight, "avatar", localID, val); break; | ||
748 | |||
749 | default: ret = false; break; | ||
750 | } | ||
751 | return ret; | ||
752 | } | ||
753 | |||
754 | // check to see if we are updating a parameter for a particular or all of the prims | ||
755 | private void UpdateParameterPrims(ref float loc, string parm, uint localID, float val) | ||
756 | { | ||
757 | List<uint> operateOn; | ||
758 | lock (m_prims) operateOn = new List<uint>(m_prims.Keys); | ||
759 | UpdateParameterSet(operateOn, ref loc, parm, localID, val); | ||
760 | } | ||
761 | |||
762 | // check to see if we are updating a parameter for a particular or all of the avatars | ||
763 | private void UpdateParameterAvatars(ref float loc, string parm, uint localID, float val) | ||
764 | { | ||
765 | List<uint> operateOn; | ||
766 | lock (m_avatars) operateOn = new List<uint>(m_avatars.Keys); | ||
767 | UpdateParameterSet(operateOn, ref loc, parm, localID, val); | ||
768 | } | ||
769 | |||
770 | // update all the localIDs specified | ||
771 | // If the local ID is APPLY_TO_NONE, just change the default value | ||
772 | // If the localID is APPLY_TO_ALL change the default value and apply the new value to all the lIDs | ||
773 | // If the localID is a specific object, apply the parameter change to only that object | ||
774 | private void UpdateParameterSet(List<uint> lIDs, ref float defaultLoc, string parm, uint localID, float val) | ||
775 | { | ||
776 | switch (localID) | ||
777 | { | ||
778 | case PhysParameterEntry.APPLY_TO_NONE: | ||
779 | defaultLoc = val; // setting only the default value | ||
780 | break; | ||
781 | case PhysParameterEntry.APPLY_TO_ALL: | ||
782 | defaultLoc = val; // setting ALL also sets the default value | ||
783 | List<uint> objectIDs = lIDs; | ||
784 | string xparm = parm.ToLower(); | ||
785 | float xval = val; | ||
786 | TaintedObject(delegate() { | ||
787 | foreach (uint lID in objectIDs) | ||
788 | { | ||
789 | BulletSimAPI.UpdateParameter(m_worldID, lID, xparm, xval); | ||
790 | } | ||
791 | }); | ||
792 | break; | ||
793 | default: | ||
794 | // setting only one localID | ||
795 | TaintedUpdateParameter(parm, localID, val); | ||
796 | break; | ||
797 | } | ||
798 | } | ||
799 | |||
800 | // schedule the actual updating of the paramter to when the phys engine is not busy | ||
801 | private void TaintedUpdateParameter(string parm, uint localID, float val) | ||
802 | { | ||
803 | uint xlocalID = localID; | ||
804 | string xparm = parm.ToLower(); | ||
805 | float xval = val; | ||
806 | TaintedObject(delegate() { | ||
807 | BulletSimAPI.UpdateParameter(m_worldID, xlocalID, xparm, xval); | ||
808 | }); | ||
809 | } | ||
810 | |||
811 | // Get parameter. | ||
812 | // Return 'false' if not able to get the parameter. | ||
813 | public bool GetPhysicsParameter(string parm, out float value) | ||
814 | { | ||
815 | float val = 0f; | ||
816 | bool ret = true; | ||
817 | switch (parm.ToLower()) | ||
818 | { | ||
819 | case "meshlod": val = (float)m_meshLOD; break; | ||
820 | case "sculptlod": val = (float)m_sculptLOD; break; | ||
821 | case "maxsubstep": val = (float)m_maxSubSteps; break; | ||
822 | case "fixedtimestep": val = m_fixedTimeStep; break; | ||
823 | case "maxobjectmass": val = m_maximumObjectMass; break; | ||
824 | |||
825 | case "defaultfriction": val = m_params[0].defaultFriction; break; | ||
826 | case "defaultdensity": val = m_params[0].defaultDensity; break; | ||
827 | case "defaultrestitution": val = m_params[0].defaultRestitution; break; | ||
828 | case "collisionmargin": val = m_params[0].collisionMargin; break; | ||
829 | case "gravity": val = m_params[0].gravity; break; | ||
830 | |||
831 | case "lineardamping": val = m_params[0].linearDamping; break; | ||
832 | case "angulardamping": val = m_params[0].angularDamping; break; | ||
833 | case "deactivationtime": val = m_params[0].deactivationTime; break; | ||
834 | case "linearsleepingthreshold": val = m_params[0].linearSleepingThreshold; break; | ||
835 | case "angularsleepingthreshold": val = m_params[0].angularDamping; break; | ||
836 | case "ccdmotionthreshold": val = m_params[0].ccdMotionThreshold; break; | ||
837 | case "ccdsweptsphereradius": val = m_params[0].ccdSweptSphereRadius; break; | ||
838 | |||
839 | case "terrainfriction": val = m_params[0].terrainFriction; break; | ||
840 | case "terrainhitfraction": val = m_params[0].terrainHitFraction; break; | ||
841 | case "terrainrestitution": val = m_params[0].terrainRestitution; break; | ||
842 | |||
843 | case "avatarfriction": val = m_params[0].avatarFriction; break; | ||
844 | case "avatardensity": val = m_params[0].avatarDensity; break; | ||
845 | case "avatarrestitution": val = m_params[0].avatarRestitution; break; | ||
846 | case "avatarcapsuleradius": val = m_params[0].avatarCapsuleRadius; break; | ||
847 | case "avatarcapsuleheight": val = m_params[0].avatarCapsuleHeight; break; | ||
848 | default: ret = false; break; | ||
849 | |||
850 | } | ||
851 | value = val; | ||
852 | return ret; | ||
853 | } | ||
854 | |||
855 | #endregion IPhysicsParameters | ||
856 | |||
857 | #endregion Runtime settable parameters | ||
858 | |||
859 | } | ||
860 | } | ||
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs b/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs new file mode 100644 index 0000000..a610c8d --- /dev/null +++ b/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs | |||
@@ -0,0 +1,257 @@ | |||
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.Runtime.InteropServices; | ||
29 | using System.Security; | ||
30 | using System.Text; | ||
31 | using OpenMetaverse; | ||
32 | |||
33 | namespace OpenSim.Region.Physics.BulletSPlugin { | ||
34 | |||
35 | [StructLayout(LayoutKind.Sequential)] | ||
36 | public struct ConvexHull | ||
37 | { | ||
38 | Vector3 Offset; | ||
39 | int VertexCount; | ||
40 | Vector3[] Vertices; | ||
41 | } | ||
42 | [StructLayout(LayoutKind.Sequential)] | ||
43 | public struct ShapeData | ||
44 | { | ||
45 | public enum PhysicsShapeType | ||
46 | { | ||
47 | SHAPE_AVATAR = 0, | ||
48 | SHAPE_BOX = 1, | ||
49 | SHAPE_CONE = 2, | ||
50 | SHAPE_CYLINDER = 3, | ||
51 | SHAPE_SPHERE = 4, | ||
52 | SHAPE_MESH = 5, | ||
53 | SHAPE_HULL = 6 | ||
54 | }; | ||
55 | public uint ID; | ||
56 | public PhysicsShapeType Type; | ||
57 | public Vector3 Position; | ||
58 | public Quaternion Rotation; | ||
59 | public Vector3 Velocity; | ||
60 | public Vector3 Scale; | ||
61 | public float Mass; | ||
62 | public float Buoyancy; | ||
63 | public System.UInt64 HullKey; | ||
64 | public System.UInt64 MeshKey; | ||
65 | public float Friction; | ||
66 | public float Restitution; | ||
67 | public int Collidable; | ||
68 | public int Static; // true if a static object. Otherwise gravity, etc. | ||
69 | |||
70 | // note that bools are passed as ints since bool size changes by language and architecture | ||
71 | public const int numericTrue = 1; | ||
72 | public const int numericFalse = 0; | ||
73 | } | ||
74 | [StructLayout(LayoutKind.Sequential)] | ||
75 | public struct SweepHit | ||
76 | { | ||
77 | public uint ID; | ||
78 | public float Fraction; | ||
79 | public Vector3 Normal; | ||
80 | public Vector3 Point; | ||
81 | } | ||
82 | [StructLayout(LayoutKind.Sequential)] | ||
83 | public struct RaycastHit | ||
84 | { | ||
85 | public uint ID; | ||
86 | public float Fraction; | ||
87 | public Vector3 Normal; | ||
88 | } | ||
89 | [StructLayout(LayoutKind.Sequential)] | ||
90 | public struct CollisionDesc | ||
91 | { | ||
92 | public uint aID; | ||
93 | public uint bID; | ||
94 | public Vector3 point; | ||
95 | public Vector3 normal; | ||
96 | } | ||
97 | [StructLayout(LayoutKind.Sequential)] | ||
98 | public struct EntityProperties | ||
99 | { | ||
100 | public uint ID; | ||
101 | public Vector3 Position; | ||
102 | public Quaternion Rotation; | ||
103 | public Vector3 Velocity; | ||
104 | public Vector3 Acceleration; | ||
105 | public Vector3 RotationalVelocity; | ||
106 | } | ||
107 | |||
108 | // Format of this structure must match the definition in the C++ code | ||
109 | [StructLayout(LayoutKind.Sequential)] | ||
110 | public struct ConfigurationParameters | ||
111 | { | ||
112 | public float defaultFriction; | ||
113 | public float defaultDensity; | ||
114 | public float defaultRestitution; | ||
115 | public float collisionMargin; | ||
116 | public float gravity; | ||
117 | |||
118 | public float linearDamping; | ||
119 | public float angularDamping; | ||
120 | public float deactivationTime; | ||
121 | public float linearSleepingThreshold; | ||
122 | public float angularSleepingThreshold; | ||
123 | public float ccdMotionThreshold; | ||
124 | public float ccdSweptSphereRadius; | ||
125 | |||
126 | public float terrainFriction; | ||
127 | public float terrainHitFraction; | ||
128 | public float terrainRestitution; | ||
129 | public float avatarFriction; | ||
130 | public float avatarDensity; | ||
131 | public float avatarRestitution; | ||
132 | public float avatarCapsuleRadius; | ||
133 | public float avatarCapsuleHeight; | ||
134 | |||
135 | public const float numericTrue = 1f; | ||
136 | public const float numericFalse = 0f; | ||
137 | } | ||
138 | |||
139 | static class BulletSimAPI { | ||
140 | |||
141 | [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] | ||
142 | [return: MarshalAs(UnmanagedType.LPStr)] | ||
143 | public static extern string GetVersion(); | ||
144 | |||
145 | [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] | ||
146 | public static extern uint Initialize(Vector3 maxPosition, IntPtr parms, | ||
147 | int maxCollisions, IntPtr collisionArray, | ||
148 | int maxUpdates, IntPtr updateArray); | ||
149 | |||
150 | [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] | ||
151 | public static extern bool UpdateParameter(uint worldID, uint localID, | ||
152 | [MarshalAs(UnmanagedType.LPStr)]string paramCode, float value); | ||
153 | |||
154 | [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] | ||
155 | public static extern void SetHeightmap(uint worldID, [MarshalAs(UnmanagedType.LPArray)] float[] heightMap); | ||
156 | |||
157 | [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] | ||
158 | public static extern void Shutdown(uint worldID); | ||
159 | |||
160 | |||
161 | [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] | ||
162 | public static extern int PhysicsStep(uint worldID, float timeStep, int maxSubSteps, float fixedTimeStep, | ||
163 | out int updatedEntityCount, | ||
164 | out IntPtr updatedEntitiesPtr, | ||
165 | out int collidersCount, | ||
166 | out IntPtr collidersPtr); | ||
167 | |||
168 | [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] | ||
169 | public static extern bool CreateHull(uint worldID, System.UInt64 meshKey, | ||
170 | int hullCount, [MarshalAs(UnmanagedType.LPArray)] float[] hulls | ||
171 | ); | ||
172 | |||
173 | [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] | ||
174 | public static extern bool CreateMesh(uint worldID, System.UInt64 meshKey, | ||
175 | int indexCount, [MarshalAs(UnmanagedType.LPArray)] int[] indices, | ||
176 | int verticesCount, [MarshalAs(UnmanagedType.LPArray)] float[] vertices | ||
177 | ); | ||
178 | |||
179 | [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] | ||
180 | public static extern bool DestroyHull(uint worldID, System.UInt64 meshKey); | ||
181 | |||
182 | [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] | ||
183 | public static extern bool DestroyMesh(uint worldID, System.UInt64 meshKey); | ||
184 | |||
185 | [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] | ||
186 | public static extern bool CreateObject(uint worldID, ShapeData shapeData); | ||
187 | |||
188 | [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] | ||
189 | public static extern void CreateLinkset(uint worldID, int objectCount, ShapeData[] shapeDatas); | ||
190 | |||
191 | [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] | ||
192 | public static extern void AddConstraint(uint worldID, uint id1, uint id2, | ||
193 | Vector3 frame1, Quaternion frame1rot, | ||
194 | Vector3 frame2, Quaternion frame2rot, | ||
195 | Vector3 lowLinear, Vector3 hiLinear, Vector3 lowAngular, Vector3 hiAngular); | ||
196 | |||
197 | [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] | ||
198 | public static extern bool RemoveConstraintByID(uint worldID, uint id1); | ||
199 | |||
200 | [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] | ||
201 | public static extern bool RemoveConstraint(uint worldID, uint id1, uint id2); | ||
202 | |||
203 | [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] | ||
204 | public static extern Vector3 GetObjectPosition(uint WorldID, uint id); | ||
205 | |||
206 | [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] | ||
207 | public static extern bool SetObjectTranslation(uint worldID, uint id, Vector3 position, Quaternion rotation); | ||
208 | |||
209 | [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] | ||
210 | public static extern bool SetObjectVelocity(uint worldID, uint id, Vector3 velocity); | ||
211 | |||
212 | [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] | ||
213 | public static extern bool SetObjectAngularVelocity(uint worldID, uint id, Vector3 angularVelocity); | ||
214 | |||
215 | [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] | ||
216 | public static extern bool SetObjectForce(uint worldID, uint id, Vector3 force); | ||
217 | |||
218 | [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] | ||
219 | public static extern bool SetObjectScaleMass(uint worldID, uint id, Vector3 scale, float mass, bool isDynamic); | ||
220 | |||
221 | [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] | ||
222 | public static extern bool SetObjectCollidable(uint worldID, uint id, bool phantom); | ||
223 | |||
224 | [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] | ||
225 | public static extern bool SetObjectDynamic(uint worldID, uint id, bool isDynamic, float mass); | ||
226 | |||
227 | [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] | ||
228 | public static extern bool SetObjectGhost(uint worldID, uint id, bool ghostly); | ||
229 | |||
230 | [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] | ||
231 | public static extern bool SetObjectProperties(uint worldID, uint id, bool isStatic, bool isSolid, bool genCollisions, float mass); | ||
232 | |||
233 | [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] | ||
234 | public static extern bool SetObjectBuoyancy(uint worldID, uint id, float buoyancy); | ||
235 | |||
236 | [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] | ||
237 | public static extern bool HasObject(uint worldID, uint id); | ||
238 | |||
239 | [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] | ||
240 | public static extern bool DestroyObject(uint worldID, uint id); | ||
241 | |||
242 | [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] | ||
243 | public static extern SweepHit ConvexSweepTest(uint worldID, uint id, Vector3 to, float extraMargin); | ||
244 | |||
245 | [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] | ||
246 | public static extern RaycastHit RayTest(uint worldID, uint id, Vector3 from, Vector3 to); | ||
247 | |||
248 | [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] | ||
249 | public static extern Vector3 RecoverFromPenetration(uint worldID, uint id); | ||
250 | |||
251 | // Log a debug message | ||
252 | [UnmanagedFunctionPointer(CallingConvention.Cdecl)] | ||
253 | public delegate void DebugLogCallback([MarshalAs(UnmanagedType.LPStr)]string msg); | ||
254 | [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] | ||
255 | public static extern void SetDebugLogCallback(DebugLogCallback callback); | ||
256 | } | ||
257 | } | ||