如今,伴随移动互联网应用逐步深入人们日常生活与工作,依靠高速信息传输网络,很多人即可足不出户就能享受到快捷、方便、高速的网络服务,3G网络已经开始逐渐普及开来.但是随着移动互联网上人们的多样化需求,高速,大流量的网络传输对于传输带宽提出了新的要求,也正是意识到这个问题,未来的发展也指向了4G. 2007年,由全球700多家运营商组成的贸易协会GSMA选定LTE为4G移动通讯标准。
LTE在全球已呈现出快速增长的态势。美国最大的移动运营商Verizon于2010年12月正式商用FDD-LTE网络,目前已覆盖美国165个城市和111个机场,覆盖人口达1.86亿,FDD-LTE用户已超过540万,占全球LTE商用用户的60%。截止今年3月底全球已有91个LTE商用网络在47个国家开始运行,至2012年底还将有超过40个网络投入运行,LTE的用户总量将高达4400万,5年内将突破10亿。与此同时,中国移动也已经完成了TD-LTE的试验网设备招标,并在多个城市开始试商用,按照中国移动的规划,到2013年TD-LTE网络基站规模将超过20万,投资总额达到1800亿元。中国移动与浙江电视台经济生活频道合作,在新闻直播中通过使用TD-LTE网络,实现4G网络电视直播,直播画面图像清晰流畅。
4G标准的运营需要整个生态环境的支持,通信运营商,芯片设计厂商等针对产业链上游进行布局与提供核心技术支持,是推动4G网络运营与发展的核心力量.国际芯片设计厂依靠自身的通信方面的技术积累与经验,占据着市场重要的位置. 他们与国内通信运营商支持着3G网络的运营.通讯芯片的研发与上市,需要大量的资金与技术工作量,同时还需要每代的技术堆叠与积累.国内芯片厂商在这一领域还在不断积累与成熟过程中,作为国内领先的芯片厂商新岸线早在今年上半年就发布了支持GSM/WCDMA双模基带芯片Telink7619,并相继推出了基于Telink7619的基带芯片的相关方案, 而新岸线后续基带产品开发方向直指LTE。据新岸线相关负责人介绍,新岸线的LTE方案计划同时兼容TDD/FDD模式,还会结合之前的3G方案的技术,实现真正的多模LTE基带方案。目前产品研发进展顺利。新岸线表示,目标明年正式推出4G LTE的基带方案。
LTE作为3G的演进,已经成为未来的新标准,新岸线结合自身计算通讯一体化的目标,基带处理器芯片和计算处理器芯片均已逐步实现市场化, 明年新岸线计划发布LTE的方案,势必为其在布局手机市场提供更为核心的优势和基础。
本系列博客是由扭曲45原创,欢迎转载,转载时注明出处,http://blog.csdn.net/cg0206/article/details/8441644
TOI全称Time of Impact,中文的意思是撞击时间,在Box2d中,我们用b2TimeOfImpact来确定两个形状运动时的撞击时间(TOI)。同时b2TimeOfImpact也主要防止两个形状快速移动时可能在一个时间步内彼此穿越对方的情况,也就是我们经常所说的隧道效应。
我们就一起看源码吧。
1)、b2TimeOfImpact.h文件。// b2TimeOfImpace的输入参数 struct b2TOIInput { b2DistanceProxy proxyA; //距离代理A b2DistanceProxy proxyB; //距离代理B b2Sweep sweepA; //扫描A b2Sweep sweepB; //扫描B float32 tMax; //定义扫频间隔 [0, tMax] }; //b2TimeOfImpact的输出参数 struct b2TOIOutput { enum State { e_unknown, //未知 e_failed, //失败 e_overlapped, //重叠 e_touching, //触碰 e_separated //分离 }; State state; //状态 float32 t; //扫频间隔 }; /************************************************************************** * 功能描述:在两个形状穿透之前,及时的求出上边界。用分数表示时间 在[0,tMax]之间。它使用扫频分离轴和可能丢失一些像非隧道效应碰撞的 中间体,如果你改变时间间隔,你需要重新调用这个函数 注意:使用b2Distance去求在一个撞击时间内的接触点和法线 * 参数说明:output:TOI输出参数指针 input :TOI输入参数指针 * 返 回 值: (void) **************************************************************************/ void b2TimeOfImpact(b2TOIOutput* output, const b2TOIInput* input);
我们可以看到此处定义了用于保存TOI信息的结构体,分别是b2TOIInput、b2TOIOutput结构体,表示碰撞时间的输入和输出参数。对于b2TimeOfImpact函数,则是这篇文章的主角,用于防止两物体之间的隧道效应,关于此函数的具体情况,等到实现的时候在详细的和大家聊聊。
2)、b2TimeOfImpact.cpp文件。
我们再来看看b2TimeOfImpact.cpp文件。为了更好的看源码,将分成以下三点:
1、全局变量的定义
int32 b2_toiCalls, b2_toiIters, b2_toiMaxIters; //调用次数、toi的迭代次数、toi的最大迭代次数(两层循环中取最大的那个) int32 b2_toiRootIters, b2_toiMaxRootIters; //根总共迭代次数、在所有根迭代中最大的那次
struct b2SeparationFunction { enum Type { e_points, //点 e_faceA, //面A e_faceB //面B }; /************************************************************************** * 功能描述:如果不需要就返回间距值 * 参数说明:cache :单纯形缓存指针 proxyA:多边形A的指针 sweepA:扫频对象的引用 proxyB:多边形B的指针 sweepB:扫频对象的引用 t1 :扫频间隔 * 返 回 值: 间距值 **************************************************************************/ float32 Initialize(const b2SimplexCache* cache, const b2DistanceProxy* proxyA, const b2Sweep& sweepA, const b2DistanceProxy* proxyB, const b2Sweep& sweepB, float32 t1) { //赋值代理 m_proxyA = proxyA; m_proxyB = proxyB; // 获取缓存中的顶点数,并验证 int32 count = cache->count; b2Assert(0 < count && count < 3); //赋值扫频 m_sweepA = sweepA; m_sweepB = sweepB; //获取变换 b2Transform xfA, xfB; m_sweepA.GetTransform(&xfA, t1); m_sweepB.GetTransform(&xfB, t1); //一个顶点 if (count == 1) { //赋值,获得A、B的局部顶点 m_type = e_points; b2Vec2 localPointA = m_proxyA->GetVertex(cache->indexA[0]); b2Vec2 localPointB = m_proxyB->GetVertex(cache->indexB[0]); //获取变换后的A、B点 b2Vec2 pointA = b2Mul(xfA, localPointA); b2Vec2 pointB = b2Mul(xfB, localPointB); //获取从B到的A的向量,返回其长度,并标准化 m_axis = pointB - pointA; float32 s = m_axis.Normalize(); return s; } else if (cache->indexA[0] == cache->indexA[1]) { // 两个点在B上和一个在A上 //赋值,获取B上的两个局部顶点 m_type = e_faceB; b2Vec2 localPointB1 = proxyB->GetVertex(cache->indexB[0]); b2Vec2 localPointB2 = proxyB->GetVertex(cache->indexB[1]); //获取B2到B1形成向量的垂直向量,并标准化 m_axis = b2Cross(localPointB2 - localPointB1, 1.0f); m_axis.Normalize(); //获取法向量 b2Vec2 normal = b2Mul(xfB.q, m_axis); // 获取B1到B2的中间点 m_localPoint = 0.5f * (localPointB1 + localPointB2); b2Vec2 pointB = b2Mul(xfB, m_localPoint); // 获取局部点A,并求得点A b2Vec2 localPointA = proxyA->GetVertex(cache->indexA[0]); b2Vec2 pointA = b2Mul(xfA, localPointA); // 获取距离 float32 s = b2Dot(pointA - pointB, normal); // 距离为负,置反 if (s < 0.0f) { m_axis = -m_axis; s = -s; } return s; } else { // 两个点在A上和一个或者两个点在B上 m_type = e_faceA; b2Vec2 localPointA1 = m_proxyA->GetVertex(cache->indexA[0]); b2Vec2 localPointA2 = m_proxyA->GetVertex(cache->indexA[1]); //获取A2到A1形成向量的垂直向量,并标准化 m_axis = b2Cross(localPointA2 - localPointA1, 1.0f); m_axis.Normalize(); //获取法向量 b2Vec2 normal = b2Mul(xfA.q, m_axis); //获取A1和A2的中间点 m_localPoint = 0.5f * (localPointA1 + localPointA2); b2Vec2 pointA = b2Mul(xfA, m_localPoint); //获取局部点,并求得点B b2Vec2 localPointB = m_proxyB->GetVertex(cache->indexB[0]); b2Vec2 pointB = b2Mul(xfB, localPointB); //获取距离,并处理 float32 s = b2Dot(pointB - pointA, normal); if (s < 0.0f) { m_axis = -m_axis; s = -s; } return s; } } /************************************************************************** * 功能描述:寻找最小距离 * 参数说明:indexA :点A的索引 indexB :点B的索引 t :时间值 * 返 回 值: 最小距离 **************************************************************************/ float32 FindMinSeparation(int32* indexA, int32* indexB, float32 t) const { //声明变换A、B,用于获取在t时间里获得窜改变换 b2Transform xfA, xfB; m_sweepA.GetTransform(&xfA, t); m_sweepB.GetTransform(&xfB, t); //处理不同的类型 switch (m_type) { case e_points: //点 { //通过转置旋转m_axis获取单纯形支撑点的方向向量 b2Vec2 axisA = b2MulT(xfA.q, m_axis); b2Vec2 axisB = b2MulT(xfB.q, -m_axis); //通过方向向量获取局部顶点的索引 *indexA = m_proxyA->GetSupport(axisA); *indexB = m_proxyB->GetSupport(axisB); //通过索引获取局部顶点 b2Vec2 localPointA = m_proxyA->GetVertex(*indexA); b2Vec2 localPointB = m_proxyB->GetVertex(*indexB); //通过变换局部点获取两形状之间的顶点 b2Vec2 pointA = b2Mul(xfA, localPointA); b2Vec2 pointB = b2Mul(xfB, localPointB); //求两形状的间距,并返回。 float32 separation = b2Dot(pointB - pointA, m_axis); return separation; } case e_faceA: //面A { //通过转置旋转m_axis获取单纯形支撑点的方向向量 //通过变换局部点获取当前图形的点 b2Vec2 normal = b2Mul(xfA.q, m_axis); b2Vec2 pointA = b2Mul(xfA, m_localPoint); //通过转置旋转m_axis获取单纯形支撑点的方向向量 b2Vec2 axisB = b2MulT(xfB.q, -normal); //通过索引获取局部顶点 *indexA = -1; *indexB = m_proxyB->GetSupport(axisB); //通过变换局部点获形状B的顶点 b2Vec2 localPointB = m_proxyB->GetVertex(*indexB); b2Vec2 pointB = b2Mul(xfB, localPointB); //求两形状的间距,并返回。 float32 separation = b2Dot(pointB - pointA, normal); return separation; } case e_faceB: //面B { //通过转置旋转m_axis获取单纯形支撑点的方向向量 //通过变换局部点获取当前图形的点 b2Vec2 normal = b2Mul(xfB.q, m_axis); b2Vec2 pointB = b2Mul(xfB, m_localPoint); //通过转置旋转m_axis获取单纯形支撑点的方向向量 b2Vec2 axisA = b2MulT(xfA.q, -normal); //通过索引获取局部顶点 *indexB = -1; *indexA = m_proxyA->GetSupport(axisA); //通过变换局部点获形状A的顶点 b2Vec2 localPointA = m_proxyA->GetVertex(*indexA); b2Vec2 pointA = b2Mul(xfA, localPointA); //求两形状的间距,并返回。 float32 separation = b2Dot(pointA - pointB, normal); return separation; } default: b2Assert(false); *indexA = -1; *indexB = -1; return 0.0f; } } /************************************************************************** * 功能描述:当前时间步里两形状的距离 * 参数说明:indexA :点A的索引 indexB :点B的索引 t :时间值 * 返 回 值: 当前时间步里两形状的距离 **************************************************************************/ float32 Evaluate(int32 indexA, int32 indexB, float32 t) const { b2Transform xfA, xfB; m_sweepA.GetTransform(&xfA, t); m_sweepB.GetTransform(&xfB, t); switch (m_type) { case e_points: //点 { //通过转置旋转m_axis获取顶点的方向向量 b2Vec2 axisA = b2MulT(xfA.q, m_axis); b2Vec2 axisB = b2MulT(xfB.q, -m_axis); //通过变换局部点获形状A、B的顶点 b2Vec2 localPointA = m_proxyA->GetVertex(indexA); b2Vec2 localPointB = m_proxyB->GetVertex(indexB); //获取当前时间步内的两形状上的点 b2Vec2 pointA = b2Mul(xfA, localPointA); b2Vec2 pointB = b2Mul(xfB, localPointB); //计算间距,并返回间距 float32 separation = b2Dot(pointB - pointA, m_axis); return separation; } case e_faceA: //面A { //旋转m_axis向量,获取法向量,同时根据局部点求形状A上的点 b2Vec2 normal = b2Mul(xfA.q, m_axis); b2Vec2 pointA = b2Mul(xfA, m_localPoint); //通过转置旋转m_axis获取单纯形支撑点的方向向量 b2Vec2 axisB = b2MulT(xfB.q, -normal); //通过索引获取局部顶点,进而通过变换局部点获取当前时间步内的点 b2Vec2 localPointB = m_proxyB->GetVertex(indexB); b2Vec2 pointB = b2Mul(xfB, localPointB); //获取间距 float32 separation = b2Dot(pointB - pointA, normal); return separation; } case e_faceB: //面B { //旋转m_axis向量,获取法向量,同时根据局部点求形状B上的点 b2Vec2 normal = b2Mul(xfB.q, m_axis); b2Vec2 pointB = b2Mul(xfB, m_localPoint); //通过转置旋转m_axis获取单纯形支撑点的方向向量 b2Vec2 axisA = b2MulT(xfA.q, -normal); //通过索引获取局部顶点,进而通过变换局部点获取当前时间步内的点 b2Vec2 localPointA = m_proxyA->GetVertex(indexA); b2Vec2 pointA = b2Mul(xfA, localPointA); //获取间距 float32 separation = b2Dot(pointA - pointB, normal); return separation; } default: b2Assert(false); return 0.0f; } } const b2DistanceProxy* m_proxyA; //代理A const b2DistanceProxy* m_proxyB; //代理B b2Sweep m_sweepA, m_sweepB; //扫描A、B Type m_type; //类型变量 b2Vec2 m_localPoint; //局部点 b2Vec2 m_axis; //方向向量,主要用于变换次向量之后求形状的顶点 };
关于b2SeparationFunction结构体主要用于查找两个形状间距的相关操作。我们主要来说说其内部函数的实现。
关于Initialize函数主要初始化成员变量,并返回两个形状之间的距离。
关于FindMinSeparation函数主要是根据不同的单纯形类型在时间步内寻找最小距离,并返回其两个顶点的索引,作为两形状是否碰撞的见证点。
关于Evaluate函数主要是根据不同的单纯形类型和FindMinSeparation所查到的见证点获取当前两形状的距离。
3、 b2TimeOfImpact函数的实现
//CCD(continuous collision detection,持续碰撞检验)经过局部的分离轴方法。 //这种寻求进展通过计算最大的时间保持分离。 void b2TimeOfImpact(b2TOIOutput* output, const b2TOIInput* input) { //调用次数自加 ++b2_toiCalls; //赋值output output->state = b2TOIOutput::e_unknown; output->t = input->tMax; //获取距离代理 const b2DistanceProxy* proxyA = &input->proxyA; const b2DistanceProxy* proxyB = &input->proxyB; //获取扫频 b2Sweep sweepA = input->sweepA; b2Sweep sweepB = input->sweepB; // 大型旋转可以使根检索器失效,所以我们标准化扫频角度 sweepA.Normalize(); sweepB.Normalize(); //获取扫频间隔 float32 tMax = input->tMax; //获取两个形状半径之和 float32 totalRadius = proxyA->m_radius + proxyB->m_radius; float32 target = b2Max(b2_linearSlop, totalRadius - 3.0f * b2_linearSlop); //允许误差 float32 tolerance = 0.25f * b2_linearSlop; //验证有效值 b2Assert(target > tolerance); float32 t1 = 0.0f; //最大迭代次数 const int32 k_maxIterations = 20; // TODO_ERIN b2Settings // int32 iter = 0; // 初始化距离输入参数 b2SimplexCache cache; cache.count = 0; b2DistanceInput distanceInput; distanceInput.proxyA = input->proxyA; distanceInput.proxyB = input->proxyB; distanceInput.useRadii = false; // 外面的循环逐步尝试计算新的分离轴 // 当一个轴是重复的(没有进展),这个循环终止 for(;;) { b2Transform xfA, xfB; sweepA.GetTransform(&xfA, t1); sweepB.GetTransform(&xfB, t1); // 获取形状之间的距离。我们也可以使用这个结果去获得一个分离轴 distanceInput.transformA = xfA; distanceInput.transformB = xfB; b2DistanceOutput distanceOutput; b2Distance(&distanceOutput, &cache, &distanceInput); // 如果形状重叠,我们放弃连续碰撞 if (distanceOutput.distance <= 0.0f) { //失败! output->state = b2TOIOutput::e_overlapped; output->t = 0.0f; break; } if (distanceOutput.distance < target + tolerance) { //胜利! output->state = b2TOIOutput::e_touching; output->t = t1; break; } // 初始化分离轴 b2SeparationFunction fcn; fcn.Initialize(&cache, proxyA, sweepA, proxyB, sweepB, t1); #if 0 // Dump the curve seen by the root finder { const int32 N = 100; float32 dx = 1.0f / N; float32 xs[N+1]; float32 fs[N+1]; float32 x = 0.0f; for (int32 i = 0; i <= N; ++i) { sweepA.GetTransform(&xfA, x); sweepB.GetTransform(&xfB, x); float32 f = fcn.Evaluate(xfA, xfB) - target; printf("%g %g\n", x, f); xs[i] = x; fs[i] = f; x += dx; } } #endif //在分离轴上计算TOI(碰撞时间),我们先后解决最深处的点。这个循环是以顶点数为终止条件的 bool done = false; float32 t2 = tMax; int32 pushBackIter = 0; for (;;) { // 在t2上查找最深点,存储见证点索引 int32 indexA, indexB; float32 s2 = fcn.FindMinSeparation(&indexA, &indexB, t2); // 是否是最终的外形分离 if (s2 > target + tolerance) { //胜利! output->state = b2TOIOutput::e_separated; output->t = tMax; done = true; break; } //分离值是否达到误差值 if (s2 > target - tolerance) { // 推进扫描 t1 = t2; break; } // 使用见证点计算最初的间距 float32 s1 = fcn.Evaluate(indexA, indexB, t1); // 检验最初重叠。有可能发生根检索器超出了迭代总的次数的现象。 if (s1 < target - tolerance) { output->state = b2TOIOutput::e_failed; output->t = t1; done = true; break; } // 检查触碰 if (s1 <= target + tolerance) { // 胜利!t1必须保留TOI(只有可能是0) output->state = b2TOIOutput::e_touching; output->t = t1; done = true; break; } //计算1D root : f(x) - target = 0 int32 rootIterCount = 0; float32 a1 = t1, a2 = t2; for (;;) { // 混合使用割线规则和二分法 float32 t; if (rootIterCount & 1) { // 割线规则来提高收敛 t = a1 + (target - s1) * (a2 - a1) / (s2 - s1); } else { // 二分法保证进度 t = 0.5f * (a1 + a2); } float32 s = fcn.Evaluate(indexA, indexB, t); if (b2Abs(s - target) < tolerance) { // 赋值 t2 = t; break; } // 确保我们查找根 if (s > target) { a1 = t; s1 = s; } else { a2 = t; s2 = s; } //根迭代器 ++rootIterCount; ++b2_toiRootIters; // 循环到达50次后,退出 if (rootIterCount == 50) { break; } } b2_toiMaxRootIters = b2Max(b2_toiMaxRootIters, rootIterCount); //记录顶点迭代器 ++pushBackIter; //达到顶点的最大次数,退出 if (pushBackIter == b2_maxPolygonVertices) { break; } } //根迭代器 ++iter; //toi的迭代次数自增 ++b2_toiIters; if (done) { break; } if (iter == k_maxIterations) { //没有找到根 output->state = b2TOIOutput::e_failed; output->t = t1; break; } } //获取toi最大迭代器 b2_toiMaxIters = b2Max(b2_toiMaxIters, iter); }
关于b2TimeOfImpact函数,主要以3重for循环为主线的,第一层for循环主要是逐步尝试计算新的分离轴,并当出现一个轴是重复的时,终止循环。第二层for循环主要是在分离轴上计算TOI(碰撞时间),我们先后解决最深处的点。这个循环是以顶点数为终止条件的。第三层for循环主要使用割线规则和二分法进行求解在t时间内,两物体碰撞的具体的时间值。这个循环是以找到在误差允许的范围内的时间值或者循环50次为终止条件的。
另外想说一下,在这里我们每个循环的写法是for(;;)这样的,个人感觉不太雅致,也不能看一眼而不用思索的就知道是死循环的写法,如改成while(true)或者while(1)更好。
关于两物体间是否碰撞了?在Box2d中目前我们至少知道3种可以判断的方法,它们分别是:
- a)、通过两物体的aabb,判断是否重叠。
- b)、通过GJK算法算出两物体间的距离,根据距离判断是否碰撞
- c)、通过SAT分离轴算法看是否能找出两物体间的分离轴,如果找得出就没有碰撞,找不出则碰撞。
Ok,碰撞部分终于学完了,下面我们将继续学习动力学部分。不早了,各位早安。。。
ps:
以上文章仅是一家之言,若有不妥、错误之处,请大家多多指出。同时也希望能与大家多多交流,共同进步。
今天再做软键盘隐藏时遇到的问题记录一下:
我在上一个界面的onResume方法里隐藏软键盘始终隐藏不掉,后来在当前界面的onPause方法里面写才算解决问题。网上查资料是这样的:
软键盘的显示与隐藏可以写在某控件的onClick事件里来或者用timer来控制,若你直接在onCreate或onResume里面写是不行的,因为软件盘要在所有view画完才能显示的。
后来做个试验:
在onResume方法中,通过handler延迟200毫秒执行隐藏键盘操作,果然OK了。记录在此,纪念耗费的时间。