Box2d源码学习<十>形状(下):圆形和多边形的实现

本系列博客是由扭曲45原创,欢迎转载,转载时注明出处,http://blog.csdn.net/cg0206/article/details/8303010

我们今天就来看看另外两个形状的实现。

1、圆形,有坐标和半径,(有点废话了,没有坐标和半径的能叫圆吗?)。圆形不能是空心的,必须是实心的。下面我们就来看看圆形是如何实现的。在b2CircleShape.h文件中,我们来看看源码。

//  圆形状 ,继承自b2Shape
class b2CircleShape : public b2Shape
{
public:
	/**************************************************************************
	* 功能描述:圆形构造函数
	* 参数说明:(void)
	* 返 回 值:(void)
	***************************************************************************/
	b2CircleShape();
	/**************************************************************************
	* 功能描述:用soa块分配器克隆一个具体的形状【实现b2shape】
	* 参数说明: allocator :soa分配器对象指针
	* 返 回 值: (void)
	***************************************************************************/
	b2Shape* Clone(b2BlockAllocator* allocator) const;
	/**************************************************************************
	* 功能描述:获取形状的子对象的数量
	* 参数说明: (void)
	* 返 回 值: 子对象数量
	***************************************************************************/
	int32 GetChildCount() const;
	/**************************************************************************
	* 功能描述:在这个形状中测试这个点的密封性,只适合用于凸的形状
	* 参数说明: xf : 形状的变换
	             p  : world坐标中的一个点
	* 返 回 值: true : 密封
	             false:敞开
	***************************************************************************/
	bool TestPoint(const b2Transform& transform, const b2Vec2& p) const;
	/**************************************************************************
	* 功能描述:投射一束光到一个圆形状中
	* 参数说明: output      :输出光线投射的结果
	             input       :输入光线投射
				 transform   :变换应用到此形状中
				 childeIndex :孩子形状索引
	* 返 回 值:?++? true : 成功
	             false:失败
	***************************************************************************/
	bool RayCast(b2RayCastOutput* output, const b2RayCastInput& input,
				const b2Transform& transform, int32 childIndex) const;
	/**************************************************************************
	* 功能描述:给出一个变换,计算一个圆形状的轴对齐包围盒(aabb)
	* 参数说明: aabb       : 孩子形状的aabb指针
	             xf         : 一个变换的引用
				 childIndex : 孩子的索引值
	* 返 回 值: true : 成功
	             false:失败
	***************************************************************************/
	void ComputeAABB(b2AABB* aabb, const b2Transform& transform, int32 childIndex) const;

	/**************************************************************************
	* 功能描述:用它的大小和密度计算形状的质量
	* 参数说明: massData   : 计算形状的质量
	             density    : 密度
	* 返 回 值: (void)
	***************************************************************************/
	void ComputeMass(b2MassData* massData, float32 density) const;
	/**************************************************************************
	* 功能描述:根据既定的方向,得到支撑顶点索引
	            圆形没有顶点,故永远返回0
	* 参数说明: d :二维列向量
	* 返 回 值: 0
	***************************************************************************/
	int32 GetSupport(const b2Vec2& d) const;
	/**************************************************************************
	* 功能描述:根据既定的方向,得到支持的顶点
	            获取支撑点索引,圆形没有顶点,故永远返回0
	* 参数说明: d :二维列向量
	* 返 回 值: 0
	***************************************************************************/
	const b2Vec2& GetSupportVertex(const b2Vec2& d) const;
	/**************************************************************************
	* 功能描述:获取顶点数量
	* 参数说明:(void)
	* 返 回 值: 顶点数量
	***************************************************************************/
	int32 GetVertexCount() const { return 1; }
	/**************************************************************************
	* 功能描述:通过索引获得一个顶点,用于b2Distance
	* 参数说明:(void)
	* 返 回 值: 坐标点
	***************************************************************************/
	const b2Vec2& GetVertex(int32 index) const;
	//坐标点
	b2Vec2 m_p;
};
//构造函数
inline b2CircleShape::b2CircleShape()
{
	m_type = e_circle;
	m_radius = 0.0f;
	m_p.SetZero();
}
//获取支撑点索引,圆形没有顶点,故永远返回0
inline int32 b2CircleShape::GetSupport(const b2Vec2 &d) const
{
	B2_NOT_USED(d);
	return 0;
}
//获取支撑点数量
inline const b2Vec2& b2CircleShape::GetSupportVertex(const b2Vec2 &d) const
{
	B2_NOT_USED(d);
	return m_p;
}
//通过索引获得一个顶点,用于b2Distance
inline const b2Vec2& b2CircleShape::GetVertex(int32 index) const
{
	B2_NOT_USED(index);
	b2Assert(index == 0);
	return m_p;
}


圆形依然是继承自b2Shape,对虚函数进行了重新定义和实现。也许有人会注意到还有三个内联函数是上两个形状所没有的,关于它有什么用,我们到b2Distance那部分时会进一步说明,现在留意一下就行。

关于b2CircleShape的实现,我们看下面代码就可:

//用soa块分配器克隆一个具体的形状【实现b2shape】
b2Shape* b2CircleShape::Clone(b2BlockAllocator* allocator) const
{
	void* mem = allocator->Allocate(sizeof(b2CircleShape));
	b2CircleShape* clone = new (mem) b2CircleShape;
	*clone = *this;
	return clone;
}
//获取形状的子对象的数量
int32 b2CircleShape::GetChildCount() const
{
	return 1;
}
//在这个形状中测试这个点的密封性,只适合用于凸的形状
bool b2CircleShape::TestPoint(const b2Transform& transform, const b2Vec2& p) const
{
	b2Vec2 center = transform.p + b2Mul(transform.q, m_p);
	b2Vec2 d = p - center;
	return b2Dot(d, d) <= m_radius * m_radius;
}


// 《Collision Detection in Interactive 3D Environments》 by Gino van den Bergen
// 在3.1.2节,【从 http://download.csdn.net/detail/cg0206/4875309 下载】
// x = s + a * r
// norm(x) = radius
// (关于norm函数,请看 http://zh.wikipedia.org/wiki/范数 或 
// http://en.wikipedia.org/wiki/Norm_(mathematics) )
bool b2CircleShape::RayCast(b2RayCastOutput* output, const b2RayCastInput& input,
							const b2Transform& transform, int32 childIndex) const
{
	//消除警告
	B2_NOT_USED(childIndex);
	//
	b2Vec2 position = transform.p + b2Mul(transform.q, m_p);
	b2Vec2 s = input.p1 - position;
	float32 b = b2Dot(s, s) - m_radius * m_radius;

	// 解决二元方程
	b2Vec2 r = input.p2 - input.p1;
	float32 c =  b2Dot(s, r);
	float32 rr = b2Dot(r, r);
	float32 sigma = c * c - rr * b;
	//检测负的判别式和短线段
	if (sigma < 0.0f || rr < b2_epsilon)
	{
		return false;
	}
	//找到线与圆的交叉点
	float32 a = -(c + b2Sqrt(sigma));
	// 判断交叉点是否在线段上
	if (0.0f <= a && a <= input.maxFraction * rr)
	{
		a /= rr;
		output->fraction = a;
		output->normal = s + a * r;
		output->normal.Normalize();
		return true;
	}

	return false;
}

//给出一个变换,计算一个圆形状的轴对齐包围盒(aabb)
void b2CircleShape::ComputeAABB(b2AABB* aabb, const b2Transform& transform, int32 childIndex) const
{
	//消除警告
	B2_NOT_USED(childIndex);
	//获取变换后的变量,并重新设置aabb
	b2Vec2 p = transform.p + b2Mul(transform.q, m_p);
	aabb->lowerBound.Set(p.x - m_radius, p.y - m_radius);
	aabb->upperBound.Set(p.x + m_radius, p.y + m_radius);
}
//用它的大小和密度计算形状的质量
void b2CircleShape::ComputeMass(b2MassData* massData, float32 density) const
{
	//获取形状的质量、质心
	massData->mass = density * b2_pi * m_radius * m_radius;
	massData->center = m_p;
	// 惯量相对于本地原点
	massData->I = massData->mass * (0.5f * m_radius * m_radius + b2Dot(m_p, m_p));
}

这次我们可以看到圆形是有质量、密封性为真的形状。剩下的就不多说了。看注释哈。

2、多边形

我们这里说的多边形是凸多边形,所谓凸多边形就是一条直线与该类多边形相交交点最多不超过两个。同时还是实心的,不能是空心。多边形的顶点范围应该在[3,b2_maxPolygonVertices]内,box2d中定义为8个,你也可以在公共模块的b2Settings.h文件中修改它的值,不过一般不建议那么做。好了,我们就来看看它的实现。

看b2PolygonShape.h文件。

//一个凸多边形
// 多边形最多有b2_maxPolygonVertices个顶点
//在大多数情况下你不需要具有太多顶点的多边形
class b2PolygonShape : public b2Shape
{
public:
	/**************************************************************************
	* 功能描述:多边形构造函数,初始化数据
	* 参数说明: (void)
	* 返 回 值: (void)
	***************************************************************************/
	b2PolygonShape();
	/**************************************************************************
	* 功能描述:用soa块分配器克隆一个具体的形状
	* 参数说明: allocator :soa分配器对象指针
	* 返 回 值: (void)
	***************************************************************************/
	b2Shape* Clone(b2BlockAllocator* allocator) const;

	/**************************************************************************
	* 功能描述:获取孩子形状个数,你可以使用它去创建形状
	* 参数说明: (void)
	* 返 回 值: 孩子形状个数
	***************************************************************************/
	int32 GetChildCount() const;
	/**************************************************************************
	* 功能描述:复制顶点。假定所有的顶点定义了一个凸多边形
	            它假定了外部每一个边都是从右边开始的
				顶点数在【3,b2_maxPolygonVertices】之间
	* 参数说明: vertices    :顶点数组
	             vertexCount :顶点数量
	* 返 回 值: (void)
	***************************************************************************/
	void Set(const b2Vec2* vertices, int32 vertexCount);
	/**************************************************************************
	* 功能描述:构建一个包围着顶点轴对齐盒子
	* 参数说明: hx :半宽
	             hy :半高
	* 返 回 值: (void)
	***************************************************************************/
	void SetAsBox(float32 hx, float32 hy);
	/**************************************************************************
	* 功能描述:构建一个包围着顶点确定方位的轴对齐盒子
	* 参数说明: hx    :半宽
	             hy    :半高
				 center:盒子的中心点
				 angle :盒子的旋转角度
	* 返 回 值: (void)
	***************************************************************************/
	void SetAsBox(float32 hx, float32 hy, const b2Vec2& center, float32 angle);

	/**************************************************************************
	* 功能描述:在这个形状中测试这个点的密封性,只适合用于凸的形状
	* 参数说明: xf : 形状的变换
	             p  : world坐标中的一个点
	* 返 回 值: true : 密封
	             false:敞开
	***************************************************************************/
	bool TestPoint(const b2Transform& transform, const b2Vec2& p) const;

	/**************************************************************************
	* 功能描述:投射一束光到一个孩子形状中
	* 参数说明: output      :输出光线投射的结果
	             input       :输入光线投射
				 transform   :变换应用到此形状中
				 childeIndex :孩子形状索引
	* 返 回 值: true : 成功
	             false:失败
	***************************************************************************/
	bool RayCast(b2RayCastOutput* output, const b2RayCastInput& input,
					const b2Transform& transform, int32 childIndex) const;

	/**************************************************************************
	* 功能描述:给出一个变换,计算一个孩子形状的轴对齐包围盒(aabb)
	* 参数说明: aabb       : 孩子形状的aabb指针
	             xf         : 一个变换的引用
				 childIndex : 孩子的索引值
	* 返 回 值: (void)
	***************************************************************************/
	void ComputeAABB(b2AABB* aabb, const b2Transform& transform, int32 childIndex) const;

	/**************************************************************************
	* 功能描述:用它的大小和密度计算形状的质量
	* 参数说明: massData   : 计算形状的质量
	             density    : 密度
	* 返 回 值: (void)
	***************************************************************************/
	void ComputeMass(b2MassData* massData, float32 density) const;
	/**************************************************************************
	* 功能描述:获取顶点数量
	* 参数说明: (void)
	* 返 回 值: 顶点数量
	***************************************************************************/
	int32 GetVertexCount() const { return m_vertexCount; }
	/**************************************************************************
	* 功能描述:根据顶点索引获取一个顶点
	* 参数说明: index :索引值
	* 返 回 值: 顶点
	***************************************************************************/
	const b2Vec2& GetVertex(int32 index) const;
	//重心坐标
	b2Vec2 m_centroid;
	//顶点坐标数组
	b2Vec2 m_vertices[b2_maxPolygonVertices];
	//法线数组
	b2Vec2 m_normals[b2_maxPolygonVertices];
	//顶点数量
	int32 m_vertexCount;
};

//多边形构造函数,初始化数据
inline b2PolygonShape::b2PolygonShape()
{
	m_type = e_polygon;
	m_radius = b2_polygonRadius;
	m_vertexCount = 0;
	m_centroid.SetZero();
}
//根据顶点索引获取一个顶点
inline const b2Vec2& b2PolygonShape::GetVertex(int32 index) const
{
	//验证index的有效性
	b2Assert(0 <= index && index < m_vertexCount);
	return m_vertices[index];
}

同样,多边形b2PolygonShape还是继承自b2Shape。b2PolygonShape也对父类中的虚函数进行了定义和实现。关于内联函数也不多说了,我们来看b2PolygonShape的具体实现吧,(有点小激动。。。嘿嘿)看b2PolygonShape.ccp文件。

//用soa块分配器克隆一个具体的形状
b2Shape* b2PolygonShape::Clone(b2BlockAllocator* allocator) const
{
	//在内存池中申请一块内存
	void* mem = allocator->Allocate(sizeof(b2PolygonShape));
	//在一块已存在的内存上分配对象
	//这是new的另外一种用法placement new
	//参照以下链接 http://blog.sina.com.cn/s/blog_69e905cd0100k51b.html
	//或 http://club.topsage.com/thread-2467284-1-1.html
	b2PolygonShape* clone = new (mem) b2PolygonShape;
	*clone = *this;
	return clone;
}
//构建一个包围着顶点轴对齐盒子
void b2PolygonShape::SetAsBgox(float32 hx, float32 hy)
{
	//初始化盒子顶点坐标
	m_vertexCount = 4;
	m_vertices[0].Set(-hx, -hy);
	m_vertices[1].Set( hx, -hy);
	m_vertices[2].Set( hx,  hy);
	m_vertices[3].Set(-hx,  hy);
	m_normals[0].Set(0.0f, -1.0f);
	m_normals[1].Set(1.0f, 0.0f);
	m_normals[2].Set(0.0f, 1.0f);
	m_normals[3].Set(-1.0f, 0.0f);
	m_centroid.SetZero();
}
//构建一个包围着顶点确定方位的轴对齐盒子
void b2PolygonShape::SetAsBox(float32 hx, float32 hy, const b2Vec2& center, float32 angle)
{
	//初始化盒子顶点坐标
	m_vertexCount = 4;
	m_vertices[0].Set(-hx, -hy);
	m_vertices[1].Set( hx, -hy);
	m_vertices[2].Set( hx,  hy);
	m_vertices[3].Set(-hx,  hy);
	m_normals[0].Set(0.0f, -1.0f);
	m_normals[1].Set(1.0f, 0.0f);
	m_normals[2].Set(0.0f, 1.0f);
	m_normals[3].Set(-1.0f, 0.0f);
	m_centroid = center;
	//创建一个变换
	b2Transform xf;
	xf.p = center;
	xf.q.Set(angle);
	// 变换顶点和法线
	for (int32 i = 0; i < m_vertexCount; ++i)
	{
		m_vertices[i] = b2Mul(xf, m_vertices[i]);
		m_normals[i] = b2Mul(xf.q, m_normals[i]);
	}
}
//获取孩子形状个数,你可以使用它去创建形状
int32 b2PolygonShape::GetChildCount() const
{
	return 1;
}
/**************************************************************************
* 功能描述:计算重心坐标
      参见 http://www.cnblogs.com/jun930123/archive/2012/09/10/2678305.html
* 参数说明: vs   :顶点数组
             count:顶点数
* 返 回 值: 重心坐标
***************************************************************************/
static b2Vec2 ComputeCentroid(const b2Vec2* vs, int32 count)
{
	b2Assert(count >= 3);

	b2Vec2 c; c.Set(0.0f, 0.0f);
	float32 area = 0.0f;
	//三角形的一个参考点
	//它的位置并不能改变结构(除了舍入误差)
	b2Vec2 pRef(0.0f, 0.0f);
#if 0
	// This code would put the reference point inside the polygon.
	for (int32 i = 0; i < count; ++i)
	{
		pRef += vs[i];
	}
	pRef *= 1.0f / count;
#endif

	const float32 inv3 = 1.0f / 3.0f;

	for (int32 i = 0; i < count; ++i)
	{
		// 三角形顶点
		b2Vec2 p1 = pRef;
		b2Vec2 p2 = vs[i];
		b2Vec2 p3 = i + 1 < count ? vs[i+1] : vs[0];
		//三角形两边的向量
		b2Vec2 e1 = p2 - p1;
		b2Vec2 e2 = p3 - p1;
		//计算三角形面积
		// S = 1/2 * b2Cross(e1,e2)
		float32 D = b2Cross(e1, e2);
		float32 triangleArea = 0.5f * D;
		//获取面积之后
		area += triangleArea;
		// 面积加权重心
		c += triangleArea * inv3 * (p1 + p2 + p3);
	}
	//重心
	b2Assert(area > b2_epsilon);
	c *= 1.0f / area;
	return c;
}
//复制顶点
void b2PolygonShape::Set(const b2Vec2* vertices, int32 count)
{
	//验证顶点的个数
	b2Assert(3 <= count && count <= b2_maxPolygonVertices);
	m_vertexCount = count;

	//拷贝顶点
	for (int32 i = 0; i < m_vertexCount; ++i)
	{
		m_vertices[i] = vertices[i];
	}
	// 计算法线,确保每条边的长度都不为0
	for (int32 i = 0; i < m_vertexCount; ++i)
	{
		//获取两个相邻点的索引
		int32 i1 = i;
		int32 i2 = i + 1 < m_vertexCount ? i + 1 : 0;
		//获得边向量
		b2Vec2 edge = m_vertices[i2] - m_vertices[i1];
		b2Assert(edge.LengthSquared() > b2_epsilon * b2_epsilon);
		//计算法线,并标准化
		m_normals[i] = b2Cross(edge, 1.0f);
		m_normals[i].Normalize();
	}

#ifdef _DEBUG
	//确保多边形是凸的和内部边是从左到右的
	for (int32 i = 0; i < m_vertexCount; ++i)
	{
		int32 i1 = i;
		int32 i2 = i + 1 < m_vertexCount ? i + 1 : 0;
		b2Vec2 edge = m_vertices[i2] - m_vertices[i1];

		for (int32 j = 0; j < m_vertexCount; ++j)
		{
			//不检查当前边上的顶点
			if (j == i1 || j == i2)
			{
				continue;
			}
			
			b2Vec2 r = m_vertices[j] - m_vertices[i1];
			//如果这个崩溃,你的多边形不是凸多边形,含有共线的边或者缠绕的顺序不对
			float32 s = b2Cross(edge, r);
			b2Assert(s > 0.0f && "ERROR: Please ensure your polygon is convex and has a CCW winding order");
		}
	}
#endif
	//计算重心
	m_centroid = ComputeCentroid(m_vertices, m_vertexCount);
}
//在这个形状中测试这个点的密封性,只适合用于凸的形状
bool b2PolygonShape::TestPoint(const b2Transform& xf, const b2Vec2& p) const
{
	//转置
	b2Vec2 pLocal = b2MulT(xf.q, p - xf.p);
	//遍历整个顶点,便用
	for (int32 i = 0; i < m_vertexCount; ++i)
	{
		//判断点积
		float32 dot = b2Dot(m_normals[i], pLocal - m_vertices[i]);
		if (dot > 0.0f)
		{
			return false;
		}
	}

	return true;
}
//光线投射
bool b2PolygonShape::RayCast(b2RayCastOutput* output, const b2RayCastInput& input,
								const b2Transform& xf, int32 childIndex) const
{
	//消除编译器发出的警告
	B2_NOT_USED(childIndex);
	//把光线放进多边形的参照系中
	b2Vec2 p1 = b2MulT(xf.q, input.p1 - xf.p);
	b2Vec2 p2 = b2MulT(xf.q, input.p2 - xf.p);
	b2Vec2 d = p2 - p1;
	//
	float32 lower = 0.0f, upper = input.maxFraction;

	int32 index = -1;
	//遍历所有节点
	for (int32 i = 0; i < m_vertexCount; ++i)
	{
		// p = p1 + a * d
		// dot(normal, p - v) = 0
		// dot(normal, p1 - v) + a * dot(normal, d) = 0
		float32 numerator = b2Dot(m_normals[i], m_vertices[i] - p1);
		float32 denominator = b2Dot(m_normals[i], d);

		if (denominator == 0.0f)
		{	
			if (numerator < 0.0f)
			{
				return false;
			}
		}
		else
		{
			// 注意:
			// lower < numerator / denominator,其中 denominator < 0
			// 当 denominator <0 时,我们变换下面的不等式
			// lower < numerator / denominator <==>denominator * lower > numerator.
			if (denominator < 0.0f && numerator < lower * denominator)
			{
				// 增加 lower
				// 进入这半段
				lower = numerator / denominator;
				index = i;
			}
			else if (denominator > 0.0f && numerator < upper * denominator)
			{
				//减小 upper
				//进入余下的半段
				upper = numerator / denominator;
			}
		}
		//有时候lower会有最小值b2_epsilon的误差,引起断言。
		//虽然表面上用最小值b2_epsilon可以使边缘形状工作,但是现在我们将单独处理
		//if (upper < lower - b2_epsilon)
		if (upper < lower)
		{
			return false;
		}
	}
	//验证lower有效性
	b2Assert(0.0f <= lower && lower <= input.maxFraction);
	//
	if (index >= 0)
	{
		output->fraction = lower;
		output->normal = b2Mul(xf.q, m_normals[index]);
		return true;
	}

	return false;
}
//给出一个变换,计算一个孩子形状的轴对齐包围盒(aabb)
void b2PolygonShape::ComputeAABB(b2AABB* aabb, const b2Transform& xf, int32 childIndex) const
{
	B2_NOT_USED(childIndex);
	//获取上下界限
	b2Vec2 lower = b2Mul(xf, m_vertices[0]);
	b2Vec2 upper = lower;
	//变量所有顶点
	for (int32 i = 1; i < m_vertexCount; ++i)
	{
		b2Vec2 v = b2Mul(xf, m_vertices[i]);
		lower = b2Min(lower, v);
		upper = b2Max(upper, v);
	}
	//设置aabb
	b2Vec2 r(m_radius, m_radius);
	aabb->lowerBound = lower - r;
	aabb->upperBound = upper + r;
}
//计算形状质量
void b2PolygonShape::ComputeMass(b2MassData* massData, float32 density) const
{
	//多边形质量(mass)、重心(centroid)、惯量(inertia)
	//ρ是多边形密度,也是在单位面积上的质量。
	//然后 质量mass = ρ* int(dA);
	//centroid.x = (1/mass) * ρ*int(x * dA)
	//centroid.y = (1/mass) * ρ*int(x * dB)
	//I = ρ*int((x*x+y*y) * dA)
	//我们可以计算这些部分,将凸多边形每个部分的三角形加起来。
	//求每部分的三角形,我们可以列一个关于三角形(u,v)坐标的二元方程式
	// x = x0 + e1x * u + e2x * v
	// y = y0 + e1y * u + e2y * v
	//其中 0 <= u && 0 <= v && u + v <= 1
	//我们将求的 u、v均在[0,1]之间
	//我们也用到两个向量的叉积【求三角形面积用】:D = cross(e1,e2)
	//三角形重心 centroid = (1/3) * (p1 + p2 + p3)
	//其余的推导是计算机代数啦。。。
	b2Assert(m_vertexCount >= 3);

	b2Vec2 center; center.Set(0.0f, 0.0f);
	float32 area = 0.0f;
	float32 I = 0.0f;
	//s是每个形成的三角形的参考点
	//它的位置不会影响结果(除了舍入的误差)
	b2Vec2 s(0.0f, 0.0f);
	//将参考点s放入多边形内部
	for (int32 i = 0; i < m_vertexCount; ++i)
	{
		s += m_vertices[i];
	}
	s *= 1.0f / m_vertexCount;

	const float32 k_inv3 = 1.0f / 3.0f;

	for (int32 i = 0; i < m_vertexCount; ++i)
	{
		//三角形顶点
		b2Vec2 e1 = m_vertices[i] - s;
		b2Vec2 e2 = i + 1 < m_vertexCount ? m_vertices[i+1] - s : m_vertices[0] - s;

		float32 D = b2Cross(e1, e2);

		float32 triangleArea = 0.5f * D;
		area += triangleArea;
		// 面积加权重心
		center += triangleArea * k_inv3 * (e1 + e2);

		float32 ex1 = e1.x, ey1 = e1.y;
		float32 ex2 = e2.x, ey2 = e2.y;

		float32 intx2 = ex1*ex1 + ex2*ex1 + ex2*ex2;
		float32 inty2 = ey1*ey1 + ey2*ey1 + ey2*ey2;

		I += (0.25f * k_inv3 * D) * (intx2 + inty2);
	}
	//总计质量
	massData->mass = density * area;
	//质心,一般也指重心,从概念上来说是有差别的,但通常我们用混淆了。
	b2Assert(area > b2_epsilon);
	center *= 1.0f / area;
	massData->center = center + s;

	//转动惯量相对于当前的原点(s点)
	massData->I = density * I;
	// 移动质心到原始的物体原点
	massData->I += massData->mass * (b2Dot(massData->center, massData->center) - b2Dot(center, center));
}

这段代码中,我们来看看SetAsBox(float32 hx,float32 hy)函数,注意一下我们传入的两个参数分别是这个盒子的宽度的一半和高度的一半,再者就是初始化4个顶点并设置质心。在看看ComputeCentroid函数,我们可以知道这可以获取重心的坐标,这里也可以说是获取质心的坐标,有人要问了,难道质心和重心是一样的吗?答案很明显不是一样的,要不然就不会在同一个物理学科中整出两个概念来了。不过,Box2d中假设质量分布和重力分布是一致的,所以两者是重合的。关于这方面的知识可以百度或者谷歌。点击“多边形重心”将会看到如何求解的博客。

再看设置一个多边形。Set函数通过给定的顶点数组和顶点数量,我们进行连接(按逆时针方向)并判断是否是凸多边形。如不是的话则断言。若是则继续初始化。

对于RayCast函数,还是计算公式,关于公式如何得到的,参照本书CollisionDetection inInteractive 3D Environments。计算多边形质量的函数ComputeMass注释里面有详细的解说,我们也不多说了。同时我们可以看到b2PolygonShape形状有质量、密封性为真的形状。

Ok,形状部分。。。oh,on,还有一个问题,没有解决,My God。大家还记得是什么问题吗?

(都知道!!!好吧,我错了。)

下面我们就来说说,在b2Shape类中如果析构函数不是虚函数的话,当我们申请一个父类(b2Shape)指针指向子类对象(如b2CircleShape)后,再来释放父类指针占用的内存的时候,只有父类的析构函数被调用,而子类的析构函数则不会被调用,这就可能引起内存泄漏。如果是父类析构是虚函数的话,则会自低而上的依次调用。


这次真的好了,形状部分,我们将暂时告一段落,晚安,明天还要上班,早点睡啦。。。


ps:

 

以上文章仅是一家之言,若有不妥、错误之处,请大家多多指出。同时也希望能与大家多多交流,共同进步。





  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
好的,下面是一个使用CSS样式美化的登录页面: HTML代码: ``` <!DOCTYPE html> <html> <head> <title>登录页面</title> <link rel="stylesheet" type="text/css" href="style.css"> </head> <body> <div class="login-box"> <h2>Login</h2> <form method="post" action="login.php"> <div class="user-box"> <input type="text" name="username" required> <label>Username</label> </div> <div class="user-box"> <input type="password" name="password" required> <label>Password</label> </div> <input type="submit" value="Login"> </form> </div> </body> </html> ``` CSS代码: ``` body { font-family: Arial, sans-serif; } .login-box { width: 300px; background: #fff; color: #333; top: 50%; left: 50%; position: absolute; transform: translate(-50%, -50%); box-shadow: 0 3px 10px rgba(0,0,0,.5); padding: 30px; border-radius: 10px; } .login-box h2 { margin: 0 0 30px; padding: 0; color: #333; text-align: center; } .login-box .user-box { position: relative; } .login-box .user-box input { width: 100%; padding: 10px 0; font-size: 16px; color: #333; margin-bottom: 30px; border: none; border-bottom: 1px solid #333; outline: none; background: transparent; } .login-box .user-box label { position: absolute; top: 0; left: 0; padding: 10px 0; font-size: 16px; color: #333; pointer-events: none; transition: .5s; } .login-box .user-box input:focus ~ label, .login-box .user-box input:valid ~ label { top: -20px; left: 0; color: #ff9900; font-size: 12px; } .login-box input[type="submit"] { background: #ff9900; border: none; color: #fff; padding: 10px 20px; cursor: pointer; border-radius: 5px; font-size: 16px; } .login-box input[type="submit"]:hover { background: #333; } ``` 请注意,此处只是一个简单的样式示例,实际应用中可以根据需求进行自定义样式的设置。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值