Skip to content
Snippets Groups Projects
Select Git revision
  • 83e1ff781c9e4da801ac61d44b50d7137b054519
  • master default protected
  • hsh_v3.11
  • hsh_v3.10-r6
  • hsh_v3.10-r3
  • v3.9-r9
  • v3.10-r6
  • v3.9-r8
  • v3.10-r5
  • v3.9-r7
  • v3.10-r4
  • v3.9-r6
  • v3.10-r3
  • v3.9-r5
  • v3.10-r2
  • v3.9-r4
  • v3.10-r1
  • v3.9-r3
  • v3.9-r2
  • v3.9-r1
  • v3.8-r5
  • v3.8-r4
  • v3.8-r3
  • v3.8-r2
  • v3.8-r1
25 results

infobanner.min.js

Blame
  • CgPointCloud.cpp 13.22 KiB
    #include "CgPointCloud.h"
    #include "CgUtils/Timer.h"
    
    // squared distance between a point and a ray
    float sqrPointRayDist(glm::vec3 point, glm::vec3 origin, glm::vec3 direction) {
      glm::vec3 delta = point - origin;
      float t = std::max(0.0f, glm::dot(direction, delta) / glm::length2(direction));
      return glm::length2(direction * t - delta);
    }
    
    CgPointCloud::CgPointCloud(): CgPointCloud::CgPointCloud(51) {}
    
    CgPointCloud::CgPointCloud(int id):
    m_type(Cg::PointCloud),
    m_id(id) {
        m_vertices.push_back(glm::vec3(0.0,0.0,0.0));
        m_vertex_normals.push_back(glm::vec3(0.0,0.0,1.0));
        m_vertex_colors.push_back(glm::vec3(0.0,0.0,1.0));
        calculateSplatOrientations();
    }
    
    
    CgPointCloud::~CgPointCloud() {
        m_vertices.clear();
        m_vertex_normals.clear();
        m_vertex_colors.clear();
        m_splat_indices.clear();
    }
    
    
    void CgPointCloud::calculateSplatOrientations() {
      // calculate local coordinate system for splats (arbitrary orientation of ellipse in plane)
      // replace this if you have the real coordinate system, use up vector = y-Axis of your local coordinate system instead of getPerpendicularVector(...)
    
      m_splat_orientations.clear();
      m_splat_scaling.clear();
      m_splat_indices.clear();
    
      for(unsigned int i=0;i<m_vertices.size();i++)
        {
          glm::mat4 lookAt_matrix(glm::lookAt(glm::vec3(m_vertices[i]),glm::vec3(m_vertices[i]-m_vertex_normals[i]),getPerpendicularVector(m_vertex_normals[i])));
          m_splat_orientations.push_back(lookAt_matrix);
          m_splat_scaling.push_back(glm::vec2(0.02,0.005));
    
          // use all points for splatting by default
          m_splat_indices.push_back(i);
        }
    
    
    }
    
    void CgPointCloud::init( std::string filename, bool cheat_normals) {
        m_vertices.clear();
        m_vertex_normals.clear();
        m_vertex_colors.clear();
        m_splat_orientations.clear();
        m_splat_scaling.clear();
        m_splat_indices.clear();
    
        // load obj File
        ObjLoader loader;
        loader.load(filename);
        loader.getPositionData(m_vertices);
        m_centroid = getCentroid(m_vertices);
    
        buildKdTree(&m_vertices.front(), &(*(m_vertices.end() - 1)), 0);
        calculateAABB();
        // run_timed(1, [&]() {estimateNormals();});
        estimateNormals();
        std::cout << "got " << m_vertices.size() << " points" << std::endl;
    
        // calculate local coordinate system for splats 
        // (arbitrary orientation of ellipse in plane)
        // replace this if you have the real coordinate 
        // system, use up vector = y-Axis of your local 
        // coordinate system instead of getPerpendicularVector(...)
        //calculateSplatOrientations();
    
        //add a standard color for each point if lighting turned off
        for(unsigned int i = 0;i < m_vertices.size(); i++) {
          m_vertex_colors.push_back(glm::vec3(0.0,1.0,0.0));
        }
    
        unsigned int k = 50;
        // "fast" if k is relatively small, if k is on the order 
        // of m_vertices.size(), this is slower than brute force.
        std::vector<unsigned int> neighbors = getNearestNeighborsFast(0, k);
        setColors(neighbors, glm::vec3(0.0, 0.0, 1.0));
    }
    
    void CgPointCloud::buildKdTree(glm::vec3* begin, glm::vec3* end, int depth) {
        auto half_size = (end - begin) / 2;
    
        if(begin == end || half_size == 0) {
            // zero or one element, nothing to do 
            return;
        } else { 
            // more than one element!
    
            // partially sort and ensure median element 
            // is in the middle (runs in O(n))
            // about 4x faster on tyra.obj than std::sort
            std::nth_element(begin, begin + half_size, end, [=](glm::vec3 a, glm::vec3 b) {
                return a[depth % 3] < b[depth % 3];
            });
    
            // split array in two (excluding median) and recurse
            buildKdTree(begin, begin + half_size, depth + 1);
            buildKdTree(begin + half_size + 1, end, depth + 1);
        }
    }
    
    void CgPointCloud::estimateNormals() {
      unsigned int neighbourhood_size = 25;
    
      // find point with highest y-value
      //std::vector<unsigned int> spanning_tree;
      //spanning_tree.resize(m_vertices.size());
      m_vertex_normals.clear();
      m_vertex_normals.resize(m_vertices.size());
      //float max_y =-std::numeric_limits<float>::infinity();
      //unsigned int max_y_index = 0;
      //for(int i = 0; i < m_vertices.size(); i++) {
      //  float curr = m_vertices[i].y;
      //  if(max_y < curr) {
      //    max_y = curr;
      //    max_y_index = i;
      //  }
      //}
    
      // the extremal point is selected as root (has no parent)
      //spanning_tree[max_y_index] = max_y_index;
    
      // build minimal spanning tree (for each index, record the index we came from)
      // traverse the tree, search neighborhoods
      for(unsigned int i = 0; i < m_vertices.size(); i++) {
          //std::cout << "alive, now " << i << std::endl;
          std::vector<unsigned int> nh_indices = getNearestNeighborsFast(i, neighbourhood_size);
          std::vector<glm::vec3> nh;
          nh.resize(neighbourhood_size);
          for(unsigned int j = 0; j < neighbourhood_size; j++) {
            nh[j] = m_vertices[nh_indices[j]];
          }
          glm::mat3 cov_mat = getCovarianceMatrix(nh);
          CgEigenDecomposition3x3 eigen(cov_mat);
          // The eigenvalues/vectors are sorted in increasing order, and we want the smallest one.
          glm::vec3 normal = eigen.getEigenvectors()[0];
          m_vertex_normals[i] = glm::dot(normal, m_vertices[i] - m_centroid) > 0.0 
            ? normal
            : -normal;
      }
    
      // select normal pointing roughly in the same direction as parent's normal
    }
    
    void CgPointCloud::traverseBFO(std::function<void(glm::vec3*, const unsigned int)> fn) {
        // queue of pairs of vec3 pointers
        // (range of points in the current node and its children)
        std::queue<std::pair<glm::vec3*, glm::vec3*>> q;
        if(m_vertices.empty()) return;
        unsigned int depth = 0;
        q.push({&m_vertices.front(), &(*(m_vertices.end() - 1))});
        while(!q.empty()) {
            int level_count = q.size();
            for(int i = 0; i < level_count; i++) {
                auto curr = q.front();
                glm::vec3* begin = curr.first;
                glm::vec3* end = curr.second;
                unsigned int half_size = (end - begin) / 2;
                glm::vec3* curr_pointer = begin + half_size;
                fn(curr_pointer, depth);
                // push left & right half of array to reconstruct splits
                if(half_size != 0) q.push({begin, begin + half_size});
                if(begin + half_size != end) q.push({begin + half_size + 1, end});
                q.pop();
            }
            depth += 1;
        }
    }
    
    std::vector<unsigned int> CgPointCloud::getNearestNeighborsSlow(unsigned int current_point, unsigned int k) {
    
      glm::vec3 q = m_vertices[current_point];
    
      std::vector<std::pair<double,int>> distances;
    
      for(unsigned int i = 0; i < m_vertices.size(); i++) {
        double dist = glm::distance(m_vertices[i],q);
        distances.push_back(std::make_pair(dist,i));
      }
    
        std::sort(distances.begin(), distances.end());
    
        std::vector<unsigned int> erg;
    
      for(unsigned int i = 0; i < k; i++) {
           erg.push_back(distances[i].second);
      }
    
      return erg;
    }
    
    std::vector<unsigned int> CgPointCloud::getNearestNeighborsFast(unsigned int current_point, unsigned int k) {
    
      std::vector<unsigned int> erg(k);
      if(k == 0 || m_vertices.size() < k) return erg;
      // pair of distance + index into backing vec3 array 
      using P = std::pair<double, unsigned int>;
    
      glm::vec3 query = m_vertices[current_point];
      glm::vec3* first = &m_vertices.front();
      glm::vec3* last = &(*(m_vertices.end() - 1));
      auto cmp = [&](const P &left, const P &right) { return (left.first < right.first);};
    
      // using an ordered set for simplicitly, this may be slow
      // because we frequently erase elements, causing the rb-tree
      // to be rebuilt.
      std::vector<P> candidates;
      candidates.reserve(k + 1);
    
      // stack of traversed nodes (start index, end index, aabb, depth)
      std::vector<std::tuple<glm::vec3*, glm::vec3*, CgAABB, unsigned int>> nodes;
      nodes.push_back({first, last, m_aabb, 0});
      
      // traverse the tree as long as there's something to do.
      while(!nodes.empty()) {
        // unpack current node & pop it
        glm::vec3* begin;
        glm::vec3* end;
        CgAABB aabb;
        unsigned int depth;
        std::tie(begin, end, aabb, depth) = nodes.back();
        auto half_size = (end - begin) / 2;
        nodes.pop_back();
    
        glm::vec3 curr_point = *(begin + half_size);
        // insert current point into set
        candidates.push_back({glm::distance2(query, curr_point), begin + half_size - first});
        std::push_heap(candidates.begin(), candidates.end(), cmp);
        if(candidates.size() > k) {
          std::pop_heap(candidates.begin(), candidates.end(), cmp);
          candidates.pop_back();
        }
    
        // the aabb of the sub-nodes
        unsigned int axis = depth % 3;
        CgAABB lower_aabb;
        CgAABB higher_aabb;
        std::tie(lower_aabb, higher_aabb) = aabb.split(axis, curr_point[axis]);
        // only push lower child if its aabb intersects with the 
        // neighbourhood we found 
        double neighbourhood_radius = (*candidates.begin()).first;
        if(
          begin != begin + half_size
          && lower_aabb.position[axis] + lower_aabb.extent[axis] >= query[axis] - neighbourhood_radius
        ) {
          nodes.push_back({begin, begin + half_size, lower_aabb, depth + 1});
        }
        // only push higher if it intersects with
        // the neighbourhood we found.
        if(
          begin + half_size != end
          && higher_aabb.position[axis] - higher_aabb.extent[axis] <= query[axis] + neighbourhood_radius
        ) {
          nodes.push_back({begin + half_size + 1, end, higher_aabb, depth + 1});
        }
      }
    
      // copy all nearest neighbour indices we found into
      // a vector to return
      for(unsigned int i = 0; i < k; i++) erg[i] = candidates[i].second;
    
      return erg;
    }
    
    int CgPointCloud::getClosestPointToRay(glm::vec3 origin, glm::vec3 direction) {
      if(m_vertices.size() == 0) return -1;
    
      glm::vec3 inv_direction = glm::vec3(1.0 / direction.x, 1.0 / direction.y, 1.0 / direction.z);
      glm::vec3* first = &m_vertices.front();
      glm::vec3* last = &(*(m_vertices.end() - 1));
      // sensible starting point
      float erg_sqr_dist = std::numeric_limits<float>::infinity();
      int erg = -1;
    
      // stack of traversed nodes (start index, end index, aabb, depth)
      std::vector<std::tuple<glm::vec3*, glm::vec3*, CgAABB, unsigned int>> nodes;
      nodes.push_back({first, last, m_aabb, 0});
      
      // traverse the tree as long as there's something to do.
      while(!nodes.empty()) {
        // unpack current node & pop it
        glm::vec3* begin;
        glm::vec3* end;
        CgAABB aabb;
        unsigned int depth;
        std::tie(begin, end, aabb, depth) = nodes.back();
        auto half_size = (end - begin) / 2;
        nodes.pop_back();
    
        glm::vec3 curr_point = *(begin + half_size);
        // insert current point into set if it's closer to ray than last one
        float cand_dist = sqrPointRayDist(curr_point, origin, direction);
        if(cand_dist < erg_sqr_dist) {
          erg_sqr_dist = cand_dist;
          erg = begin + half_size - first;
        }
    
        // the aabb of the sub-nodes
        unsigned int axis = depth % 3;
        CgAABB lower_aabb;
        CgAABB higher_aabb;
        std::tie(lower_aabb, higher_aabb) = aabb.split(axis, curr_point[axis]);
        // only push children if their aabb is closer to ray than 
        // the closest point we found until now and there are
        // points in them
        if (
          begin != begin + half_size
          && lower_aabb.sqrDistanceToRay(origin, direction, inv_direction) < erg_sqr_dist
        ) {
          nodes.push_back({begin, begin + half_size, lower_aabb, depth + 1});
        }
        if (
          end != begin + half_size
          && higher_aabb.sqrDistanceToRay(origin, direction, inv_direction) < erg_sqr_dist
        ) {
          nodes.push_back({begin + half_size + 1, end, higher_aabb, depth + 1});
        }
      }
    
      return erg;
    }
    
    void CgPointCloud::setColors(std::vector<unsigned int> indices, glm::vec3 color) {
      for(unsigned int i = 0; i < indices.size(); i++) m_vertex_colors[indices[i]] = color;
    }
    
    void CgPointCloud::resetColors(glm::vec3 color) {
      for(unsigned int i = 0; i < m_vertices.size(); i++) m_vertex_colors[i] = color;
    }
    
    // calculates an arbitrary verctor perpendicular to the given one
    glm::vec3 CgPointCloud::getPerpendicularVector(glm::vec3 arg) {
      if((arg.x == 0.0) && (arg.y == 0.0)) {
        if(arg.z == 0.0) return glm::vec3(0.);
        return glm::vec3(0.0, 1.0, 0.0);
      }
      return glm::normalize(glm::vec3(-arg.y, arg.x, 0.0));
    }
    
    const glm::vec3 CgPointCloud::getCenter() const {
      return m_centroid;
    }
    
    void CgPointCloud::calculateAABB() {
      double max_x = -std::numeric_limits<double>::infinity();
      double max_y = -std::numeric_limits<double>::infinity();
      double max_z = -std::numeric_limits<double>::infinity();
      double min_x = std::numeric_limits<double>::infinity();
      double min_y = std::numeric_limits<double>::infinity();
      double min_z = std::numeric_limits<double>::infinity();
    
      for(unsigned int i=0; i < m_vertices.size(); i++) {
        glm::vec3 curr = m_vertices[i];
        max_x = max_x < curr[0] ? curr[0] : max_x;
        min_x = min_x > curr[0] ? curr[0] : min_x;
        max_y = max_y < curr[1] ? curr[1] : max_y;
        min_y = min_y > curr[1] ? curr[1] : min_y;
        max_z = max_z < curr[2] ? curr[2] : max_z;
        min_z = min_z > curr[2] ? curr[2] : min_z;
      }
    
      m_aabb.position = glm::vec3((max_x + min_x) * 0.5, (max_y + min_y) * 0.5, (max_z + min_z) * 0.5);
      m_aabb.extent = glm::vec3((max_x - min_x) * 0.5, (max_y - min_y) * 0.5, (max_z - min_z) * 0.5);
    }
    
    // returns the point clouds AABB
    const CgAABB CgPointCloud::getAABB() const {
      return m_aabb;
    }
    
    const std::vector<glm::vec2>& CgPointCloud::getSplatScalings() const {
      return m_splat_scaling;
    }