aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/Physics/UbitOdePlugin/ODERayCastRequestManager.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/Physics/UbitOdePlugin/ODERayCastRequestManager.cs')
-rw-r--r--OpenSim/Region/Physics/UbitOdePlugin/ODERayCastRequestManager.cs638
1 files changed, 638 insertions, 0 deletions
diff --git a/OpenSim/Region/Physics/UbitOdePlugin/ODERayCastRequestManager.cs b/OpenSim/Region/Physics/UbitOdePlugin/ODERayCastRequestManager.cs
new file mode 100644
index 0000000..799a324
--- /dev/null
+++ b/OpenSim/Region/Physics/UbitOdePlugin/ODERayCastRequestManager.cs
@@ -0,0 +1,638 @@
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 System.Reflection;
31using System.Runtime.InteropServices;
32using System.Text;
33using OpenSim.Framework;
34using OpenSim.Region.Physics.Manager;
35using OdeAPI;
36using log4net;
37using OpenMetaverse;
38
39namespace OpenSim.Region.Physics.OdePlugin
40{
41 /// <summary>
42 /// Processes raycast requests as ODE is in a state to be able to do them.
43 /// This ensures that it's thread safe and there will be no conflicts.
44 /// Requests get returned by a different thread then they were requested by.
45 /// </summary>
46 public class ODERayCastRequestManager
47 {
48 /// <summary>
49 /// Pending ray requests
50 /// </summary>
51 protected OpenSim.Framework.LocklessQueue<ODERayRequest> m_PendingRequests = new OpenSim.Framework.LocklessQueue<ODERayRequest>();
52
53 /// <summary>
54 /// Scene that created this object.
55 /// </summary>
56 private OdeScene m_scene;
57
58 IntPtr ray; // the ray. we only need one for our lifetime
59
60 private const int ColisionContactGeomsPerTest = 5;
61 private const int DefaultMaxCount = 25;
62 private const int MaxTimePerCallMS = 30;
63
64 /// <summary>
65 /// ODE near callback delegate
66 /// </summary>
67 private d.NearCallback nearCallback;
68 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
69 private List<ContactResult> m_contactResults = new List<ContactResult>();
70 private RayFilterFlags CurrentRayFilter;
71 private int CurrentMaxCount;
72
73 public ODERayCastRequestManager(OdeScene pScene)
74 {
75 m_scene = pScene;
76 nearCallback = near;
77 ray = d.CreateRay(IntPtr.Zero, 1.0f);
78 d.GeomSetCategoryBits(ray,0);
79 }
80
81 /// <summary>
82 /// Queues request for a raycast to all world
83 /// </summary>
84 /// <param name="position">Origin of Ray</param>
85 /// <param name="direction">Ray direction</param>
86 /// <param name="length">Ray length</param>
87 /// <param name="retMethod">Return method to send the results</param>
88 public void QueueRequest(Vector3 position, Vector3 direction, float length, RayCallback retMethod)
89 {
90 ODERayRequest req = new ODERayRequest();
91 req.geom = IntPtr.Zero;
92 req.callbackMethod = retMethod;
93 req.Count = DefaultMaxCount;
94 req.length = length;
95 req.Normal = direction;
96 req.Origin = position;
97 req.filter = RayFilterFlags.AllPrims;
98
99 m_PendingRequests.Enqueue(req);
100 }
101
102 /// <summary>
103 /// Queues request for a raycast to particular part
104 /// </summary>
105 /// <param name="position">Origin of Ray</param>
106 /// <param name="direction">Ray direction</param>
107 /// <param name="length">Ray length</param>
108 /// <param name="retMethod">Return method to send the results</param>
109 public void QueueRequest(IntPtr geom, Vector3 position, Vector3 direction, float length, RayCallback retMethod)
110 {
111 ODERayRequest req = new ODERayRequest();
112 req.geom = geom;
113 req.callbackMethod = retMethod;
114 req.length = length;
115 req.Normal = direction;
116 req.Origin = position;
117 req.Count = DefaultMaxCount;
118 req.filter = RayFilterFlags.AllPrims;
119
120 m_PendingRequests.Enqueue(req);
121 }
122
123 public void QueueRequest(Vector3 position, Vector3 direction, float length, RaycastCallback retMethod)
124 {
125 ODERayRequest req = new ODERayRequest();
126 req.geom = IntPtr.Zero;
127 req.callbackMethod = retMethod;
128 req.Count = DefaultMaxCount;
129 req.length = length;
130 req.Normal = direction;
131 req.Origin = position;
132 req.filter = RayFilterFlags.AllPrims | RayFilterFlags.land;
133
134 m_PendingRequests.Enqueue(req);
135 }
136
137 public void QueueRequest(IntPtr geom, Vector3 position, Vector3 direction, float length, RaycastCallback retMethod)
138 {
139 ODERayRequest req = new ODERayRequest();
140 req.geom = geom;
141 req.callbackMethod = retMethod;
142 req.length = length;
143 req.Normal = direction;
144 req.Origin = position;
145 req.Count = DefaultMaxCount;
146 req.filter = RayFilterFlags.AllPrims;
147
148 m_PendingRequests.Enqueue(req);
149 }
150
151 /// <summary>
152 /// Queues a raycast
153 /// </summary>
154 /// <param name="position">Origin of Ray</param>
155 /// <param name="direction">Ray normal</param>
156 /// <param name="length">Ray length</param>
157 /// <param name="count"></param>
158 /// <param name="retMethod">Return method to send the results</param>
159 public void QueueRequest(Vector3 position, Vector3 direction, float length, int count, RayCallback retMethod)
160 {
161 ODERayRequest req = new ODERayRequest();
162 req.geom = IntPtr.Zero;
163 req.callbackMethod = retMethod;
164 req.length = length;
165 req.Normal = direction;
166 req.Origin = position;
167 req.Count = count;
168 req.filter = RayFilterFlags.AllPrims;
169
170 m_PendingRequests.Enqueue(req);
171 }
172
173
174 public void QueueRequest(Vector3 position, Vector3 direction, float length, int count,RayFilterFlags filter , RayCallback retMethod)
175 {
176 ODERayRequest req = new ODERayRequest();
177 req.geom = IntPtr.Zero;
178 req.callbackMethod = retMethod;
179 req.length = length;
180 req.Normal = direction;
181 req.Origin = position;
182 req.Count = count;
183 req.filter = filter;
184
185 m_PendingRequests.Enqueue(req);
186 }
187
188 public void QueueRequest(IntPtr geom, Vector3 position, Vector3 direction, float length, int count, RayCallback retMethod)
189 {
190 ODERayRequest req = new ODERayRequest();
191 req.geom = geom;
192 req.callbackMethod = retMethod;
193 req.length = length;
194 req.Normal = direction;
195 req.Origin = position;
196 req.Count = count;
197 req.filter = RayFilterFlags.AllPrims;
198
199 m_PendingRequests.Enqueue(req);
200 }
201
202 public void QueueRequest(Vector3 position, Vector3 direction, float length, int count, RaycastCallback retMethod)
203 {
204 ODERayRequest req = new ODERayRequest();
205 req.geom = IntPtr.Zero;
206 req.callbackMethod = retMethod;
207 req.length = length;
208 req.Normal = direction;
209 req.Origin = position;
210 req.Count = count;
211 req.filter = RayFilterFlags.AllPrims;
212
213 m_PendingRequests.Enqueue(req);
214 }
215
216 public void QueueRequest(IntPtr geom, Vector3 position, Vector3 direction, float length, int count, RaycastCallback retMethod)
217 {
218 ODERayRequest req = new ODERayRequest();
219 req.geom = geom;
220 req.callbackMethod = retMethod;
221 req.length = length;
222 req.Normal = direction;
223 req.Origin = position;
224 req.Count = count;
225 req.filter = RayFilterFlags.AllPrims;
226
227 m_PendingRequests.Enqueue(req);
228 }
229
230 /// <summary>
231 /// Process all queued raycast requests
232 /// </summary>
233 /// <returns>Time in MS the raycasts took to process.</returns>
234 public int ProcessQueuedRequests()
235 {
236
237 if (m_PendingRequests.Count <= 0)
238 return 0;
239
240 if (m_scene.ContactgeomsArray == IntPtr.Zero || ray == IntPtr.Zero)
241 // oops something got wrong or scene isn't ready still
242 {
243 m_PendingRequests.Clear();
244 return 0;
245 }
246
247 int time = Util.EnvironmentTickCount();
248
249 ODERayRequest req;
250 int closestHit;
251 int backfacecull;
252 CollisionCategories catflags;
253
254 while (m_PendingRequests.Dequeue(out req))
255 {
256 if (req.callbackMethod != null)
257 {
258 CurrentRayFilter = req.filter;
259 CurrentMaxCount = req.Count;
260
261 closestHit = ((CurrentRayFilter & RayFilterFlags.ClosestHit) == 0 ? 0 : 1);
262 backfacecull = ((CurrentRayFilter & RayFilterFlags.BackFaceCull) == 0 ? 0 : 1);
263
264 d.GeomRaySetLength(ray, req.length);
265 d.GeomRaySet(ray, req.Origin.X, req.Origin.Y, req.Origin.Z, req.Normal.X, req.Normal.Y, req.Normal.Z);
266 d.GeomRaySetParams(ray, 0, backfacecull);
267 d.GeomRaySetClosestHit(ray, closestHit);
268
269 if (req.callbackMethod is RaycastCallback)
270 // if we only want one get only one per colision pair saving memory
271 CurrentRayFilter |= RayFilterFlags.ClosestHit;
272
273 if (req.geom == IntPtr.Zero)
274 {
275 // translate ray filter to colision flags
276 catflags = 0;
277 if ((CurrentRayFilter & RayFilterFlags.volumedtc) != 0)
278 catflags |= CollisionCategories.VolumeDtc;
279 if ((CurrentRayFilter & RayFilterFlags.phantom) != 0)
280 catflags |= CollisionCategories.Phantom;
281 if ((CurrentRayFilter & RayFilterFlags.agent) != 0)
282 catflags |= CollisionCategories.Character;
283 if ((CurrentRayFilter & RayFilterFlags.PrimsNonPhantom) != 0)
284 catflags |= CollisionCategories.Geom;
285 if ((CurrentRayFilter & RayFilterFlags.land) != 0)
286 catflags |= CollisionCategories.Land;
287 if ((CurrentRayFilter & RayFilterFlags.water) != 0)
288 catflags |= CollisionCategories.Water;
289
290 if (catflags != 0)
291 {
292 d.GeomSetCollideBits(ray, (uint)catflags);
293 doSpaceRay(req);
294 }
295 }
296 else
297 {
298 // if we select a geom don't use filters
299 d.GeomSetCollideBits(ray, (uint)CollisionCategories.All);
300 doGeomRay(req);
301 }
302 }
303
304 if (Util.EnvironmentTickCountSubtract(time) > MaxTimePerCallMS)
305 break;
306 }
307
308 lock (m_contactResults)
309 m_contactResults.Clear();
310
311 return Util.EnvironmentTickCountSubtract(time);
312 }
313 /// <summary>
314 /// Method that actually initiates the raycast with spaces
315 /// </summary>
316 /// <param name="req"></param>
317 ///
318
319 private const RayFilterFlags FilterActiveSpace = RayFilterFlags.agent | RayFilterFlags.physical | RayFilterFlags.LSLPhanton;
320// private const RayFilterFlags FilterStaticSpace = RayFilterFlags.water | RayFilterFlags.land | RayFilterFlags.nonphysical | RayFilterFlags.LSLPhanton;
321 private const RayFilterFlags FilterStaticSpace = RayFilterFlags.water | RayFilterFlags.nonphysical | RayFilterFlags.LSLPhanton;
322
323 private void doSpaceRay(ODERayRequest req)
324 {
325 // Collide tests
326 if ((CurrentRayFilter & FilterActiveSpace) != 0)
327 d.SpaceCollide2(ray, m_scene.ActiveSpace, IntPtr.Zero, nearCallback);
328 if ((CurrentRayFilter & FilterStaticSpace) != 0 && (m_contactResults.Count < CurrentMaxCount))
329 d.SpaceCollide2(ray, m_scene.StaticSpace, IntPtr.Zero, nearCallback);
330 if ((CurrentRayFilter & RayFilterFlags.land) != 0 && (m_contactResults.Count < CurrentMaxCount))
331 {
332 // current ode land to ray collisions is very bad
333 // so for now limit its range badly
334
335 if (req.length > 30.0f)
336 d.GeomRaySetLength(ray, 30.0f);
337
338 d.SpaceCollide2(ray, m_scene.GroundSpace, IntPtr.Zero, nearCallback);
339 }
340
341 if (req.callbackMethod is RaycastCallback)
342 {
343 // Define default results
344 bool hitYN = false;
345 uint hitConsumerID = 0;
346 float distance = float.MaxValue;
347 Vector3 closestcontact = Vector3.Zero;
348 Vector3 snormal = Vector3.Zero;
349
350 // Find closest contact and object.
351 lock (m_contactResults)
352 {
353 foreach (ContactResult cResult in m_contactResults)
354 {
355 if(cResult.Depth < distance)
356 {
357 closestcontact = cResult.Pos;
358 hitConsumerID = cResult.ConsumerID;
359 distance = cResult.Depth;
360 snormal = cResult.Normal;
361 }
362 }
363 m_contactResults.Clear();
364 }
365
366 if (distance > 0 && distance < float.MaxValue)
367 hitYN = true;
368 ((RaycastCallback)req.callbackMethod)(hitYN, closestcontact, hitConsumerID, distance, snormal);
369 }
370 else
371 {
372 List<ContactResult> cresult = new List<ContactResult>(m_contactResults.Count);
373 lock (m_PendingRequests)
374 {
375 cresult.AddRange(m_contactResults);
376 m_contactResults.Clear();
377 }
378 ((RayCallback)req.callbackMethod)(cresult);
379 }
380 }
381
382 /// <summary>
383 /// Method that actually initiates the raycast with a geom
384 /// </summary>
385 /// <param name="req"></param>
386 private void doGeomRay(ODERayRequest req)
387 {
388 // Collide test
389 d.SpaceCollide2(ray, req.geom, IntPtr.Zero, nearCallback); // still do this to have full AABB pre test
390
391 if (req.callbackMethod is RaycastCallback)
392 {
393 // Define default results
394 bool hitYN = false;
395 uint hitConsumerID = 0;
396 float distance = float.MaxValue;
397 Vector3 closestcontact = Vector3.Zero;
398 Vector3 snormal = Vector3.Zero;
399
400 // Find closest contact and object.
401 lock (m_contactResults)
402 {
403 foreach (ContactResult cResult in m_contactResults)
404 {
405 if(cResult.Depth < distance )
406 {
407 closestcontact = cResult.Pos;
408 hitConsumerID = cResult.ConsumerID;
409 distance = cResult.Depth;
410 snormal = cResult.Normal;
411 }
412 }
413 m_contactResults.Clear();
414 }
415
416 if (distance > 0 && distance < float.MaxValue)
417 hitYN = true;
418
419 ((RaycastCallback)req.callbackMethod)(hitYN, closestcontact, hitConsumerID, distance, snormal);
420 }
421 else
422 {
423 List<ContactResult> cresult = new List<ContactResult>(m_contactResults.Count);
424 lock (m_PendingRequests)
425 {
426 cresult.AddRange(m_contactResults);
427 m_contactResults.Clear();
428 }
429 ((RayCallback)req.callbackMethod)(cresult);
430 }
431 }
432
433 private bool GetCurContactGeom(int index, ref d.ContactGeom newcontactgeom)
434 {
435 IntPtr ContactgeomsArray = m_scene.ContactgeomsArray;
436 if (ContactgeomsArray == IntPtr.Zero || index >= ColisionContactGeomsPerTest)
437 return false;
438
439 IntPtr contactptr = new IntPtr(ContactgeomsArray.ToInt64() + (Int64)(index * d.ContactGeom.unmanagedSizeOf));
440 newcontactgeom = (d.ContactGeom)Marshal.PtrToStructure(contactptr, typeof(d.ContactGeom));
441 return true;
442 }
443
444 // This is the standard Near. g1 is the ray
445 private void near(IntPtr space, IntPtr g1, IntPtr g2)
446 {
447 if (g2 == IntPtr.Zero || g1 == g2)
448 return;
449
450 if (m_contactResults.Count >= CurrentMaxCount)
451 return;
452
453 if (d.GeomIsSpace(g2))
454 {
455 try
456 {
457 d.SpaceCollide2(g1, g2, IntPtr.Zero, nearCallback);
458 }
459 catch (Exception e)
460 {
461 m_log.WarnFormat("[PHYSICS Ray]: Unable to Space collide test an object: {0}", e.Message);
462 }
463 return;
464 }
465
466 int count = 0;
467 try
468 {
469 count = d.CollidePtr(g1, g2, ColisionContactGeomsPerTest, m_scene.ContactgeomsArray, d.ContactGeom.unmanagedSizeOf);
470 }
471 catch (Exception e)
472 {
473 m_log.WarnFormat("[PHYSICS Ray]: Unable to collide test an object: {0}", e.Message);
474 return;
475 }
476
477 if (count == 0)
478 return;
479
480 uint ID = 0;
481 PhysicsActor p2 = null;
482
483 m_scene.actor_name_map.TryGetValue(g2, out p2);
484
485 if (p2 == null)
486 {
487 /*
488 string name;
489
490 if (!m_scene.geom_name_map.TryGetValue(g2, out name))
491 return;
492
493 if (name == "Terrain")
494 {
495 // land colision
496 if ((CurrentRayFilter & RayFilterFlags.land) == 0)
497 return;
498 }
499 else if (name == "Water")
500 {
501 if ((CurrentRayFilter & RayFilterFlags.water) == 0)
502 return;
503 }
504 else
505 return;
506 */
507 return;
508 }
509 else
510 {
511 switch (p2.PhysicsActorType)
512 {
513 case (int)ActorTypes.Prim:
514
515 RayFilterFlags thisFlags;
516
517 if (p2.IsPhysical)
518 thisFlags = RayFilterFlags.physical;
519 else
520 thisFlags = RayFilterFlags.nonphysical;
521
522 if (p2.Phantom)
523 thisFlags |= RayFilterFlags.phantom;
524
525 if (p2.IsVolumeDtc)
526 thisFlags |= RayFilterFlags.volumedtc;
527
528 if ((thisFlags & CurrentRayFilter) == 0)
529 return;
530
531 ID = ((OdePrim)p2).LocalID;
532 break;
533
534 case (int)ActorTypes.Agent:
535
536 if ((CurrentRayFilter & RayFilterFlags.agent) == 0)
537 return;
538 else
539 ID = ((OdeCharacter)p2).LocalID;
540 break;
541
542 case (int)ActorTypes.Ground:
543
544 if ((CurrentRayFilter & RayFilterFlags.land) == 0)
545 return;
546 break;
547
548 case (int)ActorTypes.Water:
549
550 if ((CurrentRayFilter & RayFilterFlags.water) == 0)
551 return;
552 break;
553
554 default:
555 return;
556 break;
557 }
558 }
559
560 d.ContactGeom curcontact = new d.ContactGeom();
561
562 // closestHit for now only works for meshs, so must do it for others
563 if ((CurrentRayFilter & RayFilterFlags.ClosestHit) == 0)
564 {
565 // Loop all contacts, build results.
566 for (int i = 0; i < count; i++)
567 {
568 if (!GetCurContactGeom(i, ref curcontact))
569 break;
570
571 ContactResult collisionresult = new ContactResult();
572 collisionresult.ConsumerID = ID;
573 collisionresult.Pos = new Vector3(curcontact.pos.X, curcontact.pos.Y, curcontact.pos.Z);
574 collisionresult.Depth = curcontact.depth;
575 collisionresult.Normal = new Vector3(curcontact.normal.X, curcontact.normal.Y,
576 curcontact.normal.Z);
577 lock (m_contactResults)
578 {
579 m_contactResults.Add(collisionresult);
580 if (m_contactResults.Count >= CurrentMaxCount)
581 return;
582 }
583 }
584 }
585 else
586 {
587 // keep only closest contact
588 ContactResult collisionresult = new ContactResult();
589 collisionresult.ConsumerID = ID;
590 collisionresult.Depth = float.MaxValue;
591
592 for (int i = 0; i < count; i++)
593 {
594 if (!GetCurContactGeom(i, ref curcontact))
595 break;
596
597 if (curcontact.depth < collisionresult.Depth)
598 {
599 collisionresult.Pos = new Vector3(curcontact.pos.X, curcontact.pos.Y, curcontact.pos.Z);
600 collisionresult.Depth = curcontact.depth;
601 collisionresult.Normal = new Vector3(curcontact.normal.X, curcontact.normal.Y,
602 curcontact.normal.Z);
603 }
604 }
605
606 if (collisionresult.Depth != float.MaxValue)
607 {
608 lock (m_contactResults)
609 m_contactResults.Add(collisionresult);
610 }
611 }
612 }
613
614 /// <summary>
615 /// Dereference the creator scene so that it can be garbage collected if needed.
616 /// </summary>
617 internal void Dispose()
618 {
619 m_scene = null;
620 if (ray != IntPtr.Zero)
621 {
622 d.GeomDestroy(ray);
623 ray = IntPtr.Zero;
624 }
625 }
626 }
627
628 public struct ODERayRequest
629 {
630 public IntPtr geom;
631 public Vector3 Origin;
632 public Vector3 Normal;
633 public int Count;
634 public float length;
635 public object callbackMethod;
636 public RayFilterFlags filter;
637 }
638} \ No newline at end of file