diff options
Diffstat (limited to '')
-rw-r--r-- | OpenSim/Region/Physics/OdePlugin/OdePlugin.cs | 527 |
1 files changed, 452 insertions, 75 deletions
diff --git a/OpenSim/Region/Physics/OdePlugin/OdePlugin.cs b/OpenSim/Region/Physics/OdePlugin/OdePlugin.cs index 9ac43bf..4bd36aa 100644 --- a/OpenSim/Region/Physics/OdePlugin/OdePlugin.cs +++ b/OpenSim/Region/Physics/OdePlugin/OdePlugin.cs | |||
@@ -34,7 +34,6 @@ using OpenSim.Framework; | |||
34 | using OpenSim.Region.Physics.Manager; | 34 | using OpenSim.Region.Physics.Manager; |
35 | using OpenSim.Region.Physics.OdePlugin.Meshing; | 35 | using OpenSim.Region.Physics.OdePlugin.Meshing; |
36 | 36 | ||
37 | |||
38 | namespace OpenSim.Region.Physics.OdePlugin | 37 | namespace OpenSim.Region.Physics.OdePlugin |
39 | { | 38 | { |
40 | /// <summary> | 39 | /// <summary> |
@@ -84,13 +83,18 @@ namespace OpenSim.Region.Physics.OdePlugin | |||
84 | public d.TriArrayCallback triArrayCallback; | 83 | public d.TriArrayCallback triArrayCallback; |
85 | private List<OdeCharacter> _characters = new List<OdeCharacter>(); | 84 | private List<OdeCharacter> _characters = new List<OdeCharacter>(); |
86 | private List<OdePrim> _prims = new List<OdePrim>(); | 85 | private List<OdePrim> _prims = new List<OdePrim>(); |
86 | private List<OdePrim> _activeprims = new List<OdePrim>(); | ||
87 | public Dictionary<IntPtr, String> geom_name_map = new Dictionary<IntPtr, String>(); | 87 | public Dictionary<IntPtr, String> geom_name_map = new Dictionary<IntPtr, String>(); |
88 | public Dictionary<IntPtr, PhysicsActor> actor_name_map = new Dictionary<IntPtr, PhysicsActor>(); | 88 | public Dictionary<IntPtr, PhysicsActor> actor_name_map = new Dictionary<IntPtr, PhysicsActor>(); |
89 | private d.ContactGeom[] contacts = new d.ContactGeom[30]; | 89 | private d.ContactGeom[] contacts = new d.ContactGeom[30]; |
90 | private d.Contact contact; | 90 | private d.Contact contact; |
91 | private d.Contact TerrainContact; | ||
92 | private int m_physicsiterations = 10; | ||
93 | private float m_SkipFramesAtms = 0.40f; // Drop frames gracefully at a 400 ms lag | ||
91 | private PhysicsActor PANull = new NullPhysicsActor(); | 94 | private PhysicsActor PANull = new NullPhysicsActor(); |
92 | private float step_time = 0.0f; | 95 | private float step_time = 0.0f; |
93 | public IntPtr world; | 96 | public IntPtr world; |
97 | |||
94 | public IntPtr space; | 98 | public IntPtr space; |
95 | public static Object OdeLock = new Object(); | 99 | public static Object OdeLock = new Object(); |
96 | 100 | ||
@@ -106,17 +110,25 @@ namespace OpenSim.Region.Physics.OdePlugin | |||
106 | contact.surface.soft_erp = 0.005f; | 110 | contact.surface.soft_erp = 0.005f; |
107 | contact.surface.soft_cfm = 0.00003f; | 111 | contact.surface.soft_cfm = 0.00003f; |
108 | */ | 112 | */ |
113 | |||
109 | contact.surface.mu = 250.0f; | 114 | contact.surface.mu = 250.0f; |
110 | contact.surface.bounce = 0.2f; | 115 | contact.surface.bounce = 0.2f; |
116 | TerrainContact.surface.mode |= d.ContactFlags.SoftERP; | ||
117 | TerrainContact.surface.mu = 250.0f; | ||
118 | TerrainContact.surface.bounce = 0.1f; | ||
119 | TerrainContact.surface.soft_erp = 0.1025f; | ||
120 | |||
111 | lock (OdeLock) | 121 | lock (OdeLock) |
112 | { | 122 | { |
113 | world = d.WorldCreate(); | 123 | world = d.WorldCreate(); |
114 | space = d.HashSpaceCreate(IntPtr.Zero); | 124 | space = d.HashSpaceCreate(IntPtr.Zero); |
115 | contactgroup = d.JointGroupCreate(0); | 125 | contactgroup = d.JointGroupCreate(0); |
126 | //contactgroup | ||
127 | |||
116 | d.WorldSetGravity(world, 0.0f, 0.0f, -10.0f); | 128 | d.WorldSetGravity(world, 0.0f, 0.0f, -10.0f); |
117 | d.WorldSetAutoDisableFlag(world, false); | 129 | d.WorldSetAutoDisableFlag(world, false); |
118 | d.WorldSetContactSurfaceLayer(world, 0.001f); | 130 | d.WorldSetContactSurfaceLayer(world, 0.001f); |
119 | d.WorldSetQuickStepNumIterations(world, 10); | 131 | d.WorldSetQuickStepNumIterations(world, m_physicsiterations); |
120 | d.WorldSetContactMaxCorrectingVel(world, 1000.0f); | 132 | d.WorldSetContactMaxCorrectingVel(world, 1000.0f); |
121 | } | 133 | } |
122 | 134 | ||
@@ -127,6 +139,7 @@ namespace OpenSim.Region.Physics.OdePlugin | |||
127 | // This function blatantly ripped off from BoxStack.cs | 139 | // This function blatantly ripped off from BoxStack.cs |
128 | private void near(IntPtr space, IntPtr g1, IntPtr g2) | 140 | private void near(IntPtr space, IntPtr g1, IntPtr g2) |
129 | { | 141 | { |
142 | |||
130 | // no lock here! It's invoked from within Simulate(), which is thread-locked | 143 | // no lock here! It's invoked from within Simulate(), which is thread-locked |
131 | IntPtr b1 = d.GeomGetBody(g1); | 144 | IntPtr b1 = d.GeomGetBody(g1); |
132 | IntPtr b2 = d.GeomGetBody(g2); | 145 | IntPtr b2 = d.GeomGetBody(g2); |
@@ -142,6 +155,19 @@ namespace OpenSim.Region.Physics.OdePlugin | |||
142 | 155 | ||
143 | 156 | ||
144 | d.GeomClassID id = d.GeomGetClass(g1); | 157 | d.GeomClassID id = d.GeomGetClass(g1); |
158 | |||
159 | String name1 = null; | ||
160 | String name2 = null; | ||
161 | |||
162 | if (!geom_name_map.TryGetValue(g1, out name1)) | ||
163 | { | ||
164 | name1 = "null"; | ||
165 | } | ||
166 | if (!geom_name_map.TryGetValue(g2, out name2)) | ||
167 | { | ||
168 | name2 = "null"; | ||
169 | } | ||
170 | |||
145 | if (id == d.GeomClassID.TriMeshClass) | 171 | if (id == d.GeomClassID.TriMeshClass) |
146 | { | 172 | { |
147 | 173 | ||
@@ -149,33 +175,47 @@ namespace OpenSim.Region.Physics.OdePlugin | |||
149 | // MainLog.Instance.Verbose("near: A collision was detected between {1} and {2}", 0, name1, name2); | 175 | // MainLog.Instance.Verbose("near: A collision was detected between {1} and {2}", 0, name1, name2); |
150 | //System.Console.WriteLine("near: A collision was detected between {1} and {2}", 0, name1, name2); | 176 | //System.Console.WriteLine("near: A collision was detected between {1} and {2}", 0, name1, name2); |
151 | } | 177 | } |
152 | 178 | ||
153 | int count = d.Collide(g1, g2, contacts.GetLength(0), contacts, d.ContactGeom.SizeOf); | 179 | int count; |
180 | |||
181 | count = d.Collide(g1, g2, contacts.GetLength(0), contacts, d.ContactGeom.SizeOf); | ||
182 | |||
154 | for (int i = 0; i < count; i++) | 183 | for (int i = 0; i < count; i++) |
155 | { | 184 | { |
156 | contact.geom = contacts[i]; | 185 | IntPtr joint; |
157 | IntPtr joint = d.JointCreateContact(world, contactgroup, ref contact); | 186 | // If we're colliding with terrain, use 'TerrainContact' instead of contact. |
158 | d.JointAttach(joint, b1, b2); | 187 | // allows us to have different settings |
159 | PhysicsActor p1; | 188 | if (name1 == "Terrain" || name2 == "Terrain") |
160 | PhysicsActor p2; | ||
161 | |||
162 | |||
163 | if (!actor_name_map.TryGetValue(g1, out p1)) | ||
164 | { | 189 | { |
165 | p1 = PANull; | 190 | |
191 | TerrainContact.geom = contacts[i]; | ||
192 | joint = d.JointCreateContact(world, contactgroup, ref TerrainContact); | ||
193 | |||
166 | } | 194 | } |
195 | else | ||
196 | { | ||
197 | contact.geom = contacts[i]; | ||
198 | joint = d.JointCreateContact(world, contactgroup, ref contact); | ||
199 | } | ||
200 | |||
201 | |||
202 | d.JointAttach(joint, b1, b2); | ||
203 | |||
204 | |||
205 | PhysicsActor p2; | ||
206 | |||
167 | if (!actor_name_map.TryGetValue(g2, out p2)) | 207 | if (!actor_name_map.TryGetValue(g2, out p2)) |
168 | { | 208 | { |
169 | p2 = PANull; | 209 | p2 = PANull; |
170 | } | 210 | } |
171 | 211 | ||
172 | p1.IsColliding = true; | 212 | // We only need to test p2 for 'jump crouch purposes' |
173 | p2.IsColliding = true; | 213 | p2.IsColliding = true; |
174 | //System.Console.WriteLine("near: A collision was detected between {1} and {2}", 0, name1, name2); | 214 | //System.Console.WriteLine("near: A collision was detected between {1} and {2}", 0, name1, name2); |
175 | } | 215 | } |
176 | } | 216 | } |
177 | 217 | ||
178 | private void collision_optimized() | 218 | private void collision_optimized(float timeStep) |
179 | { | 219 | { |
180 | foreach (OdeCharacter chr in _characters) | 220 | foreach (OdeCharacter chr in _characters) |
181 | { | 221 | { |
@@ -183,17 +223,45 @@ namespace OpenSim.Region.Physics.OdePlugin | |||
183 | } | 223 | } |
184 | foreach (OdeCharacter chr in _characters) | 224 | foreach (OdeCharacter chr in _characters) |
185 | { | 225 | { |
226 | |||
186 | 227 | ||
187 | 228 | ||
188 | |||
189 | d.SpaceCollide2(space, chr.Shell, IntPtr.Zero, nearCallback); | 229 | d.SpaceCollide2(space, chr.Shell, IntPtr.Zero, nearCallback); |
190 | foreach (OdeCharacter ch2 in _characters) | 230 | foreach (OdeCharacter ch2 in _characters) |
191 | /// should be a separate space -- lots of avatars will be N**2 slow | 231 | /// should be a separate space -- lots of avatars will be N**2 slow |
192 | { | 232 | { |
233 | |||
193 | 234 | ||
194 | |||
195 | d.SpaceCollide2(chr.Shell, ch2.Shell, IntPtr.Zero, nearCallback); | 235 | d.SpaceCollide2(chr.Shell, ch2.Shell, IntPtr.Zero, nearCallback); |
196 | } | 236 | } |
237 | |||
238 | } | ||
239 | // If the sim is running slow this frame, | ||
240 | // don't process collision for prim! | ||
241 | if (timeStep < (m_SkipFramesAtms / 2)) | ||
242 | { | ||
243 | foreach (OdePrim chr in _activeprims) | ||
244 | { | ||
245 | // This if may not need to be there.. it might be skipped anyway. | ||
246 | if (d.BodyIsEnabled(chr.Body)) | ||
247 | { | ||
248 | d.SpaceCollide2(space, chr.prim_geom, IntPtr.Zero, nearCallback); | ||
249 | foreach (OdePrim ch2 in _prims) | ||
250 | /// should be a separate space -- lots of avatars will be N**2 slow | ||
251 | { | ||
252 | if (ch2.IsPhysical && d.BodyIsEnabled(ch2.Body)) | ||
253 | { | ||
254 | // Only test prim that are 0.03 meters away in one direction. | ||
255 | // This should be Optimized! | ||
256 | |||
257 | if ((Math.Abs(ch2.Position.X - chr.Position.X) < 0.03) || (Math.Abs(ch2.Position.Y - chr.Position.Y) < 0.03) || (Math.Abs(ch2.Position.X - chr.Position.X) < 0.03)) | ||
258 | { | ||
259 | d.SpaceCollide2(chr.prim_geom, ch2.prim_geom, IntPtr.Zero, nearCallback); | ||
260 | } | ||
261 | } | ||
262 | } | ||
263 | } | ||
264 | } | ||
197 | } | 265 | } |
198 | } | 266 | } |
199 | 267 | ||
@@ -223,14 +291,21 @@ namespace OpenSim.Region.Physics.OdePlugin | |||
223 | { | 291 | { |
224 | lock (OdeLock) | 292 | lock (OdeLock) |
225 | { | 293 | { |
226 | d.GeomDestroy(((OdePrim) prim).prim_geom); | 294 | if (prim.IsPhysical) |
227 | _prims.Remove((OdePrim) prim); | 295 | { |
296 | OdePrim p; | ||
297 | p = (OdePrim) prim; | ||
298 | p.disableBody(); | ||
299 | } | ||
300 | d.GeomDestroy(((OdePrim)prim).prim_geom); | ||
301 | _prims.Remove((OdePrim)prim); | ||
302 | |||
228 | } | 303 | } |
229 | } | 304 | } |
230 | } | 305 | } |
231 | 306 | ||
232 | private PhysicsActor AddPrim(String name, PhysicsVector position, PhysicsVector size, Quaternion rotation, | 307 | private PhysicsActor AddPrim(String name, PhysicsVector position, PhysicsVector size, Quaternion rotation, |
233 | Mesh mesh, PrimitiveBaseShape pbs) | 308 | Mesh mesh, PrimitiveBaseShape pbs, bool isphysical) |
234 | { | 309 | { |
235 | PhysicsVector pos = new PhysicsVector(); | 310 | PhysicsVector pos = new PhysicsVector(); |
236 | pos.X = position.X; | 311 | pos.X = position.X; |
@@ -248,13 +323,27 @@ namespace OpenSim.Region.Physics.OdePlugin | |||
248 | OdePrim newPrim; | 323 | OdePrim newPrim; |
249 | lock (OdeLock) | 324 | lock (OdeLock) |
250 | { | 325 | { |
251 | newPrim = new OdePrim(name, this, pos, siz, rot, mesh, pbs); | 326 | newPrim = new OdePrim(name, this, pos, siz, rot, mesh, pbs, isphysical); |
252 | } | 327 | } |
253 | _prims.Add(newPrim); | 328 | _prims.Add(newPrim); |
254 | return newPrim; | 329 | return newPrim; |
255 | } | 330 | } |
256 | 331 | ||
257 | 332 | public void addActivePrim(OdePrim activatePrim) | |
333 | { | ||
334 | // adds active prim.. (ones that should be iterated over in collisions_optimized | ||
335 | lock (OdeLock) | ||
336 | { | ||
337 | _activeprims.Add(activatePrim); | ||
338 | } | ||
339 | } | ||
340 | public void remActivePrim(OdePrim deactivatePrim) | ||
341 | { | ||
342 | lock (OdeLock) | ||
343 | { | ||
344 | _activeprims.Remove(deactivatePrim); | ||
345 | } | ||
346 | } | ||
258 | public int TriArrayCallback(IntPtr trimesh, IntPtr refObject, int[] triangleIndex, int triCount) | 347 | public int TriArrayCallback(IntPtr trimesh, IntPtr refObject, int[] triangleIndex, int triCount) |
259 | { | 348 | { |
260 | /* String name1 = null; | 349 | /* String name1 = null; |
@@ -276,7 +365,6 @@ namespace OpenSim.Region.Physics.OdePlugin | |||
276 | 365 | ||
277 | public int TriCallback(IntPtr trimesh, IntPtr refObject, int triangleIndex) | 366 | public int TriCallback(IntPtr trimesh, IntPtr refObject, int triangleIndex) |
278 | { | 367 | { |
279 | /* | ||
280 | String name1 = null; | 368 | String name1 = null; |
281 | String name2 = null; | 369 | String name2 = null; |
282 | 370 | ||
@@ -297,7 +385,7 @@ namespace OpenSim.Region.Physics.OdePlugin | |||
297 | 385 | ||
298 | d.GeomTriMeshGetTriangle(trimesh, 0, ref v0, ref v1, ref v2); | 386 | d.GeomTriMeshGetTriangle(trimesh, 0, ref v0, ref v1, ref v2); |
299 | // MainLog.Instance.Debug("Triangle {0} is <{1},{2},{3}>, <{4},{5},{6}>, <{7},{8},{9}>", triangleIndex, v0.X, v0.Y, v0.Z, v1.X, v1.Y, v1.Z, v2.X, v2.Y, v2.Z); | 387 | // MainLog.Instance.Debug("Triangle {0} is <{1},{2},{3}>, <{4},{5},{6}>, <{7},{8},{9}>", triangleIndex, v0.X, v0.Y, v0.Z, v1.X, v1.Y, v1.Z, v2.X, v2.Y, v2.Z); |
300 | */ | 388 | |
301 | return 1; | 389 | return 1; |
302 | } | 390 | } |
303 | 391 | ||
@@ -318,7 +406,6 @@ namespace OpenSim.Region.Physics.OdePlugin | |||
318 | { | 406 | { |
319 | return this.AddPrimShape(primName, pbs, position, size, rotation, false); | 407 | return this.AddPrimShape(primName, pbs, position, size, rotation, false); |
320 | } | 408 | } |
321 | |||
322 | public override PhysicsActor AddPrimShape(string primName, PrimitiveBaseShape pbs, PhysicsVector position, | 409 | public override PhysicsActor AddPrimShape(string primName, PrimitiveBaseShape pbs, PhysicsVector position, |
323 | PhysicsVector size, Quaternion rotation, bool isPhysical) | 410 | PhysicsVector size, Quaternion rotation, bool isPhysical) |
324 | { | 411 | { |
@@ -337,12 +424,13 @@ namespace OpenSim.Region.Physics.OdePlugin | |||
337 | break; | 424 | break; |
338 | } | 425 | } |
339 | 426 | ||
340 | result = AddPrim(primName, position, size, rotation, mesh, pbs); | 427 | result = AddPrim(primName, position, size, rotation, mesh, pbs, isPhysical); |
341 | 428 | ||
342 | 429 | ||
343 | return result; | 430 | return result; |
344 | } | 431 | } |
345 | 432 | ||
433 | |||
346 | public override void Simulate(float timeStep) | 434 | public override void Simulate(float timeStep) |
347 | { | 435 | { |
348 | step_time += timeStep; | 436 | step_time += timeStep; |
@@ -367,17 +455,45 @@ namespace OpenSim.Region.Physics.OdePlugin | |||
367 | rx.z + "," + ry.z + "," + rz.z); | 455 | rx.z + "," + ry.z + "," + rz.z); |
368 | } | 456 | } |
369 | } | 457 | } |
458 | |||
459 | |||
460 | // If We're loaded down by something else, | ||
461 | // or debugging with the Visual Studio project on pause | ||
462 | // skip a few frames to catch up gracefully. | ||
463 | // without shooting the physicsactors all over the place | ||
464 | |||
465 | if (step_time >= m_SkipFramesAtms) | ||
466 | { | ||
467 | // Instead of trying to catch up, it'll do one physics frame only | ||
468 | step_time = ODE_STEPSIZE; | ||
469 | this.m_physicsiterations = 5; | ||
470 | } | ||
471 | else | ||
472 | { | ||
473 | m_physicsiterations = 10; | ||
474 | } | ||
475 | // Process 10 frames if the sim is running normal.. | ||
476 | // process 5 frames if the sim is running slow | ||
477 | d.WorldSetQuickStepNumIterations(world, m_physicsiterations); | ||
478 | |||
370 | int i = 0; | 479 | int i = 0; |
371 | while (step_time > 0.0f) | 480 | while (step_time > 0.0f) |
372 | { | 481 | { |
373 | foreach (OdeCharacter actor in _characters) | 482 | foreach (OdeCharacter actor in _characters) |
374 | { | 483 | { |
375 | actor.Move(timeStep); | 484 | actor.Move(timeStep); |
485 | actor.collidelock = true; | ||
376 | } | 486 | } |
377 | 487 | ||
378 | collision_optimized(); | 488 | |
489 | collision_optimized(timeStep); | ||
379 | d.WorldQuickStep(world, ODE_STEPSIZE); | 490 | d.WorldQuickStep(world, ODE_STEPSIZE); |
380 | d.JointGroupEmpty(contactgroup); | 491 | d.JointGroupEmpty(contactgroup); |
492 | foreach (OdeCharacter actor in _characters) | ||
493 | { | ||
494 | actor.collidelock = false; | ||
495 | } | ||
496 | |||
381 | step_time -= ODE_STEPSIZE; | 497 | step_time -= ODE_STEPSIZE; |
382 | i++; | 498 | i++; |
383 | } | 499 | } |
@@ -414,6 +530,16 @@ namespace OpenSim.Region.Physics.OdePlugin | |||
414 | "1,0,0, 0,1,0, 0,0,1"); // rotation | 530 | "1,0,0, 0,1,0, 0,0,1"); // rotation |
415 | } | 531 | } |
416 | } | 532 | } |
533 | if (timeStep < 0.2f) | ||
534 | { | ||
535 | foreach (OdePrim actor in _activeprims) | ||
536 | { | ||
537 | if (actor.IsPhysical && (d.BodyIsEnabled(actor.Body) || !actor._zeroFlag)) | ||
538 | { | ||
539 | actor.UpdatePositionAndVelocity(); | ||
540 | } | ||
541 | } | ||
542 | } | ||
417 | } | 543 | } |
418 | } | 544 | } |
419 | 545 | ||
@@ -495,7 +621,7 @@ namespace OpenSim.Region.Physics.OdePlugin | |||
495 | private static float PID_P = 7000.0f; | 621 | private static float PID_P = 7000.0f; |
496 | private static float POSTURE_SERVO = 10000.0f; | 622 | private static float POSTURE_SERVO = 10000.0f; |
497 | public static float CAPSULE_RADIUS = 0.5f; | 623 | public static float CAPSULE_RADIUS = 0.5f; |
498 | public static float CAPSULE_LENGTH = 0.9f; | 624 | public static float CAPSULE_LENGTH = 0.79f; |
499 | private bool flying = false; | 625 | private bool flying = false; |
500 | private bool iscolliding = false; | 626 | private bool iscolliding = false; |
501 | private bool jumping = false; | 627 | private bool jumping = false; |
@@ -504,6 +630,7 @@ namespace OpenSim.Region.Physics.OdePlugin | |||
504 | private OdeScene _parent_scene; | 630 | private OdeScene _parent_scene; |
505 | public IntPtr Shell; | 631 | public IntPtr Shell; |
506 | public d.Mass ShellMass; | 632 | public d.Mass ShellMass; |
633 | public bool collidelock = false; | ||
507 | 634 | ||
508 | public OdeCharacter(String avName, OdeScene parent_scene, PhysicsVector pos) | 635 | public OdeCharacter(String avName, OdeScene parent_scene, PhysicsVector pos) |
509 | { | 636 | { |
@@ -514,6 +641,7 @@ namespace OpenSim.Region.Physics.OdePlugin | |||
514 | _parent_scene = parent_scene; | 641 | _parent_scene = parent_scene; |
515 | lock (OdeScene.OdeLock) | 642 | lock (OdeScene.OdeLock) |
516 | { | 643 | { |
644 | |||
517 | Shell = d.CreateCapsule(parent_scene.space, CAPSULE_RADIUS, CAPSULE_LENGTH); | 645 | Shell = d.CreateCapsule(parent_scene.space, CAPSULE_RADIUS, CAPSULE_LENGTH); |
518 | d.MassSetCapsule(out ShellMass, 50.0f, 3, 0.4f, 1.0f); | 646 | d.MassSetCapsule(out ShellMass, 50.0f, 3, 0.4f, 1.0f); |
519 | Body = d.BodyCreate(parent_scene.world); | 647 | Body = d.BodyCreate(parent_scene.world); |
@@ -608,19 +736,22 @@ namespace OpenSim.Region.Physics.OdePlugin | |||
608 | } | 736 | } |
609 | public void doForce(PhysicsVector force) | 737 | public void doForce(PhysicsVector force) |
610 | { | 738 | { |
611 | d.BodyAddForce(Body, force.X, force.Y, force.Z); | 739 | if (!collidelock) |
612 | 740 | { | |
613 | // ok -- let's stand up straight! | 741 | d.BodyAddForce(Body, force.X, force.Y, force.Z); |
614 | d.Vector3 feet; | 742 | |
615 | d.Vector3 head; | 743 | // ok -- let's stand up straight! |
616 | d.BodyGetRelPointPos(Body, 0.0f, 0.0f, -1.0f, out feet); | 744 | d.Vector3 feet; |
617 | d.BodyGetRelPointPos(Body, 0.0f, 0.0f, 1.0f, out head); | 745 | d.Vector3 head; |
618 | float posture = head.Z - feet.Z; | 746 | d.BodyGetRelPointPos(Body, 0.0f, 0.0f, -1.0f, out feet); |
619 | 747 | d.BodyGetRelPointPos(Body, 0.0f, 0.0f, 1.0f, out head); | |
620 | // restoring force proportional to lack of posture: | 748 | float posture = head.Z - feet.Z; |
621 | float servo = (2.5f - posture) * POSTURE_SERVO; | 749 | |
622 | d.BodyAddForceAtRelPos(Body, 0.0f, 0.0f, servo, 0.0f, 0.0f, 1.0f); | 750 | // restoring force proportional to lack of posture: |
623 | d.BodyAddForceAtRelPos(Body, 0.0f, 0.0f, -servo, 0.0f, 0.0f, -1.0f); | 751 | float servo = (2.5f - posture) * POSTURE_SERVO; |
752 | d.BodyAddForceAtRelPos(Body, 0.0f, 0.0f, servo, 0.0f, 0.0f, 1.0f); | ||
753 | d.BodyAddForceAtRelPos(Body, 0.0f, 0.0f, -servo, 0.0f, 0.0f, -1.0f); | ||
754 | } | ||
624 | 755 | ||
625 | } | 756 | } |
626 | public override void SetMomentum(PhysicsVector momentum) | 757 | public override void SetMomentum(PhysicsVector momentum) |
@@ -675,6 +806,8 @@ namespace OpenSim.Region.Physics.OdePlugin | |||
675 | { | 806 | { |
676 | vec.Z += 10.0f; | 807 | vec.Z += 10.0f; |
677 | } | 808 | } |
809 | |||
810 | |||
678 | doForce(vec); | 811 | doForce(vec); |
679 | } | 812 | } |
680 | 813 | ||
@@ -702,9 +835,9 @@ namespace OpenSim.Region.Physics.OdePlugin | |||
702 | else | 835 | else |
703 | { | 836 | { |
704 | vec = d.BodyGetLinearVel(Body); | 837 | vec = d.BodyGetLinearVel(Body); |
705 | _velocity.X = vec.X; | 838 | _velocity.X = (vec.X); |
706 | _velocity.Y = vec.Y; | 839 | _velocity.Y = (vec.Y); |
707 | _velocity.Z = vec.Z; | 840 | _velocity.Z = (vec.Z); |
708 | } | 841 | } |
709 | } | 842 | } |
710 | 843 | ||
@@ -723,19 +856,33 @@ namespace OpenSim.Region.Physics.OdePlugin | |||
723 | { | 856 | { |
724 | public PhysicsVector _position; | 857 | public PhysicsVector _position; |
725 | private PhysicsVector _velocity; | 858 | private PhysicsVector _velocity; |
859 | private PhysicsVector m_lastVelocity = new PhysicsVector(0.0f,0.0f,0.0f); | ||
860 | private PhysicsVector m_lastposition = new PhysicsVector(0.0f, 0.0f, 0.0f); | ||
726 | private PhysicsVector _size; | 861 | private PhysicsVector _size; |
727 | private PhysicsVector _acceleration; | 862 | private PhysicsVector _acceleration; |
728 | public Quaternion _orientation; | 863 | public Quaternion _orientation; |
864 | |||
729 | private Mesh _mesh; | 865 | private Mesh _mesh; |
730 | private PrimitiveBaseShape _pbs; | 866 | private PrimitiveBaseShape _pbs; |
731 | private OdeScene _parent_scene; | 867 | private OdeScene _parent_scene; |
732 | public IntPtr prim_geom; | 868 | public IntPtr prim_geom; |
733 | public IntPtr _triMeshData; | 869 | public IntPtr _triMeshData; |
734 | private bool iscolliding = false; | 870 | private bool iscolliding = false; |
871 | private bool m_isphysical = false; | ||
872 | public bool _zeroFlag = false; | ||
873 | public IntPtr Body = (IntPtr) 0; | ||
874 | private String m_primName; | ||
875 | private PhysicsVector _target_velocity; | ||
876 | public d.Mass pMass; | ||
877 | private const float MassMultiplier = 500f; // Ref: Water: 1000kg.. this iset to 500 | ||
878 | private int debugcounter = 0; | ||
879 | |||
735 | 880 | ||
736 | public OdePrim(String primName, OdeScene parent_scene, PhysicsVector pos, PhysicsVector size, | 881 | public OdePrim(String primName, OdeScene parent_scene, PhysicsVector pos, PhysicsVector size, |
737 | Quaternion rotation, Mesh mesh, PrimitiveBaseShape pbs) | 882 | Quaternion rotation, Mesh mesh, PrimitiveBaseShape pbs, bool pisPhysical) |
738 | { | 883 | { |
884 | |||
885 | |||
739 | _velocity = new PhysicsVector(); | 886 | _velocity = new PhysicsVector(); |
740 | _position = pos; | 887 | _position = pos; |
741 | _size = size; | 888 | _size = size; |
@@ -744,6 +891,9 @@ namespace OpenSim.Region.Physics.OdePlugin | |||
744 | _mesh = mesh; | 891 | _mesh = mesh; |
745 | _pbs = pbs; | 892 | _pbs = pbs; |
746 | _parent_scene = parent_scene; | 893 | _parent_scene = parent_scene; |
894 | m_isphysical = pisPhysical; | ||
895 | m_primName = primName; | ||
896 | |||
747 | 897 | ||
748 | 898 | ||
749 | lock (OdeScene.OdeLock) | 899 | lock (OdeScene.OdeLock) |
@@ -764,20 +914,60 @@ namespace OpenSim.Region.Physics.OdePlugin | |||
764 | myrot.Y = rotation.y; | 914 | myrot.Y = rotation.y; |
765 | myrot.Z = rotation.z; | 915 | myrot.Z = rotation.z; |
766 | d.GeomSetQuaternion(prim_geom, ref myrot); | 916 | d.GeomSetQuaternion(prim_geom, ref myrot); |
917 | |||
918 | |||
919 | if (m_isphysical && Body == (IntPtr)0) { | ||
920 | enableBody(); | ||
921 | } | ||
767 | parent_scene.geom_name_map[prim_geom] = primName; | 922 | parent_scene.geom_name_map[prim_geom] = primName; |
768 | parent_scene.actor_name_map[prim_geom] = (PhysicsActor) this; | 923 | parent_scene.actor_name_map[prim_geom] = (PhysicsActor)this; |
769 | // don't do .add() here; old geoms get recycled with the same hash | 924 | // don't do .add() here; old geoms get recycled with the same hash |
770 | } | 925 | } |
771 | } | 926 | } |
772 | 927 | public void enableBody() | |
773 | public override bool IsPhysical | ||
774 | { | 928 | { |
775 | get { return false; } | 929 | // Sets the geom to a body |
776 | set { return; } | 930 | Body = d.BodyCreate(_parent_scene.world); |
931 | |||
932 | setMass(); | ||
933 | d.BodySetPosition(Body, _position.X, _position.Y, _position.Z); | ||
934 | d.Quaternion myrot = new d.Quaternion(); | ||
935 | myrot.W = _orientation.w; | ||
936 | myrot.X = _orientation.x; | ||
937 | myrot.Y = _orientation.y; | ||
938 | myrot.Z = _orientation.z; | ||
939 | d.BodySetQuaternion(Body, ref myrot); | ||
940 | d.GeomSetBody(prim_geom, Body); | ||
941 | d.BodySetAutoDisableFlag(Body, true); | ||
942 | d.BodySetAutoDisableSteps(Body,20); | ||
943 | _parent_scene.addActivePrim(this); | ||
944 | } | ||
945 | public void setMass() | ||
946 | { | ||
947 | //Sets Mass based on member MassMultiplier. | ||
948 | if (Body != (IntPtr)0) | ||
949 | { | ||
950 | d.MassSetBox(out pMass, (_size.X * _size.Y * _size.Z * MassMultiplier), _size.X, _size.Y, _size.Z); | ||
951 | d.BodySetMass(Body, ref pMass); | ||
952 | } | ||
953 | } | ||
954 | public void disableBody() | ||
955 | { | ||
956 | //this kills the body so things like 'mesh' can re-create it. | ||
957 | if (Body != (IntPtr)0) | ||
958 | { | ||
959 | _parent_scene.remActivePrim(this); | ||
960 | d.BodyDestroy(Body); | ||
961 | Body = (IntPtr)0; | ||
962 | } | ||
777 | } | 963 | } |
778 | |||
779 | public void setMesh(OdeScene parent_scene, Mesh mesh) | 964 | public void setMesh(OdeScene parent_scene, Mesh mesh) |
780 | { | 965 | { |
966 | //Kill Body so that mesh can re-make the geom | ||
967 | if (IsPhysical && Body != (IntPtr)0) | ||
968 | { | ||
969 | disableBody(); | ||
970 | } | ||
781 | float[] vertexList = mesh.getVertexListAsFloat(); // Note, that vertextList is pinned in memory | 971 | float[] vertexList = mesh.getVertexListAsFloat(); // Note, that vertextList is pinned in memory |
782 | int[] indexList = mesh.getIndexListAsInt(); // Also pinned, needs release after usage | 972 | int[] indexList = mesh.getIndexListAsInt(); // Also pinned, needs release after usage |
783 | int VertexCount = vertexList.GetLength(0)/3; | 973 | int VertexCount = vertexList.GetLength(0)/3; |
@@ -790,6 +980,46 @@ namespace OpenSim.Region.Physics.OdePlugin | |||
790 | d.GeomTriMeshDataPreprocess(_triMeshData); | 980 | d.GeomTriMeshDataPreprocess(_triMeshData); |
791 | 981 | ||
792 | prim_geom = d.CreateTriMesh(parent_scene.space, _triMeshData, parent_scene.triCallback, null, null); | 982 | prim_geom = d.CreateTriMesh(parent_scene.space, _triMeshData, parent_scene.triCallback, null, null); |
983 | |||
984 | if (IsPhysical && Body == (IntPtr)0) | ||
985 | { | ||
986 | // Recreate the body | ||
987 | enableBody(); | ||
988 | } | ||
989 | } | ||
990 | |||
991 | public override bool IsPhysical | ||
992 | { | ||
993 | get { return m_isphysical; } | ||
994 | set { | ||
995 | |||
996 | lock (OdeScene.OdeLock) | ||
997 | { | ||
998 | if (m_isphysical == value) | ||
999 | { | ||
1000 | // If the object is already what the user checked | ||
1001 | |||
1002 | return; | ||
1003 | } | ||
1004 | if (value == true) | ||
1005 | { | ||
1006 | if (Body == (IntPtr)0) | ||
1007 | { | ||
1008 | enableBody(); | ||
1009 | } | ||
1010 | |||
1011 | } | ||
1012 | else if (value == false) | ||
1013 | { | ||
1014 | if (Body != (IntPtr)0) | ||
1015 | { | ||
1016 | disableBody(); | ||
1017 | } | ||
1018 | } | ||
1019 | m_isphysical = value; | ||
1020 | } | ||
1021 | |||
1022 | } | ||
793 | } | 1023 | } |
794 | 1024 | ||
795 | public override bool Flying | 1025 | public override bool Flying |
@@ -808,13 +1038,27 @@ namespace OpenSim.Region.Physics.OdePlugin | |||
808 | 1038 | ||
809 | public override PhysicsVector Position | 1039 | public override PhysicsVector Position |
810 | { | 1040 | { |
811 | get { return _position; } | 1041 | get { return _position;} |
812 | set | 1042 | set |
813 | { | 1043 | { |
814 | _position = value; | 1044 | _position = value; |
815 | lock (OdeScene.OdeLock) | 1045 | lock (OdeScene.OdeLock) |
816 | { | 1046 | { |
817 | d.GeomSetPosition(prim_geom, _position.X, _position.Y, _position.Z); | 1047 | if (m_isphysical) |
1048 | { | ||
1049 | // This is a fallback.. May no longer be necessary. | ||
1050 | if (Body == (IntPtr)0) | ||
1051 | enableBody(); | ||
1052 | // Prim auto disable after 20 frames, | ||
1053 | // if you move it, re-enable the prim manually. | ||
1054 | d.BodyEnable(Body); | ||
1055 | d.BodySetPosition(Body, _position.X, _position.Y, _position.Z); | ||
1056 | } | ||
1057 | else | ||
1058 | { | ||
1059 | d.GeomSetPosition(prim_geom, _position.X, _position.Y, _position.Z); | ||
1060 | |||
1061 | } | ||
818 | } | 1062 | } |
819 | } | 1063 | } |
820 | } | 1064 | } |
@@ -830,29 +1074,38 @@ namespace OpenSim.Region.Physics.OdePlugin | |||
830 | string oldname = _parent_scene.geom_name_map[prim_geom]; | 1074 | string oldname = _parent_scene.geom_name_map[prim_geom]; |
831 | 1075 | ||
832 | // Cleanup of old prim geometry | 1076 | // Cleanup of old prim geometry |
833 | d.GeomDestroy(prim_geom); | ||
834 | if (_mesh != null) | 1077 | if (_mesh != null) |
835 | { | 1078 | { |
836 | // Cleanup meshing here | 1079 | // Cleanup meshing here |
837 | } | 1080 | } |
838 | 1081 | //kill body to rebuild | |
1082 | if (IsPhysical && Body != (IntPtr)0) | ||
1083 | { | ||
1084 | disableBody(); | ||
1085 | } | ||
839 | // Construction of new prim | 1086 | // Construction of new prim |
840 | if (this._parent_scene.needsMeshing(_pbs)) | 1087 | if (this._parent_scene.needsMeshing(_pbs)) |
841 | { | 1088 | { |
1089 | |||
1090 | // Don't need to re-enable body.. it's done in SetMesh | ||
842 | Mesh mesh = Meshmerizer.CreateMesh(oldname, _pbs, _size); | 1091 | Mesh mesh = Meshmerizer.CreateMesh(oldname, _pbs, _size); |
843 | setMesh(_parent_scene, mesh); | 1092 | setMesh(_parent_scene, mesh); |
844 | } else { | 1093 | } else { |
845 | prim_geom = d.CreateBox(_parent_scene.space, _size.X, _size.Y, _size.Z); | 1094 | prim_geom = d.CreateBox(_parent_scene.space, _size.X, _size.Y, _size.Z); |
1095 | |||
1096 | if (IsPhysical && Body == (IntPtr)0) | ||
1097 | { | ||
1098 | // Re creates body on size. | ||
1099 | // EnableBody also does setMass() | ||
1100 | enableBody(); | ||
1101 | d.BodyEnable(Body); | ||
1102 | } | ||
1103 | |||
846 | } | 1104 | } |
1105 | |||
1106 | |||
847 | _parent_scene.geom_name_map[prim_geom] = oldname; | 1107 | _parent_scene.geom_name_map[prim_geom] = oldname; |
848 | 1108 | ||
849 | d.GeomSetPosition(prim_geom, _position.X, _position.Y, _position.Z); | ||
850 | d.Quaternion myrot = new d.Quaternion(); | ||
851 | myrot.W = _orientation.w; | ||
852 | myrot.X = _orientation.x; | ||
853 | myrot.Y = _orientation.y; | ||
854 | myrot.Z = _orientation.z; | ||
855 | d.GeomSetQuaternion(prim_geom, ref myrot); | ||
856 | } | 1109 | } |
857 | } | 1110 | } |
858 | } | 1111 | } |
@@ -866,11 +1119,17 @@ namespace OpenSim.Region.Physics.OdePlugin | |||
866 | { | 1119 | { |
867 | string oldname = _parent_scene.geom_name_map[prim_geom]; | 1120 | string oldname = _parent_scene.geom_name_map[prim_geom]; |
868 | 1121 | ||
869 | // Cleanup of old prim geometry | 1122 | // Cleanup of old prim geometry and Bodies |
1123 | if (IsPhysical && Body != (IntPtr)0) | ||
1124 | { | ||
1125 | disableBody(); | ||
1126 | } | ||
870 | d.GeomDestroy(prim_geom); | 1127 | d.GeomDestroy(prim_geom); |
871 | if (_mesh != null) | 1128 | if (_mesh != null) |
872 | { | 1129 | { |
873 | // Cleanup meshing here | 1130 | |
1131 | d.GeomBoxSetLengths(prim_geom, _size.X, _size.Y, _size.Z); | ||
1132 | |||
874 | } | 1133 | } |
875 | 1134 | ||
876 | // Construction of new prim | 1135 | // Construction of new prim |
@@ -881,15 +1140,24 @@ namespace OpenSim.Region.Physics.OdePlugin | |||
881 | } else { | 1140 | } else { |
882 | prim_geom = d.CreateBox(_parent_scene.space, _size.X, _size.Y, _size.Z); | 1141 | prim_geom = d.CreateBox(_parent_scene.space, _size.X, _size.Y, _size.Z); |
883 | } | 1142 | } |
1143 | if (IsPhysical && Body == (IntPtr)0) | ||
1144 | { | ||
1145 | //re-create new body | ||
1146 | enableBody(); | ||
1147 | } | ||
1148 | else | ||
1149 | { | ||
1150 | d.GeomSetPosition(prim_geom, _position.X, _position.Y, _position.Z); | ||
1151 | d.Quaternion myrot = new d.Quaternion(); | ||
1152 | myrot.W = _orientation.w; | ||
1153 | myrot.X = _orientation.x; | ||
1154 | myrot.Y = _orientation.y; | ||
1155 | myrot.Z = _orientation.z; | ||
1156 | d.GeomSetQuaternion(prim_geom, ref myrot); | ||
1157 | } | ||
884 | _parent_scene.geom_name_map[prim_geom] = oldname; | 1158 | _parent_scene.geom_name_map[prim_geom] = oldname; |
885 | 1159 | ||
886 | d.GeomSetPosition(prim_geom, _position.X, _position.Y, _position.Z); | 1160 | |
887 | d.Quaternion myrot = new d.Quaternion(); | ||
888 | myrot.W = _orientation.w; | ||
889 | myrot.X = _orientation.x; | ||
890 | myrot.Y = _orientation.y; | ||
891 | myrot.Z = _orientation.z; | ||
892 | d.GeomSetQuaternion(prim_geom, ref myrot); | ||
893 | 1161 | ||
894 | } | 1162 | } |
895 | } | 1163 | } |
@@ -897,7 +1165,15 @@ namespace OpenSim.Region.Physics.OdePlugin | |||
897 | 1165 | ||
898 | public override PhysicsVector Velocity | 1166 | public override PhysicsVector Velocity |
899 | { | 1167 | { |
900 | get { return _velocity; } | 1168 | get { |
1169 | // Averate previous velocity with the new one so | ||
1170 | // client object interpolation works a 'little' better | ||
1171 | PhysicsVector returnVelocity = new PhysicsVector(); | ||
1172 | returnVelocity.X = (m_lastVelocity.X + _velocity.X) / 2; | ||
1173 | returnVelocity.Y = (m_lastVelocity.Y + _velocity.Y) / 2; | ||
1174 | returnVelocity.Z = (m_lastVelocity.Z + _velocity.Z) / 2; | ||
1175 | return returnVelocity; | ||
1176 | } | ||
901 | set { _velocity = value; } | 1177 | set { _velocity = value; } |
902 | } | 1178 | } |
903 | 1179 | ||
@@ -921,6 +1197,10 @@ namespace OpenSim.Region.Physics.OdePlugin | |||
921 | myrot.Y = _orientation.y; | 1197 | myrot.Y = _orientation.y; |
922 | myrot.Z = _orientation.z; | 1198 | myrot.Z = _orientation.z; |
923 | d.GeomSetQuaternion(prim_geom, ref myrot); | 1199 | d.GeomSetQuaternion(prim_geom, ref myrot); |
1200 | if (m_isphysical && Body != (IntPtr)0) | ||
1201 | { | ||
1202 | d.BodySetQuaternion(Body, ref myrot); | ||
1203 | } | ||
924 | } | 1204 | } |
925 | } | 1205 | } |
926 | } | 1206 | } |
@@ -930,6 +1210,7 @@ namespace OpenSim.Region.Physics.OdePlugin | |||
930 | get { return _acceleration; } | 1210 | get { return _acceleration; } |
931 | } | 1211 | } |
932 | 1212 | ||
1213 | |||
933 | public void SetAcceleration(PhysicsVector accel) | 1214 | public void SetAcceleration(PhysicsVector accel) |
934 | { | 1215 | { |
935 | _acceleration = accel; | 1216 | _acceleration = accel; |
@@ -938,7 +1219,103 @@ namespace OpenSim.Region.Physics.OdePlugin | |||
938 | public override void AddForce(PhysicsVector force) | 1219 | public override void AddForce(PhysicsVector force) |
939 | { | 1220 | { |
940 | } | 1221 | } |
1222 | public void Move(float timestep) | ||
1223 | { | ||
1224 | |||
1225 | } | ||
1226 | |||
1227 | public void UpdatePositionAndVelocity() { | ||
1228 | // no lock; called from Simulate() -- if you call this from elsewhere, gotta lock or do Monitor.Enter/Exit! | ||
1229 | if (Body != (IntPtr)0) | ||
1230 | { | ||
1231 | d.Vector3 vec = d.BodyGetPosition(Body); | ||
1232 | d.Quaternion ori = d.BodyGetQuaternion(Body); | ||
1233 | d.Vector3 vel = d.BodyGetLinearVel(Body); | ||
1234 | PhysicsVector l_position = new PhysicsVector(); | ||
1235 | // kluge to keep things in bounds. ODE lets dead avatars drift away (they should be removed!) | ||
1236 | if (vec.X < 0.0f) vec.X = 0.0f; | ||
1237 | if (vec.Y < 0.0f) vec.Y = 0.0f; | ||
1238 | if (vec.X > 255.95f) vec.X = 255.95f; | ||
1239 | if (vec.Y > 255.95f) vec.Y = 255.95f; | ||
1240 | m_lastposition = _position; | ||
1241 | |||
1242 | l_position.X = vec.X; | ||
1243 | l_position.Y = vec.Y; | ||
1244 | l_position.Z = vec.Z; | ||
1245 | if (l_position.Z < 0) | ||
1246 | { | ||
1247 | // This is so prim that get lost underground don't fall forever and suck up | ||
1248 | // | ||
1249 | // Sim resources and memory. | ||
1250 | // Disables the prim's movement physics.... | ||
1251 | // It's a hack and will generate a console message if it fails. | ||
1252 | |||
1253 | try | ||
1254 | { | ||
1255 | disableBody(); | ||
1256 | |||
1257 | } | ||
1258 | catch (System.Exception e) | ||
1259 | { | ||
1260 | if (Body != (IntPtr)0) | ||
1261 | { | ||
1262 | d.BodyDestroy(Body); | ||
1263 | Body = (IntPtr)0; | ||
1264 | |||
1265 | } | ||
1266 | } | ||
1267 | IsPhysical = false; | ||
1268 | _velocity.X = 0; | ||
1269 | _velocity.Y = 0; | ||
1270 | _velocity.Z = 0; | ||
1271 | _zeroFlag = true; | ||
1272 | } | ||
1273 | |||
1274 | if (m_lastposition == l_position) | ||
1275 | { | ||
1276 | _zeroFlag = true; | ||
1277 | } | ||
1278 | else | ||
1279 | { | ||
1280 | _zeroFlag = false; | ||
1281 | } | ||
1282 | m_lastposition = l_position; | ||
1283 | |||
1284 | |||
1285 | if (_zeroFlag) | ||
1286 | { | ||
1287 | // Supposedly this is supposed to tell SceneObjectGroup that | ||
1288 | // no more updates need to be sent.. | ||
1289 | // but it seems broken. | ||
1290 | _velocity.X = 0.0f; | ||
1291 | _velocity.Y = 0.0f; | ||
1292 | _velocity.Z = 0.0f; | ||
1293 | _orientation.w = 0f; | ||
1294 | _orientation.x = 0f; | ||
1295 | _orientation.y = 0f; | ||
1296 | _orientation.z = 0f; | ||
941 | 1297 | ||
1298 | |||
1299 | } | ||
1300 | else | ||
1301 | { | ||
1302 | m_lastVelocity = _velocity; | ||
1303 | |||
1304 | _position = l_position; | ||
1305 | |||
1306 | _velocity.X = vel.X; | ||
1307 | _velocity.Y = vel.Y; | ||
1308 | _velocity.Z = vel.Z; | ||
1309 | |||
1310 | _orientation.w = ori.W; | ||
1311 | _orientation.x = ori.X; | ||
1312 | _orientation.y = ori.Y; | ||
1313 | _orientation.z = ori.Z; | ||
1314 | } | ||
1315 | |||
1316 | } | ||
1317 | |||
1318 | } | ||
942 | public override void SetMomentum(PhysicsVector momentum) | 1319 | public override void SetMomentum(PhysicsVector momentum) |
943 | { | 1320 | { |
944 | } | 1321 | } |