坐标系:

 z-axis
^
|
| y-axis
| /
| /
|/
+----------------> x-axis

围绕Z轴旋转叫做偏航角,Yaw;围绕X轴旋转叫做 俯仰角,Pitch;围绕Y轴旋转,叫做滚转角,Roll 

///////////////////////////////////////////////////////////////////////////////////////////////////////////////

相机的标记:
//使用三个标记,记录相机行进方向;相机无非就是向前,向后,旋转,这三个状态
int m_flags;
int m_forwardFlags;
int m_backwardsFlags;

初始化时:

    m_flags = eVCam_goingForwards | eVCam_canRotate; // 向前 + 可旋转
m_forwardFlags = eVCam_rotationSpring | eVCam_rotationClamp; //带悬架的旋转 + 限制旋转角度
m_backwardsFlags = m_forwardFlags;//和上面那个值一样,所以感觉没什么用处

接受用户输入的旋转:

void CVehicleViewSteer::OnAction(const TVehicleActionId actionId, int activationMode, float value)
{//看看上面的图就了然了
if (m_flags & eVCam_canRotate)
{
if (actionId == eVAI_RotateYaw)
{
m_rotatingAction.z += value;
}
else if (actionId == eVAI_RotatePitch)
{
m_rotatingAction.x += value;
}
}
}

//////////////////////////////////////////////////////////////////////////////////////////////////

    IEntity * pEntity = m_pVehicle->GetEntity();
assert(pEntity); CVehicleMovementBase * pVehicleMovement = static_cast<CVehicleMovementBase*>(m_pVehicle->GetMovement());
const pe_status_dynamics& dynStatus = pVehicleMovement->GetPhysicsDyn(); //这个貌似是取出了物理系统 SMovementState movementState;
pVehicleMovement->GetMovementState(movementState);
const float pedal = pVehicleMovement->GetEnginePedal();//获取坦克引擎力
const float maxSpeed = movementState.maxSpeed;//获取当前坦克的最大速度
const Matrix34& pose = m_pAimPart ? m_pAimPart->GetWorldTM() : pEntity->GetWorldTM();//取出坦克的世界坐标到局部坐标的矩阵
const Vec3 entityPos = pose.GetColumn3();
const Vec3 xAxis = pose.GetColumn0();
const Vec3 yAxis = pose.GetColumn1();
const Vec3 zAxis = pose.GetColumn2();
const float forwardSpeed = dynStatus.v.dot(yAxis);//当前坦克的速度,在Y轴上的投影,就是Y轴方向上的速度了
const float speedNorm = clamp(forwardSpeed / maxSpeed, 0.0f, 1.0f);//计算出这个Y轴的速度与最大速度比例,其实就是个百分比
const Vec3 maxRotation = m_maxRotation + speedNorm * (m_maxRotation2 - m_maxRotation);//这个不知道 CalcLookAt(pose);//根据世界坐标矩阵,计算 m_lookAt,看起来是将look at转到坦克本地坐标系下
if (m_lookAt.IsValid())
{
if (!m_lastOffset.IsValid())
{
       //如果之前没有设置过last offset
m_position = pose * m_localSpaceCameraOffset; //这个应该是想把相机设置到坦克局部坐标系下,同时偏移了一个offset
m_lastOffset = m_position - m_lookAt;// 基本上相当于相机和坦克之间的向量
m_lastOffsetBeforeElev = m_lastOffset;
} Vec3 offset = m_lastOffsetBeforeElev; if (pedal<0.1f && forwardSpeed<1.0f)
{//Y轴速度小于 1,并且当前引擎马力小于 0.1,说明相机要向后移动
// Going Backwards
m_flags &= ~(eVCam_goingForwards | m_forwardFlags);
m_flags |= m_backwardsFlags;
} if (offset.dot(yAxis) < 0.8f && forwardSpeed>.f)
{//Y轴速度大于1,并且上次的偏移在Y轴上的投影大于等于0.8,说明相机要向前移动
// Going Forwards
m_flags &= ~m_backwardsFlags;
m_flags |= eVCam_goingForwards | m_forwardFlags;
}

     
float sensitivity = (.f-speedNorm) * m_stickSensitivity.z + speedNorm * m_stickSensitivity2.z;//影响值。speedNorm是个比例,意义是,在stickSensitivity1,2之间做插值
float rotate = -m_rotatingAction.z * sensitivity;//输入的Z要乘以个0.5,即sensitivity的值
rotate = rotate * dt;//再乘以时间? if (zAxis.z > 0.1f)
{//如果坦克的高度超过 0.1
// Safe to update curYaw
Vec3 projectedX = xAxis; projectedX.z = .f;
Vec3 projectedY = yAxis; projectedY.z = .f;
const float newYaw = atan2_tpl(offset.dot(projectedX), -(offset.dot(projectedY)));
const float maxChange = DEG2RAD(.f)*dt;
const float delta = clamp(newYaw - m_curYaw, -maxChange, +maxChange);
m_curYaw += delta;
} // Rotation Action
{
if (m_flags & eVCam_rotationClamp)
{
float newYaw = clamp(m_curYaw + rotate, -maxRotation.z, +maxRotation.z);
rotate = newYaw - m_curYaw;
rotate = clamp(newYaw - m_curYaw, -fabsf(rotate), +fabsf(rotate));
m_rotation.z += rotate;
}
else
{
m_rotation.z=.f;
} if (speedNorm > 0.1f)
{
float reduce = dt * .f;
m_rotation.z = m_rotation.z - reduce*m_rotation.z/(fabsf(m_rotation.z) + reduce);
}
} // Ang Spring
{
float angSpeedCorrection = dt*dt*m_angSpeedCorrection/(dt*m_angSpeedCorrection+.f)*dynStatus.w.z;
if ((m_flags & eVCam_rotationSpring)==)
{
m_angReturnSpeed = .f;
angSpeedCorrection = .f;
} float difference = m_rotation.z - m_curYaw;
float relax = difference * (m_angReturnSpeed*dt) / ((m_angReturnSpeed*dt) + .f); const float delta = +relax + angSpeedCorrection + rotate;
m_curYaw += delta; Matrix33 rot = Matrix33::CreateRotationZ(delta);
offset = rot * offset; // Lerp the spring speed
float angSpeedTarget = m_angReturnSpeed1 + speedNorm * (m_angReturnSpeed2 - m_angReturnSpeed1);
m_angReturnSpeed += (angSpeedTarget - m_angReturnSpeed) * (dt/(dt+0.3f));
m_angSpeedCorrection += (m_angSpeedCorrection0 - m_angSpeedCorrection) * (dt/(dt+0.3f));
} if (!offset.IsValid()) offset = m_lastOffset; // Velocity influence
Vec3 displacement = -((.f-speedNorm) * dt) * dynStatus.v;//车辆产生位移,这里算的是位移和2倍位移之间的插值,例如,-5和-10之间的插值 float dot = offset.dot(displacement); //Offset 是从相机的偏移位置 到 坦克位置的向量,这个向量和坦克的移动做点积,根据两个向量的方向 ,判断是向前还是向后,再做修正。
if (dot < .f)
{
displacement = displacement + offset * -0.1f * (offset.dot(displacement) / offset.GetLengthSquared());
}
offset = offset + displacement;//这个部分计算的应该是跟随,车辆的移动,需要对偏移值做修正 const float radius0 = fabsf(m_localSpaceCameraOffset.y);
const float minRadius = radius0 * m_radiusMin;
const float maxRadius = radius0 * m_radiusMax;
float radiusXY = sqrtf(sqr(offset.x) + sqr(offset.y)); Vec3 offsetXY = offset; offsetXY.z = .f;
Vec3 accelerationV = (dynStatus.v - m_lastVehVel);//速度变化,加速度
float acceleration = offsetXY.dot(accelerationV) / radiusXY; // m_lastVehVel = dynStatus.v;
m_radiusVel -= acceleration;
m_radius += m_radiusVel * dt - dt*m_radiusVelInfluence * offsetXY.dot(dynStatus.v)/radiusXY;
m_radiusVel *= expf(-dt*m_radiusDampRate);
m_radius += (radius0-m_radius)*(dt*m_radiusRelaxRate)/(dt*m_radiusRelaxRate+.f);
m_radius = clamp(m_radius, minRadius, maxRadius);
offset = offset * (m_radius/radiusXY); // Vertical motion
float targetOffsetHeight = m_localSpaceCameraOffset.z * (m_radius/radius0);//
float oldOffsetHeight = offset.z;
offset.z += (targetOffsetHeight - offset.z)*(dt/(dt+0.3f));
Limit(offset.z, targetOffsetHeight - .f, targetOffsetHeight + .f);
float verticalChange = offset.z - oldOffsetHeight; m_lastOffsetBeforeElev = offset; // Add up and down camera tilt
{
offset.z -= verticalChange;
m_rotation.x += dt * m_stickSensitivity.x * m_rotatingAction.x;
m_rotation.x = clamp(m_rotation.x, -maxRotation.x, +maxRotation.x); float elevAngleVehicle = m_inheritedElev * yAxis.z; // yAxis.z == approx elevation angle float elevationAngle = m_rotation.x - elevAngleVehicle; float sinElev, cosElev;
cry_sincosf(elevationAngle, &sinElev, &cosElev);
float horizLen = sqrtf(offset.GetLengthSquared2D());
float horizLenNew = horizLen * cosElev - sinElev * offset.z;
if (horizLen>1e-4f)
{
horizLenNew /= horizLen;
offset.x *= horizLenNew;
offset.y *= horizLenNew;
offset.z = offset.z * cosElev + sinElev * horizLen;
}
offset.z += verticalChange;
} if (!offset.IsValid()) offset = m_lastOffset; m_position = m_lookAt + offset; // intersection check...
{
IPhysicalEntity* pSkipEntities[];
int nSkip = ;
if(m_pVehicle)
{
nSkip = m_pVehicle->GetSkipEntities(pSkipEntities, );
} primitives::sphere sphere;
sphere.center = m_lookAt;
sphere.r = g_CameraRadius;
Vec3 dir = m_position-m_lookAt; geom_contact *pContact = ;
float hitDist = gEnv->pPhysicalWorld->PrimitiveWorldIntersection(sphere.type, &sphere, dir, ent_static|ent_terrain|ent_rigid|ent_sleeping_rigid,
&pContact, , (geom_colltype_player<<rwi_colltype_bit) | rwi_stop_at_pierceable, , , , pSkipEntities, nSkip);
if(hitDist > 0.0f)
{
// Interpolate the offset
Vec3 newPos = m_lookAt + hitDist * dir.GetNormalizedSafe();
offset = newPos - m_lookAt;
}
} Interpolate(m_lastOffset, offset, .f, dt); m_position = m_lookAt + m_lastOffset;
}
else
{
CRY_ASSERT_MESSAGE(, "camera will fail because lookat position is invalid");
} //float sensitivity = (m_pSensitivity) ? m_pSensitivity->GetFVal() : 1.0f;
m_rotatingAction.zero();

VehicleCamera解读的更多相关文章

  1. SDWebImage源码解读之SDWebImageDownloaderOperation

    第七篇 前言 本篇文章主要讲解下载操作的相关知识,SDWebImageDownloaderOperation的主要任务是把一张图片从服务器下载到内存中.下载数据并不难,如何对下载这一系列的任务进行设计 ...

  2. SDWebImage源码解读 之 NSData+ImageContentType

    第一篇 前言 从今天开始,我将开启一段源码解读的旅途了.在这里先暂时不透露具体解读的源码到底是哪些?因为也可能随着解读的进行会更改计划.但能够肯定的是,这一系列之中肯定会有Swift版本的代码. 说说 ...

  3. SDWebImage源码解读 之 UIImage+GIF

    第二篇 前言 本篇是和GIF相关的一个UIImage的分类.主要提供了三个方法: + (UIImage *)sd_animatedGIFNamed:(NSString *)name ----- 根据名 ...

  4. SDWebImage源码解读 之 SDWebImageCompat

    第三篇 前言 本篇主要解读SDWebImage的配置文件.正如compat的定义,该配置文件主要是兼容Apple的其他设备.也许我们真实的开发平台只有一个,但考虑各个平台的兼容性,对于框架有着很重要的 ...

  5. SDWebImage源码解读_之SDWebImageDecoder

    第四篇 前言 首先,我们要弄明白一个问题? 为什么要对UIImage进行解码呢?难道不能直接使用吗? 其实不解码也是可以使用的,假如说我们通过imageNamed:来加载image,系统默认会在主线程 ...

  6. SDWebImage源码解读之SDWebImageCache(上)

    第五篇 前言 本篇主要讲解图片缓存类的知识,虽然只涉及了图片方面的缓存的设计,但思想同样适用于别的方面的设计.在架构上来说,缓存算是存储设计的一部分.我们把各种不同的存储内容按照功能进行切割后,图片缓 ...

  7. SDWebImage源码解读之SDWebImageCache(下)

    第六篇 前言 我们在SDWebImageCache(上)中了解了这个缓存类大概的功能是什么?那么接下来就要看看这些功能是如何实现的? 再次强调,不管是图片的缓存还是其他各种不同形式的缓存,在原理上都极 ...

  8. AFNetworking 3.0 源码解读 总结(干货)(下)

    承接上一篇AFNetworking 3.0 源码解读 总结(干货)(上) 21.网络服务类型NSURLRequestNetworkServiceType 示例代码: typedef NS_ENUM(N ...

  9. AFNetworking 3.0 源码解读 总结(干货)(上)

    养成记笔记的习惯,对于一个软件工程师来说,我觉得很重要.记得在知乎上看到过一个问题,说是人类最大的缺点是什么?我个人觉得记忆算是一个缺点.它就像时间一样,会自己消散. 前言 终于写完了 AFNetwo ...

随机推荐

  1. Event Sourcing Pattern 事件源模式

    Use an append-only store to record the full series of events that describe actions taken on data in ...

  2. php的laravel框架快速集成微信登录

    最终的解决方案是:https://github.com/liuyunzhuge/php_weixin_provider,详细的介绍请往下阅读. 本文面向的是php语言laravel框架的用户,介绍的是 ...

  3. 怎样写一个webpack loader

    div{display:table-cell;vertical-align:middle}#crayon-theme-info .content *{float:left}#crayon-theme- ...

  4. 三种上传文件不刷新页面的方法讨论:iframe/FormData/FileReader

    发请求有两种方式,一种是用ajax,另一种是用form提交,默认的form提交如果不做处理的话,会使页面重定向.以一个简单的demo做说明: html如下所示,请求的路径action为"up ...

  5. Asp.Net Core 项目实战之权限管理系统(0) 无中生有

    0 Asp.Net Core 项目实战之权限管理系统(0) 无中生有 1 Asp.Net Core 项目实战之权限管理系统(1) 使用AdminLTE搭建前端 2 Asp.Net Core 项目实战之 ...

  6. Oracle用户被锁原因及办法

    Oracle用户被锁原因及办法   在登陆时被告知test用户被锁 1.用dba角色的用户登陆,进行解锁,先设置具体时间格式,以便查看具体时间 SQL> alter session set nl ...

  7. Consul的一个更新:服务端节点故障后重连

    研究了一段时间Consul,想写个攻略来着,但太赖了而且表达能力非正常人...今天发现HashiCorp果然接纳大众意见改了点东西.. 场景是: 假如Consul集群内有三个Server Node 时 ...

  8. spring+mybatis多数据源动态切换

    spring mvc+mybatis+多数据源切换 选取oracle,mysql作为例子切换数据源.oracle为默认数据源,在测试的action中,进行mysql和oracle的动态切换. web. ...

  9. php静态缓存简单制作

    制作缓存的目的是为了让我们的页面运行更加快速,减少读取数据库内容的次数,给用户更好的体验,为此我们可以使自己的程序做一下缓存,并且设置一个缓存过期的时间,来保证与数据库的一致,当然并不是所有的程序都适 ...

  10. 配置rsync服务,数据同步。

    这部分设计服务器端和客户端. [服务器端] 如果服务器没有安装rsync服务则使用yum安装rsync服务. yum install rsync 然后 vim /etc/xinetd.d/rsync ...