void PhotonAccelerator::build_recursive(int left_index, int right_index, BVHNode *node, int depth) { static int MAX_NUM_PRIMITIVES_IN_LEAF = 4; static int MAX_DEPTH = 20; int n_objs = right_index - left_index; if (n_objs <= MAX_NUM_PRIMITIVES_IN_LEAF || depth == MAX_DEPTH) { node->makeLeaf(left_index, n_objs); } else { // Sort the hitpoints along the largest axis AABB& node_box = node->getAABB(); int sort_dim = node_box.getLargestAxis(); std::sort(hps.begin() + left_index, hps.begin() + right_index, [&] (Hitpoint* a, Hitpoint* b) -> bool { float ca = a->is.mPosition(sort_dim); float cb = b->is.mPosition(sort_dim); return ca < cb; }); // Find split index float mid = (node_box.mMax(sort_dim) + node_box.mMin(sort_dim)) * 0.5f; int split_index = left_index; float cb; do { cb = hps[split_index++]->is.mPosition(sort_dim); } while (cb <= mid && split_index < right_index); if (split_index == left_index || split_index == right_index) { split_index = static_cast<int>((left_index + right_index - 1) * 0.5f + 0.5f); } // Create left and right nodes with AABB BVHNode left_node; BVHNode right_node; AABB& left_box = left_node.getAABB(); AABB& right_box = right_node.getAABB(); std::for_each(hps.begin() + left_index, hps.begin() + split_index, [&] (Hitpoint* hp) { AABB aabb(hp->is.mPosition); aabb.grow(hp->radius); left_box.include(aabb); }); std::for_each(hps.begin() + split_index, hps.begin() + right_index, [&] (Hitpoint* hp) { AABB aabb(hp->is.mPosition); aabb.grow(hp->radius); right_box.include(aabb); }); int n_nodes = nodes.size(); nodes.push_back(left_node); nodes.push_back(right_node); // Initiate current node as interior node node->makeNode(n_nodes); // Recurse build_recursive(left_index, split_index, &nodes[n_nodes], depth + 1); build_recursive(split_index, right_index, &nodes[n_nodes + 1], depth + 1); } }
void Object::init(const string &mesh_id) { if (!J3D_CACHE2(exists, Mesh, mesh_id)) J3D_DEBUG_FATAL("Mesh could not be found: " << mesh_id); mp_mesh = J3D_CACHE_GET(Mesh, mesh_id); aabb(mp_mesh->min(), mp_mesh->max()); }
void SimpleShear::createSphere(shared_ptr<Body>& body, Vector3r position, Real radius) { body = shared_ptr<Body>(new Body); body->groupMask=1; shared_ptr<NormalInelasticMat> mat(new NormalInelasticMat); shared_ptr<Aabb> aabb(new Aabb); shared_ptr<Sphere> iSphere(new Sphere); body->state->pos =position; body->state->ori =Quaternionr::Identity(); body->state->vel =Vector3r(0,0,0); body->state->angVel =Vector3r(0,0,0); Real masse =4.0/3.0*Mathr::PI*radius*radius*radius*density; body->state->mass =masse; body->state->inertia = Vector3r(2.0/5.0*masse*radius*radius,2.0/5.0*masse*radius*radius,2.0/5.0*masse*radius*radius); mat->young = sphereYoungModulus; mat->poisson = spherePoissonRatio; mat->frictionAngle = sphereFrictionDeg * Mathr::PI/180.0; body->material = mat; aabb->color = Vector3r(0,1,0); iSphere->radius = radius; iSphere->color = ((int)(floor(8*position.x()/length)))%2?Vector3r(0.7,0.7,0.7):Vector3r(0.45,0.45,0.45);// so that we have eight different colour bands body->shape = iSphere; body->bound = aabb; }
void ogl::node::buff(GLfloat *v, GLfloat *n, GLfloat *t, GLfloat *u, bool b) { if (b || rebuff) { // Have each unit pretransform its vertex data and compute its bound. my_aabb = aabb(); for (unit_s::iterator i = my_unit.begin(); i != my_unit.end(); ++i) { (*i)->buff(b); my_aabb.merge((*i)->get_bound()); } // Upload each mesh's vertex data to the bound buffer object. for (mesh_m::iterator i = my_mesh.begin(); i != my_mesh.end(); ++i) { const GLsizei vc = i->second->count_verts(); i->second->buffv(v, n, t, u); v += vc * 3; n += vc * 3; t += vc * 3; u += vc * 3; } } rebuff = false; }
TEST(GeoLibAABB, RandomNumberOfPointersToRandomPointsInAVector) { /* initialize random seed: */ srand ( static_cast<unsigned>(time(NULL)) ); int n (rand() % 100000); int box_size (rand()); int half_box_size(box_size/2); int minus_half_box_size(-half_box_size); // fill list with points std::vector<GeoLib::Point*> pnts; for (int k(0); k<n; k++) { pnts.push_back(new GeoLib::Point(rand() % box_size - half_box_size, rand() % box_size - half_box_size, rand() % box_size - half_box_size)); } // construct from list points a axis algined bounding box GeoLib::AABB<GeoLib::Point> aabb(pnts.begin(), pnts.end()); MathLib::Point3d const& min_pnt(aabb.getMinPoint()); MathLib::Point3d const& max_pnt(aabb.getMaxPoint()); ASSERT_LE(minus_half_box_size, min_pnt[0]) << "coordinate 0 of min_pnt is smaller than " << minus_half_box_size; ASSERT_LE(minus_half_box_size, min_pnt[1]) << "coordinate 1 of min_pnt is smaller than " << minus_half_box_size; ASSERT_LE(minus_half_box_size, min_pnt[2]) << "coordinate 2 of min_pnt is smaller than " << minus_half_box_size; ASSERT_GE(half_box_size, max_pnt[0]) << "coordinate 0 of max_pnt is greater than " << half_box_size; ASSERT_GE(half_box_size, max_pnt[1]) << "coordinate 1 of max_pnt is greater than " << half_box_size; ASSERT_GE(half_box_size, max_pnt[2]) << "coordinate 2 of max_pnt is greater than " << half_box_size; for (std::vector<GeoLib::Point*>::iterator it(pnts.begin()); it != pnts.end(); it++) { delete *it; } }
Hit Composite::closest_intersection(Ray const& ray) const { // First, check if there was an intersection with the bounding box of the current composite // If not, there is no closest intersection if (name() != "Root Comp" && !aabb().intersect(ray).m_hit) { return Hit{}; } // Store the current closest intersection Hit closest_intersection; // Iterate over all shapes that belong to the current composite for (auto&& shape : m_shapes) { Hit temp_isect; // Check if the shape is a composite itself … if (shape.second->type() == SHAPE_TYPE) { // … and calculate its closest intersection temp_isect = std::dynamic_pointer_cast<Composite>( shape.second )->closest_intersection(ray); } else { // `shape` is not a composite so we need to calculate its intersection temp_isect = shape.second->intersect(ray); } if (temp_isect.m_hit && temp_isect.m_distance < closest_intersection.m_distance) { closest_intersection = temp_isect; } } return closest_intersection; }
/** * @brief Tests whether the stored shapes or the stored shapes of any child * composites are intersected by a ray. * @return Returns true as soon as any intersection was found. * * The composite intersect method does not return a Hit object, because * multiple intersections can occur. This conflicts with the Hit object storing * the intersected shape and surface normal for that intersection. */ bool Composite::intersects_with(Ray const& ray) const { // First, check if there was an intersection with the bounding box of the current composite // If not, there is no closest intersection if (name() != "Root Comp" && !aabb().intersect(ray).m_hit) { return false; } for (auto&& shape : m_shapes) { Hit intersection; // Check if shape is a composite if (shape.second->type() == SHAPE_TYPE) { // If the composite is intersected … if (std::dynamic_pointer_cast<Composite>(shape.second)->intersects_with(ray)) { return true; } } else { // If the shape is intersected … if (shape.second->intersect(ray).m_hit) { return true; } } } return false; }
TEST(GeoLibAABB, RandomNumberOfPointsRandomBox) { /* initialize random seed: */ srand (static_cast<unsigned>(time(NULL))); int n (rand() % 1000000); int box_size_x (rand()); int box_size_y (rand()); int box_size_z (rand()); int half_box_size_x(box_size_x/2); int half_box_size_y(box_size_y/2); int half_box_size_z(box_size_z/2); int minus_half_box_size_x(-half_box_size_x); int minus_half_box_size_y(-half_box_size_y); int minus_half_box_size_z(-half_box_size_z); // fill list with points std::list<GeoLib::Point> pnts; for (int k(0); k<n; k++) { pnts.push_back(GeoLib::Point(rand() % box_size_x - half_box_size_x, rand() % box_size_y - half_box_size_y, rand() % box_size_z - half_box_size_z)); } // construct from list points a axis aligned bounding box GeoLib::AABB<GeoLib::Point> aabb(pnts.begin(), pnts.end()); MathLib::Point3d const& min_pnt(aabb.getMinPoint()); MathLib::Point3d const& max_pnt(aabb.getMaxPoint()); ASSERT_LE(minus_half_box_size_x, min_pnt[0]) << "coordinate 0 of min_pnt is smaller than " << minus_half_box_size_x; ASSERT_LE(minus_half_box_size_y, min_pnt[1]) << "coordinate 1 of min_pnt is smaller than " << minus_half_box_size_y; ASSERT_LE(minus_half_box_size_z, min_pnt[2]) << "coordinate 2 of min_pnt is smaller than " << minus_half_box_size_z; ASSERT_GE(half_box_size_x, max_pnt[0]) << "coordinate 0 of max_pnt is greater than " << half_box_size_x; ASSERT_GE(half_box_size_y, max_pnt[1]) << "coordinate 1 of max_pnt is greater than " << half_box_size_y; ASSERT_GE(half_box_size_z, max_pnt[2]) << "coordinate 2 of max_pnt is greater than " << half_box_size_z; }
void test_add_point_no_change() { AABB aabb(glm::vec3(0.f), glm::vec3(1.f)); glm::vec3 p(0.5f); aabb.add_point(p); CPPUNIT_ASSERT_VEC3_EQUAL(glm::vec3(0.f), aabb.min, 0.01f); CPPUNIT_ASSERT_VEC3_EQUAL(glm::vec3(1.f), aabb.max, 0.01f); }
void ogl::mesh::cache_verts(const ogl::mesh *that, const mat4& M, const mat4& I, int id) { const size_t n = that->vv.size(); // Cache that mesh's transformed vertices here. Update bounding volume. vv.resize(n); nv.resize(n); tv.resize(n); uv.resize(n); bound = aabb(); for (size_t i = 0; i < n; ++i) { transform_vertex(vv[i].v, M, that->vv[i].v); transform_normal(nv[i].v, transpose(I), that->nv[i].v); transform_normal(tv[i].v, transpose(I), that->tv[i].v); bound.merge(vec3(double(vv[i].v[0]), double(vv[i].v[1]), double(vv[i].v[2]))); // Handy trick: Store the unit ID in the texture coordinate. uv[i].v[0] = that->uv[i].v[0]; uv[i].v[1] = that->uv[i].v[1]; uv[i].v[2] = GLfloat(id); } dirty_verts = true; }
bool BoxShapeSW::intersect_segment(const Vector3& p_begin,const Vector3& p_end,Vector3 &r_result, Vector3 &r_normal) const { AABB aabb(-half_extents,half_extents*2.0); return aabb.intersects_segment(p_begin,p_end,&r_result,&r_normal); }
TEST(AABBTest, IntersectsMiss) { AABB aabb({-1, -1, -1}, {1, 1, 1}); auto miss_rays = {Ray({5, 0, 0}, {1, 0, 0}, 0), Ray({5, 0, 0}, {0, 1, 0}, 0), Ray({5, 0, 0}, {0, -1, 0}, 0), Ray({5, 0, 0}, {0, 0, 1}, 0), Ray({5, 0, 0}, {0, 0, -1}, 0), Ray({-5, 0, 0}, {-1, 0, 0}, 0), Ray({-5, 0, 0}, {0, 1, 0}, 0), Ray({-5, 0, 0}, {0, -1, 0}, 0), Ray({-5, 0, 0}, {0, 0, 1}, 0), Ray({-5, 0, 0}, {0, 0, -1}, 0), Ray({0, 5, 0}, {1, 0, 0}, 0), Ray({0, 5, 0}, {-1, 0, 0}, 0), Ray({0, 5, 0}, {0, 1, 0}, 0), Ray({0, 5, 0}, {0, 0, 1}, 0), Ray({0, 5, 0}, {0, 0, -1}, 0), Ray({0, -5, 0}, {1, 0, 0}, 0), Ray({0, -5, 0}, {-1, 0, 0}, 0), Ray({0, -5, 0}, {0, -1, 0}, 0), Ray({0, -5, 0}, {0, 0, 1}, 0), Ray({0, -5, 0}, {0, 0, -1}, 0), Ray({0, 0, 5}, {1, 0, 0}, 0), Ray({0, 0, 5}, {-1, 0, 0}, 0), Ray({0, 0, 5}, {0, 1, 0}, 0), Ray({0, 0, 5}, {0, -1, 0}, 0), Ray({0, 0, 5}, {0, 0, 1}, 0), Ray({0, 0, -5}, {1, 0, 0}, 0), Ray({0, 0, -5}, {-1, 0, 0}, 0), Ray({0, 0, -5}, {0, 1, 0}, 0), Ray({0, 0, -5}, {0, -1, 0}, 0), Ray({0, 0, -5}, {0, 0, -1}, 0)}; for (const Ray &ray : miss_rays) { real_t t_min = 12345, t_max = 67890; EXPECT_FALSE(aabb.intersects(ray, t_min, t_max)); EXPECT_NEAR(12345, t_min, 1e-100); EXPECT_NEAR(67890, t_max, 1e-100); } }
TEST(GeoLibAABB, RandomNumberOfPointersToRandomPointsInAVector) { /* initialize random seed: */ srand ( static_cast<unsigned>(time(nullptr)) ); int n (rand() % 100000); int box_size (rand()); double half_box_size(box_size/2); double minus_half_box_size(-half_box_size); // fill list with points std::vector<GeoLib::Point*> pnts; for (int k(0); k<n; k++) { pnts.push_back(new GeoLib::Point(rand() % box_size - half_box_size, rand() % box_size - half_box_size, rand() % box_size - half_box_size)); } // construct from list points a axis aligned bounding box GeoLib::AABB aabb(pnts.begin(), pnts.end()); MathLib::Point3d const& min_pnt(aabb.getMinPoint()); MathLib::Point3d const& max_pnt(aabb.getMaxPoint()); ASSERT_LE(minus_half_box_size, min_pnt[0]) << "coordinate 0 of min_pnt is smaller than " << minus_half_box_size; ASSERT_LE(minus_half_box_size, min_pnt[1]) << "coordinate 1 of min_pnt is smaller than " << minus_half_box_size; ASSERT_LE(minus_half_box_size, min_pnt[2]) << "coordinate 2 of min_pnt is smaller than " << minus_half_box_size; // since the interval is half-open we have to move the upper bound also half_box_size = std::nexttoward(half_box_size, std::numeric_limits<double>::max()); ASSERT_GE(half_box_size, max_pnt[0]) << "coordinate 0 of max_pnt is greater than " << half_box_size; ASSERT_GE(half_box_size, max_pnt[1]) << "coordinate 1 of max_pnt is greater than " << half_box_size; ASSERT_GE(half_box_size, max_pnt[2]) << "coordinate 2 of max_pnt is greater than " << half_box_size; for (std::vector<GeoLib::Point*>::iterator it(pnts.begin()); it != pnts.end(); it++) { delete *it; } }
void ParticlePool::Update(unsigned long deltaTime) { BaseClass::Update(deltaTime); int activeCount = 0; // 更新所有粒子 std::vector<Particle>::iterator iter; for (iter=m_Particles.begin(); iter!=m_Particles.end(); iter++) { if (iter->AdvanceTime(deltaTime)) { float bound = Math::Max(iter->scale * iter->m_ScreenScaleX, iter->scale * iter->m_ScreenScaleY); Vector3f size = Vector3f(bound, bound, bound) * 0.5f; AABB aabb(iter->m_Position - size, iter->m_Position + size); m_AABB.Expand(aabb); activeCount++; } } m_ActiveParticleCount = activeCount; // 发射器如果已经销毁,当粒子池为空的时候销毁 //if (!m_ActiveParticleCount && !m_Emitter && m_VanishOnEmpty) // m_Scene->RemoveObject(this); }
void PointCloudSelectionHandler::onSelect(const Picked& obj) { S_uint64::iterator it = obj.extra_handles.begin(); S_uint64::iterator end = obj.extra_handles.end(); for (; it != end; ++it) { int global_index = (*it & 0xffffffff) - 1; int index = 0; PointCloudBase::CloudInfoPtr cloud; getCloudAndLocalIndexByGlobalIndex(global_index, cloud, index); if (!cloud) { continue; } sensor_msgs::PointCloud2ConstPtr message = cloud->message_; Ogre::Vector3 pos = cloud->transform_ * pointFromCloud(message, index); float size = 0.002; if (display_->style_ != PointCloudBase::Points) { size = display_->billboard_size_ / 2.0; } Ogre::AxisAlignedBox aabb(pos - size, pos + size); createBox(std::make_pair(obj.handle, global_index), aabb, "RVIZ/Cyan"); } }
AABB Mesh::get_bounding_box() const { AABB aabb(points[0]); for (int i = 1; i < points.size(); ++i) { aabb = aabb.union_point(points[i]); } return aabb; }
void Sprite3DWithOBBPerfromanceTest::addNewOBBWithCoords(Vec2 p) { Vec3 extents = Vec3(10, 10, 10); AABB aabb(-extents, extents); auto obb = OBB(aabb); obb._center = Vec3(p.x,p.y,0); _obb.push_back(obb); }
static inline void _plot_face(uint8_t*** p_cell_status,int x,int y,int z,int len_x,int len_y,int len_z,const Vector3& voxelsize,const Face3& p_face) { AABB aabb( Vector3(x,y,z),Vector3(len_x,len_y,len_z)); aabb.pos=aabb.pos*voxelsize; aabb.size=aabb.size*voxelsize; if (!p_face.intersects_aabb(aabb)) return; if (len_x==1 && len_y==1 && len_z==1) { p_cell_status[x][y][z]=_CELL_SOLID; return; } int div_x=len_x>1?2:1; int div_y=len_y>1?2:1; int div_z=len_z>1?2:1; #define _SPLIT(m_i,m_div,m_v,m_len_v,m_new_v,m_new_len_v)\ if (m_div==1) {\ m_new_v=m_v;\ m_new_len_v=1; \ } else if (m_i==0) {\ m_new_v=m_v;\ m_new_len_v=m_len_v/2;\ } else {\ m_new_v=m_v+m_len_v/2;\ m_new_len_v=m_len_v-m_len_v/2; \ } int new_x; int new_len_x; int new_y; int new_len_y; int new_z; int new_len_z; for (int i=0;i<div_x;i++) { _SPLIT(i,div_x,x,len_x,new_x,new_len_x); for (int j=0;j<div_y;j++) { _SPLIT(j,div_y,y,len_y,new_y,new_len_y); for (int k=0;k<div_z;k++) { _SPLIT(k,div_z,z,len_z,new_z,new_len_z); _plot_face(p_cell_status,new_x,new_y,new_z,new_len_x,new_len_y,new_len_z,voxelsize,p_face); } } } }
void OBB::Triangulate(int x, int y, int z, vec *outPos, vec *outNormal, float2 *outUV, bool ccwIsFrontFacing) const { AABB aabb(POINT_VEC_SCALAR(0), r*2.f); aabb.Triangulate(x, y, z, outPos, outNormal, outUV, ccwIsFrontFacing); float3x4 localToWorld = LocalToWorld(); assume(localToWorld.HasUnitaryScale()); // Transforming of normals will fail otherwise. localToWorld.BatchTransformPos(outPos, NumVerticesInTriangulation(x,y,z), sizeof(vec)); localToWorld.BatchTransformDir(outNormal, NumVerticesInTriangulation(x,y,z), sizeof(vec)); }
//------------------------------------------------------- utTEST(coCollision_f, sphere_aabb) { coAABB aabb(coVec3(0.f), coVec3(1.f)); utEXPECT_TRUE(coCollision_f::intersectSolidSolid(aabb, coSphere(coVec3(0.f), 1.f))); utEXPECT_TRUE(coCollision_f::intersectSolidSolid(aabb, coSphere(coVec3(1.f), 1.f))); utEXPECT_TRUE(coCollision_f::intersectSolidSolid(aabb, coSphere(coVec3(2.f, 1.f, 1.f), 1.f))); utEXPECT_FALSE(coCollision_f::intersectSolidSolid(aabb, coSphere(coVec3(-1.f), 1.f))); }
void GraphicsWorld::DebugDrawFloat3x4(const float3x4 &t, float axisLength, float boxSize, const Color &clr, bool depthTest) { AABB aabb(float3::FromScalar(-boxSize/2.f), float3::FromScalar(boxSize/2.f)); OBB obb = aabb.Transform(t); DebugDrawOBB(obb, clr); DebugDrawLineSegment(LineSegment(t.TranslatePart(), t.TranslatePart() + axisLength * t.Col(0)), 1, 0, 0, depthTest); DebugDrawLineSegment(LineSegment(t.TranslatePart(), t.TranslatePart() + axisLength * t.Col(1)), 0, 1, 0, depthTest); DebugDrawLineSegment(LineSegment(t.TranslatePart(), t.TranslatePart() + axisLength * t.Col(2)), 0, 0, 1, depthTest); }
/*****************Scene**************************/ Scene::Scene() { m_triangles = NULL; LoadScene(); SetTriangle(); vector3 p1 = vector3( 0, 0, 0 ), p2 = vector3( 10, 10, 10 ); m_box = aabb( p1, p2 - p1 ); }
virtual void draw() { geometry::AABB aabb(width(), height()); graphics::Color c; if(m_focus) c = graphics::Color(0, 0, 255); else c = graphics::Color(127, 127, 255); m_gfx->draw(aabb, c); }
Boxf BoxCollider3D::ComputeAABB(const Matrix4f& offsetMatrix, const Vector3f& scale) const { Vector3f halfLengths(m_lengths * 0.5f); Boxf aabb(-halfLengths.x, -halfLengths.y, -halfLengths.z, m_lengths.x, m_lengths.y, m_lengths.z); aabb.Transform(offsetMatrix, true); aabb *= scale; return aabb; }
/// See http://paulbourke.net/geometry/platonic/ Polyhedron Polyhedron::Hexahedron(const float3 ¢erPos, float scale, bool ccwIsFrontFacing) { AABB aabb(float3(-1,-1,-1), float3(1,1,1)); aabb.Scale(float3::zero, scale * 0.5f); aabb.Translate(centerPos); Polyhedron p = aabb.ToPolyhedron(); if (ccwIsFrontFacing) p.FlipWindingOrder(); return p; }
void init_body(Sim_Body* b) { b->shape = aabb(v2(0, 0), 0, 0); b->inv_mass = 1.0f; b->restitution = 0.3f; b->velocity = v2(0,0); b->damping = 0.5f; b->force = v2(0, 0); b->flags = Body_Flag_None; }
void GraphicsWorld::DebugDrawCamera(const float3x4 &t, float size, const Color &clr, bool depthTest) { AABB aabb(float3(-size/2.f, -size/2.f, -size), float3(size/2.f, size/2.f, size)); OBB obb = aabb.Transform(t); DebugDrawOBB(obb, clr, depthTest); float3 translate(0, 0, -size * 1.25f); AABB aabb2(translate + float3::FromScalar(-size/4.f), translate + float3::FromScalar(size/4.f)); OBB obb2 = aabb2.Transform(t); DebugDrawOBB(obb2, clr, depthTest); }
bool AABBCollider::collides(Collider* pCollider) const { AABBCollider* aabb(static_cast<AABBCollider*>(pCollider)); if (aabb) return collides(*aabb); PointCollider* point(static_cast<PointCollider*>(pCollider)); if (point) return collides(*point); return Collider::collides(pCollider); }
RigidBody::RigidBody() : m_LinearDamping(0.8f), m_AngularDamping(0.8f), m_InverseMass(1) { m_DebugDraw = true; shared_ptr<AABB> aabb(new AABB()); m_AABB = aabb; //m_ConstantAccel = Vector3(0,-0.3,0); m_ConstantAccel = Vector3(0,-9.8,0); }
void Sprite3DWithOBBPerfromanceTest::addOBBWithCount(float value) { for(int i = 0; i < value; i++) { Vec2 randompos = Vec2(CCRANDOM_0_1() * Director::getInstance()->getWinSize().width,CCRANDOM_0_1() * Director::getInstance()->getWinSize().height); Vec3 extents = Vec3(10, 10, 10); AABB aabb(-extents, extents); auto obb = OBB(aabb); obb._center = Vec3(randompos.x,randompos.y,0); _obb.push_back(obb); } }