Cocos2d-x 3.x 图形学渲染系列五

标签: cocos2dcocos2d-xAABB算法
3485人阅读 评论(0) 收藏 举报
分类:

笔者介绍:姜雪伟,IT公司技术合伙人,IT高级讲师,CSDN社区专家,特邀编辑,畅销书作者,国家专利发明人,已出版书籍:《手把手教你架构3D游戏引擎》电子工业出版社 和《Unity3D实战核心技术详解》电子工业出版社等书籍。

AABB(Axis-Aligned Bounding Box)包围盒被称为轴对其包围盒。其在坐标系中的表示如下图:


在Cocos2d-x 3.x版本中,为开发者提供了AABB类,用于保存包围盒的最大顶点与最小顶点的信息,并且为每个Sprite3D对象提供了获取AABB包围盒的接口,在AABB类同时提供了判断碰撞检测的方法。有一点需要注意的是,AABB类中一开始保存的最大顶点与最小顶点的信息实际上是物体坐标系中的信息,在碰撞检测时需要将其转换成世界坐标系中的点,可通过AABB类中的transform()方法来完成。下面对AABB的源码进行分析:

#include "3d/CCAABB.h"

NS_CC_BEGIN

AABB::AABB()
{
    reset();
}

AABB::AABB(const Vec3& min, const Vec3& max)
{
    set(min, max);
}

AABB::AABB(const AABB& box)
{
	set(box._min,box._max);
}

//获取包围盒中心点坐标
Vec3 AABB::getCenter()
{
    Vec3 center;
	center.x = 0.5f*(_min.x+_max.x);
	center.y = 0.5f*(_min.y+_max.y);
	center.z = 0.5f*(_min.z+_max.z);

    return center;
}
//获取包围盒八个顶点信息
void AABB::getCorners(Vec3 *dst) const
{
    assert(dst);

	// 朝着Z轴正方向的面

	// 左上顶点坐标
    dst[0].set(_min.x, _max.y, _max.z);
	// 左下顶点坐标
    dst[1].set(_min.x, _min.y, _max.z);
	// 右下顶点坐标
    dst[2].set(_max.x, _min.y, _max.z);
	// 右上顶点坐标
    dst[3].set(_max.x, _max.y, _max.z);

	// 朝着Z轴负方向的面

	// 右上顶点坐标
    dst[4].set(_max.x, _max.y, _min.z);
	// 右下顶点坐标.
    dst[5].set(_max.x, _min.y, _min.z);
	// 左下顶点坐标.
    dst[6].set(_min.x, _min.y, _min.z);
	// 左上顶点坐标.
    dst[7].set(_min.x, _max.y, _min.z);
}
//判断两个包围盒是否碰撞
bool AABB::intersects(const AABB& aabb) const
{

	//就是各轴互相是否包含,(aabb 包含当前包围盒)||  (当前的包围盒包含 aabb)
	return ((_min.x >= aabb._min.x && _min.x <= aabb._max.x) || 		(aabb._min.x >= _min.x && aabb._min.x <= _max.x)) &&
           ((_min.y >= aabb._min.y && _min.y <= aabb._max.y) || 	(aabb._min.y >= _min.y && aabb._min.y <= _max.y)) &&
           ((_min.z >= aabb._min.z && _min.z <= aabb._max.z) || 	(aabb._min.z >= _min.z && aabb._min.z <= _max.z));
}

//判断点和包围盒是否碰撞
bool AABB::containPoint(const Vec3& point) const
{
	if (point.x < _min.x) returnfalse;
	if (point.y < _min.y) returnfalse;
	if (point.z < _min.z) returnfalse;
	if (point.x > _max.x) returnfalse;
	if (point.y > _max.y) returnfalse;
	if (point.z > _max.z) returnfalse;
	return true;
}

//生成一个新的包围盒同时容纳两个包围盒,新的包围盒的_min 各轴要是其他两个最小的那个,_max各轴要是其他两个最大的那个
void AABB::merge(const AABB& box)
{
	//计算新的最小点坐标
    _min.x = std::min(_min.x, box._min.x);
    _min.y = std::min(_min.y, box._min.y);
    _min.z = std::min(_min.z, box._min.z);

	//计算新的最大点坐标
    _max.x = std::max(_max.x, box._max.x);
    _max.y = std::max(_max.y, box._max.y);
    _max.z = std::max(_max.z, box._max.z);
}
//设置最大顶点与最小顶点
void AABB::set(const Vec3& min, const Vec3& max)
{
	this->_min = min;
	this->_max = max;
}
//顶点复位初始化信息
void AABB::reset()
{
	_min.set(99999.0f, 99999.0f, 99999.0f);
	_max.set(-99999.0f, -99999.0f, -99999.0f);
}
//检测坐标信息是否有误
bool AABB::isEmpty() const
{
	return _min.x > _max.x || _min.y > _max.y || _min.z > _max.z;
}
//由给定点坐标点重新确定最大最小的坐标向量
void AABB::updateMinMax(const Vec3* point, ssize_t num)
{
	for (ssize_t i = 0; i < num; i++)
    {
		// 左边点.
		if (point[i].x < _min.x)
            _min.x = point[i].x;

		//最低点
		if (point[i].y < _min.y)
            _min.y = point[i].y;

		// 最远点.
		if (point[i].z < _min.z)
            _min.z = point[i].z;

		// 右边最大点
		if (point[i].x > _max.x)
            _max.x = point[i].x;

		// 最高点
		if (point[i].y > _max.y)
            _max.y = point[i].y;

		// 最近点
		if (point[i].z > _max.z)
            _max.z = point[i].z;
    }
}
//通过给定的变换矩阵对包围盒进行变换
void AABB::transform(const Mat4& mat)
{
    Vec3 corners[8];//保存包围盒八个顶点


	//朝向z轴正方向的面

	// 左-顶-前
    corners[0].set(_min.x, _max.y, _max.z);
	// 左-低-前
    corners[1].set(_min.x, _min.y, _max.z);
	// 右-低-前.
    corners[2].set(_max.x, _min.y, _max.z);
	// 右-顶-前
    corners[3].set(_max.x, _max.y, _max.z);



	 //朝向z轴负方向的面

	// 右-顶-背面.
    corners[4].set(_max.x, _max.y, _min.z);
	// 右-底-背面.
    corners[5].set(_max.x, _min.y, _min.z);
	// 左-底-背面.
    corners[6].set(_min.x, _min.y, _min.z);
	// 左-顶-背面.
    corners[7].set(_min.x, _max.y, _min.z);

	//顶点变换
	for (int i = 0; i <8; i++)
        mat.transformPoint(&corners[i]);   //mat 是变换矩阵,变换&corners[i] 向量。
	//复位最大顶点最小顶点
    reset();
	//重新计算最大最小点信息
    updateMinMax(corners, 8);
}

当然在AABB类中也使用了矩阵转换函数,因为场景中的物体是可以移动旋转的,下面把AABB碰撞盒的变换函数给大家展示一下:

void AABB::transform(const Mat4& mat)
{
	Vec3corners[8];

	// 左-顶-前.
	corners[0].set(_min.x, _max.y, _max.z);
	// 左-底-前面.
	corners[1].set(_min.x, _min.y, _max.z);
	// 右-底-前.
	corners[2].set(_max.x, _min.y, _max.z);
	// 右-顶-前面.
	corners[3].set(_max.x, _max.y, _max.z);

	// 右-顶-背面.
	corners[4].set(_max.x, _max.y, _min.z);
	// 右-底-背面.
	corners[5].set(_max.x, _min.y, _min.z);
	// 左-底-背面.
	corners[6].set(_min.x, _min.y, _min.z);
	// 左-顶-背面.
	corners[7].set(_min.x, _max.y, _min.z);

	// 重新计算点的最大和最小值
	for (int i = 0; i <8; i++)
		mat.transformPoint(&corners[i]);

	reset();

	updateMinMax(corners, 8);
}

物体在游戏场景中并不是静止不动的,而是可以移动、旋转的,它自身的碰撞盒也要根据物体的变化而变换。在函数的最后是碰撞盒顶点的更新函数如下所示:

void AABB::updateMinMax(const Vec3* point, ssize_t num)
{
	for (ssize_t i = 0; i < num; i++)
    {
		// 左边最大点.
		if (point[i].x<_min.x)
				_min.x = point[i].x;

		// 最低的点.
		if (point[i].y<_min.y)
			_min.y = point[i].y;

		// 最远的点.
		if (point[i].z<_min.z)
			_min.z = point[i].z;

		// 右边最大点.
		if (point[i].x>_max.x)
			_max.x = point[i].x;

		// 最高的点.
		if (point[i].y>_max.y)
			_max.y = point[i].y;

	// 最近的点.
	if (point[i].z>_max.z)
		_max.z = point[i].z;
    }
}

AABB碰撞盒的每个顶点都需要随时更新变换位置,相比于OBB碰撞盒盒,AABB碰撞盒算法更简单,在程序上更容易实现,它应用在场景中的效果如下图:




0
0

猜你在找
【视频】C语言及程序设计(讲师:贺利坚)
【视频】Python爬虫工程师培养课程全套(讲师:韦玮)
【视频】Python全栈开发入门与实战(讲师:李杰)
【视频】2017软考网络规划设计师套餐(讲师:任铄)
【视频】2017软考软件设计师套餐(讲师:任铄)
【视频】2017软考信息系统项目管理师套餐(讲师:任铄)
【视频】软考(高级)项目经理实战营(讲师:张传波)
【视频】微信公众平台开发套餐(讲师:刘运强)
深度学习原理+实战+算法+主流框架套餐(讲师:唐宇迪)
2017系统集成项目管理工程师通关套餐(讲师:徐朋)
查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:191324次
    • 积分:3790
    • 等级:
    • 排名:第8283名
    • 原创:176篇
    • 转载:1篇
    • 译文:0篇
    • 评论:124条
    著作书籍
    《Unity3D实战核心技术详解》电子工业出版社 《手把手教你架构3D游戏引擎》电子工业出版社
    博客专栏
    最新评论