Simplified camera code somewhat, added support for final-camera, and made reverse

faster.


git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/trunk@4306 178a84e3-b1eb-0310-8ba1-8eac791a3b58
This commit is contained in:
hikerstk 2009-12-16 21:37:33 +00:00
parent f6b4197b7d
commit de0e948c9a
5 changed files with 223 additions and 138 deletions

View File

@ -38,8 +38,6 @@ Camera::Camera(int camera_index, const Kart* kart)
setupCamera();
m_distance = kart->getKartProperties()->getCameraDistance() * 0.5f;
m_kart = kart;
m_angle_up = 0.0f;
m_angle_around = 0.0f;
m_ambient_light = RaceManager::getTrack()->getDefaultAmbientColor();
// TODO: Put these values into a config file
@ -136,12 +134,49 @@ void Camera::setupCamera()
} // setupCamera
// ----------------------------------------------------------------------------
/** Sets the mode of the camera.
* \param mode Mode the camera should be switched to.
*/
void Camera::setMode(Mode mode)
{
m_mode = mode;
// If we switch from reverse view, move the camera immediately to the
// correct position.
if(m_mode==CM_REVERSE && mode==CM_NORMAL)
{
Vec3 wanted_position, wanted_target;
computeNormalCameraPosition(&wanted_position, &wanted_target);
m_camera->setPosition(wanted_position.toIrrVector());
m_camera->setTarget(wanted_target.toIrrVector());
}
if(mode==CM_FINAL)
{
const Track* track = RaceManager::getTrack();
core::vector3df wanted_position(track->getCameraPosition().toIrrVector());
core::vector3df curr_position(m_camera->getPosition());
m_lin_velocity = (wanted_position-curr_position)
/ stk_config->m_final_camera_time;
float distance = (m_kart->getXYZ() - curr_position).length();
core::vector3df hpr = track->getCameraHPR().toIrrHPR();
core::vector3df target = hpr.rotationToDirection(core::vector3df(0, 0, 1)*distance)
+ wanted_position;
core::vector3df curr_hpr = m_camera->getRotation();
m_target_velocity = (target - m_camera->getTarget())
/ stk_config->m_final_camera_time;
m_final_time = 0.0f;
}
// If the camera is set to final mode but there is no camera
// end position defined, ignore this request and leave the camera
// in normal mode.
if(mode!=CM_FINAL || RaceManager::getTrack()->hasFinalCamera())
{
m_mode = mode;
}
} // setMode
// ----------------------------------------------------------------------------
/** Returns the current mode of the camera.
*/
Camera::Mode Camera::getMode()
{
return m_mode;
@ -154,9 +189,6 @@ Camera::Mode Camera::getMode()
void Camera::reset()
{
setMode(CM_NORMAL);
// m_position, m_target etc. are set when the worlds has computed the right starting
// position of all karts and calls setInitialTransform for each camera.
} // reset
//-----------------------------------------------------------------------------
@ -165,107 +197,30 @@ void Camera::reset()
*/
void Camera::setInitialTransform()
{
m_target = m_kart->getXYZ();
m_position = m_target - Vec3(0,50,-25);
m_temp_position = m_position;
m_temp_target = m_target;
} // updateKartPosition
m_camera->setPosition( m_kart->getXYZ().toIrrVector() - core::vector3df(0, -25, 50) );
} // setInitialTransform
//-----------------------------------------------------------------------------
void Camera::update(float dt)
/** Moves the camera smoothly from the current camera position (and target)
* to the new position and target.
* \param wanted_position The position the camera wanted to reach.
* \param wanted_target The point the camera wants to point to.
*/
void Camera::smoothMoveCamera(float dt, const Vec3 &wanted_position,
const Vec3 &wanted_target)
{
const Track* track=RaceManager::getTrack();
float steering;
float dampened_steer;
// Each case should set m_target and m_position according to what is needed for that mode.
// Yes, there is a lot of duplicate code but it is (IMHO) much easier to follow this way.
switch(m_mode)
{
case CM_NORMAL:
// This first line moves the camera around behind the kart, pointing it
// towards where the kart is turning (and turning even more while skidding).
steering = m_kart->getSteerPercent() * (1.0f + (m_kart->getSkidding() - 1.0f)/2.3f ); // dampen skidding effect
dampened_steer = fabsf(steering) * steering; // quadratically to dampen small variations (but keep sign)
m_angle_around = m_kart->getHPR().getX() + m_rotation_range * dampened_steer * 0.5f;
m_angle_up = m_kart->getHPR().getY() - 30.0f*DEGREE_TO_RAD;
m_target = m_kart->getXYZ();
m_target.setZ(m_target.getZ()+0.75f);
m_position.setX( sin(m_angle_around));
m_position.setY(-cos(m_angle_around));
m_position.setZ(-sin(m_angle_up));
m_position *= m_distance;
m_position += m_target;
break;
case CM_REVERSE: // Same as CM_NORMAL except it looks backwards
m_angle_around = m_kart->getHPR().getX() - m_rotation_range * m_kart->getSteerPercent() * m_kart->getSkidding();
m_angle_up = m_kart->getHPR().getY() + 30.0f*DEGREE_TO_RAD;
m_target = m_kart->getXYZ();
m_target.setZ(m_target.getZ()+0.75f);
m_position.setX(-sin(m_angle_around));
m_position.setY( cos(m_angle_around));
m_position.setZ( sin(m_angle_up));
m_position *= m_distance * 2.0f;
m_position += m_target;
break;
case CM_CLOSEUP: // Lower to the ground and closer to the kart
m_angle_around = m_kart->getHPR().getX() + m_rotation_range * m_kart->getSteerPercent() * m_kart->getSkidding();
m_angle_up = m_kart->getHPR().getY() - 20.0f*DEGREE_TO_RAD;
m_target = m_kart->getXYZ();
m_target.setZ(m_target.getZ()+0.75f);
m_position.setX( sin(m_angle_around));
m_position.setY(-cos(m_angle_around));
m_position.setZ(-sin(m_angle_up));
m_position *= m_distance * 0.5f;
m_position += m_target;
break;
case CM_LEADER_MODE: // Follows the leader kart, higher off of the ground, further from the kart,
// and turns in the opposite direction from the kart for a nice effect. :)
m_angle_around = RaceManager::getKart(0)->getHPR().getX();
m_angle_up = RaceManager::getKart(0)->getHPR().getY() + 40.0f*DEGREE_TO_RAD;
m_target = RaceManager::getKart(0)->getXYZ();
m_position.setX(sin(m_angle_around));
m_position.setY(cos(m_angle_around));
m_position.setZ(sin(m_angle_up));
m_position *= m_distance * 2.0f;
m_position += m_target;
break;
case CM_FINAL:
if(!track->hasFinalCamera())
{
m_mode = CM_NORMAL;
break;
}
m_position = track->getCameraPosition();
m_target.setX(-sin( track->getCameraHPR().getX() ) );
m_target.setY( cos( track->getCameraHPR().getX() ) );
m_target.setZ( sin( track->getCameraHPR().getY() ) );
m_target *= 10.0f;
m_target += m_position;
break;
case CM_SIMPLE_REPLAY:
// TODO: Implement
break;
}
// Smoothly interpolate towards the position and target
m_temp_target += ((m_target - m_temp_target) * m_target_speed) * dt;
m_temp_position += ((m_position - m_temp_position) * m_position_speed) * dt;
core::vector3df current_position = m_camera->getPosition();
core::vector3df current_target = m_camera->getTarget();
current_target += ((wanted_target.toIrrVector() - current_target ) * m_target_speed ) * dt;
current_position += ((wanted_position.toIrrVector() - current_position) * m_position_speed) * dt;
m_camera->setPosition(current_position);
m_camera->setTarget(current_target);
if(race_manager->getNumLocalPlayers() < 2)
sound_manager->positionListener(current_position,
current_target - current_position);
m_camera->setPosition(m_temp_position.toIrrVector());
m_camera->setTarget(m_temp_target.toIrrVector());
// The following settings give a debug camera which shows the track from
// high above the kart straight down.
#undef DEBUG_CAMERA
@ -275,9 +230,120 @@ void Camera::update(float dt)
xyz.Y = xyz.Y+30;
m_camera->setPosition(xyz);
#endif
} // smoothMoveCamera
if(race_manager->getNumLocalPlayers() < 2)
sound_manager->positionListener(m_temp_position, m_temp_target - m_temp_position);
//-----------------------------------------------------------------------------
/** Computes the wanted camera position and target for normal camera mode.
* Besides being used in update(dt), it is also used when switching the
* camera from reverse mode to normal mode - in which case we don't want
* to have a smooth camera.
* \param wanted_position The position the camera should be.
* \param wanted_target The target position the camera should target.
*/
void Camera::computeNormalCameraPosition(Vec3 *wanted_position,
Vec3 *wanted_target)
{
*wanted_target = m_kart->getXYZ();
wanted_target->setZ(wanted_target->getZ()+ 0.75f);
// This first line moves the camera around behind the kart, pointing it
// towards where the kart is turning (and turning even more while skidding).
float steering = m_kart->getSteerPercent() * (1.0f + (m_kart->getSkidding() - 1.0f)/2.3f ); // dampen skidding effect
float dampened_steer = fabsf(steering) * steering; // quadratically to dampen small variations (but keep sign)
float angle_around = m_kart->getHPR().getX() + m_rotation_range * dampened_steer * 0.5f;
float angle_up = m_kart->getHPR().getY() - 30.0f*DEGREE_TO_RAD;
wanted_position->setX( sin(angle_around));
wanted_position->setY(-cos(angle_around));
wanted_position->setZ(-sin(angle_up) );
*wanted_position *= m_distance;
*wanted_position += *wanted_target;
} // computeNormalCameraPosition
//-----------------------------------------------------------------------------
/** Called once per time frame to move the camera to the right position.
* \param dt Time step.
*/
void Camera::update(float dt)
{
printf("cam: %f %f %f\n",
m_camera->getRotation().X,
m_camera->getRotation().Y,
m_camera->getRotation().Z);
Vec3 wanted_position;
Vec3 wanted_target = m_kart->getXYZ();
// Each case should set wanted_position and wanted_target according to
// what is needed for that mode. Yes, there is a lot of duplicate code
// but it is (IMHO) much easier to follow this way.
switch(m_mode)
{
case CM_NORMAL:
{
computeNormalCameraPosition(&wanted_position, &wanted_target);
smoothMoveCamera(dt, wanted_position, wanted_target);
break;
}
case CM_REVERSE: // Same as CM_NORMAL except it looks backwards
{
wanted_target.setZ(wanted_target.getZ()+ 0.75f);
float angle_around = m_kart->getHPR().getX() - m_rotation_range * m_kart->getSteerPercent() * m_kart->getSkidding();
float angle_up = m_kart->getHPR().getY() + 30.0f*DEGREE_TO_RAD;
wanted_position.setX(-sin(angle_around));
wanted_position.setY( cos(angle_around));
wanted_position.setZ( sin(angle_up) );
wanted_position *= m_distance * 2.0f;
wanted_position += wanted_target;
smoothMoveCamera(dt, wanted_position, wanted_target);
m_camera->setPosition(wanted_position.toIrrVector());
m_camera->setTarget(wanted_target.toIrrVector());
break;
}
case CM_CLOSEUP: // Lower to the ground and closer to the kart
{
wanted_target.setZ(wanted_target.getZ()+0.75f);
float angle_around = m_kart->getHPR().getX() + m_rotation_range * m_kart->getSteerPercent() * m_kart->getSkidding();
float angle_up = m_kart->getHPR().getY() - 20.0f*DEGREE_TO_RAD;
wanted_position.setX( sin(angle_around));
wanted_position.setY(-cos(angle_around));
wanted_position.setZ(-sin(angle_up) );
wanted_position *= m_distance * 0.5f;
wanted_position += wanted_target;
smoothMoveCamera(dt, wanted_position, wanted_target);
break;
}
case CM_LEADER_MODE:
{
wanted_target = RaceManager::getKart(0)->getXYZ().toIrrVector();
// Follows the leader kart, higher off of the ground, further from the kart,
// and turns in the opposite direction from the kart for a nice effect. :)
float angle_around = RaceManager::getKart(0)->getHPR().getX();
float angle_up = RaceManager::getKart(0)->getHPR().getY() + 40.0f*DEGREE_TO_RAD;
wanted_position.setX(sin(angle_around));
wanted_position.setY(cos(angle_around));
wanted_position.setZ(sin(angle_up) );
wanted_position *= m_distance * 2.0f;
wanted_position += wanted_target;
smoothMoveCamera(dt, wanted_position, wanted_target);
break;
}
case CM_FINAL:
{
m_final_time +=dt;
if(m_final_time < stk_config->m_final_camera_time)
{
const Track* track=RaceManager::getTrack();
core::vector3df new_pos = m_camera->getPosition()+m_lin_velocity*dt;
m_camera->setPosition(new_pos);
core::vector3df new_target = m_camera->getTarget()+m_target_velocity*dt;
m_camera->setTarget(new_target);
}
return;
break;
}
case CM_SIMPLE_REPLAY:
// TODO: Implement
break;
}
} // update

View File

@ -51,35 +51,16 @@ private:
* attached to. */
unsigned int m_index;
/** The ultimate position which the camera wants to obtain. */
Vec3 m_position;
/** The position the camera currently has. */
Vec3 m_temp_position;
/** The ultimate target which the camera wants to obtain. */
Vec3 m_target;
/** The target the camera currently has. */
Vec3 m_temp_target;
/** Current ambient light for this camera. */
video::SColor m_ambient_light;
/** Distance between the camera and the kart. */
float m_distance;
/** Angle between the ground and the camera (with the kart as the
* vertex of the angle). */
float m_angle_up;
/** Angle around the kart (should actually match the rotation of the kart). */
float m_angle_around;
/** The speed at which the camera changes position. */
float m_position_speed;
/** The speed at which the camera changes targets. */
/** The speed at which the camera target changes position. */
float m_target_speed;
/** Factor of the effects of steering in camera aim. */
@ -100,8 +81,21 @@ private:
/** Aspect ratio for camera. */
float m_aspect;
void setupCamera();
/** Linear velocity of the camera, only used for end camera. */
core::vector3df m_lin_velocity;
/** Velocity of the target of the camera, only used for end camera. */
core::vector3df m_target_velocity;
/** Counts the time for the end camera only, to indicate when
* the end camera should stop moving. */
float m_final_time;
void setupCamera();
void smoothMoveCamera(float dt, const Vec3 &wanted_position,
const Vec3 &wanted_target);
void computeNormalCameraPosition(Vec3 *wanted_position,
Vec3 *wanted_target);
public:
Camera (int camera_index, const Kart* kart);
~Camera ();

View File

@ -278,9 +278,9 @@ void PlayerKart::update(float dt)
if (m_powerup.getType()==POWERUP_NOTHING)
Kart::beep();
}
m_camera->setMode(m_controls.m_look_back ? Camera::CM_REVERSE
: Camera::CM_NORMAL);
if(m_camera->getMode()!=Camera::CM_FINAL)
m_camera->setMode(m_controls.m_look_back ? Camera::CM_REVERSE
: Camera::CM_NORMAL);
// We can't restrict rescue to fulfil isOnGround() (which would be more like
// MK), since e.g. in the City track it is possible for the kart to end

View File

@ -237,14 +237,6 @@ void Track::loadTrackInfo(const std::string &filename)
if(m_groups.size()==0)
m_groups.push_back("standard");
// if both camera position and rotation are defined,
// set the flag that the track has final camera position
m_has_final_camera = root->get("camera-final-position",
&m_camera_final_position)!=1;
m_has_final_camera &= root->get("camera-final-hpr",
&m_camera_final_hpr) !=1;
m_camera_final_hpr.degreeToRad();
const XMLNode *xml_node = root->getNode("curves");
if(xml_node)
loadCurves(*xml_node);
@ -614,9 +606,13 @@ void Track::createWater(const XMLNode &node)
* animations, items, ... It is called from world during initialisation.
* Track is the first model to be loaded, so at this stage the root scene node
* is empty.
* \param mode_id Which of the modes of a track to use. This determines which
* scene, quad, and graph file to load.
*/
void Track::loadTrackModel(unsigned int mode_id)
{
m_has_final_camera = false;
m_is_arena = false;
item_manager->setStyle();
// Load the graph only now: this function is called from world, after
@ -740,6 +736,10 @@ void Track::loadTrackModel(unsigned int mode_id)
{
handleSky(*node, path);
}
else if (name=="camera")
{
handleCamera(*node);
}
else
{
fprintf(stderr, "Warning: element '%s' not found.\n",
@ -862,8 +862,26 @@ void Track::handleSky(const XMLNode &xml_node, const std::string &filename)
} // handleSky
//-----------------------------------------------------------------------------
/** Reads the final camera position.
* \param root The XML node with the camera node.
*/
void Track::handleCamera(const XMLNode &root)
{
m_has_final_camera = true;
root.get("final-position", &m_camera_final_position);
root.get("final-hpr", &m_camera_final_hpr );
m_camera_final_hpr.degreeToRad();
} // handleCamera
//-----------------------------------------------------------------------------
/** Handle creation and placement of an item.
* \param xyz The position of the item.
* \param type The item type.
* \param need_height True if the item Z position should be determined based on
* the track topology.
*/
void Track::itemCommand(const Vec3 &xyz, Item::ItemType type,
int bNeedHeight)
int need_height)
{
// Some modes (e.g. time trial) don't have any bonus boxes
if(type==Item::ITEM_BONUS_BOX &&
@ -872,7 +890,7 @@ void Track::itemCommand(const Vec3 &xyz, Item::ItemType type,
Vec3 loc(xyz);
// if only 2d coordinates are given, let the item fall from very high
if(bNeedHeight)
if(need_height)
{
loc.setZ(1000);
loc.setZ(getTerrainHeight(loc));

View File

@ -73,14 +73,20 @@ private:
scene::ILightSceneNode *m_sun;
TriangleMesh* m_track_mesh;
TriangleMesh* m_non_collision_mesh;
/** True if the track/scene has a final camera position. */
bool m_has_final_camera;
/** The final xyz coordinates of the camera. */
Vec3 m_camera_final_position;
/** The final hpr rotation of the camera. */
Vec3 m_camera_final_hpr;
/** Minimum coordinates of this track. */
Vec3 m_aabb_min;
/** Maximum coordinates of this track. */
Vec3 m_aabb_max;
/** True if this track is an arena. */
bool m_is_arena;
/** The version of this track. A certain STK version will only support
* certain track versions. */
int m_version;
/** The graph used to connect the quads. */
@ -166,6 +172,7 @@ private:
void loadCurves(const XMLNode &node);
void handleAnimatedTextures(scene::ISceneNode *node, const XMLNode &xml);
void handleSky(const XMLNode &root, const std::string &filename);
void handleCamera(const XMLNode &root);
public: