MezzanineEngine 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
rayquerytool.cpp
1 // © Copyright 2010 - 2014 BlackTopp Studios Inc.
2 /* This file is part of The Mezzanine Engine.
3 
4  The Mezzanine Engine is free software: you can redistribute it and/or modify
5  it under the terms of the GNU General Public License as published by
6  the Free Software Foundation, either version 3 of the License, or
7  (at your option) any later version.
8 
9  The Mezzanine Engine is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  GNU General Public License for more details.
13 
14  You should have received a copy of the GNU General Public License
15  along with The Mezzanine Engine. If not, see <http://www.gnu.org/licenses/>.
16 */
17 /* The original authors have included a copy of the license specified above in the
18  'Docs' folder. See 'gpl.txt'
19 */
20 /* We welcome the use of the Mezzanine engine to anyone, including companies who wish to
21  Build professional software and charge for their product.
22 
23  However there are some practical restrictions, so if your project involves
24  any of the following you should contact us and we will try to work something
25  out:
26  - DRM or Copy Protection of any kind(except Copyrights)
27  - Software Patents You Do Not Wish to Freely License
28  - Any Kind of Linking to Non-GPL licensed Works
29  - Are Currently In Violation of Another Copyright Holder's GPL License
30  - If You want to change our code and not add a few hundred MB of stuff to
31  your distribution
32 
33  These and other limitations could cause serious legal problems if you ignore
34  them, so it is best to simply contact us or the Free Software Foundation, if
35  you have any questions.
36 
37  Joseph Toppi - toppij@gmail.com
38  John Blackwood - makoenergy02@gmail.com
39 */
40 #ifndef _rayquerytool_cpp
41 #define _rayquerytool_cpp
42 
43 using namespace std;
44 
45 #include "rayquerytool.h"
46 #include "actor.h"
47 #include "actormanager.h"
48 #include "areaeffect.h"
49 #include "areaeffectmanager.h"
50 #include "debris.h"
51 #include "debrismanager.h"
52 #include "eventmanager.h"
53 #include "worldobject.h"
54 #include "plane.h"
55 #include "ray.h"
56 #include "managedptr.h"
57 #include "serialization.h"
58 #include "entresol.h"
59 
60 #include "Graphics/graphicsmanager.h"
61 #include "Graphics/scenemanager.h"
62 #include "Graphics/cameramanager.h"
63 #include "Graphics/cameraproxy.h"
64 #include "Graphics/gamewindow.h"
65 #include "Graphics/viewport.h"
67 
68 #include "Input/mouse.h"
69 #include "Input/inputmanager.h"
70 
71 #include "Internal/meshtools.h.cpp"
72 
73 #include <Ogre.h>
74 
75 #include <exception>
76 
77 #ifdef GetObject
78 #undef GetObject
79 #endif
80 
81 namespace Mezzanine
82 {
83 
84  namespace
85  {
86  /// @internal
87  /// @brief Ogre demands the use of special functions to delete a Ogre::RaySceneQuery, this handles that with RAII
88  class RayQueryHandle
89  {
90  public:
91  /// @brief This will work with a raw pointer to a Ogre::RaySceneQuery to manage a Ogre::RaySceneQuery.
92  typedef Ogre::RaySceneQuery* TargetPtrType;
93  /// @brief This will manage a Ogre::RaySceneQuery
94  typedef Ogre::RaySceneQuery TargetType;
95  /// @brief The actual ogre object we want to use.
96  Ogre::RaySceneQuery* RayQuery;
97 
98  /// @brief Create the ogre specific handle and sort items for raycasting.
99  void Construct()
100  {
101  RayQuery = Entresol::GetSingletonPtr()->GetSceneManager()->_GetGraphicsWorldPointer()->createRayQuery(Ogre::Ray(), Ogre::SceneManager::WORLD_GEOMETRY_TYPE_MASK);
102  RayQuery->setSortByDistance(true);
103  }
104  /// @brief CAll the Ogre API to clean up this wierd handle thing
105  void Deconstruct()
106  {
107  if(GetPointer())
108  { Entresol::GetSingletonPtr()->GetSceneManager()->_GetGraphicsWorldPointer()->destroyQuery(RayQuery); }
109  }
110 
111  /// @brief This is what ManagedPtr will use in copy and assignment operations as well as invaliding handles.
112  /// @param Value The new value for the pointer. If NULL the only thing that the ManagedPtr will do to the handle is call its deconstruct method.
113  void SetPointer(TargetPtrType Value)
114  { RayQuery = Value; }
115  /// @brief This is what the ManagedPtr with use for dereferencing.
116  /// @return The pointer to the managed data. This is expected to return a value that resolves to false when used as a condition when invalid.
117  TargetPtrType GetPointer()
118  { return RayQuery; }
119  };
120 
121  typedef ManagedPtr<RayQueryHandle> ManagedRayQuery;
122 
123  /// @internal
124  /// @brief Exact an Ogre::RayQuery with some default parameters and see if we hit anything
125  /// @param RayQuery A ManagedRayQuery
126  /// @param ActorRay The Ray to follow and see if it hits something
127  /// @return True if something is hit, false otherwise.
128  Boolean ExecuteQuery(ManagedRayQuery& RayQuery, Ogre::Ray& Ooray)
129  {
130  if(RayQuery) //Double check that the Rayquery is valid
131  {
132  RayQuery->setRay(Ooray);
133  RayQuery->setQueryMask(-1); // GetFirstActorOnRayByAABB did not do this
134  if( RayQuery->execute().size() <= 0 ) //Did we hit anything
135  { return false; }
136  return true;
137  }else{ // Something Failed
138  MEZZ_EXCEPTION(Exception::PARAMETERS_EXCEPTION,"Attempting to run a query on Null RaySceneQuery");
139  }
140  }
141  }
142 
143  RayQueryTool::RayQueryTool()
144  : ValidResult(false), IntersectedObject(NULL)
145  { ClearReturns(); }
146 
147  ///////////////////////////////////////////////////////////////////////////////
148  // World Ray Query Results
149  ///////////////////////////////////////
150 
152  {
153  ValidResult = false;
154  Offset = Vector3();
155  IntersectedObject = NULL;
156  return ValidResult;
157  }
158 
160  { return ValidResult; }
161 
163  { return Offset; }
164 
166  { return IntersectedObject; }
167 
168  ///////////////////////////////////////////////////////////////////////////////
169  // Query Helpers
170  ///////////////////////////////////////
171 
172 
173  ///////////////////////////////////////////////////////////////////////////////
174  // Ray Queries
175  ///////////////////////////////////////
176  Boolean RayQueryTool::GetFirstObjectOnRayByPolygon(Ray ObjectRay, Whole ObjectFlags)
177  {
178  ManagedRayQuery RayQuery;
179  Ogre::Ray Ooray = ObjectRay.GetOgreRay();
180 
181  if(!ExecuteQuery(RayQuery, Ooray))
182  { return ClearReturns(); }
183 
184  // at this point we have raycast to a series of different objects bounding boxes.
185  // we need to test these different objects to see which is the first polygon hit.
186  // there are some minor optimizations (distance based) that mean we wont have to
187  // check all of the objects most of the time, but the worst case scenario is that
188  // we need to test every triangle of every object.
189  Ogre::Real closest_distance = -1.0f;
190  Vector3 closest_result;
191  IntersectedObject=NULL;
192  Ogre::RaySceneQueryResult &query_result = RayQuery->getLastResults();
193  for (size_t qr_idx = 0; qr_idx < query_result.size(); qr_idx++)
194  {
195  // stop checking if we have found a raycast hit that is closer than all remaining entities
196  if ( (0.0f <= closest_distance) && (closest_distance < query_result[qr_idx].distance))
197  { break; }
198 
199  // only check this result if its a hit against an entity
200  if ((NULL != query_result[qr_idx].movable) && (0 == query_result[qr_idx].movable->getMovableType().compare("Entity")))
201  {
202  // get the entity to check
203  Ogre::Entity *pentity = static_cast<Ogre::Entity*>(query_result[qr_idx].movable);
204 
205  try
206  {
207  WorldObject* HitMetaInfo = Ogre::any_cast<Graphics::RenderableProxy*>(pentity->getUserAny())->GetParentObject();
208  if( HitMetaInfo && ( HitMetaInfo->GetType() & ObjectFlags ) )
209  {
210  // mesh data to retrieve
211  size_t vertex_count;
212  size_t index_count;
213  Ogre::Vector3 *vertices;
214  unsigned long *indices;
215 
216  // get the mesh information
217  Internal::MeshTools::GetMeshInformation( pentity, vertex_count, vertices, index_count, indices,
218  pentity->getParentNode()->_getDerivedPosition(),
219  pentity->getParentNode()->_getDerivedOrientation(),
220  pentity->getParentNode()->_getDerivedScale());
221 
222  // test for hitting individual triangles on the mesh
223  bool new_closest_found = false;
224  for (size_t i = 0; i < index_count; i += 3)
225  {
226  // check for a hit against this triangle
227  std::pair<bool, Ogre::Real> hit = Ogre::Math::intersects(Ooray, vertices[indices[i]], vertices[indices[i+1]], vertices[indices[i+2]], true, false);
228 
229  // if it was a hit check if its the closest
230  if (hit.first && ((0.0f > closest_distance) || (hit.second < closest_distance)) )
231  {
232  closest_distance = hit.second; // this is the closest so far, save it off
233  new_closest_found = true;
234  }
235  }
236 
237  // free the vertices and indices memory
238  delete[] vertices;
239  delete[] indices;
240 
241  // if we found a new closest raycast for this object, update the closest_result before moving on to the next object.
242  if (new_closest_found)
243  {
244  closest_result = Ooray.getPoint(closest_distance);
245  IntersectedObject = Ogre::any_cast<Graphics::RenderableProxy*>(pentity->getUserAny())->GetParentObject();
246  }
247 
248  } // \if WSO_ActorRigid
249  }catch(...){
250  ClearReturns();
251  MEZZ_EXCEPTION(Exception::INTERNAL_EXCEPTION,"Failed during cast in actor raycast.");
252  }
253  } // \if entity
254  } // \if qr_idx
255 
256  // ©hange the closest point into a point relative to the Actor
257  if (IntersectedObject)
258  {
259  Offset = IntersectedObject->GetOrientation() * ((closest_result - IntersectedObject->GetLocation()) * IntersectedObject->GetScale());
260  ValidResult=true;
261  return ValidResult;
262  }else{
263  return ClearReturns();
264  }
265  }
266 
267  Boolean RayQueryTool::GetFirstObjectOnRayByAABB(Ray ObjectRay, Whole ObjectFlags)
268  {
269  ManagedRayQuery RayQuery;
270  Ogre::Ray Ooray = ObjectRay.GetOgreRay();
271  if(!ExecuteQuery(RayQuery, Ooray))
272  { return ClearReturns(); }
273 
274  Ogre::RaySceneQueryResult &query_result = RayQuery->getLastResults();
275 
276  if (0 < query_result.size())
277  {
278  Ogre::Entity *pentity = static_cast<Ogre::Entity*>(query_result[0].movable);
279  IntersectedObject = Ogre::any_cast<Graphics::RenderableProxy*>(pentity->getUserAny())->GetParentObject();
280  Offset = Vector3();
281  ValidResult = true;
282  /// @todo TODO: The function WorldQueryTool::GetFirstActorOnRayByAABB does not return an valid offset. This needs to be calculated somehow.
283  /// @todo TODO: The function WorldQueryTool::GetFirstActorOnRayByAABB has not been tested and needs to be tested
284  /// @todo TODO: The function WorldQueryTool::GetFirstActorOnRayByAABB does not take other obstructions into account
285  return ValidResult;
286  }else{
287  return ClearReturns();
288  }
289  }
290 
291  Boolean RayQueryTool::RayPlaneIntersection(const Ray& QueryRay, const Plane& QueryPlane)
292  {
293  try{
294  Vector3 u = QueryRay.Destination - QueryRay.Origin;
295  Vector3 p0 = Vector3(0,0,0);
296 
297  if(QueryPlane.Normal.X == 0 && QueryPlane.Normal.Y == 0 && QueryPlane.Normal.Z == 0)
298  { return ClearReturns(); }
299  else
300  {
301  if(QueryPlane.Normal.X != 0)
302  { p0 = Vector3(QueryPlane.Distance,0,0); }
303  else if(QueryPlane.Normal.Y != 0)
304  { p0 = Vector3(0,QueryPlane.Distance,0); }
305  else
306  { p0 = Vector3(0,0,QueryPlane.Distance); }
307  }
308 
309  Vector3 w = QueryRay.Origin - p0;
310 
311  Real D = u.DotProduct(QueryPlane.Normal);
312  Real N = -1 * w.DotProduct(QueryPlane.Normal);
313 
314  Real SMALL_NUM = 0.00000001;
315 
316  if( (D<0? -D : D) < SMALL_NUM) // ©hecks if the Plane behind the RAy
317  {
318  if(N == 0)
319  {
320  Offset=QueryRay.Origin;
321  IntersectedObject=NULL;
322  ValidResult=true;
323  return ValidResult;
324  }
325  else
326  { return ClearReturns(); }
327  }
328 
329  Real sI = N/D;
330 
331  if(sI < 0 || sI > 1) // ©hecks if the ray is too long
332  { return ClearReturns(); }
333 
334  Vector3 return_vector(QueryRay.Origin + (u * sI));
335 
336  Real distance = return_vector.Distance(QueryRay.Origin);
337 
338  if(distance > QueryRay.Origin.Distance(QueryRay.Destination))
339  { return ClearReturns(); }
340 
341  Offset=return_vector;
342  IntersectedObject=NULL;
343  ValidResult=true;
344  return ValidResult;
345  } catch(exception e) {
346  //In case we divide b
347  Entresol::GetSingletonPtr()->Log("WorldQueryTool Error:Failed while calculating Ray/Plane Intersection, Assuming no valid intersection. Error follows:");
348  Entresol::GetSingletonPtr()->Log(e.what());
349  return ClearReturns();
350  }
351  }
352 
354  {
355  Graphics::Viewport* HoveredViewport = Input::InputManager::GetSingletonPtr()->GetSystemMouse()->GetHoveredViewport();
356  Vector2 MousePos = Input::InputManager::GetSingletonPtr()->GetSystemMouse()->GetViewportPosition();
357  Ray MouseRay;
358  if(HoveredViewport)
359  {
360  MouseRay = Ray( HoveredViewport->GetViewportCamera()->GetCameraToViewportRay(
361  MousePos.X / (Real)(HoveredViewport->GetActualWidth()),
362  MousePos.Y / (Real)(HoveredViewport->GetActualHeight()) ) );
363  MouseRay *= Length;
364  }
365  return MouseRay;
366  }
367 
368  void RayQueryTool::ProtoSerialize(XML::Node& CurrentRoot) const
369  {
370  Mezzanine::XML::Node RayQueryToolNode = CurrentRoot.AppendChild(SerializableName());
371  RayQueryToolNode.SetName(SerializableName());
372 
373  Mezzanine::XML::Attribute VersionAttr = RayQueryToolNode.AppendAttribute("Version");
374  if( VersionAttr && VersionAttr.SetValue("1"))
375  {
376  Mezzanine::XML::Attribute ResultAttr = RayQueryToolNode.AppendAttribute("ValidResult");
377  if( ResultAttr && ResultAttr.SetValue(ValidResult))
378  {}
379  else
380  { SerializeError("Create XML Attribute for ValidResult", SerializableName(), true); }
381 
382  Mezzanine::XML::Node OffsetNode = RayQueryToolNode.AppendChild("Offset");
383  if( OffsetNode )
384  { Offset.ProtoSerialize(OffsetNode); }
385  else
386  { SerializeError("Create XML Node for Offset", SerializableName(), true); }
387 
388  Mezzanine::XML::Attribute WorldObjectAttr = RayQueryToolNode.AppendAttribute("WorldObject");
389  if( WorldObjectAttr )
390  {
391  if( IntersectedObject )
392  {
393  if(WorldObjectAttr.SetValue(IntersectedObject->GetName().c_str()))
394  {}
395  else
396  { SerializeError("Create XML Node for Offset", SerializableName(),true); }
397  }
398  }
399  else
400  { SerializeError("Create XML Attribute for Offset", SerializableName(),true); }
401  }else{
402  SerializeError("Create XML Version Attibute", SerializableName(),true);
403  }
404  }
405 
407  {
409  {
410  if(OneNode.GetAttribute("Version").AsInt() == 1)
411  {
412  ValidResult=OneNode.GetAttribute("ValidResult").AsBool();
413 
414  XML::Node VecNode = OneNode.GetChild("Offset");
415  if(!VecNode)
416  { DeSerializeError("Could not Deserialize Offset",SerializableName()); }
417  Offset.ProtoDeSerialize(VecNode);
418 
419  String WorldObjectName(OneNode.GetAttribute("WorldObject").AsString());
420  if (WorldObjectName.size()) {
421  /// @todo This is temporary code that should be replaced with something more robust to find the proper world object.
422  IntersectedObject = Entresol::GetSingletonPtr()->GetDebrisManager()->GetDebris(WorldObjectName);
423  if( IntersectedObject == NULL ) {
424  IntersectedObject = Entresol::GetSingletonPtr()->GetActorManager()->GetActor(WorldObjectName);
425  }
426  if( IntersectedObject == NULL ) {
427  IntersectedObject = Entresol::GetSingletonPtr()->GetAreaEffectManager()->GetAreaEffect(WorldObjectName);
428  }
429  }else{
430  IntersectedObject = NULL;
431  }
432  }else{
433  MEZZ_EXCEPTION(Exception::INVALID_VERSION_EXCEPTION,"Incompatible XML Version for " + SerializableName() + ": Not Version 1.");
434  }
435  }else{
436  MEZZ_EXCEPTION(Exception::II_IDENTITY_INVALID_EXCEPTION,"Attempting to deserialize a " + SerializableName() + ", found a " + String(OneNode.Name()) + ".");
437  }
438  }
439 
441  { return "RayQueryTool"; }
442 }
443 
444 #endif