Cocos2d-x tutorial (35)-3D pickup Ray-AABB Collision Detection Algorithm
Certificate -----------------------------------------------------------------------------------------------------------------------------------------------------------
1. 3D pickup Technology
In 3D games, users can select certain objects in the 3D world for operations such as drag and drop, in this case, the program needs to convert the point coordinate on the two-dimensional screen into the coordinates in the Three-dimensional world and compare them. In this process, we need to use 3D pickup.
The basic principle of 3D pickup is not complex, and we still analyze it in Cocos2d-x 3.3beta0. The concept of picking up can be simply understood as: first obtain the coordinates of the touch points on the screen, and then calculate a ray Based on the camera projection matrix and the touch points on the screen. Note, under normal circumstances, you should find the box that is located at the intersection of the ray and the point closest to the starting point of the ray. This box is the box that should be touched, but in fact Cocos2d-x 3.3beta0 does not do this operation, this problem is discussed later.
2. Schematic diagram
3D pickup schematic 1-1 is shown:
Figure 1-1
In this case, rays will actually interwork with object A and object B, but object A should actually be the object to be touched. But the Cocos2d-x 3.3beta0 has not done this processing, only to determine whether the ray and a certain existing surrounded box existence intersection. Next Take A Look At The Cocos2d-x 3.3beta0 in the obb surrounded by a section of the Demo code:
Void Sprite3DWithOBBPerfromanceTest: onTouchesBegan (const std: vector
& Touches, Event * event) {for (auto touch: touches) {auto location = touch-> getLocationInView (); // obtain the if (_ obb. size ()> 0) // determines whether there is an OBB surrounding box {_ intersetList. clear (); Ray ray; // ray // calculate the start point and direction vector calculateRayByLocationInView (& Ray, location) of the ray in the world coordinate system based on the touch Point Coordinate System of the screen coordinate system ); for (int I = 0; I <_ obb. size (); I ++) {if (ray. intersects (_ obb [I]) // determines whether the rays and the cylinder are intersecting {_ intersetList. insert (I); return ;}}}}}
This algorithm is used to repeat the perimeter box. Once the rays come into conflict with a perimeter box, the loop is terminated and the surrounding box of the object is obtained. However, if the two boxes overlap, you should determine which box is closer to the starting point of the ray. Closer is the box to be located. This approach is equivalent to finding the two overlapping boxes that are first traversed before the container.
Let's move back to Figure 1-1. As shown in Figure 1-1, what needs to be done is to find the intersection between the ray and the near plane and the far Plane Based on the touch point on the screen, so that we can get the required rays. In Cocos2d-x 3.3beta0, Ray represents the Ray class, which contains the starting point and direction vector of the Ray, and provides an algorithm for Collision Detection with AABB and OBB. In the above Code, a method is called: calculateRayByLocationInView (Ray * ray, const Vec2 & location ). This method is used to calculate the ray Based on the coordinate at a point in the screen coordinate system. The following describes the implementation:
// Convert the coordinates on the screen to the void Sprite3DWithOBBPerfromanceTest: unproject (const Mat4 & viewProjection, const Size * viewport, Vec3 * src, Vec3 * dst) coordinates in the world coordinate system) {assert (dst); assert (viewport-> width! = 0.0f & viewport-> height! = 0.0f); // calculate the coordinates of the point in the camera coordinate system, and use the linear correlation between the coordinates of the touch point and the camera's near-plane coordinates. Vec4 screen (src-> x/viewport-> width, (viewport-> height-src-> y)/viewport-> height, src-> z, 1.0f); screen. x = screen. x * 2.0f-1.0f; screen. y = screen. y * 2.0f-1.0f; screen. z = screen. z * 2.0f-1.0f; // convert the coordinates in the camera coordinate system to the inverse matrix of the camera matrix to get the world coordinate viewProjection. getInversed (). transformVector (screen, & screen); // normalized if (screen. w! = 0.0f) {screen. x/= screen. w; screen. y/= screen. w; screen. z/= screen. w;} // save the world coordinate dst-> set (screen. x, screen. y, screen. z);} // calculated Ray void Sprite3DWithOBBPerfromanceTest: calculateRayByLocationInView (ray * Ray, const Vec2 & location) {auto dir = Director :: getInstance (); auto view = dir-> getWinSize (); // obtain the window size to calculate the position of the touch point in the camera coordinate system Mat4 mat = dir-> getMatrix (MATRIX_STACK_TYPE: MATRIX_STACK_MODELVIEW ); // obtain the top element of the projection matrix stack (that is, copying the top element of the original stack, carrying the transformation information of the parent node) mat = dir-> getMatrix (MATRIX_STACK_TYPE: MATRIX_STACK_PROJECTION ); vec3 src = Vec3 (location. x, location. y,-1); Vec3 nearPoint; // near-Plane Point unproject (mat, & view, & src, & nearPoint ); // calculate the coordinates of the near-plane point in the world coordinate system src = Vec3 (location. x, location. y, 1); Vec3 farPoint; // unproject (mat, & view, & src, & farPoint); // calculates the coordinate Vec3 ction of the remote point in the world coordinate system; // direction vector Vec3: subtract (farPoint, nearPoint, & direction); // calculate the direction vector ction from the far surface point minus the near Plane Point. normalize (); // normalized ray-> _ origin = nearPoint; // ray-> _ direction = direction; // ray direction vector}
3. after Ray-AABB collision detection finds the rays, what needs to be done is the Collision Detection with the surrounding box, as shown in the previous Code, during the collision detection, the Ray class in Cocos2d-x 3.3beta0 provides the intersects () method for us. The parameters of this method include the OBB object and the AABB object. In fact, they are all converted into the AABB detection, finally, let's take a look at the collision detection code:
Bool Ray: intersects (const AABB & aabb) const {Vec3 ptOnPlane; // intersection of rays and a surface of the box Vec3 min = aabb. _ min; // the coordinate of the minimum point in the aabb box Vec3 max = aabb. _ max; // The maximum coordinate of the aabb surround box const Vec3 & origin = _ origin; // The ray start point const Vec3 & dir = _ direction; // The direction vector float t; // determine the intersection of the ray and the plane. // determine whether there is an intersection between the x-axis of the ray and the box. if (dir. x! = 0.f) // the x-axis component is not 0. If the x-axis component of the X-ray vector is 0, rays cannot pass through the two sides of the box toward the X axis {/* use the formula of the intersection of rays and planes to calculate the intersection point */if (dir. x> 0) // If the x-ray is shifted along the positive direction of the x-axis, t = (min. x-origin. x)/dir. x; else // The offset t = (max. x-origin. x)/dir. x; if (t> 0.f) // t> 0, then the rays and planes intersect {ptOnPlane = origin + t * dir; // calculate the intersection coordinate // determine whether the intersection is in the current plane if (min. y <ptOnPlane. y & ptOnPlane. y <max. y & min. z <ptOnPlane. z & ptOnPlane. z <max. z) {return true; // the intersection between the ray and the surround box }}}/ /If the ray has components along the y axis, determine whether there is an intersection with the y axis of the box. if (dir. y! = 0.f) {if (dir. y> 0) t = (min. y-origin. y)/dir. y; else t = (max. y-origin. y)/dir. y; if (t> 0.f) {ptOnPlane = origin + t * dir; if (min. z <ptOnPlane. z & ptOnPlane. z <max. z & min. x <ptOnPlane. x & ptOnPlane. x <max. x) {return true ;}}// if the x-Ray has a component along the Z axis, determine whether there is an intersection with the Y axis of the box. if (dir. z! = 0.f) {if (dir. z> 0) t = (min. z-origin. z)/dir. z; else t = (max. z-origin. z)/dir. z; if (t> 0.f) {ptOnPlane = origin + t * dir; if (min. x <ptOnPlane. x & ptOnPlane. x <max. x & min. y <ptOnPlane. y & ptOnPlane. y <max. y) {return true ;}}return false ;}