aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/Physics
diff options
context:
space:
mode:
authorUbitUmarov2012-07-17 00:54:23 +0100
committerUbitUmarov2012-07-17 00:54:23 +0100
commitd5f4fb7b50fb7c594b018f8241399e22f88fc951 (patch)
tree8f3dab3d170596f02b1d1d836a0bc08a3e6b8ebd /OpenSim/Region/Physics
parentUbitOde: remove useless water collider from active code. (diff)
parentMerge branch 'avination' into careminster (diff)
downloadopensim-SC-d5f4fb7b50fb7c594b018f8241399e22f88fc951.zip
opensim-SC-d5f4fb7b50fb7c594b018f8241399e22f88fc951.tar.gz
opensim-SC-d5f4fb7b50fb7c594b018f8241399e22f88fc951.tar.bz2
opensim-SC-d5f4fb7b50fb7c594b018f8241399e22f88fc951.tar.xz
Merge branch 'avination' into ubitwork
Diffstat (limited to '')
-rw-r--r--OpenSim/Region/Physics/BasicPhysicsPlugin/BasicPhysicsActor.cs42
-rw-r--r--OpenSim/Region/Physics/BasicPhysicsPlugin/BasicPhysicsPrim.cs315
-rw-r--r--OpenSim/Region/Physics/BasicPhysicsPlugin/BasicPhysicsScene.cs41
-rw-r--r--OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs102
-rw-r--r--OpenSim/Region/Physics/BulletSPlugin/BSPlugin.cs11
-rw-r--r--OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs29
-rw-r--r--OpenSim/Region/Physics/BulletSPlugin/BSScene.cs23
-rw-r--r--OpenSim/Region/Physics/Manager/PhysicsScene.cs14
-rw-r--r--OpenSim/Region/Physics/Meshing/PrimMesher.cs326
-rw-r--r--OpenSim/Region/Physics/OdePlugin/ODECharacter.cs10
-rw-r--r--OpenSim/Region/Physics/OdePlugin/ODEPrim.cs472
-rw-r--r--OpenSim/Region/Physics/OdePlugin/OdeScene.cs452
12 files changed, 1267 insertions, 570 deletions
diff --git a/OpenSim/Region/Physics/BasicPhysicsPlugin/BasicPhysicsActor.cs b/OpenSim/Region/Physics/BasicPhysicsPlugin/BasicPhysicsActor.cs
index b1a3ff9..e43136a 100644
--- a/OpenSim/Region/Physics/BasicPhysicsPlugin/BasicPhysicsActor.cs
+++ b/OpenSim/Region/Physics/BasicPhysicsPlugin/BasicPhysicsActor.cs
@@ -36,13 +36,7 @@ namespace OpenSim.Region.Physics.BasicPhysicsPlugin
36{ 36{
37 public class BasicActor : PhysicsActor 37 public class BasicActor : PhysicsActor
38 { 38 {
39 private Vector3 _position;
40 private Vector3 _velocity;
41 private Vector3 _acceleration;
42 private Vector3 _size; 39 private Vector3 _size;
43 private Vector3 m_rotationalVelocity;
44 private bool flying;
45 private bool iscolliding;
46 40
47 public BasicActor(Vector3 size) 41 public BasicActor(Vector3 size)
48 { 42 {
@@ -55,11 +49,7 @@ namespace OpenSim.Region.Physics.BasicPhysicsPlugin
55 set { return; } 49 set { return; }
56 } 50 }
57 51
58 public override Vector3 RotationalVelocity 52 public override Vector3 RotationalVelocity { get; set; }
59 {
60 get { return m_rotationalVelocity; }
61 set { m_rotationalVelocity = value; }
62 }
63 53
64 public override bool SetAlwaysRun 54 public override bool SetAlwaysRun
65 { 55 {
@@ -105,17 +95,9 @@ namespace OpenSim.Region.Physics.BasicPhysicsPlugin
105 set { return; } 95 set { return; }
106 } 96 }
107 97
108 public override bool Flying 98 public override bool Flying { get; set; }
109 {
110 get { return flying; }
111 set { flying = value; }
112 }
113 99
114 public override bool IsColliding 100 public override bool IsColliding { get; set; }
115 {
116 get { return iscolliding; }
117 set { iscolliding = value; }
118 }
119 101
120 public override bool CollidingGround 102 public override bool CollidingGround
121 { 103 {
@@ -134,11 +116,7 @@ namespace OpenSim.Region.Physics.BasicPhysicsPlugin
134 get { return false; } 116 get { return false; }
135 } 117 }
136 118
137 public override Vector3 Position 119 public override Vector3 Position { get; set; }
138 {
139 get { return _position; }
140 set { _position = value; }
141 }
142 120
143 public override Vector3 Size 121 public override Vector3 Size
144 { 122 {
@@ -206,11 +184,7 @@ namespace OpenSim.Region.Physics.BasicPhysicsPlugin
206 get { return Vector3.Zero; } 184 get { return Vector3.Zero; }
207 } 185 }
208 186
209 public override Vector3 Velocity 187 public override Vector3 Velocity { get; set; }
210 {
211 get { return _velocity; }
212 set { _velocity = value; }
213 }
214 188
215 public override Vector3 Torque 189 public override Vector3 Torque
216 { 190 {
@@ -230,11 +204,7 @@ namespace OpenSim.Region.Physics.BasicPhysicsPlugin
230 set { } 204 set { }
231 } 205 }
232 206
233 public override Vector3 Acceleration 207 public override Vector3 Acceleration { get; set; }
234 {
235 get { return _acceleration; }
236 set { _acceleration = value; }
237 }
238 208
239 public override bool Kinematic 209 public override bool Kinematic
240 { 210 {
diff --git a/OpenSim/Region/Physics/BasicPhysicsPlugin/BasicPhysicsPrim.cs b/OpenSim/Region/Physics/BasicPhysicsPlugin/BasicPhysicsPrim.cs
new file mode 100644
index 0000000..47d7df3
--- /dev/null
+++ b/OpenSim/Region/Physics/BasicPhysicsPlugin/BasicPhysicsPrim.cs
@@ -0,0 +1,315 @@
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
28using System;
29using System.Collections.Generic;
30using Nini.Config;
31using OpenMetaverse;
32using OpenSim.Framework;
33using OpenSim.Region.Physics.Manager;
34
35namespace OpenSim.Region.Physics.BasicPhysicsPlugin
36{
37 public class BasicPhysicsPrim : PhysicsActor
38 {
39 private Vector3 _size;
40// private PrimitiveBaseShape _shape;
41
42 public BasicPhysicsPrim(
43 string name, uint localId, Vector3 position, Vector3 size, Quaternion orientation, PrimitiveBaseShape shape)
44 {
45 Name = name;
46 LocalID = localId;
47 Position = position;
48 Size = size;
49 Orientation = orientation;
50 Shape = shape;
51 }
52
53 public override int PhysicsActorType
54 {
55 get { return (int) ActorTypes.Agent; }
56 set { return; }
57 }
58
59 public override Vector3 RotationalVelocity { get; set; }
60
61 public override bool SetAlwaysRun
62 {
63 get { return false; }
64 set { return; }
65 }
66
67 public override uint LocalID
68 {
69 set { return; }
70 }
71
72 public override bool Grabbed
73 {
74 set { return; }
75 }
76
77 public override bool Selected
78 {
79 set { return; }
80 }
81
82 public override float Buoyancy
83 {
84 get { return 0f; }
85 set { return; }
86 }
87
88 public override bool FloatOnWater
89 {
90 set { return; }
91 }
92
93 public override bool IsPhysical
94 {
95 get { return false; }
96 set { return; }
97 }
98
99 public override bool ThrottleUpdates
100 {
101 get { return false; }
102 set { return; }
103 }
104
105 public override bool Flying { get; set; }
106
107 public override bool IsColliding { get; set; }
108
109 public override bool CollidingGround
110 {
111 get { return false; }
112 set { return; }
113 }
114
115 public override bool CollidingObj
116 {
117 get { return false; }
118 set { return; }
119 }
120
121 public override bool Stopped
122 {
123 get { return false; }
124 }
125
126 public override Vector3 Position { get; set; }
127
128 public override Vector3 Size
129 {
130 get { return _size; }
131 set {
132 _size = value;
133 _size.Z = _size.Z / 2.0f;
134 }
135 }
136
137 public override PrimitiveBaseShape Shape
138 {
139// set { _shape = value; }
140 set {}
141 }
142
143 public override float Mass
144 {
145 get { return 0f; }
146 }
147
148 public override Vector3 Force
149 {
150 get { return Vector3.Zero; }
151 set { return; }
152 }
153
154 public override int VehicleType
155 {
156 get { return 0; }
157 set { return; }
158 }
159
160 public override void VehicleFloatParam(int param, float value)
161 {
162
163 }
164
165 public override void VehicleVectorParam(int param, Vector3 value)
166 {
167
168 }
169
170 public override void VehicleRotationParam(int param, Quaternion rotation)
171 {
172
173 }
174
175 public override void VehicleFlags(int param, bool remove)
176 {
177
178 }
179
180 public override void SetVolumeDetect(int param)
181 {
182
183 }
184
185 public override Vector3 CenterOfMass
186 {
187 get { return Vector3.Zero; }
188 }
189
190 public override Vector3 GeometricCenter
191 {
192 get { return Vector3.Zero; }
193 }
194
195 public override Vector3 Velocity { get; set; }
196
197 public override Vector3 Torque
198 {
199 get { return Vector3.Zero; }
200 set { return; }
201 }
202
203 public override float CollisionScore
204 {
205 get { return 0f; }
206 set { }
207 }
208
209 public override Quaternion Orientation { get; set; }
210
211 public override Vector3 Acceleration { get; set; }
212
213 public override bool Kinematic
214 {
215 get { return true; }
216 set { }
217 }
218
219 public override void link(PhysicsActor obj)
220 {
221 }
222
223 public override void delink()
224 {
225 }
226
227 public override void LockAngularMotion(Vector3 axis)
228 {
229 }
230
231 public override void AddForce(Vector3 force, bool pushforce)
232 {
233 }
234
235 public override void AddAngularForce(Vector3 force, bool pushforce)
236 {
237 }
238
239 public override void SetMomentum(Vector3 momentum)
240 {
241 }
242
243 public override void CrossingFailure()
244 {
245 }
246
247 public override Vector3 PIDTarget
248 {
249 set { return; }
250 }
251
252 public override bool PIDActive
253 {
254 set { return; }
255 }
256
257 public override float PIDTau
258 {
259 set { return; }
260 }
261
262 public override float PIDHoverHeight
263 {
264 set { return; }
265 }
266
267 public override bool PIDHoverActive
268 {
269 set { return; }
270 }
271
272 public override PIDHoverType PIDHoverType
273 {
274 set { return; }
275 }
276
277 public override float PIDHoverTau
278 {
279 set { return; }
280 }
281
282 public override Quaternion APIDTarget
283 {
284 set { return; }
285 }
286
287 public override bool APIDActive
288 {
289 set { return; }
290 }
291
292 public override float APIDStrength
293 {
294 set { return; }
295 }
296
297 public override float APIDDamping
298 {
299 set { return; }
300 }
301
302 public override void SubscribeEvents(int ms)
303 {
304 }
305
306 public override void UnSubscribeEvents()
307 {
308 }
309
310 public override bool SubscribedEvents()
311 {
312 return false;
313 }
314 }
315}
diff --git a/OpenSim/Region/Physics/BasicPhysicsPlugin/BasicPhysicsScene.cs b/OpenSim/Region/Physics/BasicPhysicsPlugin/BasicPhysicsScene.cs
index 2e14216..f5826ed 100644
--- a/OpenSim/Region/Physics/BasicPhysicsPlugin/BasicPhysicsScene.cs
+++ b/OpenSim/Region/Physics/BasicPhysicsPlugin/BasicPhysicsScene.cs
@@ -34,9 +34,17 @@ using OpenSim.Region.Physics.Manager;
34 34
35namespace OpenSim.Region.Physics.BasicPhysicsPlugin 35namespace OpenSim.Region.Physics.BasicPhysicsPlugin
36{ 36{
37 /// <summary>
38 /// This is an incomplete extremely basic physics implementation
39 /// </summary>
40 /// <remarks>
41 /// Not useful for anything at the moment apart from some regression testing in other components where some form
42 /// of physics plugin is needed.
43 /// </remarks>
37 public class BasicScene : PhysicsScene 44 public class BasicScene : PhysicsScene
38 { 45 {
39 private List<BasicActor> _actors = new List<BasicActor>(); 46 private List<BasicActor> _actors = new List<BasicActor>();
47 private List<BasicPhysicsPrim> _prims = new List<BasicPhysicsPrim>();
40 private float[] _heightMap; 48 private float[] _heightMap;
41 49
42 //protected internal string sceneIdentifier; 50 //protected internal string sceneIdentifier;
@@ -50,10 +58,19 @@ namespace OpenSim.Region.Physics.BasicPhysicsPlugin
50 { 58 {
51 } 59 }
52 60
53 public override void Dispose() 61 public override void Dispose() {}
62
63 public override PhysicsActor AddPrimShape(string primName, PrimitiveBaseShape pbs, Vector3 position,
64 Vector3 size, Quaternion rotation, bool isPhysical, uint localid)
54 { 65 {
66 BasicPhysicsPrim prim = new BasicPhysicsPrim(primName, localid, position, size, rotation, pbs);
67 prim.IsPhysical = isPhysical;
68
69 _prims.Add(prim);
55 70
71 return prim;
56 } 72 }
73
57 public override PhysicsActor AddAvatar(string avName, Vector3 position, Vector3 size, bool isFlying) 74 public override PhysicsActor AddAvatar(string avName, Vector3 position, Vector3 size, bool isFlying)
58 { 75 {
59 BasicActor act = new BasicActor(size); 76 BasicActor act = new BasicActor(size);
@@ -63,30 +80,18 @@ namespace OpenSim.Region.Physics.BasicPhysicsPlugin
63 return act; 80 return act;
64 } 81 }
65 82
66 public override void RemovePrim(PhysicsActor prim) 83 public override void RemovePrim(PhysicsActor actor)
67 { 84 {
85 BasicPhysicsPrim prim = (BasicPhysicsPrim)actor;
86 if (_prims.Contains(prim))
87 _prims.Remove(prim);
68 } 88 }
69 89
70 public override void RemoveAvatar(PhysicsActor actor) 90 public override void RemoveAvatar(PhysicsActor actor)
71 { 91 {
72 BasicActor act = (BasicActor) actor; 92 BasicActor act = (BasicActor)actor;
73 if (_actors.Contains(act)) 93 if (_actors.Contains(act))
74 {
75 _actors.Remove(act); 94 _actors.Remove(act);
76 }
77 }
78
79/*
80 public override PhysicsActor AddPrim(Vector3 position, Vector3 size, Quaternion rotation)
81 {
82 return null;
83 }
84*/
85
86 public override PhysicsActor AddPrimShape(string primName, PrimitiveBaseShape pbs, Vector3 position,
87 Vector3 size, Quaternion rotation, bool isPhysical, uint localid)
88 {
89 return null;
90 } 95 }
91 96
92 public override void AddPhysicsActorTaint(PhysicsActor prim) 97 public override void AddPhysicsActorTaint(PhysicsActor prim)
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs
index b08d5db..dc0c008 100644
--- a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs
@@ -74,7 +74,7 @@ public class BSCharacter : PhysicsActor
74 private float _buoyancy; 74 private float _buoyancy;
75 75
76 private int _subscribedEventsMs = 0; 76 private int _subscribedEventsMs = 0;
77 private int _lastCollisionTime = 0; 77 private int _nextCollisionOkTime = 0;
78 78
79 private Vector3 _PIDTarget; 79 private Vector3 _PIDTarget;
80 private bool _usePID; 80 private bool _usePID;
@@ -360,17 +360,22 @@ public class BSCharacter : PhysicsActor
360 } 360 }
361 //m_lastUpdateSent = false; 361 //m_lastUpdateSent = false;
362 } 362 }
363
363 public override void AddAngularForce(Vector3 force, bool pushforce) { 364 public override void AddAngularForce(Vector3 force, bool pushforce) {
364 } 365 }
365 public override void SetMomentum(Vector3 momentum) { 366 public override void SetMomentum(Vector3 momentum) {
366 } 367 }
368
369 // Turn on collision events at a rate no faster than one every the given milliseconds
367 public override void SubscribeEvents(int ms) { 370 public override void SubscribeEvents(int ms) {
368 _subscribedEventsMs = ms; 371 _subscribedEventsMs = ms;
369 _lastCollisionTime = Util.EnvironmentTickCount() - _subscribedEventsMs; // make first collision happen 372 _nextCollisionOkTime = Util.EnvironmentTickCount() - _subscribedEventsMs; // make first collision happen
370 } 373 }
374 // Stop collision events
371 public override void UnSubscribeEvents() { 375 public override void UnSubscribeEvents() {
372 _subscribedEventsMs = 0; 376 _subscribedEventsMs = 0;
373 } 377 }
378 // Return 'true' if someone has subscribed to events
374 public override bool SubscribedEvents() { 379 public override bool SubscribedEvents() {
375 return (_subscribedEventsMs > 0); 380 return (_subscribedEventsMs > 0);
376 } 381 }
@@ -386,47 +391,57 @@ public class BSCharacter : PhysicsActor
386 _mass = _density * _avatarVolume; 391 _mass = _density * _avatarVolume;
387 } 392 }
388 393
394 // Set to 'true' if the individual changed items should be checked
395 // (someday RequestPhysicsTerseUpdate() will take a bitmap of changed properties)
396 const bool SHOULD_CHECK_FOR_INDIVIDUAL_CHANGES = false;
397
389 // The physics engine says that properties have updated. Update same and inform 398 // The physics engine says that properties have updated. Update same and inform
390 // the world that things have changed. 399 // the world that things have changed.
391 public void UpdateProperties(EntityProperties entprop) 400 public void UpdateProperties(EntityProperties entprop)
392 { 401 {
393 bool changed = false; 402 bool changed = false;
394 // we assign to the local variables so the normal set action does not happen 403 if (SHOULD_CHECK_FOR_INDIVIDUAL_CHANGES) {
395 if (_position != entprop.Position) 404 // we assign to the local variables so the normal set action does not happen
396 { 405 if (_position != entprop.Position) {
397 _position = entprop.Position; 406 _position = entprop.Position;
398 changed = true; 407 changed = true;
408 }
409 if (_orientation != entprop.Rotation) {
410 _orientation = entprop.Rotation;
411 changed = true;
412 }
413 if (_velocity != entprop.Velocity) {
414 _velocity = entprop.Velocity;
415 changed = true;
416 }
417 if (_acceleration != entprop.Acceleration) {
418 _acceleration = entprop.Acceleration;
419 changed = true;
420 }
421 if (_rotationalVelocity != entprop.RotationalVelocity) {
422 _rotationalVelocity = entprop.RotationalVelocity;
423 changed = true;
424 }
425 if (changed) {
426 // m_log.DebugFormat("{0}: UpdateProperties: id={1}, c={2}, pos={3}, rot={4}", LogHeader, LocalID, changed, _position, _orientation);
427 // Avatar movement is not done by generating this event. There is code in the heartbeat
428 // loop that updates avatars.
429 // base.RequestPhysicsterseUpdate();
430 }
399 } 431 }
400 if (_orientation != entprop.Rotation) 432 else {
401 { 433 _position = entprop.Position;
402 _orientation = entprop.Rotation; 434 _orientation = entprop.Rotation;
403 changed = true;
404 }
405 if (_velocity != entprop.Velocity)
406 {
407 _velocity = entprop.Velocity; 435 _velocity = entprop.Velocity;
408 changed = true;
409 }
410 if (_acceleration != entprop.Acceleration)
411 {
412 _acceleration = entprop.Acceleration; 436 _acceleration = entprop.Acceleration;
413 changed = true;
414 }
415 if (_rotationalVelocity != entprop.RotationalVelocity)
416 {
417 _rotationalVelocity = entprop.RotationalVelocity; 437 _rotationalVelocity = entprop.RotationalVelocity;
418 changed = true;
419 }
420 if (changed)
421 {
422 // m_log.DebugFormat("{0}: UpdateProperties: id={1}, c={2}, pos={3}, rot={4}", LogHeader, LocalID, changed, _position, _orientation);
423 // Avatar movement is not done by generating this event. There is a system that
424 // checks for avatar updates each heartbeat loop.
425 // base.RequestPhysicsterseUpdate(); 438 // base.RequestPhysicsterseUpdate();
426 } 439 }
427 } 440 }
428 441
429 // Called by the scene when a collision with this object is reported 442 // Called by the scene when a collision with this object is reported
443 // The collision, if it should be reported to the character, is placed in a collection
444 // that will later be sent to the simulator when SendCollisions() is called.
430 CollisionEventUpdate collisionCollection = null; 445 CollisionEventUpdate collisionCollection = null;
431 public void Collide(uint collidingWith, ActorTypes type, Vector3 contactPoint, Vector3 contactNormal, float pentrationDepth) 446 public void Collide(uint collidingWith, ActorTypes type, Vector3 contactPoint, Vector3 contactNormal, float pentrationDepth)
432 { 447 {
@@ -440,29 +455,34 @@ public class BSCharacter : PhysicsActor
440 } 455 }
441 456
442 // throttle collisions to the rate specified in the subscription 457 // throttle collisions to the rate specified in the subscription
443 if (_subscribedEventsMs == 0) return; // don't want collisions 458 if (_subscribedEventsMs != 0) {
444 int nowTime = _scene.SimulationNowTime; 459 int nowTime = _scene.SimulationNowTime;
445 if (nowTime < (_lastCollisionTime + _subscribedEventsMs)) return; 460 if (nowTime >= _nextCollisionOkTime) {
446 _lastCollisionTime = nowTime; 461 _nextCollisionOkTime = nowTime + _subscribedEventsMs;
447 462
448 if (collisionCollection == null) 463 if (collisionCollection == null)
449 collisionCollection = new CollisionEventUpdate(); 464 collisionCollection = new CollisionEventUpdate();
450 collisionCollection.AddCollider(collidingWith, new ContactPoint(contactPoint, contactNormal, pentrationDepth)); 465 collisionCollection.AddCollider(collidingWith, new ContactPoint(contactPoint, contactNormal, pentrationDepth));
466 }
467 }
451 } 468 }
452 469
453 public void SendCollisions() 470 public void SendCollisions()
454 { 471 {
455 // if (collisionCollection != null) 472 /*
456 // { 473 if (collisionCollection != null && collisionCollection.Count > 0)
457 // base.SendCollisionUpdate(collisionCollection); 474 {
458 // collisionCollection = null; 475 base.SendCollisionUpdate(collisionCollection);
459 // } 476 collisionCollection = null;
477 }
478 */
460 // Kludge to make a collision call even if there are no collisions. 479 // Kludge to make a collision call even if there are no collisions.
461 // This causes the avatar animation to get updated. 480 // This causes the avatar animation to get updated.
462 if (collisionCollection == null) 481 if (collisionCollection == null)
463 collisionCollection = new CollisionEventUpdate(); 482 collisionCollection = new CollisionEventUpdate();
464 base.SendCollisionUpdate(collisionCollection); 483 base.SendCollisionUpdate(collisionCollection);
465 collisionCollection = null; 484 collisionCollection.Clear();
485 // End kludge
466 } 486 }
467 487
468} 488}
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPlugin.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPlugin.cs
index 0730824..0f027b8 100644
--- a/OpenSim/Region/Physics/BulletSPlugin/BSPlugin.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSPlugin.cs
@@ -32,6 +32,14 @@ using OpenMetaverse;
32 32
33namespace OpenSim.Region.Physics.BulletSPlugin 33namespace OpenSim.Region.Physics.BulletSPlugin
34{ 34{
35 /// <summary>
36 /// Entry for a port of Bullet (http://bulletphysics.org/) to OpenSim.
37 /// This module interfaces to an unmanaged C++ library which makes the
38 /// actual calls into the Bullet physics engine.
39 /// The unmanaged library is found in opensim-libs::trunk/unmanaged/BulletSim/.
40 /// The unmanaged library is compiled and linked statically with Bullet
41 /// to create BulletSim.dll and libBulletSim.so (for both 32 and 64 bit).
42 /// </summary>
35public class BSPlugin : IPhysicsPlugin 43public class BSPlugin : IPhysicsPlugin
36{ 44{
37 //private static readonly log4net.ILog m_log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); 45 //private static readonly log4net.ILog m_log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
@@ -53,6 +61,9 @@ public class BSPlugin : IPhysicsPlugin
53 { 61 {
54 if (Util.IsWindows()) 62 if (Util.IsWindows())
55 Util.LoadArchSpecificWindowsDll("BulletSim.dll"); 63 Util.LoadArchSpecificWindowsDll("BulletSim.dll");
64 // If not Windows, loading is performed by the
65 // Mono loader as specified in
66 // "bin/Physics/OpenSim.Region.Physics.BulletSPlugin.dll.config".
56 67
57 _mScene = new BSScene(sceneIdentifier); 68 _mScene = new BSScene(sceneIdentifier);
58 } 69 }
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs
index 248d1f2..130f1ca 100644
--- a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs
@@ -90,7 +90,7 @@ public sealed class BSPrim : PhysicsActor
90 private BSPrim _parentPrim; 90 private BSPrim _parentPrim;
91 91
92 private int _subscribedEventsMs = 0; 92 private int _subscribedEventsMs = 0;
93 private int _lastCollisionTime = 0; 93 private int _nextCollisionOkTime = 0;
94 long _collidingStep; 94 long _collidingStep;
95 long _collidingGroundStep; 95 long _collidingGroundStep;
96 96
@@ -597,7 +597,8 @@ public sealed class BSPrim : PhysicsActor
597 } 597 }
598 public override void SubscribeEvents(int ms) { 598 public override void SubscribeEvents(int ms) {
599 _subscribedEventsMs = ms; 599 _subscribedEventsMs = ms;
600 _lastCollisionTime = Util.EnvironmentTickCount() - _subscribedEventsMs; // make first collision happen 600 // make sure first collision happens
601 _nextCollisionOkTime = Util.EnvironmentTickCount() - _subscribedEventsMs;
601 } 602 }
602 public override void UnSubscribeEvents() { 603 public override void UnSubscribeEvents() {
603 _subscribedEventsMs = 0; 604 _subscribedEventsMs = 0;
@@ -1338,23 +1339,27 @@ public sealed class BSPrim : PhysicsActor
1338 _collidingGroundStep = _scene.SimulationStep; 1339 _collidingGroundStep = _scene.SimulationStep;
1339 } 1340 }
1340 1341
1341 if (_subscribedEventsMs == 0) return; // nothing in the object is waiting for collision events 1342 // if someone is subscribed to collision events....
1342 // throttle the collisions to the number of milliseconds specified in the subscription 1343 if (_subscribedEventsMs != 0) {
1343 int nowTime = _scene.SimulationNowTime; 1344 // throttle the collisions to the number of milliseconds specified in the subscription
1344 if (nowTime < (_lastCollisionTime + _subscribedEventsMs)) return; 1345 int nowTime = _scene.SimulationNowTime;
1345 _lastCollisionTime = nowTime; 1346 if (nowTime >= _nextCollisionOkTime) {
1347 _nextCollisionOkTime = nowTime + _subscribedEventsMs;
1346 1348
1347 if (collisionCollection == null) 1349 if (collisionCollection == null)
1348 collisionCollection = new CollisionEventUpdate(); 1350 collisionCollection = new CollisionEventUpdate();
1349 collisionCollection.AddCollider(collidingWith, new ContactPoint(contactPoint, contactNormal, pentrationDepth)); 1351 collisionCollection.AddCollider(collidingWith, new ContactPoint(contactPoint, contactNormal, pentrationDepth));
1352 }
1353 }
1350 } 1354 }
1351 1355
1356 // The scene is telling us it's time to pass our collected collisions into the simulator
1352 public void SendCollisions() 1357 public void SendCollisions()
1353 { 1358 {
1354 if (collisionCollection != null) 1359 if (collisionCollection != null && collisionCollection.Count > 0)
1355 { 1360 {
1356 base.SendCollisionUpdate(collisionCollection); 1361 base.SendCollisionUpdate(collisionCollection);
1357 collisionCollection = null; 1362 collisionCollection.Clear();
1358 } 1363 }
1359 } 1364 }
1360} 1365}
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs
index 94a0ccf..417cb5f 100644
--- a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs
@@ -52,6 +52,7 @@ using OpenSim.Region.Framework;
52// Should prim.link() and prim.delink() membership checking happen at taint time? 52// Should prim.link() and prim.delink() membership checking happen at taint time?
53// Mesh sharing. Use meshHash to tell if we already have a hull of that shape and only create once 53// Mesh sharing. Use meshHash to tell if we already have a hull of that shape and only create once
54// Do attachments need to be handled separately? Need collision events. Do not collide with VolumeDetect 54// Do attachments need to be handled separately? Need collision events. Do not collide with VolumeDetect
55// Use collision masks for collision with terrain and phantom objects
55// Implement the genCollisions feature in BulletSim::SetObjectProperties (don't pass up unneeded collisions) 56// Implement the genCollisions feature in BulletSim::SetObjectProperties (don't pass up unneeded collisions)
56// Implement LockAngularMotion 57// Implement LockAngularMotion
57// Decide if clearing forces is the right thing to do when setting position (BulletSim::SetObjectTranslation) 58// Decide if clearing forces is the right thing to do when setting position (BulletSim::SetObjectTranslation)
@@ -62,9 +63,6 @@ using OpenSim.Region.Framework;
62// Multiple contact points on collision? 63// Multiple contact points on collision?
63// See code in ode::near... calls to collision_accounting_events() 64// See code in ode::near... calls to collision_accounting_events()
64// (This might not be a problem. ODE collects all the collisions with one object in one tick.) 65// (This might not be a problem. ODE collects all the collisions with one object in one tick.)
65// Use collision masks for collision with terrain and phantom objects
66// Figure out how to not allocate a new Dictionary and List for every collision
67// in BSPrim.Collide() and BSCharacter.Collide(). Can the same ones be reused?
68// Raycast 66// Raycast
69// 67//
70namespace OpenSim.Region.Physics.BulletSPlugin 68namespace OpenSim.Region.Physics.BulletSPlugin
@@ -405,6 +403,8 @@ public class BSScene : PhysicsScene, IPhysicsParameters
405 // prevent simulation until we've been initialized 403 // prevent simulation until we've been initialized
406 if (!m_initialized) return 10.0f; 404 if (!m_initialized) return 10.0f;
407 405
406 long simulateStartTime = Util.EnvironmentTickCount();
407
408 // update the prim states while we know the physics engine is not busy 408 // update the prim states while we know the physics engine is not busy
409 ProcessTaints(); 409 ProcessTaints();
410 410
@@ -437,13 +437,18 @@ public class BSScene : PhysicsScene, IPhysicsParameters
437 } 437 }
438 } 438 }
439 439
440 // The SendCollision's batch up the collisions on the objects. Now push the collisions into the simulator. 440 // The above SendCollision's batch up the collisions on the objects.
441 // Now push the collisions into the simulator.
441 foreach (BSPrim bsp in m_primsWithCollisions) 442 foreach (BSPrim bsp in m_primsWithCollisions)
442 bsp.SendCollisions(); 443 bsp.SendCollisions();
443 m_primsWithCollisions.Clear(); 444 m_primsWithCollisions.Clear();
445
446 // This is a kludge to get avatar movement updated.
447 // Don't send collisions only if there were collisions -- send everytime.
448 // ODE sends collisions even if there are none and this is used to update
449 // avatar animations and stuff.
444 // foreach (BSCharacter bsc in m_avatarsWithCollisions) 450 // foreach (BSCharacter bsc in m_avatarsWithCollisions)
445 // bsc.SendCollisions(); 451 // bsc.SendCollisions();
446 // This is a kludge to get avatar movement updated. ODE sends collisions even if there isn't any
447 foreach (KeyValuePair<uint, BSCharacter> kvp in m_avatars) 452 foreach (KeyValuePair<uint, BSCharacter> kvp in m_avatars)
448 kvp.Value.SendCollisions(); 453 kvp.Value.SendCollisions();
449 m_avatarsWithCollisions.Clear(); 454 m_avatarsWithCollisions.Clear();
@@ -465,10 +470,12 @@ public class BSScene : PhysicsScene, IPhysicsParameters
465 if (m_avatars.TryGetValue(entprop.ID, out actor)) 470 if (m_avatars.TryGetValue(entprop.ID, out actor))
466 { 471 {
467 actor.UpdateProperties(entprop); 472 actor.UpdateProperties(entprop);
473 continue;
468 } 474 }
469 } 475 }
470 } 476 }
471 477
478 // If enabled, call into the physics engine to dump statistics
472 if (m_detailedStatsStep > 0) 479 if (m_detailedStatsStep > 0)
473 { 480 {
474 if ((m_simulationStep % m_detailedStatsStep) == 0) 481 if ((m_simulationStep % m_detailedStatsStep) == 0)
@@ -477,6 +484,11 @@ public class BSScene : PhysicsScene, IPhysicsParameters
477 } 484 }
478 } 485 }
479 486
487 // this is a waste since the outside routine also calcuates the physics simulation
488 // period. TODO: There should be a way of computing physics frames from simulator computation.
489 // long simulateTotalTime = Util.EnvironmentTickCountSubtract(simulateStartTime);
490 // return (timeStep * (float)simulateTotalTime);
491
480 // TODO: FIX THIS: fps calculation wrong. This calculation always returns about 1 in normal operation. 492 // TODO: FIX THIS: fps calculation wrong. This calculation always returns about 1 in normal operation.
481 return timeStep / (numSubSteps * m_fixedTimeStep) * 1000f; 493 return timeStep / (numSubSteps * m_fixedTimeStep) * 1000f;
482 } 494 }
@@ -528,6 +540,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters
528 public override void SetWaterLevel(float baseheight) 540 public override void SetWaterLevel(float baseheight)
529 { 541 {
530 m_waterLevel = baseheight; 542 m_waterLevel = baseheight;
543 // TODO: pass to physics engine so things will float?
531 } 544 }
532 public float GetWaterLevel() 545 public float GetWaterLevel()
533 { 546 {
diff --git a/OpenSim/Region/Physics/Manager/PhysicsScene.cs b/OpenSim/Region/Physics/Manager/PhysicsScene.cs
index d10a2aa..cfede55 100644
--- a/OpenSim/Region/Physics/Manager/PhysicsScene.cs
+++ b/OpenSim/Region/Physics/Manager/PhysicsScene.cs
@@ -241,8 +241,22 @@ namespace OpenSim.Region.Physics.Manager
241 241
242 public abstract void AddPhysicsActorTaint(PhysicsActor prim); 242 public abstract void AddPhysicsActorTaint(PhysicsActor prim);
243 243
244 /// <summary>
245 /// Perform a simulation of the current physics scene over the given timestep.
246 /// </summary>
247 /// <param name="timeStep"></param>
248 /// <returns>The number of frames simulated over that period.</returns>
244 public abstract float Simulate(float timeStep); 249 public abstract float Simulate(float timeStep);
245 250
251 /// <summary>
252 /// Get statistics about this scene.
253 /// </summary>
254 /// <remarks>This facility is currently experimental and subject to change.</remarks>
255 /// <returns>
256 /// A dictionary where the key is the statistic name. If no statistics are supplied then returns null.
257 /// </returns>
258 public virtual Dictionary<string, float> GetStats() { return null; }
259
246 public abstract void GetResults(); 260 public abstract void GetResults();
247 261
248 public abstract void SetTerrain(float[] heightMap); 262 public abstract void SetTerrain(float[] heightMap);
diff --git a/OpenSim/Region/Physics/Meshing/PrimMesher.cs b/OpenSim/Region/Physics/Meshing/PrimMesher.cs
index 53022ad..4049ee1 100644
--- a/OpenSim/Region/Physics/Meshing/PrimMesher.cs
+++ b/OpenSim/Region/Physics/Meshing/PrimMesher.cs
@@ -236,6 +236,13 @@ namespace PrimMesher
236 this.U = u; 236 this.U = u;
237 this.V = v; 237 this.V = v;
238 } 238 }
239
240 public UVCoord Flip()
241 {
242 this.U = 1.0f - this.U;
243 this.V = 1.0f - this.V;
244 return this;
245 }
239 } 246 }
240 247
241 public struct Face 248 public struct Face
@@ -603,40 +610,40 @@ namespace PrimMesher
603 /// <summary> 610 /// <summary>
604 /// generates a profile for extrusion 611 /// generates a profile for extrusion
605 /// </summary> 612 /// </summary>
606 internal class Profile 613 public class Profile
607 { 614 {
608 private const float twoPi = 2.0f * (float)Math.PI; 615 private const float twoPi = 2.0f * (float)Math.PI;
609 616
610 internal string errorMessage = null; 617 public string errorMessage = null;
611 618
612 internal List<Coord> coords; 619 public List<Coord> coords;
613 internal List<Face> faces; 620 public List<Face> faces;
614 internal List<Coord> vertexNormals; 621 public List<Coord> vertexNormals;
615 internal List<float> us; 622 public List<float> us;
616 internal List<UVCoord> faceUVs; 623 public List<UVCoord> faceUVs;
617 internal List<int> faceNumbers; 624 public List<int> faceNumbers;
618 625
619 // use these for making individual meshes for each prim face 626 // use these for making individual meshes for each prim face
620 internal List<int> outerCoordIndices = null; 627 public List<int> outerCoordIndices = null;
621 internal List<int> hollowCoordIndices = null; 628 public List<int> hollowCoordIndices = null;
622 internal List<int> cut1CoordIndices = null; 629 public List<int> cut1CoordIndices = null;
623 internal List<int> cut2CoordIndices = null; 630 public List<int> cut2CoordIndices = null;
624 631
625 internal Coord faceNormal = new Coord(0.0f, 0.0f, 1.0f); 632 public Coord faceNormal = new Coord(0.0f, 0.0f, 1.0f);
626 internal Coord cutNormal1 = new Coord(); 633 public Coord cutNormal1 = new Coord();
627 internal Coord cutNormal2 = new Coord(); 634 public Coord cutNormal2 = new Coord();
628 635
629 internal int numOuterVerts = 0; 636 public int numOuterVerts = 0;
630 internal int numHollowVerts = 0; 637 public int numHollowVerts = 0;
631 638
632 internal int outerFaceNumber = -1; 639 public int outerFaceNumber = -1;
633 internal int hollowFaceNumber = -1; 640 public int hollowFaceNumber = -1;
634 641
635 internal bool calcVertexNormals = false; 642 public bool calcVertexNormals = false;
636 internal int bottomFaceNumber = 0; 643 public int bottomFaceNumber = 0;
637 internal int numPrimFaces = 0; 644 public int numPrimFaces = 0;
638 645
639 internal Profile() 646 public Profile()
640 { 647 {
641 this.coords = new List<Coord>(); 648 this.coords = new List<Coord>();
642 this.faces = new List<Face>(); 649 this.faces = new List<Face>();
@@ -646,7 +653,7 @@ namespace PrimMesher
646 this.faceNumbers = new List<int>(); 653 this.faceNumbers = new List<int>();
647 } 654 }
648 655
649 internal Profile(int sides, float profileStart, float profileEnd, float hollow, int hollowSides, bool createFaces, bool calcVertexNormals) 656 public Profile(int sides, float profileStart, float profileEnd, float hollow, int hollowSides, bool createFaces, bool calcVertexNormals)
650 { 657 {
651 this.calcVertexNormals = calcVertexNormals; 658 this.calcVertexNormals = calcVertexNormals;
652 this.coords = new List<Coord>(); 659 this.coords = new List<Coord>();
@@ -657,7 +664,6 @@ namespace PrimMesher
657 this.faceNumbers = new List<int>(); 664 this.faceNumbers = new List<int>();
658 665
659 Coord center = new Coord(0.0f, 0.0f, 0.0f); 666 Coord center = new Coord(0.0f, 0.0f, 0.0f);
660 //bool hasCenter = false;
661 667
662 List<Coord> hollowCoords = new List<Coord>(); 668 List<Coord> hollowCoords = new List<Coord>();
663 List<Coord> hollowNormals = new List<Coord>(); 669 List<Coord> hollowNormals = new List<Coord>();
@@ -682,8 +688,8 @@ namespace PrimMesher
682 float yScale = 0.5f; 688 float yScale = 0.5f;
683 if (sides == 4) // corners of a square are sqrt(2) from center 689 if (sides == 4) // corners of a square are sqrt(2) from center
684 { 690 {
685 xScale = 0.707f; 691 xScale = 0.707107f;
686 yScale = 0.707f; 692 yScale = 0.707107f;
687 } 693 }
688 694
689 float startAngle = profileStart * twoPi; 695 float startAngle = profileStart * twoPi;
@@ -724,7 +730,6 @@ namespace PrimMesher
724 else if (!simpleFace) 730 else if (!simpleFace)
725 { 731 {
726 this.coords.Add(center); 732 this.coords.Add(center);
727 //hasCenter = true;
728 if (this.calcVertexNormals) 733 if (this.calcVertexNormals)
729 this.vertexNormals.Add(new Coord(0.0f, 0.0f, 1.0f)); 734 this.vertexNormals.Add(new Coord(0.0f, 0.0f, 1.0f));
730 this.us.Add(0.0f); 735 this.us.Add(0.0f);
@@ -752,7 +757,10 @@ namespace PrimMesher
752 else 757 else
753 hollowNormals.Add(new Coord(-angle.X, -angle.Y, 0.0f)); 758 hollowNormals.Add(new Coord(-angle.X, -angle.Y, 0.0f));
754 759
755 hollowUs.Add(angle.angle * hollow); 760 if (hollowSides == 4)
761 hollowUs.Add(angle.angle * hollow * 0.707107f);
762 else
763 hollowUs.Add(angle.angle * hollow);
756 } 764 }
757 } 765 }
758 } 766 }
@@ -829,9 +837,6 @@ namespace PrimMesher
829 837
830 if (createFaces) 838 if (createFaces)
831 { 839 {
832 //int numOuterVerts = this.coords.Count;
833 //numOuterVerts = this.coords.Count;
834 //int numHollowVerts = hollowCoords.Count;
835 int numTotalVerts = this.numOuterVerts + this.numHollowVerts; 840 int numTotalVerts = this.numOuterVerts + this.numHollowVerts;
836 841
837 if (this.numOuterVerts == this.numHollowVerts) 842 if (this.numOuterVerts == this.numHollowVerts)
@@ -993,11 +998,7 @@ namespace PrimMesher
993 if (startVert > 0) 998 if (startVert > 0)
994 this.faceNumbers.Add(-1); 999 this.faceNumbers.Add(-1);
995 for (int i = 0; i < this.numOuterVerts - 1; i++) 1000 for (int i = 0; i < this.numOuterVerts - 1; i++)
996 //this.faceNumbers.Add(sides < 5 ? faceNum++ : faceNum); 1001 this.faceNumbers.Add(sides < 5 && i <= sides ? faceNum++ : faceNum);
997 this.faceNumbers.Add(sides < 5 && i < sides ? faceNum++ : faceNum);
998
999 //if (!hasHollow && !hasProfileCut)
1000 // this.bottomFaceNumber = faceNum++;
1001 1002
1002 this.faceNumbers.Add(hasProfileCut ? -1 : faceNum++); 1003 this.faceNumbers.Add(hasProfileCut ? -1 : faceNum++);
1003 1004
@@ -1014,8 +1015,7 @@ namespace PrimMesher
1014 1015
1015 this.hollowFaceNumber = faceNum++; 1016 this.hollowFaceNumber = faceNum++;
1016 } 1017 }
1017 //if (hasProfileCut || hasHollow) 1018
1018 // this.bottomFaceNumber = faceNum++;
1019 this.bottomFaceNumber = faceNum++; 1019 this.bottomFaceNumber = faceNum++;
1020 1020
1021 if (hasHollow && hasProfileCut) 1021 if (hasHollow && hasProfileCut)
@@ -1030,19 +1030,19 @@ namespace PrimMesher
1030 1030
1031 } 1031 }
1032 1032
1033 internal void MakeFaceUVs() 1033 public void MakeFaceUVs()
1034 { 1034 {
1035 this.faceUVs = new List<UVCoord>(); 1035 this.faceUVs = new List<UVCoord>();
1036 foreach (Coord c in this.coords) 1036 foreach (Coord c in this.coords)
1037 this.faceUVs.Add(new UVCoord(0.5f + c.X, 0.5f - c.Y)); 1037 this.faceUVs.Add(new UVCoord(1.0f - (0.5f + c.X), 1.0f - (0.5f - c.Y)));
1038 } 1038 }
1039 1039
1040 internal Profile Copy() 1040 public Profile Copy()
1041 { 1041 {
1042 return this.Copy(true); 1042 return this.Copy(true);
1043 } 1043 }
1044 1044
1045 internal Profile Copy(bool needFaces) 1045 public Profile Copy(bool needFaces)
1046 { 1046 {
1047 Profile copy = new Profile(); 1047 Profile copy = new Profile();
1048 1048
@@ -1071,12 +1071,12 @@ namespace PrimMesher
1071 return copy; 1071 return copy;
1072 } 1072 }
1073 1073
1074 internal void AddPos(Coord v) 1074 public void AddPos(Coord v)
1075 { 1075 {
1076 this.AddPos(v.X, v.Y, v.Z); 1076 this.AddPos(v.X, v.Y, v.Z);
1077 } 1077 }
1078 1078
1079 internal void AddPos(float x, float y, float z) 1079 public void AddPos(float x, float y, float z)
1080 { 1080 {
1081 int i; 1081 int i;
1082 int numVerts = this.coords.Count; 1082 int numVerts = this.coords.Count;
@@ -1092,7 +1092,7 @@ namespace PrimMesher
1092 } 1092 }
1093 } 1093 }
1094 1094
1095 internal void AddRot(Quat q) 1095 public void AddRot(Quat q)
1096 { 1096 {
1097 int i; 1097 int i;
1098 int numVerts = this.coords.Count; 1098 int numVerts = this.coords.Count;
@@ -1113,7 +1113,7 @@ namespace PrimMesher
1113 } 1113 }
1114 } 1114 }
1115 1115
1116 internal void Scale(float x, float y) 1116 public void Scale(float x, float y)
1117 { 1117 {
1118 int i; 1118 int i;
1119 int numVerts = this.coords.Count; 1119 int numVerts = this.coords.Count;
@@ -1131,7 +1131,7 @@ namespace PrimMesher
1131 /// <summary> 1131 /// <summary>
1132 /// Changes order of the vertex indices and negates the center vertex normal. Does not alter vertex normals of radial vertices 1132 /// Changes order of the vertex indices and negates the center vertex normal. Does not alter vertex normals of radial vertices
1133 /// </summary> 1133 /// </summary>
1134 internal void FlipNormals() 1134 public void FlipNormals()
1135 { 1135 {
1136 int i; 1136 int i;
1137 int numFaces = this.faces.Count; 1137 int numFaces = this.faces.Count;
@@ -1171,7 +1171,7 @@ namespace PrimMesher
1171 } 1171 }
1172 } 1172 }
1173 1173
1174 internal void AddValue2FaceVertexIndices(int num) 1174 public void AddValue2FaceVertexIndices(int num)
1175 { 1175 {
1176 int numFaces = this.faces.Count; 1176 int numFaces = this.faces.Count;
1177 Face tmpFace; 1177 Face tmpFace;
@@ -1186,7 +1186,7 @@ namespace PrimMesher
1186 } 1186 }
1187 } 1187 }
1188 1188
1189 internal void AddValue2FaceNormalIndices(int num) 1189 public void AddValue2FaceNormalIndices(int num)
1190 { 1190 {
1191 if (this.calcVertexNormals) 1191 if (this.calcVertexNormals)
1192 { 1192 {
@@ -1204,7 +1204,7 @@ namespace PrimMesher
1204 } 1204 }
1205 } 1205 }
1206 1206
1207 internal void DumpRaw(String path, String name, String title) 1207 public void DumpRaw(String path, String name, String title)
1208 { 1208 {
1209 if (path == null) 1209 if (path == null)
1210 return; 1210 return;
@@ -1261,6 +1261,15 @@ namespace PrimMesher
1261 1261
1262 public void Create(PathType pathType, int steps) 1262 public void Create(PathType pathType, int steps)
1263 { 1263 {
1264 if (this.taperX > 0.999f)
1265 this.taperX = 0.999f;
1266 if (this.taperX < -0.999f)
1267 this.taperX = -0.999f;
1268 if (this.taperY > 0.999f)
1269 this.taperY = 0.999f;
1270 if (this.taperY < -0.999f)
1271 this.taperY = -0.999f;
1272
1264 if (pathType == PathType.Linear || pathType == PathType.Flexible) 1273 if (pathType == PathType.Linear || pathType == PathType.Flexible)
1265 { 1274 {
1266 int step = 0; 1275 int step = 0;
@@ -1273,12 +1282,12 @@ namespace PrimMesher
1273 1282
1274 float start = -0.5f; 1283 float start = -0.5f;
1275 float stepSize = length / (float)steps; 1284 float stepSize = length / (float)steps;
1276 float percentOfPathMultiplier = stepSize; 1285 float percentOfPathMultiplier = stepSize * 0.999999f;
1277 float xOffset = 0.0f; 1286 float xOffset = this.topShearX * this.pathCutBegin;
1278 float yOffset = 0.0f; 1287 float yOffset = this.topShearY * this.pathCutBegin;
1279 float zOffset = start; 1288 float zOffset = start;
1280 float xOffsetStepIncrement = this.topShearX / steps; 1289 float xOffsetStepIncrement = this.topShearX * length / steps;
1281 float yOffsetStepIncrement = this.topShearY / steps; 1290 float yOffsetStepIncrement = this.topShearY * length / steps;
1282 1291
1283 float percentOfPath = this.pathCutBegin; 1292 float percentOfPath = this.pathCutBegin;
1284 zOffset += percentOfPath; 1293 zOffset += percentOfPath;
@@ -1573,13 +1582,6 @@ namespace PrimMesher
1573 this.hollow = 0.99f; 1582 this.hollow = 0.99f;
1574 if (hollow < 0.0f) 1583 if (hollow < 0.0f)
1575 this.hollow = 0.0f; 1584 this.hollow = 0.0f;
1576
1577 //if (sphereMode)
1578 // this.hasProfileCut = this.profileEnd - this.profileStart < 0.4999f;
1579 //else
1580 // //this.hasProfileCut = (this.profileStart > 0.0f || this.profileEnd < 1.0f);
1581 // this.hasProfileCut = this.profileEnd - this.profileStart < 0.9999f;
1582 //this.hasHollow = (this.hollow > 0.001f);
1583 } 1585 }
1584 1586
1585 /// <summary> 1587 /// <summary>
@@ -1614,10 +1616,9 @@ namespace PrimMesher
1614 steps = (int)(steps * 4.5 * length); 1616 steps = (int)(steps * 4.5 * length);
1615 } 1617 }
1616 1618
1617 if (sphereMode) 1619 if (this.sphereMode)
1618 this.hasProfileCut = this.profileEnd - this.profileStart < 0.4999f; 1620 this.hasProfileCut = this.profileEnd - this.profileStart < 0.4999f;
1619 else 1621 else
1620 //this.hasProfileCut = (this.profileStart > 0.0f || this.profileEnd < 1.0f);
1621 this.hasProfileCut = this.profileEnd - this.profileStart < 0.9999f; 1622 this.hasProfileCut = this.profileEnd - this.profileStart < 0.9999f;
1622 this.hasHollow = (this.hollow > 0.001f); 1623 this.hasHollow = (this.hollow > 0.001f);
1623 1624
@@ -1630,6 +1631,22 @@ namespace PrimMesher
1630 1631
1631 float hollow = this.hollow; 1632 float hollow = this.hollow;
1632 1633
1634 if (pathType == PathType.Circular)
1635 {
1636 needEndFaces = false;
1637 if (this.pathCutBegin != 0.0f || this.pathCutEnd != 1.0f)
1638 needEndFaces = true;
1639 else if (this.taperX != 0.0f || this.taperY != 0.0f)
1640 needEndFaces = true;
1641 else if (this.skew != 0.0f)
1642 needEndFaces = true;
1643 else if (twistTotal != 0.0f)
1644 needEndFaces = true;
1645 else if (this.radius != 0.0f)
1646 needEndFaces = true;
1647 }
1648 else needEndFaces = true;
1649
1633 // sanity checks 1650 // sanity checks
1634 float initialProfileRot = 0.0f; 1651 float initialProfileRot = 0.0f;
1635 if (pathType == PathType.Circular) 1652 if (pathType == PathType.Circular)
@@ -1689,20 +1706,13 @@ namespace PrimMesher
1689 1706
1690 this.numPrimFaces = profile.numPrimFaces; 1707 this.numPrimFaces = profile.numPrimFaces;
1691 1708
1692 //profileOuterFaceNumber = profile.faceNumbers[0]; 1709 int cut1FaceNumber = profile.bottomFaceNumber + 1;
1693 //if (!needEndFaces) 1710 int cut2FaceNumber = cut1FaceNumber + 1;
1694 // profileOuterFaceNumber--; 1711 if (!needEndFaces)
1695 //profileOuterFaceNumber = needEndFaces ? 1 : 0; 1712 {
1696 1713 cut1FaceNumber -= 2;
1697 1714 cut2FaceNumber -= 2;
1698 //if (hasHollow) 1715 }
1699 //{
1700 // if (needEndFaces)
1701 // profileHollowFaceNumber = profile.faceNumbers[profile.numOuterVerts + 1];
1702 // else
1703 // profileHollowFaceNumber = profile.faceNumbers[profile.numOuterVerts] - 1;
1704 //}
1705
1706 1716
1707 profileOuterFaceNumber = profile.outerFaceNumber; 1717 profileOuterFaceNumber = profile.outerFaceNumber;
1708 if (!needEndFaces) 1718 if (!needEndFaces)
@@ -1732,7 +1742,8 @@ namespace PrimMesher
1732 1742
1733 Coord lastCutNormal1 = new Coord(); 1743 Coord lastCutNormal1 = new Coord();
1734 Coord lastCutNormal2 = new Coord(); 1744 Coord lastCutNormal2 = new Coord();
1735 float lastV = 1.0f; 1745 float thisV = 0.0f;
1746 float lastV = 0.0f;
1736 1747
1737 Path path = new Path(); 1748 Path path = new Path();
1738 path.twistBegin = twistBegin; 1749 path.twistBegin = twistBegin;
@@ -1754,23 +1765,6 @@ namespace PrimMesher
1754 1765
1755 path.Create(pathType, steps); 1766 path.Create(pathType, steps);
1756 1767
1757
1758 if (pathType == PathType.Circular)
1759 {
1760 needEndFaces = false;
1761 if (this.pathCutBegin != 0.0f || this.pathCutEnd != 1.0f)
1762 needEndFaces = true;
1763 else if (this.taperX != 0.0f || this.taperY != 0.0f)
1764 needEndFaces = true;
1765 else if (this.skew != 0.0f)
1766 needEndFaces = true;
1767 else if (twistTotal != 0.0f)
1768 needEndFaces = true;
1769 else if (this.radius != 0.0f)
1770 needEndFaces = true;
1771 }
1772 else needEndFaces = true;
1773
1774 for (int nodeIndex = 0; nodeIndex < path.pathNodes.Count; nodeIndex++) 1768 for (int nodeIndex = 0; nodeIndex < path.pathNodes.Count; nodeIndex++)
1775 { 1769 {
1776 PathNode node = path.pathNodes[nodeIndex]; 1770 PathNode node = path.pathNodes[nodeIndex];
@@ -1784,7 +1778,7 @@ namespace PrimMesher
1784 { 1778 {
1785 newLayer.FlipNormals(); 1779 newLayer.FlipNormals();
1786 1780
1787 // add the top faces to the viewerFaces list here 1781 // add the bottom faces to the viewerFaces list
1788 if (this.viewerMode) 1782 if (this.viewerMode)
1789 { 1783 {
1790 Coord faceNormal = newLayer.faceNormal; 1784 Coord faceNormal = newLayer.faceNormal;
@@ -1811,6 +1805,13 @@ namespace PrimMesher
1811 newViewerFace.uv2 = newLayer.faceUVs[face.v2]; 1805 newViewerFace.uv2 = newLayer.faceUVs[face.v2];
1812 newViewerFace.uv3 = newLayer.faceUVs[face.v3]; 1806 newViewerFace.uv3 = newLayer.faceUVs[face.v3];
1813 1807
1808 if (pathType == PathType.Linear)
1809 {
1810 newViewerFace.uv1.Flip();
1811 newViewerFace.uv2.Flip();
1812 newViewerFace.uv3.Flip();
1813 }
1814
1814 this.viewerFaces.Add(newViewerFace); 1815 this.viewerFaces.Add(newViewerFace);
1815 } 1816 }
1816 } 1817 }
@@ -1835,7 +1836,10 @@ namespace PrimMesher
1835 // fill faces between layers 1836 // fill faces between layers
1836 1837
1837 int numVerts = newLayer.coords.Count; 1838 int numVerts = newLayer.coords.Count;
1838 Face newFace = new Face(); 1839 Face newFace1 = new Face();
1840 Face newFace2 = new Face();
1841
1842 thisV = 1.0f - node.percentOfPath;
1839 1843
1840 if (nodeIndex > 0) 1844 if (nodeIndex > 0)
1841 { 1845 {
@@ -1853,14 +1857,23 @@ namespace PrimMesher
1853 1857
1854 int whichVert = i - startVert; 1858 int whichVert = i - startVert;
1855 1859
1856 newFace.v1 = i; 1860 newFace1.v1 = i;
1857 newFace.v2 = i - numVerts; 1861 newFace1.v2 = i - numVerts;
1858 newFace.v3 = iNext - numVerts; 1862 newFace1.v3 = iNext;
1859 this.faces.Add(newFace); 1863
1864 newFace1.n1 = newFace1.v1;
1865 newFace1.n2 = newFace1.v2;
1866 newFace1.n3 = newFace1.v3;
1867 this.faces.Add(newFace1);
1860 1868
1861 newFace.v2 = iNext - numVerts; 1869 newFace2.v1 = iNext;
1862 newFace.v3 = iNext; 1870 newFace2.v2 = i - numVerts;
1863 this.faces.Add(newFace); 1871 newFace2.v3 = iNext - numVerts;
1872
1873 newFace2.n1 = newFace2.v1;
1874 newFace2.n2 = newFace2.v2;
1875 newFace2.n3 = newFace2.v3;
1876 this.faces.Add(newFace2);
1864 1877
1865 if (this.viewerMode) 1878 if (this.viewerMode)
1866 { 1879 {
@@ -1873,10 +1886,16 @@ namespace PrimMesher
1873 ViewerFace newViewerFace1 = new ViewerFace(primFaceNum); 1886 ViewerFace newViewerFace1 = new ViewerFace(primFaceNum);
1874 ViewerFace newViewerFace2 = new ViewerFace(primFaceNum); 1887 ViewerFace newViewerFace2 = new ViewerFace(primFaceNum);
1875 1888
1876 float u1 = newLayer.us[whichVert]; 1889 int uIndex = whichVert;
1890 if (!hasHollow && sides > 4 && uIndex < newLayer.us.Count - 1)
1891 {
1892 uIndex++;
1893 }
1894
1895 float u1 = newLayer.us[uIndex];
1877 float u2 = 1.0f; 1896 float u2 = 1.0f;
1878 if (whichVert < newLayer.us.Count - 1) 1897 if (uIndex < (int)newLayer.us.Count - 1)
1879 u2 = newLayer.us[whichVert + 1]; 1898 u2 = newLayer.us[uIndex + 1];
1880 1899
1881 if (whichVert == cut1Vert || whichVert == cut2Vert) 1900 if (whichVert == cut1Vert || whichVert == cut2Vert)
1882 { 1901 {
@@ -1894,13 +1913,22 @@ namespace PrimMesher
1894 u1 -= (int)u1; 1913 u1 -= (int)u1;
1895 if (u2 < 0.1f) 1914 if (u2 < 0.1f)
1896 u2 = 1.0f; 1915 u2 = 1.0f;
1897 //this.profileOuterFaceNumber = primFaceNum;
1898 } 1916 }
1899 else if (whichVert > profile.coords.Count - profile.numHollowVerts - 1) 1917 }
1918
1919 if (this.sphereMode)
1920 {
1921 if (whichVert != cut1Vert && whichVert != cut2Vert)
1900 { 1922 {
1901 u1 *= 2.0f; 1923 u1 = u1 * 2.0f - 1.0f;
1902 u2 *= 2.0f; 1924 u2 = u2 * 2.0f - 1.0f;
1903 //this.profileHollowFaceNumber = primFaceNum; 1925
1926 if (whichVert >= newLayer.numOuterVerts)
1927 {
1928 u1 -= hollow;
1929 u2 -= hollow;
1930 }
1931
1904 } 1932 }
1905 } 1933 }
1906 1934
@@ -1908,37 +1936,39 @@ namespace PrimMesher
1908 newViewerFace1.uv2.U = u1; 1936 newViewerFace1.uv2.U = u1;
1909 newViewerFace1.uv3.U = u2; 1937 newViewerFace1.uv3.U = u2;
1910 1938
1911 newViewerFace1.uv1.V = 1.0f - node.percentOfPath; 1939 newViewerFace1.uv1.V = thisV;
1912 newViewerFace1.uv2.V = lastV; 1940 newViewerFace1.uv2.V = lastV;
1913 newViewerFace1.uv3.V = lastV; 1941 newViewerFace1.uv3.V = thisV;
1914 1942
1915 newViewerFace2.uv1.U = u1; 1943 newViewerFace2.uv1.U = u2;
1916 newViewerFace2.uv2.U = u2; 1944 newViewerFace2.uv2.U = u1;
1917 newViewerFace2.uv3.U = u2; 1945 newViewerFace2.uv3.U = u2;
1918 1946
1919 newViewerFace2.uv1.V = 1.0f - node.percentOfPath; 1947 newViewerFace2.uv1.V = thisV;
1920 newViewerFace2.uv2.V = lastV; 1948 newViewerFace2.uv2.V = lastV;
1921 newViewerFace2.uv3.V = 1.0f - node.percentOfPath; 1949 newViewerFace2.uv3.V = lastV;
1922 1950
1923 newViewerFace1.v1 = this.coords[i]; 1951 newViewerFace1.v1 = this.coords[newFace1.v1];
1924 newViewerFace1.v2 = this.coords[i - numVerts]; 1952 newViewerFace1.v2 = this.coords[newFace1.v2];
1925 newViewerFace1.v3 = this.coords[iNext - numVerts]; 1953 newViewerFace1.v3 = this.coords[newFace1.v3];
1926 1954
1927 newViewerFace2.v1 = this.coords[i]; 1955 newViewerFace2.v1 = this.coords[newFace2.v1];
1928 newViewerFace2.v2 = this.coords[iNext - numVerts]; 1956 newViewerFace2.v2 = this.coords[newFace2.v2];
1929 newViewerFace2.v3 = this.coords[iNext]; 1957 newViewerFace2.v3 = this.coords[newFace2.v3];
1930 1958
1931 newViewerFace1.coordIndex1 = i; 1959 newViewerFace1.coordIndex1 = newFace1.v1;
1932 newViewerFace1.coordIndex2 = i - numVerts; 1960 newViewerFace1.coordIndex2 = newFace1.v2;
1933 newViewerFace1.coordIndex3 = iNext - numVerts; 1961 newViewerFace1.coordIndex3 = newFace1.v3;
1934 1962
1935 newViewerFace2.coordIndex1 = i; 1963 newViewerFace2.coordIndex1 = newFace2.v1;
1936 newViewerFace2.coordIndex2 = iNext - numVerts; 1964 newViewerFace2.coordIndex2 = newFace2.v2;
1937 newViewerFace2.coordIndex3 = iNext; 1965 newViewerFace2.coordIndex3 = newFace2.v3;
1938 1966
1939 // profile cut faces 1967 // profile cut faces
1940 if (whichVert == cut1Vert) 1968 if (whichVert == cut1Vert)
1941 { 1969 {
1970 newViewerFace1.primFaceNumber = cut1FaceNumber;
1971 newViewerFace2.primFaceNumber = cut1FaceNumber;
1942 newViewerFace1.n1 = newLayer.cutNormal1; 1972 newViewerFace1.n1 = newLayer.cutNormal1;
1943 newViewerFace1.n2 = newViewerFace1.n3 = lastCutNormal1; 1973 newViewerFace1.n2 = newViewerFace1.n3 = lastCutNormal1;
1944 1974
@@ -1947,10 +1977,14 @@ namespace PrimMesher
1947 } 1977 }
1948 else if (whichVert == cut2Vert) 1978 else if (whichVert == cut2Vert)
1949 { 1979 {
1980 newViewerFace1.primFaceNumber = cut2FaceNumber;
1981 newViewerFace2.primFaceNumber = cut2FaceNumber;
1950 newViewerFace1.n1 = newLayer.cutNormal2; 1982 newViewerFace1.n1 = newLayer.cutNormal2;
1951 newViewerFace1.n2 = newViewerFace1.n3 = lastCutNormal2; 1983 newViewerFace1.n2 = lastCutNormal2;
1984 newViewerFace1.n3 = lastCutNormal2;
1952 1985
1953 newViewerFace2.n1 = newViewerFace2.n3 = newLayer.cutNormal2; 1986 newViewerFace2.n1 = newLayer.cutNormal2;
1987 newViewerFace2.n3 = newLayer.cutNormal2;
1954 newViewerFace2.n2 = lastCutNormal2; 1988 newViewerFace2.n2 = lastCutNormal2;
1955 } 1989 }
1956 1990
@@ -1963,13 +1997,13 @@ namespace PrimMesher
1963 } 1997 }
1964 else 1998 else
1965 { 1999 {
1966 newViewerFace1.n1 = this.normals[i]; 2000 newViewerFace1.n1 = this.normals[newFace1.n1];
1967 newViewerFace1.n2 = this.normals[i - numVerts]; 2001 newViewerFace1.n2 = this.normals[newFace1.n2];
1968 newViewerFace1.n3 = this.normals[iNext - numVerts]; 2002 newViewerFace1.n3 = this.normals[newFace1.n3];
1969 2003
1970 newViewerFace2.n1 = this.normals[i]; 2004 newViewerFace2.n1 = this.normals[newFace2.n1];
1971 newViewerFace2.n2 = this.normals[iNext - numVerts]; 2005 newViewerFace2.n2 = this.normals[newFace2.n2];
1972 newViewerFace2.n3 = this.normals[iNext]; 2006 newViewerFace2.n3 = this.normals[newFace2.n3];
1973 } 2007 }
1974 } 2008 }
1975 2009
@@ -1982,14 +2016,13 @@ namespace PrimMesher
1982 2016
1983 lastCutNormal1 = newLayer.cutNormal1; 2017 lastCutNormal1 = newLayer.cutNormal1;
1984 lastCutNormal2 = newLayer.cutNormal2; 2018 lastCutNormal2 = newLayer.cutNormal2;
1985 lastV = 1.0f - node.percentOfPath; 2019 lastV = thisV;
1986 2020
1987 if (needEndFaces && nodeIndex == path.pathNodes.Count - 1 && viewerMode) 2021 if (needEndFaces && nodeIndex == path.pathNodes.Count - 1 && viewerMode)
1988 { 2022 {
1989 // add the top faces to the viewerFaces list here 2023 // add the top faces to the viewerFaces list here
1990 Coord faceNormal = newLayer.faceNormal; 2024 Coord faceNormal = newLayer.faceNormal;
1991 ViewerFace newViewerFace = new ViewerFace(); 2025 ViewerFace newViewerFace = new ViewerFace(0);
1992 newViewerFace.primFaceNumber = 0;
1993 int numFaces = newLayer.faces.Count; 2026 int numFaces = newLayer.faces.Count;
1994 List<Face> faces = newLayer.faces; 2027 List<Face> faces = newLayer.faces;
1995 2028
@@ -2012,6 +2045,13 @@ namespace PrimMesher
2012 newViewerFace.uv2 = newLayer.faceUVs[face.v2 - coordsLen]; 2045 newViewerFace.uv2 = newLayer.faceUVs[face.v2 - coordsLen];
2013 newViewerFace.uv3 = newLayer.faceUVs[face.v3 - coordsLen]; 2046 newViewerFace.uv3 = newLayer.faceUVs[face.v3 - coordsLen];
2014 2047
2048 if (pathType == PathType.Linear)
2049 {
2050 newViewerFace.uv1.Flip();
2051 newViewerFace.uv2.Flip();
2052 newViewerFace.uv3.Flip();
2053 }
2054
2015 this.viewerFaces.Add(newViewerFace); 2055 this.viewerFaces.Add(newViewerFace);
2016 } 2056 }
2017 } 2057 }
diff --git a/OpenSim/Region/Physics/OdePlugin/ODECharacter.cs b/OpenSim/Region/Physics/OdePlugin/ODECharacter.cs
index 8397eb4..f3b0630 100644
--- a/OpenSim/Region/Physics/OdePlugin/ODECharacter.cs
+++ b/OpenSim/Region/Physics/OdePlugin/ODECharacter.cs
@@ -1261,14 +1261,20 @@ namespace OpenSim.Region.Physics.OdePlugin
1261 { 1261 {
1262 m_requestedUpdateFrequency = ms; 1262 m_requestedUpdateFrequency = ms;
1263 m_eventsubscription = ms; 1263 m_eventsubscription = ms;
1264 CollisionEventsThisFrame.Clear(); 1264
1265 // Don't clear collision event reporting here. This is called directly from scene code and so can lead
1266 // to a race condition with the simulate loop
1267
1265 _parent_scene.AddCollisionEventReporting(this); 1268 _parent_scene.AddCollisionEventReporting(this);
1266 } 1269 }
1267 1270
1268 public override void UnSubscribeEvents() 1271 public override void UnSubscribeEvents()
1269 { 1272 {
1270 CollisionEventsThisFrame.Clear();
1271 _parent_scene.RemoveCollisionEventReporting(this); 1273 _parent_scene.RemoveCollisionEventReporting(this);
1274
1275 // Don't clear collision event reporting here. This is called directly from scene code and so can lead
1276 // to a race condition with the simulate loop
1277
1272 m_requestedUpdateFrequency = 0; 1278 m_requestedUpdateFrequency = 0;
1273 m_eventsubscription = 0; 1279 m_eventsubscription = 0;
1274 } 1280 }
diff --git a/OpenSim/Region/Physics/OdePlugin/ODEPrim.cs b/OpenSim/Region/Physics/OdePlugin/ODEPrim.cs
index 6f37347..a41c856 100644
--- a/OpenSim/Region/Physics/OdePlugin/ODEPrim.cs
+++ b/OpenSim/Region/Physics/OdePlugin/ODEPrim.cs
@@ -156,7 +156,15 @@ namespace OpenSim.Region.Physics.OdePlugin
156 /// </summary> 156 /// </summary>
157 public IntPtr m_targetSpace = IntPtr.Zero; 157 public IntPtr m_targetSpace = IntPtr.Zero;
158 158
159 /// <summary>
160 /// The prim geometry, used for collision detection.
161 /// </summary>
162 /// <remarks>
163 /// This is never null except for a brief period when the geometry needs to be replaced (due to resizing or
164 /// mesh change) or when the physical prim is being removed from the scene.
165 /// </remarks>
159 public IntPtr prim_geom { get; private set; } 166 public IntPtr prim_geom { get; private set; }
167
160 public IntPtr _triMeshData { get; private set; } 168 public IntPtr _triMeshData { get; private set; }
161 169
162 private IntPtr _linkJointGroup = IntPtr.Zero; 170 private IntPtr _linkJointGroup = IntPtr.Zero;
@@ -325,14 +333,12 @@ namespace OpenSim.Region.Physics.OdePlugin
325 { 333 {
326 prim_geom = geom; 334 prim_geom = geom;
327//Console.WriteLine("SetGeom to " + prim_geom + " for " + Name); 335//Console.WriteLine("SetGeom to " + prim_geom + " for " + Name);
328 if (prim_geom != IntPtr.Zero)
329 {
330 d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories);
331 d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags);
332 336
333 _parent_scene.geom_name_map[prim_geom] = Name; 337 d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories);
334 _parent_scene.actor_name_map[prim_geom] = this; 338 d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags);
335 } 339
340 _parent_scene.geom_name_map[prim_geom] = Name;
341 _parent_scene.actor_name_map[prim_geom] = this;
336 342
337 if (childPrim) 343 if (childPrim)
338 { 344 {
@@ -765,11 +771,8 @@ namespace OpenSim.Region.Physics.OdePlugin
765 m_collisionCategories &= ~CollisionCategories.Body; 771 m_collisionCategories &= ~CollisionCategories.Body;
766 m_collisionFlags &= ~(CollisionCategories.Wind | CollisionCategories.Land); 772 m_collisionFlags &= ~(CollisionCategories.Wind | CollisionCategories.Land);
767 773
768 if (prim_geom != IntPtr.Zero) 774 d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories);
769 { 775 d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags);
770 d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories);
771 d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags);
772 }
773 776
774 d.BodyDestroy(Body); 777 d.BodyDestroy(Body);
775 lock (childrenPrim) 778 lock (childrenPrim)
@@ -793,11 +796,8 @@ namespace OpenSim.Region.Physics.OdePlugin
793 m_collisionCategories &= ~CollisionCategories.Body; 796 m_collisionCategories &= ~CollisionCategories.Body;
794 m_collisionFlags &= ~(CollisionCategories.Wind | CollisionCategories.Land); 797 m_collisionFlags &= ~(CollisionCategories.Wind | CollisionCategories.Land);
795 798
796 if (prim_geom != IntPtr.Zero) 799 d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories);
797 { 800 d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags);
798 d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories);
799 d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags);
800 }
801 801
802 Body = IntPtr.Zero; 802 Body = IntPtr.Zero;
803 } 803 }
@@ -864,10 +864,7 @@ namespace OpenSim.Region.Physics.OdePlugin
864// _parent_scene.waitForSpaceUnlock(m_targetSpace); 864// _parent_scene.waitForSpaceUnlock(m_targetSpace);
865 try 865 try
866 { 866 {
867 if (prim_geom == IntPtr.Zero) 867 SetGeom(d.CreateTriMesh(m_targetSpace, _triMeshData, parent_scene.triCallback, null, null));
868 {
869 SetGeom(d.CreateTriMesh(m_targetSpace, _triMeshData, parent_scene.triCallback, null, null));
870 }
871 } 868 }
872 catch (AccessViolationException) 869 catch (AccessViolationException)
873 { 870 {
@@ -890,73 +887,67 @@ namespace OpenSim.Region.Physics.OdePlugin
890#if SPAM 887#if SPAM
891Console.WriteLine("ZProcessTaints for " + Name); 888Console.WriteLine("ZProcessTaints for " + Name);
892#endif 889#endif
890
891 // This must be processed as the very first taint so that later operations have a prim_geom to work with
892 // if this is a new prim.
893 if (m_taintadd) 893 if (m_taintadd)
894 {
895 changeadd(); 894 changeadd();
896 }
897
898 if (prim_geom != IntPtr.Zero)
899 {
900 if (!_position.ApproxEquals(m_taintposition, 0f))
901 changemove();
902 895
903 if (m_taintrot != _orientation) 896 if (!_position.ApproxEquals(m_taintposition, 0f))
904 { 897 changemove();
905 if (childPrim && IsPhysical) // For physical child prim... 898
906 { 899 if (m_taintrot != _orientation)
907 rotate(); 900 {
908 // KF: ODE will also rotate the parent prim! 901 if (childPrim && IsPhysical) // For physical child prim...
909 // so rotate the root back to where it was 902 {
910 OdePrim parent = (OdePrim)_parent; 903 rotate();
911 parent.rotate(); 904 // KF: ODE will also rotate the parent prim!
912 } 905 // so rotate the root back to where it was
913 else 906 OdePrim parent = (OdePrim)_parent;
914 { 907 parent.rotate();
915 //Just rotate the prim
916 rotate();
917 }
918 } 908 }
919 909 else
920 if (m_taintPhysics != IsPhysical && !(m_taintparent != _parent)) 910 {
921 changePhysicsStatus(); 911 //Just rotate the prim
912 rotate();
913 }
914 }
915
916 if (m_taintPhysics != IsPhysical && !(m_taintparent != _parent))
917 changePhysicsStatus();
922 918
923 if (!_size.ApproxEquals(m_taintsize, 0f)) 919 if (!_size.ApproxEquals(m_taintsize, 0f))
924 changesize(); 920 changesize();
925 921
926 if (m_taintshape) 922 if (m_taintshape)
927 changeshape(); 923 changeshape();
928 924
929 if (m_taintforce) 925 if (m_taintforce)
930 changeAddForce(); 926 changeAddForce();
931 927
932 if (m_taintaddangularforce) 928 if (m_taintaddangularforce)
933 changeAddAngularForce(); 929 changeAddAngularForce();
934 930
935 if (!m_taintTorque.ApproxEquals(Vector3.Zero, 0.001f)) 931 if (!m_taintTorque.ApproxEquals(Vector3.Zero, 0.001f))
936 changeSetTorque(); 932 changeSetTorque();
937 933
938 if (m_taintdisable) 934 if (m_taintdisable)
939 changedisable(); 935 changedisable();
940 936
941 if (m_taintselected != m_isSelected) 937 if (m_taintselected != m_isSelected)
942 changeSelectedStatus(); 938 changeSelectedStatus();
943 939
944 if (!m_taintVelocity.ApproxEquals(Vector3.Zero, 0.001f)) 940 if (!m_taintVelocity.ApproxEquals(Vector3.Zero, 0.001f))
945 changevelocity(); 941 changevelocity();
946 942
947 if (m_taintparent != _parent) 943 if (m_taintparent != _parent)
948 changelink(); 944 changelink();
949 945
950 if (m_taintCollidesWater != m_collidesWater) 946 if (m_taintCollidesWater != m_collidesWater)
951 changefloatonwater(); 947 changefloatonwater();
952 948
953 if (!m_angularlock.ApproxEquals(m_taintAngularLock,0f)) 949 if (!m_angularlock.ApproxEquals(m_taintAngularLock,0f))
954 changeAngularLock(); 950 changeAngularLock();
955 }
956 else
957 {
958 m_log.ErrorFormat("[PHYSICS]: The scene reused a disposed PhysActor for {0}! *waves finger*, Don't be evil. A couple of things can cause this. An improper prim breakdown(be sure to set prim_geom to zero after d.GeomDestroy! An improper buildup (creating the geom failed). Or, the Scene Reused a physics actor after disposing it.)", Name);
959 }
960 } 951 }
961 952
962 /// <summary> 953 /// <summary>
@@ -1052,150 +1043,146 @@ Console.WriteLine("ZProcessTaints for " + Name);
1052 /// <param name="prim">Child prim</param> 1043 /// <param name="prim">Child prim</param>
1053 private void AddChildPrim(OdePrim prim) 1044 private void AddChildPrim(OdePrim prim)
1054 { 1045 {
1055//Console.WriteLine("AddChildPrim " + Name); 1046 if (LocalID == prim.LocalID)
1056 if (LocalID != prim.LocalID) 1047 return;
1048
1049 if (Body == IntPtr.Zero)
1057 { 1050 {
1058 if (Body == IntPtr.Zero) 1051 Body = d.BodyCreate(_parent_scene.world);
1052 setMass();
1053 }
1054
1055 lock (childrenPrim)
1056 {
1057 if (childrenPrim.Contains(prim))
1058 return;
1059
1060// m_log.DebugFormat(
1061// "[ODE PRIM]: Linking prim {0} {1} to {2} {3}", prim.Name, prim.LocalID, Name, LocalID);
1062
1063 childrenPrim.Add(prim);
1064
1065 foreach (OdePrim prm in childrenPrim)
1059 { 1066 {
1060 Body = d.BodyCreate(_parent_scene.world); 1067 d.Mass m2;
1061 setMass(); 1068 d.MassSetZero(out m2);
1069 d.MassSetBoxTotal(out m2, prim.CalculateMass(), prm._size.X, prm._size.Y, prm._size.Z);
1070
1071 d.Quaternion quat = new d.Quaternion();
1072 quat.W = prm._orientation.W;
1073 quat.X = prm._orientation.X;
1074 quat.Y = prm._orientation.Y;
1075 quat.Z = prm._orientation.Z;
1076
1077 d.Matrix3 mat = new d.Matrix3();
1078 d.RfromQ(out mat, ref quat);
1079 d.MassRotate(ref m2, ref mat);
1080 d.MassTranslate(ref m2, Position.X - prm.Position.X, Position.Y - prm.Position.Y, Position.Z - prm.Position.Z);
1081 d.MassAdd(ref pMass, ref m2);
1062 } 1082 }
1063 if (Body != IntPtr.Zero) 1083
1084 foreach (OdePrim prm in childrenPrim)
1064 { 1085 {
1065 lock (childrenPrim) 1086 prm.m_collisionCategories |= CollisionCategories.Body;
1066 { 1087 prm.m_collisionFlags |= (CollisionCategories.Land | CollisionCategories.Wind);
1067 if (!childrenPrim.Contains(prim))
1068 {
1069//Console.WriteLine("childrenPrim.Add " + prim);
1070 childrenPrim.Add(prim);
1071
1072 foreach (OdePrim prm in childrenPrim)
1073 {
1074 d.Mass m2;
1075 d.MassSetZero(out m2);
1076 d.MassSetBoxTotal(out m2, prim.CalculateMass(), prm._size.X, prm._size.Y, prm._size.Z);
1077
1078 d.Quaternion quat = new d.Quaternion();
1079 quat.W = prm._orientation.W;
1080 quat.X = prm._orientation.X;
1081 quat.Y = prm._orientation.Y;
1082 quat.Z = prm._orientation.Z;
1083
1084 d.Matrix3 mat = new d.Matrix3();
1085 d.RfromQ(out mat, ref quat);
1086 d.MassRotate(ref m2, ref mat);
1087 d.MassTranslate(ref m2, Position.X - prm.Position.X, Position.Y - prm.Position.Y, Position.Z - prm.Position.Z);
1088 d.MassAdd(ref pMass, ref m2);
1089 }
1090
1091 foreach (OdePrim prm in childrenPrim)
1092 {
1093 prm.m_collisionCategories |= CollisionCategories.Body;
1094 prm.m_collisionFlags |= (CollisionCategories.Land | CollisionCategories.Wind);
1095 1088
1096 if (prm.prim_geom == IntPtr.Zero)
1097 {
1098 m_log.WarnFormat(
1099 "[PHYSICS]: Unable to link one of the linkset elements {0} for parent {1}. No geom yet",
1100 prm.Name, prim.Name);
1101 continue;
1102 }
1103//Console.WriteLine(" GeomSetCategoryBits 1: " + prm.prim_geom + " - " + (int)prm.m_collisionCategories + " for " + Name); 1089//Console.WriteLine(" GeomSetCategoryBits 1: " + prm.prim_geom + " - " + (int)prm.m_collisionCategories + " for " + Name);
1104 d.GeomSetCategoryBits(prm.prim_geom, (int)prm.m_collisionCategories); 1090 d.GeomSetCategoryBits(prm.prim_geom, (int)prm.m_collisionCategories);
1105 d.GeomSetCollideBits(prm.prim_geom, (int)prm.m_collisionFlags); 1091 d.GeomSetCollideBits(prm.prim_geom, (int)prm.m_collisionFlags);
1106
1107 1092
1108 d.Quaternion quat = new d.Quaternion(); 1093 d.Quaternion quat = new d.Quaternion();
1109 quat.W = prm._orientation.W; 1094 quat.W = prm._orientation.W;
1110 quat.X = prm._orientation.X; 1095 quat.X = prm._orientation.X;
1111 quat.Y = prm._orientation.Y; 1096 quat.Y = prm._orientation.Y;
1112 quat.Z = prm._orientation.Z; 1097 quat.Z = prm._orientation.Z;
1113 1098
1114 d.Matrix3 mat = new d.Matrix3(); 1099 d.Matrix3 mat = new d.Matrix3();
1115 d.RfromQ(out mat, ref quat); 1100 d.RfromQ(out mat, ref quat);
1116 if (Body != IntPtr.Zero) 1101 if (Body != IntPtr.Zero)
1117 { 1102 {
1118 d.GeomSetBody(prm.prim_geom, Body); 1103 d.GeomSetBody(prm.prim_geom, Body);
1119 prm.childPrim = true; 1104 prm.childPrim = true;
1120 d.GeomSetOffsetWorldPosition(prm.prim_geom, prm.Position.X , prm.Position.Y, prm.Position.Z); 1105 d.GeomSetOffsetWorldPosition(prm.prim_geom, prm.Position.X , prm.Position.Y, prm.Position.Z);
1121 //d.GeomSetOffsetPosition(prim.prim_geom, 1106 //d.GeomSetOffsetPosition(prim.prim_geom,
1122 // (Position.X - prm.Position.X) - pMass.c.X, 1107 // (Position.X - prm.Position.X) - pMass.c.X,
1123 // (Position.Y - prm.Position.Y) - pMass.c.Y, 1108 // (Position.Y - prm.Position.Y) - pMass.c.Y,
1124 // (Position.Z - prm.Position.Z) - pMass.c.Z); 1109 // (Position.Z - prm.Position.Z) - pMass.c.Z);
1125 d.GeomSetOffsetWorldRotation(prm.prim_geom, ref mat); 1110 d.GeomSetOffsetWorldRotation(prm.prim_geom, ref mat);
1126 //d.GeomSetOffsetRotation(prm.prim_geom, ref mat); 1111 //d.GeomSetOffsetRotation(prm.prim_geom, ref mat);
1127 d.MassTranslate(ref pMass, -pMass.c.X, -pMass.c.Y, -pMass.c.Z); 1112 d.MassTranslate(ref pMass, -pMass.c.X, -pMass.c.Y, -pMass.c.Z);
1128 d.BodySetMass(Body, ref pMass); 1113 d.BodySetMass(Body, ref pMass);
1129 } 1114 }
1130 else 1115 else
1131 { 1116 {
1132 m_log.DebugFormat("[PHYSICS]: {0} ain't got no boooooooooddy, no body", Name); 1117 m_log.DebugFormat("[PHYSICS]: {0} ain't got no boooooooooddy, no body", Name);
1133 } 1118 }
1134 1119
1135 prm.m_interpenetrationcount = 0; 1120 prm.m_interpenetrationcount = 0;
1136 prm.m_collisionscore = 0; 1121 prm.m_collisionscore = 0;
1137 prm.m_disabled = false; 1122 prm.m_disabled = false;
1138 1123
1139 // The body doesn't already have a finite rotation mode set here 1124 // The body doesn't already have a finite rotation mode set here
1140 if ((!m_angularlock.ApproxEquals(Vector3.Zero, 0f)) && _parent == null) 1125 if ((!m_angularlock.ApproxEquals(Vector3.Zero, 0f)) && _parent == null)
1141 { 1126 {
1142 prm.createAMotor(m_angularlock); 1127 prm.createAMotor(m_angularlock);
1143 } 1128 }
1144 prm.Body = Body; 1129 prm.Body = Body;
1145 _parent_scene.ActivatePrim(prm); 1130 _parent_scene.ActivatePrim(prm);
1146 } 1131 }
1147 1132
1148 m_collisionCategories |= CollisionCategories.Body; 1133 m_collisionCategories |= CollisionCategories.Body;
1149 m_collisionFlags |= (CollisionCategories.Land | CollisionCategories.Wind); 1134 m_collisionFlags |= (CollisionCategories.Land | CollisionCategories.Wind);
1150 1135
1151//Console.WriteLine("GeomSetCategoryBits 2: " + prim_geom + " - " + (int)m_collisionCategories + " for " + Name); 1136//Console.WriteLine("GeomSetCategoryBits 2: " + prim_geom + " - " + (int)m_collisionCategories + " for " + Name);
1152 d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories); 1137 d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories);
1153//Console.WriteLine(" Post GeomSetCategoryBits 2"); 1138//Console.WriteLine(" Post GeomSetCategoryBits 2");
1154 d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags); 1139 d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags);
1155
1156 d.Quaternion quat2 = new d.Quaternion();
1157 quat2.W = _orientation.W;
1158 quat2.X = _orientation.X;
1159 quat2.Y = _orientation.Y;
1160 quat2.Z = _orientation.Z;
1161
1162 d.Matrix3 mat2 = new d.Matrix3();
1163 d.RfromQ(out mat2, ref quat2);
1164 d.GeomSetBody(prim_geom, Body);
1165 d.GeomSetOffsetWorldPosition(prim_geom, Position.X - pMass.c.X, Position.Y - pMass.c.Y, Position.Z - pMass.c.Z);
1166 //d.GeomSetOffsetPosition(prim.prim_geom,
1167 // (Position.X - prm.Position.X) - pMass.c.X,
1168 // (Position.Y - prm.Position.Y) - pMass.c.Y,
1169 // (Position.Z - prm.Position.Z) - pMass.c.Z);
1170 //d.GeomSetOffsetRotation(prim_geom, ref mat2);
1171 d.MassTranslate(ref pMass, -pMass.c.X, -pMass.c.Y, -pMass.c.Z);
1172 d.BodySetMass(Body, ref pMass);
1173
1174 d.BodySetAutoDisableFlag(Body, true);
1175 d.BodySetAutoDisableSteps(Body, body_autodisable_frames);
1176 1140
1177 m_interpenetrationcount = 0; 1141 d.Quaternion quat2 = new d.Quaternion();
1178 m_collisionscore = 0; 1142 quat2.W = _orientation.W;
1179 m_disabled = false; 1143 quat2.X = _orientation.X;
1144 quat2.Y = _orientation.Y;
1145 quat2.Z = _orientation.Z;
1180 1146
1181 // The body doesn't already have a finite rotation mode set here 1147 d.Matrix3 mat2 = new d.Matrix3();
1182 if ((!m_angularlock.ApproxEquals(Vector3.Zero, 0f)) && _parent == null) 1148 d.RfromQ(out mat2, ref quat2);
1183 { 1149 d.GeomSetBody(prim_geom, Body);
1184 createAMotor(m_angularlock); 1150 d.GeomSetOffsetWorldPosition(prim_geom, Position.X - pMass.c.X, Position.Y - pMass.c.Y, Position.Z - pMass.c.Z);
1185 } 1151 //d.GeomSetOffsetPosition(prim.prim_geom,
1186 d.BodySetPosition(Body, Position.X, Position.Y, Position.Z); 1152 // (Position.X - prm.Position.X) - pMass.c.X,
1187 if (m_vehicle.Type != Vehicle.TYPE_NONE) 1153 // (Position.Y - prm.Position.Y) - pMass.c.Y,
1188 m_vehicle.Enable(Body, _parent_scene); 1154 // (Position.Z - prm.Position.Z) - pMass.c.Z);
1155 //d.GeomSetOffsetRotation(prim_geom, ref mat2);
1156 d.MassTranslate(ref pMass, -pMass.c.X, -pMass.c.Y, -pMass.c.Z);
1157 d.BodySetMass(Body, ref pMass);
1189 1158
1190 _parent_scene.ActivatePrim(this); 1159 d.BodySetAutoDisableFlag(Body, true);
1191 } 1160 d.BodySetAutoDisableSteps(Body, body_autodisable_frames);
1192 } 1161
1162 m_interpenetrationcount = 0;
1163 m_collisionscore = 0;
1164 m_disabled = false;
1165
1166 // The body doesn't already have a finite rotation mode set here
1167 if ((!m_angularlock.ApproxEquals(Vector3.Zero, 0f)) && _parent == null)
1168 {
1169 createAMotor(m_angularlock);
1193 } 1170 }
1171
1172 d.BodySetPosition(Body, Position.X, Position.Y, Position.Z);
1173
1174 if (m_vehicle.Type != Vehicle.TYPE_NONE)
1175 m_vehicle.Enable(Body, _parent_scene);
1176
1177 _parent_scene.ActivatePrim(this);
1194 } 1178 }
1195 } 1179 }
1196 1180
1197 private void ChildSetGeom(OdePrim odePrim) 1181 private void ChildSetGeom(OdePrim odePrim)
1198 { 1182 {
1183// m_log.DebugFormat(
1184// "[ODE PRIM]: ChildSetGeom {0} {1} for {2} {3}", odePrim.Name, odePrim.LocalID, Name, LocalID);
1185
1199 //if (IsPhysical && Body != IntPtr.Zero) 1186 //if (IsPhysical && Body != IntPtr.Zero)
1200 lock (childrenPrim) 1187 lock (childrenPrim)
1201 { 1188 {
@@ -1210,12 +1197,14 @@ Console.WriteLine("ZProcessTaints for " + Name);
1210 //prm.childPrim = false; 1197 //prm.childPrim = false;
1211 } 1198 }
1212 } 1199 }
1200
1213 disableBody(); 1201 disableBody();
1214 1202
1215 if (Body != IntPtr.Zero) 1203 // Spurious - Body == IntPtr.Zero after disableBody()
1216 { 1204// if (Body != IntPtr.Zero)
1217 _parent_scene.DeactivatePrim(this); 1205// {
1218 } 1206// _parent_scene.DeactivatePrim(this);
1207// }
1219 1208
1220 lock (childrenPrim) 1209 lock (childrenPrim)
1221 { 1210 {
@@ -1229,6 +1218,9 @@ Console.WriteLine("ZProcessTaints for " + Name);
1229 1218
1230 private void ChildDelink(OdePrim odePrim) 1219 private void ChildDelink(OdePrim odePrim)
1231 { 1220 {
1221// m_log.DebugFormat(
1222// "[ODE PRIM]: Delinking prim {0} {1} from {2} {3}", odePrim.Name, odePrim.LocalID, Name, LocalID);
1223
1232 // Okay, we have a delinked child.. need to rebuild the body. 1224 // Okay, we have a delinked child.. need to rebuild the body.
1233 lock (childrenPrim) 1225 lock (childrenPrim)
1234 { 1226 {
@@ -1243,6 +1235,7 @@ Console.WriteLine("ZProcessTaints for " + Name);
1243 //prm.childPrim = false; 1235 //prm.childPrim = false;
1244 } 1236 }
1245 } 1237 }
1238
1246 disableBody(); 1239 disableBody();
1247 1240
1248 lock (childrenPrim) 1241 lock (childrenPrim)
@@ -1251,10 +1244,11 @@ Console.WriteLine("ZProcessTaints for " + Name);
1251 childrenPrim.Remove(odePrim); 1244 childrenPrim.Remove(odePrim);
1252 } 1245 }
1253 1246
1254 if (Body != IntPtr.Zero) 1247 // Spurious - Body == IntPtr.Zero after disableBody()
1255 { 1248// if (Body != IntPtr.Zero)
1256 _parent_scene.DeactivatePrim(this); 1249// {
1257 } 1250// _parent_scene.DeactivatePrim(this);
1251// }
1258 1252
1259 lock (childrenPrim) 1253 lock (childrenPrim)
1260 { 1254 {
@@ -1303,11 +1297,8 @@ Console.WriteLine("ZProcessTaints for " + Name);
1303 disableBodySoft(); 1297 disableBodySoft();
1304 } 1298 }
1305 1299
1306 if (prim_geom != IntPtr.Zero) 1300 d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories);
1307 { 1301 d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags);
1308 d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories);
1309 d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags);
1310 }
1311 1302
1312 if (IsPhysical) 1303 if (IsPhysical)
1313 { 1304 {
@@ -1328,11 +1319,8 @@ Console.WriteLine("ZProcessTaints for " + Name);
1328 if (m_collidesWater) 1319 if (m_collidesWater)
1329 m_collisionFlags |= CollisionCategories.Water; 1320 m_collisionFlags |= CollisionCategories.Water;
1330 1321
1331 if (prim_geom != IntPtr.Zero) 1322 d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories);
1332 { 1323 d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags);
1333 d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories);
1334 d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags);
1335 }
1336 1324
1337 if (IsPhysical) 1325 if (IsPhysical)
1338 { 1326 {
@@ -1472,6 +1460,9 @@ Console.WriteLine("CreateGeom:");
1472 } 1460 }
1473 else 1461 else
1474 { 1462 {
1463 m_log.WarnFormat(
1464 "[ODE PRIM]: Called RemoveGeom() on {0} {1} where geometry was already null.", Name, LocalID);
1465
1475 return false; 1466 return false;
1476 } 1467 }
1477 } 1468 }
@@ -1505,16 +1496,13 @@ Console.WriteLine("changeadd 1");
1505#endif 1496#endif
1506 CreateGeom(m_targetSpace, mesh); 1497 CreateGeom(m_targetSpace, mesh);
1507 1498
1508 if (prim_geom != IntPtr.Zero) 1499 d.GeomSetPosition(prim_geom, _position.X, _position.Y, _position.Z);
1509 { 1500 d.Quaternion myrot = new d.Quaternion();
1510 d.GeomSetPosition(prim_geom, _position.X, _position.Y, _position.Z); 1501 myrot.X = _orientation.X;
1511 d.Quaternion myrot = new d.Quaternion(); 1502 myrot.Y = _orientation.Y;
1512 myrot.X = _orientation.X; 1503 myrot.Z = _orientation.Z;
1513 myrot.Y = _orientation.Y; 1504 myrot.W = _orientation.W;
1514 myrot.Z = _orientation.Z; 1505 d.GeomSetQuaternion(prim_geom, ref myrot);
1515 myrot.W = _orientation.W;
1516 d.GeomSetQuaternion(prim_geom, ref myrot);
1517 }
1518 1506
1519 if (IsPhysical && Body == IntPtr.Zero) 1507 if (IsPhysical && Body == IntPtr.Zero)
1520 enableBody(); 1508 enableBody();
@@ -1579,24 +1567,20 @@ Console.WriteLine(" JointCreateFixed");
1579 //m_log.Debug("[BUG]: race!"); 1567 //m_log.Debug("[BUG]: race!");
1580 //} 1568 //}
1581 } 1569 }
1582 else
1583 {
1584 // string primScenAvatarIn = _parent_scene.whichspaceamIin(_position);
1585 // int[] arrayitem = _parent_scene.calculateSpaceArrayItemFromPos(_position);
1586// _parent_scene.waitForSpaceUnlock(m_targetSpace);
1587 1570
1588 IntPtr tempspace = _parent_scene.recalculateSpaceForGeom(prim_geom, _position, m_targetSpace); 1571 // string primScenAvatarIn = _parent_scene.whichspaceamIin(_position);
1589 m_targetSpace = tempspace; 1572 // int[] arrayitem = _parent_scene.calculateSpaceArrayItemFromPos(_position);
1573// _parent_scene.waitForSpaceUnlock(m_targetSpace);
1574
1575 IntPtr tempspace = _parent_scene.recalculateSpaceForGeom(prim_geom, _position, m_targetSpace);
1576 m_targetSpace = tempspace;
1590 1577
1591// _parent_scene.waitForSpaceUnlock(m_targetSpace); 1578// _parent_scene.waitForSpaceUnlock(m_targetSpace);
1592 if (prim_geom != IntPtr.Zero) 1579
1593 { 1580 d.GeomSetPosition(prim_geom, _position.X, _position.Y, _position.Z);
1594 d.GeomSetPosition(prim_geom, _position.X, _position.Y, _position.Z);
1595 1581
1596// _parent_scene.waitForSpaceUnlock(m_targetSpace); 1582// _parent_scene.waitForSpaceUnlock(m_targetSpace);
1597 d.SpaceAdd(m_targetSpace, prim_geom); 1583 d.SpaceAdd(m_targetSpace, prim_geom);
1598 }
1599 }
1600 1584
1601 changeSelectedStatus(); 1585 changeSelectedStatus();
1602 1586
@@ -2047,18 +2031,16 @@ Console.WriteLine(" JointCreateFixed");
2047 { 2031 {
2048 m_collidesWater = m_taintCollidesWater; 2032 m_collidesWater = m_taintCollidesWater;
2049 2033
2050 if (prim_geom != IntPtr.Zero) 2034 if (m_collidesWater)
2051 { 2035 {
2052 if (m_collidesWater) 2036 m_collisionFlags |= CollisionCategories.Water;
2053 {
2054 m_collisionFlags |= CollisionCategories.Water;
2055 }
2056 else
2057 {
2058 m_collisionFlags &= ~CollisionCategories.Water;
2059 }
2060 d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags);
2061 } 2037 }
2038 else
2039 {
2040 m_collisionFlags &= ~CollisionCategories.Water;
2041 }
2042
2043 d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags);
2062 } 2044 }
2063 2045
2064 /// <summary> 2046 /// <summary>
diff --git a/OpenSim/Region/Physics/OdePlugin/OdeScene.cs b/OpenSim/Region/Physics/OdePlugin/OdeScene.cs
index 842ff91..32e81e2 100644
--- a/OpenSim/Region/Physics/OdePlugin/OdeScene.cs
+++ b/OpenSim/Region/Physics/OdePlugin/OdeScene.cs
@@ -30,20 +30,21 @@
30 30
31using System; 31using System;
32using System.Collections.Generic; 32using System.Collections.Generic;
33using System.Diagnostics;
34using System.IO;
35using System.Linq;
33using System.Reflection; 36using System.Reflection;
34using System.Runtime.InteropServices; 37using System.Runtime.InteropServices;
35using System.Threading; 38using System.Threading;
36using System.IO;
37using System.Diagnostics;
38using log4net; 39using log4net;
39using Nini.Config; 40using Nini.Config;
40using Ode.NET; 41using Ode.NET;
42using OpenMetaverse;
41#if USE_DRAWSTUFF 43#if USE_DRAWSTUFF
42using Drawstuff.NET; 44using Drawstuff.NET;
43#endif 45#endif
44using OpenSim.Framework; 46using OpenSim.Framework;
45using OpenSim.Region.Physics.Manager; 47using OpenSim.Region.Physics.Manager;
46using OpenMetaverse;
47 48
48namespace OpenSim.Region.Physics.OdePlugin 49namespace OpenSim.Region.Physics.OdePlugin
49{ 50{
@@ -54,15 +55,15 @@ namespace OpenSim.Region.Physics.OdePlugin
54 End = 2 55 End = 2
55 } 56 }
56 57
57 public struct sCollisionData 58// public struct sCollisionData
58 { 59// {
59 public uint ColliderLocalId; 60// public uint ColliderLocalId;
60 public uint CollidedWithLocalId; 61// public uint CollidedWithLocalId;
61 public int NumberOfCollisions; 62// public int NumberOfCollisions;
62 public int CollisionType; 63// public int CollisionType;
63 public int StatusIndicator; 64// public int StatusIndicator;
64 public int lastframe; 65// public int lastframe;
65 } 66// }
66 67
67 [Flags] 68 [Flags]
68 public enum CollisionCategories : int 69 public enum CollisionCategories : int
@@ -131,6 +132,135 @@ namespace OpenSim.Region.Physics.OdePlugin
131 /// </remarks> 132 /// </remarks>
132 internal static Object UniversalColliderSyncObject = new Object(); 133 internal static Object UniversalColliderSyncObject = new Object();
133 134
135 /// <summary>
136 /// Is stats collecting enabled for this ODE scene?
137 /// </summary>
138 public bool CollectStats { get; set; }
139
140 /// <summary>
141 /// Statistics for this scene.
142 /// </summary>
143 private Dictionary<string, float> m_stats = new Dictionary<string, float>();
144
145 /// <summary>
146 /// Stat name for total number of avatars in this ODE scene.
147 /// </summary>
148 public const string ODETotalAvatarsStatName = "ODETotalAvatars";
149
150 /// <summary>
151 /// Stat name for total number of prims in this ODE scene.
152 /// </summary>
153 public const string ODETotalPrimsStatName = "ODETotalPrims";
154
155 /// <summary>
156 /// Stat name for total number of prims with active physics in this ODE scene.
157 /// </summary>
158 public const string ODEActivePrimsStatName = "ODEActivePrims";
159
160 /// <summary>
161 /// Stat name for the total time spent in ODE frame processing.
162 /// </summary>
163 /// <remarks>
164 /// A sanity check for the main scene loop physics time.
165 /// </remarks>
166 public const string ODETotalFrameMsStatName = "ODETotalFrameMS";
167
168 /// <summary>
169 /// Stat name for time spent processing avatar taints per frame
170 /// </summary>
171 public const string ODEAvatarTaintMsStatName = "ODEAvatarTaintFrameMS";
172
173 /// <summary>
174 /// Stat name for time spent processing prim taints per frame
175 /// </summary>
176 public const string ODEPrimTaintMsStatName = "ODEPrimTaintFrameMS";
177
178 /// <summary>
179 /// Stat name for time spent calculating avatar forces per frame.
180 /// </summary>
181 public const string ODEAvatarForcesFrameMsStatName = "ODEAvatarForcesFrameMS";
182
183 /// <summary>
184 /// Stat name for time spent calculating prim forces per frame
185 /// </summary>
186 public const string ODEPrimForcesFrameMsStatName = "ODEPrimForcesFrameMS";
187
188 /// <summary>
189 /// Stat name for time spent fulfilling raycasting requests per frame
190 /// </summary>
191 public const string ODERaycastingFrameMsStatName = "ODERaycastingFrameMS";
192
193 /// <summary>
194 /// Stat name for time spent in native code that actually steps through the simulation.
195 /// </summary>
196 public const string ODENativeStepFrameMsStatName = "ODENativeStepFrameMS";
197
198 /// <summary>
199 /// Stat name for the number of milliseconds that ODE spends in native space collision code.
200 /// </summary>
201 public const string ODENativeSpaceCollisionFrameMsStatName = "ODENativeSpaceCollisionFrameMS";
202
203 /// <summary>
204 /// Stat name for milliseconds that ODE spends in native geom collision code.
205 /// </summary>
206 public const string ODENativeGeomCollisionFrameMsStatName = "ODENativeGeomCollisionFrameMS";
207
208 /// <summary>
209 /// Time spent in collision processing that is not spent in native space or geom collision code.
210 /// </summary>
211 public const string ODEOtherCollisionFrameMsStatName = "ODEOtherCollisionFrameMS";
212
213 /// <summary>
214 /// Stat name for time spent notifying listeners of collisions
215 /// </summary>
216 public const string ODECollisionNotificationFrameMsStatName = "ODECollisionNotificationFrameMS";
217
218 /// <summary>
219 /// Stat name for milliseconds spent updating avatar position and velocity
220 /// </summary>
221 public const string ODEAvatarUpdateFrameMsStatName = "ODEAvatarUpdateFrameMS";
222
223 /// <summary>
224 /// Stat name for the milliseconds spent updating prim position and velocity
225 /// </summary>
226 public const string ODEPrimUpdateFrameMsStatName = "ODEPrimUpdateFrameMS";
227
228 /// <summary>
229 /// Stat name for avatar collisions with another entity.
230 /// </summary>
231 public const string ODEAvatarContactsStatsName = "ODEAvatarContacts";
232
233 /// <summary>
234 /// Stat name for prim collisions with another entity.
235 /// </summary>
236 public const string ODEPrimContactsStatName = "ODEPrimContacts";
237
238 /// <summary>
239 /// Used to hold tick numbers for stat collection purposes.
240 /// </summary>
241 private int m_nativeCollisionStartTick;
242
243 /// <summary>
244 /// A messy way to tell if we need to avoid adding a collision time because this was already done in the callback.
245 /// </summary>
246 private bool m_inCollisionTiming;
247
248 /// <summary>
249 /// A temporary holder for the number of avatar collisions in a frame, so we can work out how many object
250 /// collisions occured using the _perloopcontact if stats collection is enabled.
251 /// </summary>
252 private int m_tempAvatarCollisionsThisFrame;
253
254 /// <summary>
255 /// Used in calculating physics frame time dilation
256 /// </summary>
257 private int tickCountFrameRun;
258
259 /// <summary>
260 /// Used in calculating physics frame time dilation
261 /// </summary>
262 private int latertickcount;
263
134 private Random fluidRandomizer = new Random(Environment.TickCount); 264 private Random fluidRandomizer = new Random(Environment.TickCount);
135 265
136 private const uint m_regionWidth = Constants.RegionSize; 266 private const uint m_regionWidth = Constants.RegionSize;
@@ -257,12 +387,12 @@ namespace OpenSim.Region.Physics.OdePlugin
257 /// <summary> 387 /// <summary>
258 /// A dictionary of actors that should receive collision events. 388 /// A dictionary of actors that should receive collision events.
259 /// </summary> 389 /// </summary>
260 private readonly Dictionary<uint, PhysicsActor> _collisionEventPrim = new Dictionary<uint, PhysicsActor>(); 390 private readonly Dictionary<uint, PhysicsActor> m_collisionEventActors = new Dictionary<uint, PhysicsActor>();
261 391
262 /// <summary> 392 /// <summary>
263 /// A dictionary of collision event changes that are waiting to be processed. 393 /// A dictionary of collision event changes that are waiting to be processed.
264 /// </summary> 394 /// </summary>
265 private readonly Dictionary<uint, PhysicsActor> _collisionEventPrimChanges = new Dictionary<uint, PhysicsActor>(); 395 private readonly Dictionary<uint, PhysicsActor> m_collisionEventActorsChanges = new Dictionary<uint, PhysicsActor>();
266 396
267 /// <summary> 397 /// <summary>
268 /// Maps a unique geometry id (a memory location) to a physics actor name. 398 /// Maps a unique geometry id (a memory location) to a physics actor name.
@@ -345,9 +475,6 @@ namespace OpenSim.Region.Physics.OdePlugin
345 private OdePrim cp1; 475 private OdePrim cp1;
346 private OdeCharacter cc2; 476 private OdeCharacter cc2;
347 private OdePrim cp2; 477 private OdePrim cp2;
348 private int tickCountFrameRun;
349
350 private int latertickcount=0;
351 //private int cStartStop = 0; 478 //private int cStartStop = 0;
352 //private string cDictKey = ""; 479 //private string cDictKey = "";
353 480
@@ -440,6 +567,8 @@ namespace OpenSim.Region.Physics.OdePlugin
440 // Initialize the mesh plugin 567 // Initialize the mesh plugin
441 public override void Initialise(IMesher meshmerizer, IConfigSource config) 568 public override void Initialise(IMesher meshmerizer, IConfigSource config)
442 { 569 {
570 InitializeExtraStats();
571
443 mesher = meshmerizer; 572 mesher = meshmerizer;
444 m_config = config; 573 m_config = config;
445 // Defaults 574 // Defaults
@@ -464,6 +593,8 @@ namespace OpenSim.Region.Physics.OdePlugin
464 IConfig physicsconfig = m_config.Configs["ODEPhysicsSettings"]; 593 IConfig physicsconfig = m_config.Configs["ODEPhysicsSettings"];
465 if (physicsconfig != null) 594 if (physicsconfig != null)
466 { 595 {
596 CollectStats = physicsconfig.GetBoolean("collect_stats", false);
597
467 gravityx = physicsconfig.GetFloat("world_gravityx", 0f); 598 gravityx = physicsconfig.GetFloat("world_gravityx", 0f);
468 gravityy = physicsconfig.GetFloat("world_gravityy", 0f); 599 gravityy = physicsconfig.GetFloat("world_gravityy", 0f);
469 gravityz = physicsconfig.GetFloat("world_gravityz", -9.8f); 600 gravityz = physicsconfig.GetFloat("world_gravityz", -9.8f);
@@ -765,6 +896,62 @@ namespace OpenSim.Region.Physics.OdePlugin
765 #region Collision Detection 896 #region Collision Detection
766 897
767 /// <summary> 898 /// <summary>
899 /// Collides two geometries.
900 /// </summary>
901 /// <returns></returns>
902 /// <param name='geom1'></param>
903 /// <param name='geom2'>/param>
904 /// <param name='maxContacts'></param>
905 /// <param name='contactsArray'></param>
906 /// <param name='contactGeomSize'></param>
907 private int CollideGeoms(
908 IntPtr geom1, IntPtr geom2, int maxContacts, Ode.NET.d.ContactGeom[] contactsArray, int contactGeomSize)
909 {
910 int count;
911
912 lock (OdeScene.UniversalColliderSyncObject)
913 {
914 // We do this inside the lock so that we don't count any delay in acquiring it
915 if (CollectStats)
916 m_nativeCollisionStartTick = Util.EnvironmentTickCount();
917
918 count = d.Collide(geom1, geom2, maxContacts, contactsArray, contactGeomSize);
919 }
920
921 // We do this outside the lock so that any waiting threads aren't held up, though the effect is probably
922 // negligable
923 if (CollectStats)
924 m_stats[ODENativeGeomCollisionFrameMsStatName]
925 += Util.EnvironmentTickCountSubtract(m_nativeCollisionStartTick);
926
927 return count;
928 }
929
930 /// <summary>
931 /// Collide two spaces or a space and a geometry.
932 /// </summary>
933 /// <param name='space1'></param>
934 /// <param name='space2'>/param>
935 /// <param name='data'></param>
936 private void CollideSpaces(IntPtr space1, IntPtr space2, IntPtr data)
937 {
938 if (CollectStats)
939 {
940 m_inCollisionTiming = true;
941 m_nativeCollisionStartTick = Util.EnvironmentTickCount();
942 }
943
944 d.SpaceCollide2(space1, space2, data, nearCallback);
945
946 if (CollectStats && m_inCollisionTiming)
947 {
948 m_stats[ODENativeSpaceCollisionFrameMsStatName]
949 += Util.EnvironmentTickCountSubtract(m_nativeCollisionStartTick);
950 m_inCollisionTiming = false;
951 }
952 }
953
954 /// <summary>
768 /// This is our near callback. A geometry is near a body 955 /// This is our near callback. A geometry is near a body
769 /// </summary> 956 /// </summary>
770 /// <param name="space">The space that contains the geoms. Remember, spaces are also geoms</param> 957 /// <param name="space">The space that contains the geoms. Remember, spaces are also geoms</param>
@@ -772,6 +959,13 @@ namespace OpenSim.Region.Physics.OdePlugin
772 /// <param name="g2">another geometry or space</param> 959 /// <param name="g2">another geometry or space</param>
773 private void near(IntPtr space, IntPtr g1, IntPtr g2) 960 private void near(IntPtr space, IntPtr g1, IntPtr g2)
774 { 961 {
962 if (CollectStats && m_inCollisionTiming)
963 {
964 m_stats[ODENativeSpaceCollisionFrameMsStatName]
965 += Util.EnvironmentTickCountSubtract(m_nativeCollisionStartTick);
966 m_inCollisionTiming = false;
967 }
968
775// m_log.DebugFormat("[PHYSICS]: Colliding {0} and {1} in {2}", g1, g2, space); 969// m_log.DebugFormat("[PHYSICS]: Colliding {0} and {1} in {2}", g1, g2, space);
776 // no lock here! It's invoked from within Simulate(), which is thread-locked 970 // no lock here! It's invoked from within Simulate(), which is thread-locked
777 971
@@ -789,7 +983,7 @@ namespace OpenSim.Region.Physics.OdePlugin
789 // contact points in the space 983 // contact points in the space
790 try 984 try
791 { 985 {
792 d.SpaceCollide2(g1, g2, IntPtr.Zero, nearCallback); 986 CollideSpaces(g1, g2, IntPtr.Zero);
793 } 987 }
794 catch (AccessViolationException) 988 catch (AccessViolationException)
795 { 989 {
@@ -832,6 +1026,7 @@ namespace OpenSim.Region.Physics.OdePlugin
832 1026
833 // Figure out how many contact points we have 1027 // Figure out how many contact points we have
834 int count = 0; 1028 int count = 0;
1029
835 try 1030 try
836 { 1031 {
837 // Colliding Geom To Geom 1032 // Colliding Geom To Geom
@@ -843,8 +1038,11 @@ namespace OpenSim.Region.Physics.OdePlugin
843 if (b1 != IntPtr.Zero && b2 != IntPtr.Zero && d.AreConnectedExcluding(b1, b2, d.JointType.Contact)) 1038 if (b1 != IntPtr.Zero && b2 != IntPtr.Zero && d.AreConnectedExcluding(b1, b2, d.JointType.Contact))
844 return; 1039 return;
845 1040
846 lock (OdeScene.UniversalColliderSyncObject) 1041 count = CollideGeoms(g1, g2, contacts.Length, contacts, d.ContactGeom.SizeOf);
847 count = d.Collide(g1, g2, contacts.Length, contacts, d.ContactGeom.SizeOf); 1042
1043 // All code after this is only relevant if we have any collisions
1044 if (count <= 0)
1045 return;
848 1046
849 if (count > contacts.Length) 1047 if (count > contacts.Length)
850 m_log.Error("[ODE SCENE]: Got " + count + " contacts when we asked for a maximum of " + contacts.Length); 1048 m_log.Error("[ODE SCENE]: Got " + count + " contacts when we asked for a maximum of " + contacts.Length);
@@ -1113,14 +1311,12 @@ namespace OpenSim.Region.Physics.OdePlugin
1113 { 1311 {
1114 _perloopContact.Add(curContact); 1312 _perloopContact.Add(curContact);
1115 1313
1116 // If we're colliding against terrain
1117 if (name1 == "Terrain" || name2 == "Terrain") 1314 if (name1 == "Terrain" || name2 == "Terrain")
1118 { 1315 {
1119 // If we're moving
1120 if ((p2.PhysicsActorType == (int) ActorTypes.Agent) && 1316 if ((p2.PhysicsActorType == (int) ActorTypes.Agent) &&
1121 (Math.Abs(p2.Velocity.X) > 0.01f || Math.Abs(p2.Velocity.Y) > 0.01f)) 1317 (Math.Abs(p2.Velocity.X) > 0.01f || Math.Abs(p2.Velocity.Y) > 0.01f))
1122 { 1318 {
1123 // Use the movement terrain contact 1319 // Avatar is moving on terrain, use the movement terrain contact
1124 AvatarMovementTerrainContact.geom = curContact; 1320 AvatarMovementTerrainContact.geom = curContact;
1125 1321
1126 if (m_global_contactcount < maxContactsbeforedeath) 1322 if (m_global_contactcount < maxContactsbeforedeath)
@@ -1133,7 +1329,7 @@ namespace OpenSim.Region.Physics.OdePlugin
1133 { 1329 {
1134 if (p2.PhysicsActorType == (int)ActorTypes.Agent) 1330 if (p2.PhysicsActorType == (int)ActorTypes.Agent)
1135 { 1331 {
1136 // Use the non moving terrain contact 1332 // Avatar is standing on terrain, use the non moving terrain contact
1137 TerrainContact.geom = curContact; 1333 TerrainContact.geom = curContact;
1138 1334
1139 if (m_global_contactcount < maxContactsbeforedeath) 1335 if (m_global_contactcount < maxContactsbeforedeath)
@@ -1228,13 +1424,11 @@ namespace OpenSim.Region.Physics.OdePlugin
1228 } 1424 }
1229 else 1425 else
1230 { 1426 {
1231 // we're colliding with prim or avatar
1232 // check if we're moving
1233 if ((p2.PhysicsActorType == (int)ActorTypes.Agent)) 1427 if ((p2.PhysicsActorType == (int)ActorTypes.Agent))
1234 { 1428 {
1235 if ((Math.Abs(p2.Velocity.X) > 0.01f || Math.Abs(p2.Velocity.Y) > 0.01f)) 1429 if ((Math.Abs(p2.Velocity.X) > 0.01f || Math.Abs(p2.Velocity.Y) > 0.01f))
1236 { 1430 {
1237 // Use the Movement prim contact 1431 // Avatar is moving on a prim, use the Movement prim contact
1238 AvatarMovementprimContact.geom = curContact; 1432 AvatarMovementprimContact.geom = curContact;
1239 1433
1240 if (m_global_contactcount < maxContactsbeforedeath) 1434 if (m_global_contactcount < maxContactsbeforedeath)
@@ -1245,9 +1439,8 @@ namespace OpenSim.Region.Physics.OdePlugin
1245 } 1439 }
1246 else 1440 else
1247 { 1441 {
1248 // Use the non movement contact 1442 // Avatar is standing still on a prim, use the non movement contact
1249 contact.geom = curContact; 1443 contact.geom = curContact;
1250 _perloopContact.Add(curContact);
1251 1444
1252 if (m_global_contactcount < maxContactsbeforedeath) 1445 if (m_global_contactcount < maxContactsbeforedeath)
1253 { 1446 {
@@ -1355,7 +1548,7 @@ namespace OpenSim.Region.Physics.OdePlugin
1355 break; 1548 break;
1356 } 1549 }
1357 } 1550 }
1358 //m_log.DebugFormat("[Collsion]: Depth {0}", Math.Abs(contact.depth - contactGeom.depth)); 1551 //m_log.DebugFormat("[Collision]: Depth {0}", Math.Abs(contact.depth - contactGeom.depth));
1359 //m_log.DebugFormat("[Collision]: <{0},{1},{2}>", Math.Abs(contactGeom.normal.X - contact.normal.X), Math.Abs(contactGeom.normal.Y - contact.normal.Y), Math.Abs(contactGeom.normal.Z - contact.normal.Z)); 1552 //m_log.DebugFormat("[Collision]: <{0},{1},{2}>", Math.Abs(contactGeom.normal.X - contact.normal.X), Math.Abs(contactGeom.normal.Y - contact.normal.Y), Math.Abs(contactGeom.normal.Z - contact.normal.Z));
1360 } 1553 }
1361 } 1554 }
@@ -1578,7 +1771,7 @@ namespace OpenSim.Region.Physics.OdePlugin
1578 // and we'll run it again on all of them. 1771 // and we'll run it again on all of them.
1579 try 1772 try
1580 { 1773 {
1581 d.SpaceCollide2(space, chr.Shell, IntPtr.Zero, nearCallback); 1774 CollideSpaces(space, chr.Shell, IntPtr.Zero);
1582 } 1775 }
1583 catch (AccessViolationException) 1776 catch (AccessViolationException)
1584 { 1777 {
@@ -1593,6 +1786,12 @@ namespace OpenSim.Region.Physics.OdePlugin
1593 //} 1786 //}
1594 } 1787 }
1595 1788
1789 if (CollectStats)
1790 {
1791 m_tempAvatarCollisionsThisFrame = _perloopContact.Count;
1792 m_stats[ODEAvatarContactsStatsName] += m_tempAvatarCollisionsThisFrame;
1793 }
1794
1596 List<OdePrim> removeprims = null; 1795 List<OdePrim> removeprims = null;
1597 foreach (OdePrim chr in _activeprims) 1796 foreach (OdePrim chr in _activeprims)
1598 { 1797 {
@@ -1604,7 +1803,7 @@ namespace OpenSim.Region.Physics.OdePlugin
1604 { 1803 {
1605 if (space != IntPtr.Zero && chr.prim_geom != IntPtr.Zero && chr.m_taintremove == false) 1804 if (space != IntPtr.Zero && chr.prim_geom != IntPtr.Zero && chr.m_taintremove == false)
1606 { 1805 {
1607 d.SpaceCollide2(space, chr.prim_geom, IntPtr.Zero, nearCallback); 1806 CollideSpaces(space, chr.prim_geom, IntPtr.Zero);
1608 } 1807 }
1609 else 1808 else
1610 { 1809 {
@@ -1625,6 +1824,9 @@ namespace OpenSim.Region.Physics.OdePlugin
1625 } 1824 }
1626 } 1825 }
1627 1826
1827 if (CollectStats)
1828 m_stats[ODEPrimContactsStatName] += _perloopContact.Count - m_tempAvatarCollisionsThisFrame;
1829
1628 if (removeprims != null) 1830 if (removeprims != null)
1629 { 1831 {
1630 foreach (OdePrim chr in removeprims) 1832 foreach (OdePrim chr in removeprims)
@@ -1706,8 +1908,8 @@ namespace OpenSim.Region.Physics.OdePlugin
1706 { 1908 {
1707// m_log.DebugFormat("[PHYSICS]: Adding {0} {1} to collision event reporting", obj.SOPName, obj.LocalID); 1909// m_log.DebugFormat("[PHYSICS]: Adding {0} {1} to collision event reporting", obj.SOPName, obj.LocalID);
1708 1910
1709 lock (_collisionEventPrimChanges) 1911 lock (m_collisionEventActorsChanges)
1710 _collisionEventPrimChanges[obj.LocalID] = obj; 1912 m_collisionEventActorsChanges[obj.LocalID] = obj;
1711 } 1913 }
1712 1914
1713 /// <summary> 1915 /// <summary>
@@ -1718,8 +1920,8 @@ namespace OpenSim.Region.Physics.OdePlugin
1718 { 1920 {
1719// m_log.DebugFormat("[PHYSICS]: Removing {0} {1} from collision event reporting", obj.SOPName, obj.LocalID); 1921// m_log.DebugFormat("[PHYSICS]: Removing {0} {1} from collision event reporting", obj.SOPName, obj.LocalID);
1720 1922
1721 lock (_collisionEventPrimChanges) 1923 lock (m_collisionEventActorsChanges)
1722 _collisionEventPrimChanges[obj.LocalID] = null; 1924 m_collisionEventActorsChanges[obj.LocalID] = null;
1723 } 1925 }
1724 1926
1725 #region Add/Remove Entities 1927 #region Add/Remove Entities
@@ -2226,7 +2428,8 @@ namespace OpenSim.Region.Physics.OdePlugin
2226 /// <param name="prim"></param> 2428 /// <param name="prim"></param>
2227 internal void RemovePrimThreadLocked(OdePrim prim) 2429 internal void RemovePrimThreadLocked(OdePrim prim)
2228 { 2430 {
2229//Console.WriteLine("RemovePrimThreadLocked " + prim.m_primName); 2431// m_log.DebugFormat("[ODE SCENE]: Removing physical prim {0} {1}", prim.Name, prim.LocalID);
2432
2230 lock (prim) 2433 lock (prim)
2231 { 2434 {
2232 RemoveCollisionEventReporting(prim); 2435 RemoveCollisionEventReporting(prim);
@@ -2682,21 +2885,23 @@ namespace OpenSim.Region.Physics.OdePlugin
2682 2885
2683 /// <summary> 2886 /// <summary>
2684 /// This is our main simulate loop 2887 /// This is our main simulate loop
2888 /// </summary>
2889 /// <remarks>
2685 /// It's thread locked by a Mutex in the scene. 2890 /// It's thread locked by a Mutex in the scene.
2686 /// It holds Collisions, it instructs ODE to step through the physical reactions 2891 /// It holds Collisions, it instructs ODE to step through the physical reactions
2687 /// It moves the objects around in memory 2892 /// It moves the objects around in memory
2688 /// It calls the methods that report back to the object owners.. (scenepresence, SceneObjectGroup) 2893 /// It calls the methods that report back to the object owners.. (scenepresence, SceneObjectGroup)
2689 /// </summary> 2894 /// </remarks>
2690 /// <param name="timeStep"></param> 2895 /// <param name="timeStep"></param>
2691 /// <returns></returns> 2896 /// <returns>The number of frames simulated over that period.</returns>
2692 public override float Simulate(float timeStep) 2897 public override float Simulate(float timeStep)
2693 { 2898 {
2899 int startFrameTick = CollectStats ? Util.EnvironmentTickCount() : 0;
2900 int tempTick = 0, tempTick2 = 0;
2901
2694 if (framecount >= int.MaxValue) 2902 if (framecount >= int.MaxValue)
2695 framecount = 0; 2903 framecount = 0;
2696 2904
2697 //if (m_worldOffset != Vector3.Zero)
2698 // return 0;
2699
2700 framecount++; 2905 framecount++;
2701 2906
2702 float fps = 0; 2907 float fps = 0;
@@ -2704,7 +2909,7 @@ namespace OpenSim.Region.Physics.OdePlugin
2704 float timeLeft = timeStep; 2909 float timeLeft = timeStep;
2705 2910
2706 //m_log.Info(timeStep.ToString()); 2911 //m_log.Info(timeStep.ToString());
2707// step_time += timeStep; 2912// step_time += timeSte
2708// 2913//
2709// // If We're loaded down by something else, 2914// // If We're loaded down by something else,
2710// // or debugging with the Visual Studio project on pause 2915// // or debugging with the Visual Studio project on pause
@@ -2725,17 +2930,17 @@ namespace OpenSim.Region.Physics.OdePlugin
2725 // We change _collisionEventPrimChanges to avoid locking _collisionEventPrim itself and causing potential 2930 // We change _collisionEventPrimChanges to avoid locking _collisionEventPrim itself and causing potential
2726 // deadlock if the collision event tries to lock something else later on which is already locked by a 2931 // deadlock if the collision event tries to lock something else later on which is already locked by a
2727 // caller that is adding or removing the collision event. 2932 // caller that is adding or removing the collision event.
2728 lock (_collisionEventPrimChanges) 2933 lock (m_collisionEventActorsChanges)
2729 { 2934 {
2730 foreach (KeyValuePair<uint, PhysicsActor> kvp in _collisionEventPrimChanges) 2935 foreach (KeyValuePair<uint, PhysicsActor> kvp in m_collisionEventActorsChanges)
2731 { 2936 {
2732 if (kvp.Value == null) 2937 if (kvp.Value == null)
2733 _collisionEventPrim.Remove(kvp.Key); 2938 m_collisionEventActors.Remove(kvp.Key);
2734 else 2939 else
2735 _collisionEventPrim[kvp.Key] = kvp.Value; 2940 m_collisionEventActors[kvp.Key] = kvp.Value;
2736 } 2941 }
2737 2942
2738 _collisionEventPrimChanges.Clear(); 2943 m_collisionEventActorsChanges.Clear();
2739 } 2944 }
2740 2945
2741 if (SupportsNINJAJoints) 2946 if (SupportsNINJAJoints)
@@ -2770,6 +2975,9 @@ namespace OpenSim.Region.Physics.OdePlugin
2770 { 2975 {
2771 try 2976 try
2772 { 2977 {
2978 if (CollectStats)
2979 tempTick = Util.EnvironmentTickCount();
2980
2773 lock (_taintedActors) 2981 lock (_taintedActors)
2774 { 2982 {
2775 foreach (OdeCharacter character in _taintedActors) 2983 foreach (OdeCharacter character in _taintedActors)
@@ -2778,6 +2986,13 @@ namespace OpenSim.Region.Physics.OdePlugin
2778 _taintedActors.Clear(); 2986 _taintedActors.Clear();
2779 } 2987 }
2780 2988
2989 if (CollectStats)
2990 {
2991 tempTick2 = Util.EnvironmentTickCount();
2992 m_stats[ODEAvatarTaintMsStatName] += Util.EnvironmentTickCountSubtract(tempTick2, tempTick);
2993 tempTick = tempTick2;
2994 }
2995
2781 lock (_taintedPrims) 2996 lock (_taintedPrims)
2782 { 2997 {
2783 foreach (OdePrim prim in _taintedPrims) 2998 foreach (OdePrim prim in _taintedPrims)
@@ -2808,6 +3023,13 @@ namespace OpenSim.Region.Physics.OdePlugin
2808 _taintedPrims.Clear(); 3023 _taintedPrims.Clear();
2809 } 3024 }
2810 3025
3026 if (CollectStats)
3027 {
3028 tempTick2 = Util.EnvironmentTickCount();
3029 m_stats[ODEPrimTaintMsStatName] += Util.EnvironmentTickCountSubtract(tempTick2, tempTick);
3030 tempTick = tempTick2;
3031 }
3032
2811 // Move characters 3033 // Move characters
2812 foreach (OdeCharacter actor in _characters) 3034 foreach (OdeCharacter actor in _characters)
2813 actor.Move(defects); 3035 actor.Move(defects);
@@ -2827,6 +3049,13 @@ namespace OpenSim.Region.Physics.OdePlugin
2827 defects.Clear(); 3049 defects.Clear();
2828 } 3050 }
2829 3051
3052 if (CollectStats)
3053 {
3054 tempTick2 = Util.EnvironmentTickCount();
3055 m_stats[ODEAvatarForcesFrameMsStatName] += Util.EnvironmentTickCountSubtract(tempTick2, tempTick);
3056 tempTick = tempTick2;
3057 }
3058
2830 // Move other active objects 3059 // Move other active objects
2831 foreach (OdePrim prim in _activeprims) 3060 foreach (OdePrim prim in _activeprims)
2832 { 3061 {
@@ -2834,15 +3063,36 @@ namespace OpenSim.Region.Physics.OdePlugin
2834 prim.Move(timeStep); 3063 prim.Move(timeStep);
2835 } 3064 }
2836 3065
3066 if (CollectStats)
3067 {
3068 tempTick2 = Util.EnvironmentTickCount();
3069 m_stats[ODEPrimForcesFrameMsStatName] += Util.EnvironmentTickCountSubtract(tempTick2, tempTick);
3070 tempTick = tempTick2;
3071 }
3072
2837 //if ((framecount % m_randomizeWater) == 0) 3073 //if ((framecount % m_randomizeWater) == 0)
2838 // randomizeWater(waterlevel); 3074 // randomizeWater(waterlevel);
2839 3075
2840 //int RayCastTimeMS = m_rayCastManager.ProcessQueuedRequests(); 3076 //int RayCastTimeMS = m_rayCastManager.ProcessQueuedRequests();
2841 m_rayCastManager.ProcessQueuedRequests(); 3077 m_rayCastManager.ProcessQueuedRequests();
2842 3078
3079 if (CollectStats)
3080 {
3081 tempTick2 = Util.EnvironmentTickCount();
3082 m_stats[ODERaycastingFrameMsStatName] += Util.EnvironmentTickCountSubtract(tempTick2, tempTick);
3083 tempTick = tempTick2;
3084 }
3085
2843 collision_optimized(); 3086 collision_optimized();
2844 3087
2845 foreach (PhysicsActor obj in _collisionEventPrim.Values) 3088 if (CollectStats)
3089 {
3090 tempTick2 = Util.EnvironmentTickCount();
3091 m_stats[ODEOtherCollisionFrameMsStatName] += Util.EnvironmentTickCountSubtract(tempTick2, tempTick);
3092 tempTick = tempTick2;
3093 }
3094
3095 foreach (PhysicsActor obj in m_collisionEventActors.Values)
2846 { 3096 {
2847// m_log.DebugFormat("[PHYSICS]: Assessing {0} {1} for collision events", obj.SOPName, obj.LocalID); 3097// m_log.DebugFormat("[PHYSICS]: Assessing {0} {1} for collision events", obj.SOPName, obj.LocalID);
2848 3098
@@ -2866,9 +3116,19 @@ namespace OpenSim.Region.Physics.OdePlugin
2866// "[PHYSICS]: Collision contacts to process this frame = {0}", m_global_contactcount); 3116// "[PHYSICS]: Collision contacts to process this frame = {0}", m_global_contactcount);
2867 3117
2868 m_global_contactcount = 0; 3118 m_global_contactcount = 0;
2869 3119
3120 if (CollectStats)
3121 {
3122 tempTick2 = Util.EnvironmentTickCount();
3123 m_stats[ODECollisionNotificationFrameMsStatName] += Util.EnvironmentTickCountSubtract(tempTick2, tempTick);
3124 tempTick = tempTick2;
3125 }
3126
2870 d.WorldQuickStep(world, ODE_STEPSIZE); 3127 d.WorldQuickStep(world, ODE_STEPSIZE);
2871 3128
3129 if (CollectStats)
3130 m_stats[ODENativeStepFrameMsStatName] += Util.EnvironmentTickCountSubtract(tempTick);
3131
2872 d.JointGroupEmpty(contactgroup); 3132 d.JointGroupEmpty(contactgroup);
2873 } 3133 }
2874 catch (Exception e) 3134 catch (Exception e)
@@ -2879,6 +3139,9 @@ namespace OpenSim.Region.Physics.OdePlugin
2879 timeLeft -= ODE_STEPSIZE; 3139 timeLeft -= ODE_STEPSIZE;
2880 } 3140 }
2881 3141
3142 if (CollectStats)
3143 tempTick = Util.EnvironmentTickCount();
3144
2882 foreach (OdeCharacter actor in _characters) 3145 foreach (OdeCharacter actor in _characters)
2883 { 3146 {
2884 if (actor.bad) 3147 if (actor.bad)
@@ -2902,6 +3165,13 @@ namespace OpenSim.Region.Physics.OdePlugin
2902 defects.Clear(); 3165 defects.Clear();
2903 } 3166 }
2904 3167
3168 if (CollectStats)
3169 {
3170 tempTick2 = Util.EnvironmentTickCount();
3171 m_stats[ODEAvatarUpdateFrameMsStatName] += Util.EnvironmentTickCountSubtract(tempTick2, tempTick);
3172 tempTick = tempTick2;
3173 }
3174
2905 //if (timeStep < 0.2f) 3175 //if (timeStep < 0.2f)
2906 3176
2907 foreach (OdePrim prim in _activeprims) 3177 foreach (OdePrim prim in _activeprims)
@@ -2915,6 +3185,9 @@ namespace OpenSim.Region.Physics.OdePlugin
2915 } 3185 }
2916 } 3186 }
2917 3187
3188 if (CollectStats)
3189 m_stats[ODEPrimUpdateFrameMsStatName] += Util.EnvironmentTickCountSubtract(tempTick);
3190
2918 //DumpJointInfo(); 3191 //DumpJointInfo();
2919 3192
2920 // Finished with all sim stepping. If requested, dump world state to file for debugging. 3193 // Finished with all sim stepping. If requested, dump world state to file for debugging.
@@ -2936,7 +3209,7 @@ namespace OpenSim.Region.Physics.OdePlugin
2936 d.WorldExportDIF(world, fname, physics_logging_append_existing_logfile, prefix); 3209 d.WorldExportDIF(world, fname, physics_logging_append_existing_logfile, prefix);
2937 } 3210 }
2938 3211
2939 latertickcount = Util.EnvironmentTickCount() - tickCountFrameRun; 3212 latertickcount = Util.EnvironmentTickCountSubtract(tickCountFrameRun);
2940 3213
2941 // OpenSimulator above does 10 fps. 10 fps = means that the main thread loop and physics 3214 // OpenSimulator above does 10 fps. 10 fps = means that the main thread loop and physics
2942 // has a max of 100 ms to run theoretically. 3215 // has a max of 100 ms to run theoretically.
@@ -2954,6 +3227,9 @@ namespace OpenSim.Region.Physics.OdePlugin
2954 } 3227 }
2955 3228
2956 tickCountFrameRun = Util.EnvironmentTickCount(); 3229 tickCountFrameRun = Util.EnvironmentTickCount();
3230
3231 if (CollectStats)
3232 m_stats[ODETotalFrameMsStatName] += Util.EnvironmentTickCountSubtract(startFrameTick);
2957 } 3233 }
2958 3234
2959 return fps; 3235 return fps;
@@ -3189,7 +3465,7 @@ namespace OpenSim.Region.Physics.OdePlugin
3189 public override bool IsThreaded 3465 public override bool IsThreaded
3190 { 3466 {
3191 // for now we won't be multithreaded 3467 // for now we won't be multithreaded
3192 get { return (false); } 3468 get { return false; }
3193 } 3469 }
3194 3470
3195 #region ODE Specific Terrain Fixes 3471 #region ODE Specific Terrain Fixes
@@ -3765,26 +4041,19 @@ namespace OpenSim.Region.Physics.OdePlugin
3765 4041
3766 public override Dictionary<uint, float> GetTopColliders() 4042 public override Dictionary<uint, float> GetTopColliders()
3767 { 4043 {
3768 Dictionary<uint, float> returncolliders = new Dictionary<uint, float>(); 4044 Dictionary<uint, float> topColliders;
3769 int cnt = 0; 4045
3770 lock (_prims) 4046 lock (_prims)
3771 { 4047 {
3772 foreach (OdePrim prm in _prims) 4048 List<OdePrim> orderedPrims = new List<OdePrim>(_prims);
3773 { 4049 orderedPrims.OrderByDescending(p => p.CollisionScore).Take(25);
3774 if (prm.CollisionScore > 0) 4050 topColliders = orderedPrims.ToDictionary(p => p.LocalID, p => p.CollisionScore);
3775 { 4051
3776 returncolliders.Add(prm.LocalID, prm.CollisionScore); 4052 foreach (OdePrim p in _prims)
3777 cnt++; 4053 p.CollisionScore = 0;
3778 prm.CollisionScore = 0f;
3779 if (cnt > 25)
3780 {
3781 break;
3782 }
3783 }
3784 }
3785 } 4054 }
3786 4055
3787 return returncolliders; 4056 return topColliders;
3788 } 4057 }
3789 4058
3790 public override bool SupportsRayCast() 4059 public override bool SupportsRayCast()
@@ -3954,5 +4223,52 @@ namespace OpenSim.Region.Physics.OdePlugin
3954 ds.SetViewpoint(ref xyz, ref hpr); 4223 ds.SetViewpoint(ref xyz, ref hpr);
3955 } 4224 }
3956#endif 4225#endif
4226
4227 public override Dictionary<string, float> GetStats()
4228 {
4229 if (!CollectStats)
4230 return null;
4231
4232 Dictionary<string, float> returnStats;
4233
4234 lock (OdeLock)
4235 {
4236 returnStats = new Dictionary<string, float>(m_stats);
4237
4238 // FIXME: This is a SUPER DUMB HACK until we can establish stats that aren't subject to a division by
4239 // 3 from the SimStatsReporter.
4240 returnStats[ODETotalAvatarsStatName] = _characters.Count * 3;
4241 returnStats[ODETotalPrimsStatName] = _prims.Count * 3;
4242 returnStats[ODEActivePrimsStatName] = _activeprims.Count * 3;
4243
4244 InitializeExtraStats();
4245 }
4246
4247 returnStats[ODEOtherCollisionFrameMsStatName]
4248 = returnStats[ODEOtherCollisionFrameMsStatName]
4249 - returnStats[ODENativeSpaceCollisionFrameMsStatName]
4250 - returnStats[ODENativeGeomCollisionFrameMsStatName];
4251
4252 return returnStats;
4253 }
4254
4255 private void InitializeExtraStats()
4256 {
4257 m_stats[ODETotalFrameMsStatName] = 0;
4258 m_stats[ODEAvatarTaintMsStatName] = 0;
4259 m_stats[ODEPrimTaintMsStatName] = 0;
4260 m_stats[ODEAvatarForcesFrameMsStatName] = 0;
4261 m_stats[ODEPrimForcesFrameMsStatName] = 0;
4262 m_stats[ODERaycastingFrameMsStatName] = 0;
4263 m_stats[ODENativeStepFrameMsStatName] = 0;
4264 m_stats[ODENativeSpaceCollisionFrameMsStatName] = 0;
4265 m_stats[ODENativeGeomCollisionFrameMsStatName] = 0;
4266 m_stats[ODEOtherCollisionFrameMsStatName] = 0;
4267 m_stats[ODECollisionNotificationFrameMsStatName] = 0;
4268 m_stats[ODEAvatarContactsStatsName] = 0;
4269 m_stats[ODEPrimContactsStatName] = 0;
4270 m_stats[ODEAvatarUpdateFrameMsStatName] = 0;
4271 m_stats[ODEPrimUpdateFrameMsStatName] = 0;
4272 }
3957 } 4273 }
3958} \ No newline at end of file 4274} \ No newline at end of file