NewMOC - access violation when registering Entity

Problems building or running the engine, queries about how to use features etc.
Post Reply
hcl14
Kobold
Posts: 27
Joined: Mon Jan 26, 2015 8:27 pm
x 1

NewMOC - access violation when registering Entity

Post by hcl14 »

I try to use New Minimal Ogre Collision (http://www.ogre3d.org/tikiwiki/New+Mini ... +Collision, http://www.ogre3d.org/forums/viewtopic.php?f=5&t=80829), so I added to TutorialApplication project

NewMOC.h:

Code: Select all

/**
* New Minimal Ogre Collision - rewritten simple Ogre collision detection based on the MOC idea.
* Feel free to use this!
*
* Author: Ronen Ness
* Since: 22/04/14
*/

#pragma once
#include <Ogre.h>
#include <vector>
#include <utility>
#include <unordered_map>

   namespace Collision {

      
      // return struct for the function check_ray_collision (all the data about collision)
      // collided - weather or not there was a collision and the data in the struct is valid (if false ignore other fields).
      // position - will contain the collision position
      // entity - the collided entity
      // closest_distance - will contain the closest distance we collided with
      struct SCheckCollisionAnswer
      {
         bool               collided;
         Ogre::Vector3         position;
         Ogre::Entity*         entity;
         float               closest_distance;
      };

      // different collision types
      enum ECollisionType
      {
         COLLISION_ACCURATE,      // will check accurate intersection with all verticles of the entity (for complex shapes that need accurate collision)
         COLLISION_BOX,         // will only check intersection with the bounding box (good for walls and crates-like objects)
         COLLISION_SPHERE,      // will only check interection with the object sphere (good for characters)
      };

      // holds vertices data of a mesh
      struct SMeshData
      {
         unsigned int ref_count;      // how many entities we have with that mesh registered to the collision tools (when 0, this data is deleted)
         size_t vertex_count;      // vertices count
         Ogre::Vector3* vertices;   // vertices
         size_t index_count;         // indices count
         Ogre::uint32* indices;      // indices

         SMeshData() : ref_count(0), vertex_count(0), vertices(nullptr), index_count(0), indices(nullptr)
         {
         }

         // delete the inner data
         void delete_data()
         {
            delete[] vertices;
            delete[] indices;
         }
      };

      // data about an entity registered to the collision tools
      struct SCollidableEntity
      {
         Ogre::Entity*         Entity;            // the entity to check
         ECollisionType         CollisionType;      // the prefered collision type for this entity
         bool               IsStatic;         // is this entity part of a static geometry

         // Data used only for static entities
         struct SStaticData
         {
            Ogre::Sphere         Sphere;            // used only for static objects with sphere collision
            Ogre::AxisAlignedBox   Box;            // used only for static objects with box or accurate collision
            Ogre::Matrix4         Mat;            // transformation matrix
         } *StaticData;

         // delete the static data if have it
         void remove_static_data()
         {
            if (StaticData)
               delete StaticData;
            StaticData = nullptr;
         }
      };

      // collision detection manager for a specific scene
      class CollisionTools {

      private:
         std::vector<SCollidableEntity>                     m_Entities;         // the entities that are registered for collision checks
         std::unordered_map<const Ogre::Mesh*, SMeshData>      m_MeshesData;      // data about meshes we need for accurate collision

      public:

         CollisionTools();
         ~CollisionTools();

         // register a dynamic entity for collision detection
         void register_entity(Ogre::Entity* Entity, ECollisionType CollisionType = COLLISION_ACCURATE);

         // register a static entity for collision detection
         void register_static_entity(Ogre::Entity* Entity, const Ogre::Vector3& position, const Ogre::Quaternion orientation, const Ogre::Vector3 scale, ECollisionType CollisionType = COLLISION_ACCURATE);

         // unregister an entity from collision detection (make sure to call this when the entity is deleted!)
         void remove_entity(Ogre::Entity* Entity);


         // check ray collision. check out "SCheckCollisionAnswer" for info about return values.
         // ray - collision ray to check
         // queryMask - ogre's query mask (you can set for every entity
         // ignore - will ignore the entity who has the address of 'ignore'. use this if you want to prevent a character from colliding with itself..
         // maxDistance - beyond this distance we'll ignore entities
         // stopOnFirstPositive - if true, will stop on first positive collision (instead of nearest)
         SCheckCollisionAnswer check_ray_collision(const Ogre::Ray &ray, const Ogre::uint32 queryMask = 0xFFFFFFFF, void* ignore = nullptr, Ogre::Real maxDistance = 0xffff, bool stopOnFirstPositive = false);

         // check ray collision. check out "SCheckCollisionAnswer" for info about return values.
         // fromPoint - ray starting point
         // toPoint - ray ending point
         // collisionRadius - ray 'radius'
         // rayHeightLevel - will add this factor to the yof the ray.
         // queryMask - ogre's query mask (you can set for every entity
         // ignore - will ignore the entity who has the address of 'ignore'. use this if you want to prevent a character from colliding with itself..
         // stopOnFirstPositive - if true, will stop on first positive collision (instead of nearest)
         SCheckCollisionAnswer check_ray_collision(const Ogre::Vector3& fromPoint, const Ogre::Vector3& toPoint, const float collisionRadius = 1.0f, 
            const float rayHeightLevel = 0.0f, const Ogre::uint32 queryMask = 0xFFFFFFFF, void* ignore = nullptr, bool stopOnFirstPositive = false);


      private:

         // do a simple ray query and return a list of results sorted by distance
         // NOTE!!! this function only do simple query! it does not do accurate checks or anything, either box collision or sphere collision.
         // all the accurate checks and range limit is done in one of the 'check_ray_collision' functions
         // stopOnFirstPositive - if true, will stop on first positive bounding box or sphere collision (not relevant for accurate collisions)
         typedef std::pair<const SCollidableEntity*, Ogre::Real> RayQueryEntry;
         std::list<RayQueryEntry> get_basic_ray_query_entities_list(const Ogre::Ray &ray, const Ogre::uint32 queryMask = 0xFFFFFFFF, 
                     void* ignore = nullptr, Ogre::Real maxDistance = 0xffff, bool stopOnFirstPositive = false);

         // comparing function to arranage the result list of get_basic_ray_query_entities_list
         friend bool compare_query_distance (const CollisionTools::RayQueryEntry& first, const CollisionTools::RayQueryEntry& second);

         // add mesh data reference to m_MeshesData map.
         // if first mesh of this type, create all its data, if already exist just increase the reference
         void add_mesh_data(const Ogre::Mesh* mesh);

         // remove reference from mesh data. if ref count is 0, data is released
         void remove_mesh_data(const Ogre::Mesh* mesh);

         // get all the needed information of a mesh
         // we use this function to create the mesh data hash table for accurate collision
         void get_mesh_info(const Ogre::Mesh* mesh,
                              size_t &vertex_count,
                              Ogre::Vector3* &vertices,
                              size_t &index_count,
                              Ogre::uint32* &indices);

      };
   };
and NewMOC.cpp:

Code: Select all

#include "NewMOC.h"

   namespace Collision {

      CollisionTools::CollisionTools()
      {
      }

      CollisionTools::~CollisionTools()
      {
         // remove all entities and static data
         while (!m_Entities.empty())
         {
            m_Entities.back().remove_static_data();
            m_Entities.pop_back();
         }

         // remove all meshes data
         for (auto mesh_data = m_MeshesData.begin(); mesh_data != m_MeshesData.end(); ++ mesh_data)
         {
            mesh_data->second.delete_data();
         }
      }

      // unregister an entity from collision detection (make sure to call this when the entity is deleted!)
      void CollisionTools::remove_entity(Ogre::Entity* Entity)
      {
         // find the entity in the entities list
         for (auto data = m_Entities.begin(); data != m_Entities.end(); ++data)
         {
            if (data->Entity == Entity)
            {
               // remove static data and mesh data (if exist)
               data->remove_static_data();
               if (data->CollisionType == COLLISION_ACCURATE)
                  remove_mesh_data(data->Entity->getMesh().get());

               // erase this entity from the list
               m_Entities.erase(data);
               return;
            }
         }

         assert(false);
      }

      SCheckCollisionAnswer CollisionTools::check_ray_collision(const Ogre::Vector3& fromPoint, const Ogre::Vector3& toPoint, const float collisionRadius, 
            const float rayHeightLevel, const Ogre::uint32 queryMask, void* ignore, bool stopOnFirstPositive)
      {

         // convert points to a collision ray
         Ogre::Vector3 fromPointAdj(fromPoint.x, fromPoint.y + rayHeightLevel, fromPoint.z);
         Ogre::Vector3 toPointAdj(toPoint.x, toPoint.y + rayHeightLevel, toPoint.z);
         Ogre::Vector3 normal = toPointAdj - fromPointAdj;
         float distToDest = normal.normalise();
         static Ogre::Ray ray;
         ray.setOrigin(fromPointAdj);
         ray.setDirection(normal);

         // do the query
         SCheckCollisionAnswer ret = check_ray_collision(ray, queryMask, ignore, collisionRadius, stopOnFirstPositive);

         // make sure its within radius range
         if (ret.collided)
         {
            float distToColl = ret.closest_distance;
            distToColl -= collisionRadius;
            ret.collided = (distToColl <= distToDest);
         }
         return ret;
      }


      SCheckCollisionAnswer CollisionTools::check_ray_collision(const Ogre::Ray &ray, const Ogre::uint32 queryMask, void* ignore, 
         Ogre::Real maxDistance, bool stopOnFirstPositive)
      {
         // create return structure
         SCheckCollisionAnswer ret;
         memset(&ret, 0, sizeof(ret));

         // first do a simple ray query on all the entities we registered
         std::list<CollisionTools::RayQueryEntry> results = get_basic_ray_query_entities_list(ray, queryMask, ignore, maxDistance, stopOnFirstPositive);
         
         // no results? stop here
         if (results.size() <= 0)
         {
            return ret;
         }

         // at this point we have raycast to a series of different objects bounding boxes.
         // we need to test these different objects to see which is the first polygon hit.
         // there are some minor optimizations (distance based) that mean we wont have to
         // check all of the objects most of the time, but the worst case scenario is that
         // we need to test every triangle of every object.
         //Ogre::Ogre::Real closest_distance = -1.0f;
         ret.closest_distance = -1.0f;
         for (auto query_result = results.begin(); query_result != results.end(); ++query_result)
         {
            // since its sorted by distance, once we hit an entity that only collides with bounding box or sphere,
            // we stop immediatly and return it. there's no point checking the rest of the entities.
            if (query_result->first->CollisionType != COLLISION_ACCURATE)
            {
               ret.closest_distance = abs(query_result->second);
               ret.collided = true;
               ret.entity = query_result->first->Entity;
               ret.position = ray.getPoint(ret.closest_distance);
               return ret;
            }

            // stop checking if we have found a raycast hit that is closer
            // than all remaining entities
            if (((ret.closest_distance >= 0.0f) && (maxDistance < maxDistance)) &&
               (ret.closest_distance < query_result->second || stopOnFirstPositive))
            {
               break;
            }

            // only check this result if its a hit against an entity
            {
               // get the entity to check
               Ogre::MovableObject *pentity = static_cast<Ogre::MovableObject*>(query_result->first->Entity);

               // get mesh data from cache
               const SMeshData& data = m_MeshesData[query_result->first->Entity->getMesh().get()];
               assert(data.ref_count);

               // test for hitting individual triangles on the mesh
               bool new_closest_found = false;
               for (size_t i = 0; i < data.index_count; i += 3)
               {
                  // get transformation matrix
                  const Ogre::Matrix4* mat;
                  if (query_result->first->IsStatic)
                  {
                     mat = &query_result->first->StaticData->Mat;
                  }
                  else
                  {
                     mat = &query_result->first->Entity->getParentNode()->_getFullTransform();
                  }

                  // get corrent triangle and transform it
                  Ogre::Vector3 v1, v2, v3;
                  v1 = (*mat) * data.vertices[data.indices[i]];
                  v2 = (*mat) * data.vertices[data.indices[i+1]];
                  v3 = (*mat) * data.vertices[data.indices[i+2]];

                  // check for a hit against this triangle
                  std::pair<bool, Ogre::Real> hit = Ogre::Math::intersects(ray, v1, v2, v3, true, false);

                  // if it was a hit check if its the closest
                  if (hit.first && hit.second < maxDistance)
                  {
                     if ((ret.closest_distance < 0.0f) ||
                        (hit.second < ret.closest_distance))
                     {
                        // this is the closest so far, save it off
                        ret.closest_distance = hit.second;
                        new_closest_found = true;
                     }
                  }
               }

               // if we found a new closest raycast for this object, update the
               // closest_result before moving on to the next object.
               if (new_closest_found)
               {
                  ret.entity = (Ogre::Entity*)pentity;
                  ret.position = ray.getPoint(ret.closest_distance);
               }
            }
         }

         // return the result
         ret.collided = (ret.closest_distance >= 0.0f);
         return ret;
      }


      // Get the mesh information for the given mesh.
      // Code found on this forum link: http://www.ogre3d.org/wiki/index.php/RetrieveVertexData
      // TAKEN FROM MOC
      void CollisionTools::get_mesh_info(const Ogre::Mesh* mesh,
                              size_t &vertex_count,
                              Ogre::Vector3* &vertices,
                              size_t &index_count,
                              Ogre::uint32* &indices)
      {
         bool added_shared = false;
         size_t current_offset = 0;
         size_t shared_offset = 0;
         size_t next_offset = 0;
         size_t index_offset = 0;

         vertex_count = index_count = 0;

         // Calculate how many vertices and indices we're going to need
         for (unsigned short i = 0; i < mesh->getNumSubMeshes(); ++i)
         {
            Ogre::SubMesh* submesh = mesh->getSubMesh( i );

            // We only need to add the shared vertices once
            if(submesh->useSharedVertices)
            {
               if( !added_shared )
               {
                  vertex_count += mesh->sharedVertexData->vertexCount;
                  added_shared = true;
               }
            }
            else
            {
               vertex_count += submesh->vertexData->vertexCount;
            }

            // Add the indices
            index_count += submesh->indexData->indexCount;
         }


         // Allocate space for the vertices and indices
         vertices = new Ogre::Vector3[vertex_count];
         indices = new Ogre::uint32[index_count];

         added_shared = false;

         // Run through the submeshes again, adding the data into the arrays
         for ( unsigned short i = 0; i < mesh->getNumSubMeshes(); ++i)
         {
            Ogre::SubMesh* submesh = mesh->getSubMesh(i);

            Ogre::VertexData* vertex_data = submesh->useSharedVertices ? mesh->sharedVertexData : submesh->vertexData;

            if((!submesh->useSharedVertices)||(submesh->useSharedVertices && !added_shared))
            {
               if(submesh->useSharedVertices)
               {
                  added_shared = true;
                  shared_offset = current_offset;
               }

               const Ogre::VertexElement* posElem =
                  vertex_data->vertexDeclaration->findElementBySemantic(Ogre::VES_POSITION);

               Ogre::HardwareVertexBufferSharedPtr vbuf =
                  vertex_data->vertexBufferBinding->getBuffer(posElem->getSource());

               unsigned char* vertex =
                  static_cast<unsigned char*>(vbuf->lock(Ogre::HardwareBuffer::HBL_READ_ONLY));

               // There is _no_ baseVertexPointerToElement() which takes an Ogre::Ogre::Real or a double
               //  as second argument. So make it float, to avoid trouble when Ogre::Ogre::Real will
               //  be comiled/typedefed as double:
               //      Ogre::Ogre::Real* pOgre::Real;
               float* pReal;

               for( size_t j = 0; j < vertex_data->vertexCount; ++j, vertex += vbuf->getVertexSize())
               {
                  posElem->baseVertexPointerToElement(vertex, &pReal);

                  Ogre::Vector3 pt(pReal[0], pReal[1], pReal[2]);
                  vertices[current_offset + j] = pt;
               }

               vbuf->unlock();
               next_offset += vertex_data->vertexCount;
            }


            Ogre::IndexData* index_data = submesh->indexData;
            size_t numTris = index_data->indexCount / 3;
            Ogre::HardwareIndexBufferSharedPtr ibuf = index_data->indexBuffer;

            bool use32bitindexes = (ibuf->getType() == Ogre::HardwareIndexBuffer::IT_32BIT);

            Ogre::uint32*  pLong = static_cast<Ogre::uint32*>(ibuf->lock(Ogre::HardwareBuffer::HBL_READ_ONLY));
            unsigned short* pShort = reinterpret_cast<unsigned short*>(pLong);


            size_t offset = (submesh->useSharedVertices)? shared_offset : current_offset;

            if ( use32bitindexes )
            {
               for ( size_t k = 0; k < numTris*3; ++k)
               {
                  indices[index_offset++] = pLong[k] + static_cast<Ogre::uint32>(offset);
               }
            }
            else
            {
               for ( size_t k = 0; k < numTris*3; ++k)
               {
                  indices[index_offset++] = static_cast<Ogre::uint32>(pShort[k]) +
                     static_cast<Ogre::uint32>(offset);
               }
            }

            ibuf->unlock();
            current_offset = next_offset;
         }
      }

      // register a dynamic entity for collision detection
      void CollisionTools::register_entity(Ogre::Entity* Entity, ECollisionType CollisionType)
      {
         // create the new data struct
         SCollidableEntity New;
         New.Entity = Entity;
         New.CollisionType = CollisionType;
         New.IsStatic = false;
         New.StaticData = nullptr;

         // if need accurate collision, create the data for it
         if (CollisionType == COLLISION_ACCURATE)
            add_mesh_data(Entity->getMesh().get()); 

         // add it to the list of collideables
         m_Entities.push_back(New);
      }

      // register a static entity for collision detection
      void CollisionTools::register_static_entity(Ogre::Entity* Entity, const Ogre::Vector3& position, const Ogre::Quaternion orientation, const Ogre::Vector3 scale, ECollisionType CollisionType)
      {
         // create the new data struct
         SCollidableEntity New;
         New.Entity = Entity;
         New.CollisionType = CollisionType;
         New.IsStatic = true;
         New.StaticData = new SCollidableEntity::SStaticData();
         New.StaticData->Mat.makeTransform(position, scale, orientation);
         New.StaticData->Sphere.setRadius(Entity->getBoundingRadius() * scale.length());
         New.StaticData->Sphere.setCenter(position);
         New.StaticData->Box = Entity->getBoundingBox();
         Ogre::Matrix4 mat;
         mat.makeTransform(position, scale, orientation);
         New.StaticData->Box.transform(mat);

         // if need accurate collision, create the data for it
         if (CollisionType == COLLISION_ACCURATE)
            add_mesh_data(Entity->getMesh().get()); 

         // add it to the list of collideables
         m_Entities.push_back(New);
      }

      // add a reference to a mesh data
      void CollisionTools::add_mesh_data(const Ogre::Mesh* mesh)
      {
         // if already exist, just increase the reference count
         SMeshData& data = m_MeshesData[mesh];
         if (data.ref_count > 0)
         {
            data.ref_count++;
            return;
         }

         // if not exist, create it
         data.ref_count = 1;
         get_mesh_info(mesh, data.vertex_count, data.vertices, data.index_count, data.indices);
      }

      // remove reference from mesh data. if ref count is 0, data is released
      void CollisionTools::remove_mesh_data(const Ogre::Mesh* mesh)
      {
         SMeshData& data = m_MeshesData[mesh];
         data.ref_count--;
         if (data.ref_count == 0)
         {
            data.delete_data();
            m_MeshesData.erase(mesh);
         }
      }

      // to sort the return list from 'get_basic_ray_query_entities_list()'
      bool compare_query_distance (const CollisionTools::RayQueryEntry& first, const CollisionTools::RayQueryEntry& second)
      {
         // if first collision function is more simple than second, always put it first
         if (first.first->CollisionType != COLLISION_ACCURATE && second.first->CollisionType == COLLISION_ACCURATE)
            return true;

         // if second collision function is more simple than second, always put it first
         if (second.first->CollisionType != COLLISION_ACCURATE && first.first->CollisionType == COLLISION_ACCURATE)
            return false;

         // else, sort by distance
         return first.second < second.second;
      }

      // do a simple ray query and return a list of results sorted by distance
      std::list<CollisionTools::RayQueryEntry> CollisionTools::get_basic_ray_query_entities_list(const Ogre::Ray &ray, const Ogre::uint32 queryMask, void* ignore, 
         Ogre::Real maxDistance, bool stopOnFirstPositive)
      {
         // return vector
         std::list<CollisionTools::RayQueryEntry> ret;

         // loop over all the registered entities and check simple sphere/box intersection. arrange by distance.
         bool Stop = false;
         for (auto data = m_Entities.begin(); (data != m_Entities.end() && !Stop); ++data)
         {
            // skip the ignored entity
            if (data->Entity == ignore)
               continue;

            // skip if query mask don't fit
            if ((data->Entity->getQueryFlags() & queryMask) == 0)
               continue;

            // if its static, first perform simple distance check
            if (data->IsStatic &&
               ray.getOrigin().distance(data->StaticData->Sphere.getCenter()) - data->StaticData->Sphere.getRadius() > maxDistance)
               continue;

            // if invisible skip it
            if (!data->Entity->isVisible())
               continue;

            // check basic intersection
            switch (data->CollisionType)
            {

            // check box intersection
            case COLLISION_ACCURATE:
            case COLLISION_BOX:
               {

               // get the bounding box to use
               const Ogre::AxisAlignedBox* bb = (data->IsStatic) ? &data->StaticData->Box : &data->Entity->getWorldBoundingBox(true);

               // check if intersects and if so insert to return list (sorting comes in the end)
               std::pair<bool, Ogre::Real> inter = ray.intersects(*bb);
               assert(maxDistance >= 0);
               if (inter.first && inter.second < maxDistance)
               {
                  ret.push_back(CollisionTools::RayQueryEntry(data._Ptr, inter.second));
                  if (stopOnFirstPositive && data->CollisionType == COLLISION_BOX)
                     Stop = true;
               }
               break;
               }

            // check sphere collision
            case COLLISION_SPHERE:
               {
               // get the sphere to use
               const Ogre::Sphere* sp;
               if (data->IsStatic)
               {
                  sp = &data->StaticData->Sphere;
               }
               else
               {
                  Ogre::Sphere sphere = data->Entity->getWorldBoundingSphere(true);
                  sp = &sphere;
               }

               // check if intersects and if so insert to return list (sorting comes in the end)
               std::pair<bool, Ogre::Real> inter = ray.intersects(*sp);
               inter.second = abs(inter.second);
               if (inter.first && inter.second < maxDistance)
               {
                  ret.push_back(CollisionTools::RayQueryEntry(data._Ptr, inter.second));
                  if (stopOnFirstPositive)
                     Stop = true;
               }
               break;
               }

            // should never get here.
            default:
               assert(false);
            }
         }

         // now sort the list!
         ret.sort(compare_query_distance);

         // return the list
         return ret;
      }

   };
Declared an instance of CollisionTools in my .h file:

Code: Select all

protected:
         Collision::CollisionTools *mCollisionTools;
And tried to register an entity:

Code: Select all

void TutorialApplication::createScene(void)
{
    ***
    Ogre::SceneNode* KnotNode = mSceneMgr->getRootSceneNode()->createChildSceneNode("KnotNode",Ogre::Vector3( 200, -100, 0 ) );
    KnotNode->attachObject(knot3);
    mCollisionTools->register_static_entity(knot3,KnotNode->getPosition(),KnotNode->getOrientation(),KnotNode->getScale(),Collision::COLLISION_BOX);

which gives me an error "Access violation reading location 0xccccccd4" in register_static_entity() function at line

Code: Select all

 m_Entities.push_back(New) ;
Also, trying to register entity with collision type Collision::COLLISION_ACCURATE brings the same error in function add_mesh_data() in NewMOC.cpp at line

Code: Select all

 SMeshData& data = m_MeshesData[mesh];
It seems like something was not declared correctly, but my poor knowledge of C++ makes me unable to find the reason.
Post Reply