/*************************************************************************/ /* bsp_tree.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ /* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ /* "Software"), to deal in the Software without restriction, including */ /* without limitation the rights to use, copy, modify, merge, publish, */ /* distribute, sublicense, and/or sell copies of the Software, and to */ /* permit persons to whom the Software is furnished to do so, subject to */ /* the following conditions: */ /* */ /* The above copyright notice and this permission notice shall be */ /* included in all copies or substantial portions of the Software. */ /* */ /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ #include "bsp_tree.h" #include "error_macros.h" #include "print_string.h" void BSP_Tree::from_aabb(const Rect3& p_aabb) { planes.clear(); for(int i=0;i<3;i++) { Vector3 n; n[i]=1; planes.push_back(Plane(n,p_aabb.pos[i]+p_aabb.size[i])); planes.push_back(Plane(-n,-p_aabb.pos[i])); } nodes.clear(); for(int i=0;i<6;i++) { Node n; n.plane=i; n.under=(i==0)?UNDER_LEAF:i-1; n.over=OVER_LEAF; nodes.push_back(n); } aabb=p_aabb; error_radius=0; } Vector BSP_Tree::get_nodes() const { return nodes; } Vector BSP_Tree::get_planes() const { return planes; } Rect3 BSP_Tree::get_aabb() const { return aabb; } int BSP_Tree::_get_points_inside(int p_node,const Vector3* p_points,int *p_indices, const Vector3& p_center,const Vector3& p_half_extents,int p_indices_count) const { const Node *node =&nodes[p_node]; const Plane &p = planes[node->plane]; Vector3 min( (p.normal.x>0) ? -p_half_extents.x : p_half_extents.x, (p.normal.y>0) ? -p_half_extents.y : p_half_extents.y, (p.normal.z>0) ? -p_half_extents.z : p_half_extents.z ); Vector3 max=-min; max+=p_center; min+=p_center; float dist_min = p.distance_to(min); float dist_max = p.distance_to(max); if ((dist_min * dist_max) < CMP_EPSILON ) { //intersection, test point by point int under_count=0; //sort points, so the are under first, over last for(int i=0;i0) { if (node->under==UNDER_LEAF) { total+=under_count; } else { total+=_get_points_inside(node->under,p_points,p_indices,p_center,p_half_extents,under_count); } } if (under_count!=p_indices_count) { if (node->over==OVER_LEAF) { //total+=0 //if they are over an OVER_LEAF, they are outside the model } else { total+=_get_points_inside(node->over,p_points,&p_indices[under_count],p_center,p_half_extents,p_indices_count-under_count); } } return total; } else if (dist_min > 0 ) { //all points over plane if (node->over==OVER_LEAF) { return 0; // all these points are not visible } return _get_points_inside(node->over,p_points,p_indices,p_center,p_half_extents,p_indices_count); } else if (dist_min <= 0 ) { //all points behind plane if (node->under==UNDER_LEAF) { return p_indices_count; // all these points are visible } return _get_points_inside(node->under,p_points,p_indices,p_center,p_half_extents,p_indices_count); } return 0; } int BSP_Tree::get_points_inside(const Vector3* p_points,int p_point_count) const { if (nodes.size()==0) return 0; #if 1 //this version is easier to debug, and and MUCH faster in real world cases int pass_count = 0; const Node *nodesptr=&nodes[0]; const Plane *planesptr=&planes[0]; int plane_count=planes.size(); int node_count=nodes.size(); if (node_count==0) // no nodes! return 0; for(int i=0;i=node_count, false ); #endif } if (pass) pass_count++; } return pass_count; #else //this version scales better but it's slower for real world cases int *indices = (int*)alloca(p_point_count*sizeof(int)); AABB bounds; for(int i=0;i=node_count, false ); #endif steps++; } return false; } static int _bsp_find_best_half_plane(const Face3* p_faces,const Vector& p_indices,float p_tolerance) { int ic = p_indices.size(); const int*indices=p_indices.ptr(); int best_plane = -1; float best_plane_cost = 1e20; // Loop to find the polygon that best divides the set. for (int i=0;ip_tolerance) { if (d > 0) over++; else under++; } } if (over && under) num_spanning++; else if (over) num_over++; else num_under++; } //double split_cost = num_spanning / (double) face_count; double relation = Math::abs(num_over-num_under) / (double) ic; // being honest, i never found a way to add split cost to the mix in a meaninguful way // in this engine, also, will likely be ignored anyway double plane_cost = /*split_cost +*/ relation; //printf("plane %i, %i over, %i under, %i spanning, cost is %g\n",i,num_over,num_under,num_spanning,plane_cost); if (plane_cost& p_indices,Vector &p_planes, Vector &p_nodes,float p_tolerance) { ERR_FAIL_COND_V( p_nodes.size() == BSP_Tree::MAX_NODES, -1 ); // should not reach here ERR_FAIL_COND_V( p_indices.size() == 0, -1 ) int ic = p_indices.size(); const int*indices=p_indices.ptr(); int divisor_idx = _bsp_find_best_half_plane(p_faces,p_indices,p_tolerance); // returned error ERR_FAIL_COND_V( divisor_idx<0 , -1 ); Vector faces_over; Vector faces_under; Plane divisor_plane=p_faces[ indices[divisor_idx] ].get_plane(); for (int i=0;ip_tolerance) { if (d > 0) over_count++; else under_count++; } } if (over_count) faces_over.push_back( indices[i] ); if (under_count) faces_under.push_back( indices[i] ); } uint16_t over_idx=BSP_Tree::OVER_LEAF,under_idx=BSP_Tree::UNDER_LEAF; if (faces_over.size()>0) { //have facess above? int idx = _bsp_create_node( p_faces, faces_over, p_planes, p_nodes,p_tolerance ); if (idx>=0) over_idx=idx; } if (faces_under.size()>0) { //have facess above? int idx = _bsp_create_node( p_faces,faces_under, p_planes, p_nodes,p_tolerance ); if (idx>=0) under_idx=idx; } /* Create the node */ // find existing divisor plane int divisor_plane_idx=-1; for (int i=0;i plane_values; plane_values.resize(planes.size()*4); for(int i=0;i dst_nodes; dst_nodes.resize(nodes.size()*3); for(int i=0;i src_nodes = d["nodes"]; ERR_FAIL_COND(src_nodes.size()%3); if (d["planes"].get_type()==Variant::POOL_REAL_ARRAY) { PoolVector src_planes=d["planes"]; int plane_count=src_planes.size(); ERR_FAIL_COND(plane_count%4); planes.resize(plane_count/4); if (plane_count) { PoolVector::Read r = src_planes.read(); for(int i=0;i::Read r = src_nodes.read(); for(int i=0;i& p_faces,float p_error_radius) { // compute aabb int face_count=p_faces.size(); PoolVector::Read faces_r=p_faces.read(); const Face3 *facesptr = faces_r.ptr(); bool first=true; Vector indices; for (int i=0;i &p_nodes, const Vector &p_planes, const Rect3& p_aabb,float p_error_radius) { nodes=p_nodes; planes=p_planes; aabb=p_aabb; error_radius=p_error_radius; } BSP_Tree::~BSP_Tree() { }