OpenSim
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros
ODERayCastRequestManager.cs
Go to the documentation of this file.
1 /*
2  * Copyright (c) Contributors, http://opensimulator.org/
3  * See CONTRIBUTORS.TXT for a full list of copyright holders.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  * * Redistributions of source code must retain the above copyright
8  * notice, this list of conditions and the following disclaimer.
9  * * Redistributions in binary form must reproduce the above copyright
10  * notice, this list of conditions and the following disclaimer in the
11  * documentation and/or other materials provided with the distribution.
12  * * Neither the name of the OpenSimulator Project nor the
13  * names of its contributors may be used to endorse or promote products
14  * derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19  * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 using System;
29 using System.Collections.Generic;
30 using System.Reflection;
31 using System.Runtime.InteropServices;
32 using System.Text;
33 using OpenMetaverse;
34 using OpenSim.Region.PhysicsModules.SharedBase;
35 using log4net;
36 
37 namespace OpenSim.Region.PhysicsModule.ODE
38 {
45  {
49  protected List<ODERayCastRequest> m_PendingRequests = new List<ODERayCastRequest>();
50 
54  protected List<ODERayRequest> m_PendingRayRequests = new List<ODERayRequest>();
55 
59  private OdeScene m_scene;
60 
64  d.ContactGeom[] contacts = new d.ContactGeom[5];
65 
69  private d.NearCallback nearCallback;
70  private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
71  private List<ContactResult> m_contactResults = new List<ContactResult>();
72 
73 
75  {
76  m_scene = pScene;
77  nearCallback = near;
78 
79  }
80 
88  public void QueueRequest(Vector3 position, Vector3 direction, float length, RaycastCallback retMethod)
89  {
90  lock (m_PendingRequests)
91  {
93  req.callbackMethod = retMethod;
94  req.length = length;
95  req.Normal = direction;
96  req.Origin = position;
97 
98  m_PendingRequests.Add(req);
99  }
100  }
101 
110  public void QueueRequest(Vector3 position, Vector3 direction, float length, int count, RayCallback retMethod)
111  {
112  lock (m_PendingRequests)
113  {
114  ODERayRequest req = new ODERayRequest();
115  req.callbackMethod = retMethod;
116  req.length = length;
117  req.Normal = direction;
118  req.Origin = position;
119  req.Count = count;
120 
121  m_PendingRayRequests.Add(req);
122  }
123  }
124 
130  {
131  int time = System.Environment.TickCount;
132  lock (m_PendingRequests)
133  {
134  if (m_PendingRequests.Count > 0)
135  {
136  ODERayCastRequest[] reqs = m_PendingRequests.ToArray();
137  for (int i = 0; i < reqs.Length; i++)
138  {
139  if (reqs[i].callbackMethod != null) // quick optimization here, don't raycast
140  RayCast(reqs[i]); // if there isn't anyone to send results
141  }
142 
143  m_PendingRequests.Clear();
144  }
145  }
146 
147  lock (m_PendingRayRequests)
148  {
149  if (m_PendingRayRequests.Count > 0)
150  {
151  ODERayRequest[] reqs = m_PendingRayRequests.ToArray();
152  for (int i = 0; i < reqs.Length; i++)
153  {
154  if (reqs[i].callbackMethod != null) // quick optimization here, don't raycast
155  RayCast(reqs[i]); // if there isn't anyone to send results
156  }
157 
158  m_PendingRayRequests.Clear();
159  }
160  }
161 
162  lock (m_contactResults)
163  m_contactResults.Clear();
164 
165  return System.Environment.TickCount - time;
166  }
167 
172  private void RayCast(ODERayCastRequest req)
173  {
174  // NOTE: limit ray length or collisions will take all avaiable stack space
175  // this value may still be too large, depending on machine configuration
176  // of maximum stack
177  float len = req.length;
178  if (len > 100f)
179  len = 100f;
180 
181  // Create the ray
182  IntPtr ray = d.CreateRay(m_scene.space, len);
183  d.GeomRaySet(ray, req.Origin.X, req.Origin.Y, req.Origin.Z, req.Normal.X, req.Normal.Y, req.Normal.Z);
184 
185  // Collide test
186  d.SpaceCollide2(m_scene.space, ray, IntPtr.Zero, nearCallback);
187 
188  // Remove Ray
189  d.GeomDestroy(ray);
190 
191  // Define default results
192  bool hitYN = false;
193  uint hitConsumerID = 0;
194  float distance = 999999999999f;
195  Vector3 closestcontact = new Vector3(99999f, 99999f, 99999f);
196  Vector3 snormal = Vector3.Zero;
197 
198  // Find closest contact and object.
199  lock (m_contactResults)
200  {
201  foreach (ContactResult cResult in m_contactResults)
202  {
203  if (Vector3.Distance(req.Origin, cResult.Pos) < Vector3.Distance(req.Origin, closestcontact))
204  {
205  closestcontact = cResult.Pos;
206  hitConsumerID = cResult.ConsumerID;
207  distance = cResult.Depth;
208  hitYN = true;
209  snormal = cResult.Normal;
210  }
211  }
212 
213  m_contactResults.Clear();
214  }
215 
216  // Return results
217  if (req.callbackMethod != null)
218  req.callbackMethod(hitYN, closestcontact, hitConsumerID, distance, snormal);
219  }
220 
225  private void RayCast(ODERayRequest req)
226  {
227  // limit ray length or collisions will take all avaiable stack space
228  float len = req.length;
229  if (len > 100f)
230  len = 100f;
231 
232  // Create the ray
233  IntPtr ray = d.CreateRay(m_scene.space, len);
234  d.GeomRaySet(ray, req.Origin.X, req.Origin.Y, req.Origin.Z, req.Normal.X, req.Normal.Y, req.Normal.Z);
235 
236  // Collide test
237  d.SpaceCollide2(m_scene.space, ray, IntPtr.Zero, nearCallback);
238 
239  // Remove Ray
240  d.GeomDestroy(ray);
241 
242  // Find closest contact and object.
243  lock (m_contactResults)
244  {
245  // Return results
246  if (req.callbackMethod != null)
247  req.callbackMethod(m_contactResults);
248  }
249  }
250 
251  // This is the standard Near. Uses space AABBs to speed up detection.
252  private void near(IntPtr space, IntPtr g1, IntPtr g2)
253  {
254 
255  if (g1 == IntPtr.Zero || g2 == IntPtr.Zero)
256  return;
257 // if (d.GeomGetClass(g1) == d.GeomClassID.HeightfieldClass || d.GeomGetClass(g2) == d.GeomClassID.HeightfieldClass)
258 // return;
259 
260  // Raytest against AABBs of spaces first, then dig into the spaces it hits for actual geoms.
261  if (d.GeomIsSpace(g1) || d.GeomIsSpace(g2))
262  {
263  if (g1 == IntPtr.Zero || g2 == IntPtr.Zero)
264  return;
265 
266  // Separating static prim geometry spaces.
267  // We'll be calling near recursivly if one
268  // of them is a space to find all of the
269  // contact points in the space
270  try
271  {
272  d.SpaceCollide2(g1, g2, IntPtr.Zero, nearCallback);
273  }
274  catch (AccessViolationException)
275  {
276  m_log.Warn("[PHYSICS]: Unable to collide test a space");
277  return;
278  }
279  //Colliding a space or a geom with a space or a geom. so drill down
280 
281  //Collide all geoms in each space..
282  //if (d.GeomIsSpace(g1)) d.SpaceCollide(g1, IntPtr.Zero, nearCallback);
283  //if (d.GeomIsSpace(g2)) d.SpaceCollide(g2, IntPtr.Zero, nearCallback);
284  return;
285  }
286 
287  if (g1 == IntPtr.Zero || g2 == IntPtr.Zero)
288  return;
289 
290  int count = 0;
291  try
292  {
293 
294  if (g1 == g2)
295  return; // Can't collide with yourself
296 
297  lock (contacts)
298  {
299  count = d.Collide(g1, g2, contacts.GetLength(0), contacts, d.ContactGeom.unmanagedSizeOf);
300  }
301  }
302  catch (SEHException)
303  {
304  m_log.Error("[PHYSICS]: The Operating system shut down ODE because of corrupt memory. This could be a result of really irregular terrain. If this repeats continuously, restart using Basic Physics and terrain fill your terrain. Restarting the sim.");
305  }
306  catch (Exception e)
307  {
308  m_log.WarnFormat("[PHYSICS]: Unable to collide test an object: {0}", e.Message);
309  return;
310  }
311 
312  PhysicsActor p1 = null;
313  PhysicsActor p2 = null;
314 
315  if (g1 != IntPtr.Zero)
316  m_scene.actor_name_map.TryGetValue(g1, out p1);
317 
318  if (g2 != IntPtr.Zero)
319  m_scene.actor_name_map.TryGetValue(g1, out p2);
320 
321  // Loop over contacts, build results.
322  for (int i = 0; i < count; i++)
323  {
324  if (p1 != null)
325  {
326  if (p1 is OdePrim)
327  {
328  ContactResult collisionresult = new ContactResult();
329 
330  collisionresult.ConsumerID = p1.LocalID;
331  collisionresult.Pos = new Vector3(contacts[i].pos.X, contacts[i].pos.Y, contacts[i].pos.Z);
332  collisionresult.Depth = contacts[i].depth;
333  collisionresult.Normal = new Vector3(contacts[i].normal.X, contacts[i].normal.Y,
334  contacts[i].normal.Z);
335  lock (m_contactResults)
336  m_contactResults.Add(collisionresult);
337  }
338  }
339 
340  if (p2 != null)
341  {
342  if (p2 is OdePrim)
343  {
344  ContactResult collisionresult = new ContactResult();
345 
346  collisionresult.ConsumerID = p2.LocalID;
347  collisionresult.Pos = new Vector3(contacts[i].pos.X, contacts[i].pos.Y, contacts[i].pos.Z);
348  collisionresult.Depth = contacts[i].depth;
349  collisionresult.Normal = new Vector3(contacts[i].normal.X, contacts[i].normal.Y,
350  contacts[i].normal.Z);
351 
352  lock (m_contactResults)
353  m_contactResults.Add(collisionresult);
354  }
355  }
356  }
357  }
358 
362  internal void Dispose()
363  {
364  m_scene = null;
365  }
366  }
367 
368  public struct ODERayCastRequest
369  {
370  public Vector3 Origin;
371  public Vector3 Normal;
372  public float length;
374  }
375 
376  public struct ODERayRequest
377  {
378  public Vector3 Origin;
379  public Vector3 Normal;
380  public int Count;
381  public float length;
383  }
384 }
void QueueRequest(Vector3 position, Vector3 direction, float length, int count, RayCallback retMethod)
Queues a raycast
delegate void RayCallback(List< ContactResult > list)
void QueueRequest(Vector3 position, Vector3 direction, float length, RaycastCallback retMethod)
Queues a raycast
delegate void RaycastCallback(bool hitYN, Vector3 collisionPoint, uint localid, float distance, Vector3 normal)
Processes raycast requests as ODE is in a state to be able to do them. This ensures that it's thread ...
int ProcessQueuedRequests()
Process all queued raycast requests