Split view controller to it's own class, added InputFrame code.

This conforms better to the new Input paradigm, and fixes a number of bugs
relating to the ship view incorrectly rotating while in another view.
master
Webster Sheets 2019-02-14 20:14:15 -05:00 committed by Karl F
parent f4186dacb1
commit 34e896550a
17 changed files with 479 additions and 216 deletions

View File

@ -80,6 +80,7 @@ list(APPEND SRC_FOLDERS
src/graphics/opengl
src/gui
src/scenegraph
src/ship
src/terrain
src/text
src/ui

View File

@ -66,7 +66,7 @@ static sigc::connection onChangeCamTypeConnection;
void AmbientSounds::Init()
{
onChangeCamTypeConnection = Pi::game->GetWorldView()->onChangeCamType.connect(sigc::ptr_fun(&AmbientSounds::UpdateForCamType));
onChangeCamTypeConnection = Pi::game->GetWorldView()->shipView.onChangeCamType.connect(sigc::ptr_fun(&AmbientSounds::UpdateForCamType));
}
void AmbientSounds::Uninit()
@ -76,7 +76,7 @@ void AmbientSounds::Uninit()
void AmbientSounds::Update()
{
const float v_env = (Pi::game->GetWorldView()->GetCameraController()->IsExternal() ? 1.0f : 0.5f) * Sound::GetSfxVolume();
const float v_env = (Pi::game->GetWorldView()->shipView.GetCameraController()->IsExternal() ? 1.0f : 0.5f) * Sound::GetSfxVolume();
if (Pi::player->GetFlightState() == Ship::DOCKED) {
if (s_starNoise.IsPlaying()) {
@ -264,8 +264,8 @@ void AmbientSounds::Update()
void AmbientSounds::UpdateForCamType()
{
const WorldView::CamType cam = Pi::game->GetWorldView()->GetCamType();
float v_env = (cam == WorldView::CAM_EXTERNAL ? 1.0f : 0.5f) * Sound::GetSfxVolume();
const ShipViewController::CamType cam = Pi::game->GetWorldView()->shipView.GetCamType();
float v_env = (cam == ShipViewController::CAM_EXTERNAL ? 1.0f : 0.5f) * Sound::GetSfxVolume();
if (s_stationNoise.IsPlaying())
s_stationNoise.SetVolume(0.3f * v_env, 0.3f * v_env);

View File

@ -31,6 +31,54 @@ void Input::InitGame()
}
}
InputResponse Input::InputFrame::ProcessSDLEvent(SDL_Event &event)
{
bool matched = false;
for (KeyBindings::ActionBinding *action : actions) {
auto resp = action->CheckSDLEventAndDispatch(&event);
if (resp == RESPONSE_MATCHED) return resp;
matched = matched || resp > RESPONSE_NOMATCH;
}
for (KeyBindings::AxisBinding *axis : axes) {
auto resp = axis->CheckSDLEventAndDispatch(&event);
if (resp == RESPONSE_MATCHED) return resp;
matched = matched || resp > RESPONSE_NOMATCH;
}
return matched ? RESPONSE_PASSTHROUGH : RESPONSE_NOMATCH;
}
bool Input::PushInputFrame(Input::InputFrame *frame)
{
if (HasInputFrame(frame)) {
return false;
}
inputFrames.push_front(frame);
frame->onFrameAdded();
return true;
}
Input::InputFrame *Input::PopInputFrame()
{
if (inputFrames.size() > 0) {
auto frame = inputFrames.front();
inputFrames.pop_front();
frame->onFrameRemoved();
return frame;
}
return nullptr;
}
void Input::RemoveInputFrame(Input::InputFrame *frame)
{
inputFrames.remove(frame);
frame->onFrameRemoved();
}
KeyBindings::ActionBinding *Input::AddActionBinding(std::string id, BindingGroup *group, KeyBindings::ActionBinding binding)
{
// TODO: should we throw an error if we attempt to bind over an already-bound action?
@ -109,6 +157,11 @@ void Input::HandleSDLEvent(SDL_Event &event)
joysticks[event.jhat.which].hats[event.jhat.hat] = event.jhat.value;
break;
}
for (auto inputFrame : inputFrames) {
auto resp = inputFrame->ProcessSDLEvent(event);
if (resp == RESPONSE_MATCHED) break;
}
}
void Input::InitJoysticks()

View File

@ -7,6 +7,9 @@
#include "KeyBindings.h"
#include "utils.h"
#include <algorithm>
#include <list>
class Input {
// TODO: better decouple these two classes.
friend class Pi;
@ -36,6 +39,41 @@ public:
BindingPage *GetBindingPage(std::string id) { return &bindingPages[id]; }
std::map<std::string, BindingPage> GetBindingPages() { return bindingPages; }
struct InputFrame {
std::vector<KeyBindings::ActionBinding *> actions;
std::vector<KeyBindings::AxisBinding *> axes;
// Call this at startup to register all the bindings associated with the frame.
virtual void RegisterBindings(){};
// Called when the frame is added to the stack.
virtual void onFrameAdded(){};
// Called when the frame is removed from the stack.
virtual void onFrameRemoved(){};
// Check the event against all the inputs in this frame.
InputResponse ProcessSDLEvent(SDL_Event &event);
};
// Pushes an InputFrame onto the input stack.
bool PushInputFrame(InputFrame *frame);
// Pops the most-recently pushed InputFrame from the stack.
InputFrame *PopInputFrame();
// Get a read-only list of input frames.
const std::list<InputFrame *> &GetInputFrames() { return inputFrames; }
// Check if a specific input frame is currently on the stack.
bool HasInputFrame(InputFrame *frame)
{
return std::count(inputFrames.begin(), inputFrames.end(), frame) > 0;
}
// Remove an arbitrary input frame from the input stack.
void RemoveInputFrame(InputFrame *frame);
// Creates a new action binding, copying the provided binding.
// The returned binding pointer points to the actual binding.
KeyBindings::ActionBinding *AddActionBinding(std::string id, BindingGroup *group, KeyBindings::ActionBinding binding);
@ -115,6 +153,8 @@ private:
std::map<std::string, BindingPage> bindingPages;
std::map<std::string, KeyBindings::ActionBinding> actionBindings;
std::map<std::string, KeyBindings::AxisBinding> axisBindings;
std::list<InputFrame *> inputFrames;
};
#endif

View File

@ -368,9 +368,9 @@ namespace KeyBindings {
return binding1.Matches(sym) || binding2.Matches(sym);
}
void ActionBinding::CheckSDLEventAndDispatch(const SDL_Event *event)
InputResponse ActionBinding::CheckSDLEventAndDispatch(const SDL_Event *event)
{
if (m_disableBindings) return;
if (m_disableBindings) return RESPONSE_NOMATCH;
switch (event->type) {
case SDL_KEYDOWN:
case SDL_KEYUP: {
@ -379,6 +379,7 @@ namespace KeyBindings {
onPress.emit();
else if (event->key.state == SDL_RELEASED)
onRelease.emit();
return RESPONSE_MATCHED;
}
break;
}
@ -389,6 +390,7 @@ namespace KeyBindings {
onPress.emit();
else if (event->jbutton.state == SDL_RELEASED)
onRelease.emit();
return RESPONSE_MATCHED;
}
break;
}
@ -397,11 +399,13 @@ namespace KeyBindings {
onPress.emit();
// XXX to emit onRelease, we need to have access to the state of the joystick hat prior to this event,
// so that we can detect the case of switching from a direction that matches the binding to some other direction
return RESPONSE_MATCHED;
}
break;
}
default: break;
}
return RESPONSE_NOMATCH;
}
bool JoyAxisBinding::IsActive() const
@ -587,15 +591,16 @@ namespace KeyBindings {
return axis.IsActive() ? axis.GetValue() : value;
}
void AxisBinding::CheckSDLEventAndDispatch(const SDL_Event *event)
InputResponse AxisBinding::CheckSDLEventAndDispatch(const SDL_Event *event)
{
if (m_disableBindings) return;
if (m_disableBindings) return RESPONSE_NOMATCH;
float value = GetValue();
switch (event->type) {
case SDL_KEYDOWN:
case SDL_KEYUP: {
if (positive.Matches(&event->key.keysym) && negative.Matches(&event->key.keysym)) {
onAxis.emit(value);
return RESPONSE_MATCHED;
}
break;
}
@ -603,6 +608,7 @@ namespace KeyBindings {
case SDL_JOYBUTTONUP: {
if (positive.Matches(&event->jbutton) || negative.Matches(&event->jbutton)) {
onAxis.emit(value);
return RESPONSE_MATCHED;
}
break;
}
@ -611,14 +617,20 @@ namespace KeyBindings {
onAxis.emit(value);
// XXX to emit onRelease, we need to have access to the state of the joystick hat prior to this event,
// so that we can detect the case of switching from a direction that matches the binding to some other direction
return RESPONSE_MATCHED;
}
break;
}
case SDL_JOYAXISMOTION: {
if (axis.Matches(event)) onAxis.emit(value);
if (axis.Matches(event)) {
onAxis.emit(value);
return RESPONSE_MATCHED;
}
}
default: break;
}
return RESPONSE_NOMATCH;
}
void DispatchSDLEvent(const SDL_Event *event)

View File

@ -7,6 +7,15 @@
#include "libs.h"
#include <iosfwd>
enum InputResponse {
// None of the inputs match the event.
RESPONSE_NOMATCH = 0,
// An input matched, but won't consume the event.
RESPONSE_PASSTHROUGH,
// An input matched and consumed the event.
RESPONSE_MATCHED
};
namespace KeyBindings {
enum Type {
BINDING_DISABLED,
@ -104,7 +113,7 @@ namespace KeyBindings {
std::string ToString() const;
bool IsActive() const;
void CheckSDLEventAndDispatch(const SDL_Event *event);
InputResponse CheckSDLEventAndDispatch(const SDL_Event *event);
bool Matches(const SDL_Keysym *sym) const;
};
@ -192,7 +201,7 @@ namespace KeyBindings {
bool IsActive() const;
float GetValue() const;
void CheckSDLEventAndDispatch(const SDL_Event *event);
InputResponse CheckSDLEventAndDispatch(const SDL_Event *event);
};
struct BindingPrototype {

View File

@ -22,7 +22,7 @@ static int l_dev_set_camera_offset(lua_State *l)
{
if (!Pi::game || !Pi::game->GetWorldView())
return luaL_error(l, "Dev.SetCameraOffset only works when there is a game running");
CameraController *cam = Pi::game->GetWorldView()->GetCameraController();
CameraController *cam = Pi::game->GetWorldView()->shipView.GetCameraController();
const float x = luaL_checknumber(l, 1);
const float y = luaL_checknumber(l, 2);
const float z = luaL_checknumber(l, 3);

View File

@ -576,11 +576,11 @@ static int l_game_set_view(lua_State *l)
static int l_game_get_world_cam_type(lua_State *l)
{
switch (Pi::game->GetWorldView()->GetCamType()) {
case WorldView::CAM_INTERNAL: lua_pushstring(l, "internal"); break;
case WorldView::CAM_EXTERNAL: lua_pushstring(l, "external"); break;
case WorldView::CAM_SIDEREAL: lua_pushstring(l, "sidereal"); break;
case WorldView::CAM_FLYBY: lua_pushstring(l, "flyby"); break;
switch (Pi::game->GetWorldView()->shipView.GetCamType()) {
case ShipViewController::CAM_INTERNAL: lua_pushstring(l, "internal"); break;
case ShipViewController::CAM_EXTERNAL: lua_pushstring(l, "external"); break;
case ShipViewController::CAM_SIDEREAL: lua_pushstring(l, "sidereal"); break;
case ShipViewController::CAM_FLYBY: lua_pushstring(l, "flyby"); break;
default: Output("Unknown world view cam type\n"); break;
}
return 1;
@ -596,13 +596,13 @@ static int l_game_set_world_cam_type(lua_State *l)
{
std::string cam = luaL_checkstring(l, 1);
if (!cam.compare("internal"))
Pi::game->GetWorldView()->SetCamType(WorldView::CAM_INTERNAL);
Pi::game->GetWorldView()->shipView.SetCamType(ShipViewController::CAM_INTERNAL);
else if (!cam.compare("external"))
Pi::game->GetWorldView()->SetCamType(WorldView::CAM_EXTERNAL);
Pi::game->GetWorldView()->shipView.SetCamType(ShipViewController::CAM_EXTERNAL);
else if (!cam.compare("sidereal"))
Pi::game->GetWorldView()->SetCamType(WorldView::CAM_SIDEREAL);
Pi::game->GetWorldView()->shipView.SetCamType(ShipViewController::CAM_SIDEREAL);
else if (!cam.compare("flyby"))
Pi::game->GetWorldView()->SetCamType(WorldView::CAM_FLYBY);
Pi::game->GetWorldView()->shipView.SetCamType(ShipViewController::CAM_FLYBY);
else {
// TODO else error
}

View File

@ -81,6 +81,7 @@
#include "galaxy/StarSystem.h"
#include "gameui/Lua.h"
#include "libs.h"
#include "ship/ShipViewController.h"
#include "graphics/Renderer.h"
@ -421,6 +422,8 @@ void RegisterInputBindings()
{
PlayerShipController::RegisterInputBindings();
ShipViewController::InputBindings.RegisterBindings();
WorldView::RegisterInputBindings();
}

View File

@ -897,7 +897,7 @@ void Ship::DoThrusterSounds() const
// XXX sound logic could be part of a bigger class (ship internal sounds)
/* Ship engine noise. less loud inside */
float v_env = (Pi::game->GetWorldView()->GetCameraController()->IsExternal() ? 1.0f : 0.5f) * Sound::GetSfxVolume();
float v_env = (Pi::game->GetWorldView()->shipView.GetCameraController()->IsExternal() ? 1.0f : 0.5f) * Sound::GetSfxVolume();
static Sound::Event sndev;
float volBoth = 0.0f;
volBoth += 0.5f * fabs(GetPropulsion()->GetLinThrusterState().y);
@ -1459,7 +1459,7 @@ void Ship::SetShipType(const ShipType::Id &shipId)
Init();
onFlavourChanged.emit();
if (IsType(Object::PLAYER))
Pi::game->GetWorldView()->SetCamType(Pi::game->GetWorldView()->GetCamType());
Pi::game->GetWorldView()->shipView.SetCamType(Pi::game->GetWorldView()->shipView.GetCamType());
InitEquipSet();
LuaEvent::Queue("onShipTypeChanged", this);

View File

@ -43,7 +43,7 @@ void ShipCockpit::Render(Graphics::Renderer *renderer, const Camera *camera, con
inline void ShipCockpit::resetInternalCameraController()
{
m_icc = static_cast<InternalCameraController *>(Pi::game->GetWorldView()->GetCameraController());
m_icc = static_cast<InternalCameraController *>(Pi::game->GetWorldView()->shipView.GetCameraController());
}
void ShipCockpit::Update(const Player *player, float timeStep)

View File

@ -141,17 +141,6 @@ void PlayerShipController::StaticUpdate(const float timeStep)
int mouseMotion[2];
SDL_GetRelativeMouseState(mouseMotion + 0, mouseMotion + 1); // call to flush
// external camera mouselook
if (Pi::input.MouseButtonState(SDL_BUTTON_MIDDLE)) {
MoveableCameraController *mcc = static_cast<MoveableCameraController *>(Pi::game->GetWorldView()->GetCameraController());
const double accel = 0.01; // XXX configurable?
mcc->RotateLeft(mouseMotion[0] * accel);
mcc->RotateUp(mouseMotion[1] * accel);
// only mouselook if the player presses both mmb and rmb
mouseMotion[0] = 0;
mouseMotion[1] = 0;
}
if (m_ship->GetFlightState() == Ship::FLYING) {
switch (m_flightControlState) {
case CONTROL_FIXSPEED:

View File

@ -33,8 +33,6 @@
const double WorldView::PICK_OBJECT_RECT_SIZE = 20.0;
namespace {
static const Color s_hudTextColor(0, 255, 0, 230);
static const float ZOOM_SPEED = 1.f;
static const float WHEEL_SENSITIVITY = .05f; // Should be a variable in user settings.
static const float HUD_CROSSHAIR_SIZE = 8.0f;
static const Color white(255, 255, 255, 204);
static const Color green(0, 255, 0, 204);
@ -44,27 +42,26 @@ namespace {
WorldView::WorldView(Game *game) :
UIView(),
m_game(game)
m_game(game),
shipView(this)
{
m_camType = CAM_INTERNAL;
InitObject();
}
WorldView::WorldView(const Json &jsonObj, Game *game) :
UIView(),
m_game(game)
m_game(game),
shipView(this)
{
if (!jsonObj["world_view"].is_object()) throw SavedGameCorruptException();
Json worldViewObj = jsonObj["world_view"];
if (!worldViewObj["cam_type"].is_number_integer()) throw SavedGameCorruptException();
m_camType = CamType(worldViewObj["cam_type"]);
shipView.m_camType = worldViewObj["cam_type"];
InitObject();
m_internalCameraController->LoadFromJson(worldViewObj);
m_externalCameraController->LoadFromJson(worldViewObj);
m_siderealCameraController->LoadFromJson(worldViewObj);
m_flybyCameraController->LoadFromJson(worldViewObj);
shipView.LoadFromJson(jsonObj);
}
WorldView::InputBinding WorldView::InputBindings;
@ -85,23 +82,6 @@ void WorldView::RegisterInputBindings()
KEY_BINDING(toggleHudMode, "BindToggleHudMode", SDLK_TAB, 0)
KEY_BINDING(increaseTimeAcceleration, "BindIncreaseTimeAcceleration", SDLK_PAGEUP, 0)
KEY_BINDING(decreaseTimeAcceleration, "BindDecreaseTimeAcceleration", SDLK_PAGEDOWN, 0)
BINDING_GROUP(GENERAL_VIEW_CONTROLS)
AXIS_BINDING(viewZoom, "BindViewZoom", SDLK_EQUALS, SDLK_MINUS)
BINDING_GROUP(INTERNAL_VIEW)
KEY_BINDING(frontCamera, "BindFrontCamera", SDLK_KP_8, SDLK_UP)
KEY_BINDING(rearCamera, "BindRearCamera", SDLK_KP_2, SDLK_DOWN)
KEY_BINDING(leftCamera, "BindLeftCamera", SDLK_KP_4, SDLK_LEFT)
KEY_BINDING(rightCamera, "BindRightCamera", SDLK_KP_6, SDLK_RIGHT)
KEY_BINDING(topCamera, "BindTopCamera", SDLK_KP_9, 0)
KEY_BINDING(bottomCamera, "BindBottomCamera", SDLK_KP_3, 0)
BINDING_GROUP(EXTERNAL_VIEW)
AXIS_BINDING(cameraRoll, "BindCameraRoll", SDLK_KP_1, SDLK_KP_3)
AXIS_BINDING(cameraPitch, "BindCameraPitch", SDLK_KP_2, SDLK_KP_8)
AXIS_BINDING(cameraYaw, "BindCameraYaw", SDLK_KP_4, SDLK_KP_6)
KEY_BINDING(resetCamera, "BindResetCamera", SDLK_HOME, 0)
}
void WorldView::InitObject()
@ -165,18 +145,11 @@ void WorldView::InitObject()
m_cameraContext.Reset(new CameraContext(Graphics::GetScreenWidth(), Graphics::GetScreenHeight(), fovY, znear, zfar));
m_camera.reset(new Camera(m_cameraContext, Pi::renderer));
m_internalCameraController.reset(new InternalCameraController(m_cameraContext, Pi::player));
m_externalCameraController.reset(new ExternalCameraController(m_cameraContext, Pi::player));
m_siderealCameraController.reset(new SiderealCameraController(m_cameraContext, Pi::player));
m_flybyCameraController.reset(new FlyByCameraController(m_cameraContext, Pi::player));
SetCamType(m_camType); //set the active camera
shipView.Init();
m_onPlayerChangeTargetCon =
Pi::onPlayerChangeTarget.connect(sigc::mem_fun(this, &WorldView::OnPlayerChangeTarget));
m_onMouseWheelCon =
Pi::input.onMouseWheel.connect(sigc::mem_fun(this, &WorldView::MouseWheel));
Pi::player->GetPlayerController()->SetMouseForRearView(GetCamType() == CAM_INTERNAL && m_internalCameraController->GetMode() == InternalCameraController::MODE_REAR);
m_onToggleHudModeCon = InputBindings.toggleHudMode->onPress.connect(sigc::mem_fun(this, &WorldView::OnToggleLabels));
m_onIncTimeAccelCon = InputBindings.increaseTimeAcceleration->onPress.connect(sigc::mem_fun(this, &WorldView::OnRequestTimeAccelInc));
m_onDecTimeAccelCon = InputBindings.decreaseTimeAcceleration->onPress.connect(sigc::mem_fun(this, &WorldView::OnRequestTimeAccelDec));
@ -185,7 +158,6 @@ void WorldView::InitObject()
WorldView::~WorldView()
{
m_onPlayerChangeTargetCon.disconnect();
m_onMouseWheelCon.disconnect();
m_onToggleHudModeCon.disconnect();
m_onIncTimeAccelCon.disconnect();
m_onDecTimeAccelCon.disconnect();
@ -195,57 +167,11 @@ void WorldView::SaveToJson(Json &jsonObj)
{
Json worldViewObj = Json::object(); // Create JSON object to contain world view data.
worldViewObj["cam_type"] = int(m_camType);
m_internalCameraController->SaveToJson(worldViewObj);
m_externalCameraController->SaveToJson(worldViewObj);
m_siderealCameraController->SaveToJson(worldViewObj);
m_flybyCameraController->SaveToJson(worldViewObj);
shipView.SaveToJson(worldViewObj);
jsonObj["world_view"] = worldViewObj; // Add world view object to supplied object.
}
void WorldView::SetCamType(enum CamType c)
{
Pi::BoinkNoise();
// don't allow external cameras when docked inside space stations.
// they would clip through the station model
//if (Pi::player->GetFlightState() == Ship::DOCKED && !Pi::player->GetDockedWith()->IsGroundStation())
// c = CAM_INTERNAL;
m_camType = c;
switch (m_camType) {
case CAM_INTERNAL:
m_activeCameraController = m_internalCameraController.get();
Pi::player->OnCockpitActivated();
break;
case CAM_EXTERNAL:
m_activeCameraController = m_externalCameraController.get();
break;
case CAM_SIDEREAL:
m_activeCameraController = m_siderealCameraController.get();
break;
case CAM_FLYBY:
m_activeCameraController = m_flybyCameraController.get();
break;
}
Pi::player->GetPlayerController()->SetMouseForRearView(m_camType == CAM_INTERNAL && m_internalCameraController->GetMode() == InternalCameraController::MODE_REAR);
m_activeCameraController->Reset();
onChangeCamType.emit();
}
void WorldView::ChangeInternalCameraMode(InternalCameraController::Mode m)
{
if (m_internalCameraController->GetMode() != m)
Pi::BoinkNoise();
m_internalCameraController->SetMode(m);
Pi::player->GetPlayerController()->SetMouseForRearView(m_camType == CAM_INTERNAL && m_internalCameraController->GetMode() == InternalCameraController::MODE_REAR);
}
/* This is UI click to change flight control state (manual, speed ctrl) */
void WorldView::ChangeFlightState()
{
@ -308,9 +234,9 @@ void WorldView::Draw3D()
Body *excludeBody = nullptr;
ShipCockpit *cockpit = nullptr;
if (GetCamType() == CAM_INTERNAL) {
if (shipView.GetCamType() == ShipViewController::CAM_INTERNAL) {
excludeBody = Pi::player;
if (m_internalCameraController->GetMode() == InternalCameraController::MODE_FRONT)
if (shipView.m_internalCameraController->GetMode() == InternalCameraController::MODE_FRONT)
cockpit = Pi::player->GetCockpit();
}
m_camera->Draw(excludeBody, cockpit);
@ -414,42 +340,7 @@ void WorldView::Update()
// show state-appropriate buttons
RefreshButtonStateAndVisibility();
// XXX ugly hack checking for console here
if (!Pi::IsConsoleActive()) {
if (GetCamType() == CAM_INTERNAL) {
if (InputBindings.frontCamera->IsActive())
ChangeInternalCameraMode(InternalCameraController::MODE_FRONT);
else if (InputBindings.rearCamera->IsActive())
ChangeInternalCameraMode(InternalCameraController::MODE_REAR);
else if (InputBindings.leftCamera->IsActive())
ChangeInternalCameraMode(InternalCameraController::MODE_LEFT);
else if (InputBindings.rightCamera->IsActive())
ChangeInternalCameraMode(InternalCameraController::MODE_RIGHT);
else if (InputBindings.topCamera->IsActive())
ChangeInternalCameraMode(InternalCameraController::MODE_TOP);
else if (InputBindings.bottomCamera->IsActive())
ChangeInternalCameraMode(InternalCameraController::MODE_BOTTOM);
} else {
MoveableCameraController *cam = static_cast<MoveableCameraController *>(m_activeCameraController);
vector3d rotate = vector3d(
-InputBindings.cameraPitch->GetValue(),
InputBindings.cameraYaw->GetValue(),
InputBindings.cameraRoll->GetValue());
// Horribly abuse our knowledge of the internals of cam->RotateUp/Down.
if (rotate.x != 0.0) cam->RotateUp(frameTime * rotate.x);
if (rotate.y != 0.0) cam->RotateLeft(frameTime * rotate.y);
if (rotate.z != 0.0) cam->RollLeft(frameTime * rotate.z);
if (InputBindings.viewZoom->IsActive())
cam->ZoomEvent(-InputBindings.viewZoom->GetValue() * ZOOM_SPEED * frameTime);
if (InputBindings.resetCamera->IsActive())
cam->Reset();
cam->ZoomEventUpdate(frameTime);
}
}
m_activeCameraController->Update();
shipView.Update();
m_cameraContext->BeginFrame();
m_camera->Update();
@ -498,10 +389,12 @@ void WorldView::OnSwitchTo()
{
UIView::OnSwitchTo();
RefreshButtonStateAndVisibility();
shipView.Activated();
}
void WorldView::OnSwitchFrom()
{
shipView.Deactivated();
Pi::DrawGUI = true;
}
@ -545,13 +438,14 @@ void WorldView::OnPlayerChangeTarget()
int WorldView::GetActiveWeapon() const
{
switch (GetCamType()) {
case CAM_INTERNAL:
return m_internalCameraController->GetMode() == InternalCameraController::MODE_REAR ? 1 : 0;
using CamType = ShipViewController::CamType;
switch (shipView.GetCamType()) {
case CamType::CAM_INTERNAL:
return shipView.m_internalCameraController->GetMode() == InternalCameraController::MODE_REAR ? 1 : 0;
case CAM_EXTERNAL:
case CAM_SIDEREAL:
case CAM_FLYBY:
case CamType::CAM_EXTERNAL:
case CamType::CAM_SIDEREAL:
case CamType::CAM_FLYBY:
default:
return 0;
}
@ -585,8 +479,8 @@ void WorldView::UpdateProjectedObjects()
// calculate firing solution and relative velocity along our z axis
int laser = -1;
double projspeed = 0;
if (GetCamType() == CAM_INTERNAL) {
switch (m_internalCameraController->GetMode()) {
if (shipView.GetCamType() == ShipViewController::CAM_INTERNAL) {
switch (shipView.m_internalCameraController->GetMode()) {
case InternalCameraController::MODE_FRONT: laser = 0; break;
case InternalCameraController::MODE_REAR: laser = 1; break;
default: break;
@ -907,19 +801,6 @@ void WorldView::DrawEdgeMarker(const Indicator &marker, const Color &c)
m_edgeMarker.Draw(m_renderer, m_blendState);
}
void WorldView::MouseWheel(bool up)
{
if (this == Pi::GetView()) {
if (m_activeCameraController->IsExternal()) {
MoveableCameraController *cam = static_cast<MoveableCameraController *>(m_activeCameraController);
if (!up) // Zoom out
cam->ZoomEvent(ZOOM_SPEED * WHEEL_SENSITIVITY);
else
cam->ZoomEvent(-ZOOM_SPEED * WHEEL_SENSITIVITY);
}
}
}
NavTunnelWidget::NavTunnelWidget(WorldView *worldview, Graphics::RenderState *rs) :
Widget(),
m_worldView(worldview),
@ -935,7 +816,7 @@ void NavTunnelWidget::Draw()
if (navtarget) {
const vector3d navpos = navtarget->GetPositionRelTo(Pi::player);
const matrix3x3d &rotmat = Pi::player->GetOrient();
const vector3d eyevec = rotmat * m_worldView->m_activeCameraController->GetOrient().VectorZ();
const vector3d eyevec = rotmat * m_worldView->shipView.GetCameraController()->GetOrient().VectorZ();
if (eyevec.Dot(navpos) >= 0.0) return;
const double distToDest = Pi::player->GetPositionRelTo(navtarget).Length();
@ -1091,7 +972,7 @@ static vector3d projectToScreenSpace(const vector3d &pos, RefCountedPtr<CameraCo
// needs to run inside m_cameraContext->Begin/EndFrame();
vector3d WorldView::WorldSpaceToScreenSpace(const Body *body) const
{
if (body->IsType(Object::PLAYER) && GetCamType() == CAM_INTERNAL)
if (body->IsType(Object::PLAYER) && shipView.GetCamType() == ShipViewController::CAM_INTERNAL)
return vector3d(0, 0, 0);
const Frame *cam_frame = m_cameraContext->GetCamFrame();
vector3d pos = body->GetInterpPositionRelTo(cam_frame);
@ -1126,7 +1007,7 @@ vector3d WorldView::CameraSpaceToScreenSpace(const vector3d &pos) const
// needs to run inside m_cameraContext->Begin/EndFrame();
vector3d WorldView::GetTargetIndicatorScreenPosition(const Body *body) const
{
if (body->IsType(Object::PLAYER) && GetCamType() == CAM_INTERNAL)
if (body->IsType(Object::PLAYER) && shipView.GetCamType() == ShipViewController::CAM_INTERNAL)
return vector3d(0, 0, 0);
const Frame *cam_frame = m_cameraContext->GetCamFrame();
vector3d pos = body->GetTargetIndicatorPosition(cam_frame);
@ -1140,7 +1021,7 @@ vector3d WorldView::GetMouseDirection() const
const Frame *cam_frame = m_cameraContext->GetCamFrame();
matrix3x3d cam_rot = cam_frame->GetInterpOrient();
vector3d mouseDir = Pi::player->GetPlayerController()->GetMouseDir() * cam_rot;
if (GetCamType() == CAM_INTERNAL && m_internalCameraController->GetMode() == InternalCameraController::MODE_REAR)
if (shipView.GetCamType() == ShipViewController::CAM_INTERNAL && shipView.m_internalCameraController->GetMode() == InternalCameraController::MODE_REAR)
mouseDir = -mouseDir;
return (Pi::player->GetPhysRadius() * 1.5) * mouseDir;
}

View File

@ -9,6 +9,7 @@
#include "SpeedLines.h"
#include "UIView.h"
#include "gui/GuiWidget.h"
#include "ship/ShipViewController.h"
class Body;
class Frame;
@ -54,15 +55,10 @@ public:
static const double PICK_OBJECT_RECT_SIZE;
virtual void SaveToJson(Json &jsonObj);
virtual void HandleSDLEvent(SDL_Event &event);
enum CamType {
CAM_INTERNAL,
CAM_EXTERNAL,
CAM_SIDEREAL,
CAM_FLYBY
};
void SetCamType(enum CamType);
enum CamType GetCamType() const { return m_camType; }
CameraController *GetCameraController() const { return m_activeCameraController; }
RefCountedPtr<CameraContext> GetCameraContext() const { return m_cameraContext; }
ShipViewController shipView;
/* start deprecated */
void ChangeFlightState();
@ -71,8 +67,6 @@ public:
int GetActiveWeapon() const;
void OnClickBlastoff();
sigc::signal<void> onChangeCamType;
std::tuple<double, double, double> CalculateHeadingPitchRoll(enum PlaneType);
vector3d WorldSpaceToScreenSpace(const Body *body) const;
@ -97,8 +91,6 @@ private:
void RefreshButtonStateAndVisibility();
void ChangeInternalCameraMode(InternalCameraController::Mode m);
enum IndicatorSide {
INDICATOR_HIDDEN,
INDICATOR_ONSCREEN,
@ -135,7 +127,7 @@ private:
void OnRequestTimeAccelInc();
/// Handler for "requestTimeAccelerationDec" event
void OnRequestTimeAccelDec();
void MouseWheel(bool up);
void SelectBody(Body *, bool reselectIsDeselect);
Game *m_game;
@ -144,7 +136,6 @@ private:
Gui::Label *m_pauseText;
bool m_labelsOn;
enum CamType m_camType;
/* Only use #if WITH_DEVKEYS */
Gui::Label *m_debugInfo;
@ -158,18 +149,12 @@ private:
sigc::connection m_onHyperspaceTargetChangedCon;
sigc::connection m_onPlayerChangeTargetCon;
sigc::connection m_onChangeFlightControlStateCon;
sigc::connection m_onMouseWheelCon;
sigc::connection m_onToggleHudModeCon;
sigc::connection m_onIncTimeAccelCon;
sigc::connection m_onDecTimeAccelCon;
RefCountedPtr<CameraContext> m_cameraContext;
std::unique_ptr<Camera> m_camera;
std::unique_ptr<InternalCameraController> m_internalCameraController;
std::unique_ptr<ExternalCameraController> m_externalCameraController;
std::unique_ptr<SiderealCameraController> m_siderealCameraController;
std::unique_ptr<FlyByCameraController> m_flybyCameraController;
CameraController *m_activeCameraController; //one of the above
Indicator m_combatTargetIndicator;
Indicator m_targetLeadIndicator;
@ -186,20 +171,6 @@ private:
ActionBinding *toggleHudMode;
ActionBinding *increaseTimeAcceleration;
ActionBinding *decreaseTimeAcceleration;
AxisBinding *viewZoom;
ActionBinding *frontCamera;
ActionBinding *rearCamera;
ActionBinding *leftCamera;
ActionBinding *rightCamera;
ActionBinding *topCamera;
ActionBinding *bottomCamera;
AxisBinding *cameraRoll;
AxisBinding *cameraPitch;
AxisBinding *cameraYaw;
ActionBinding *resetCamera;
} InputBindings;
};

View File

@ -0,0 +1,16 @@
#pragma once
class WorldView;
class InteractionController {
public:
InteractionController(WorldView *v) :
parentView(v) {}
virtual void Activated() = 0;
virtual void Deactivated() = 0;
virtual void Update() = 0;
WorldView *parentView;
};

View File

@ -0,0 +1,214 @@
#include "ShipViewController.h"
#include "CameraController.h"
#include "WorldView.h"
#include "Pi.h"
#include "Player.h"
namespace {
static const float ZOOM_SPEED = 1.f;
static const float WHEEL_SENSITIVITY = .05f; // Should be a variable in user settings.
} // namespace
ShipViewController::InputBinding ShipViewController::InputBindings;
void ShipViewController::InputBinding::RegisterBindings()
{
using namespace KeyBindings;
Input::BindingPage *page = Pi::input.GetBindingPage("VIEW");
Input::BindingGroup *group;
#define BINDING_GROUP(n) group = page->GetBindingGroup(#n);
#define KEY_BINDING(n, id, k1, k2) \
n = \
Pi::input.AddActionBinding(id, group, ActionBinding(k1, k2)); \
actions.push_back(n);
#define AXIS_BINDING(n, id, k1, k2) \
n = \
Pi::input.AddAxisBinding(id, group, AxisBinding(k1, k2)); \
axes.push_back(n);
BINDING_GROUP(GENERAL_VIEW_CONTROLS)
KEY_BINDING(cycleCameraMode, "BindCycleCameraMode", SDLK_F1, 0)
AXIS_BINDING(cameraRoll, "BindCameraRoll", SDLK_KP_1, SDLK_KP_3)
AXIS_BINDING(cameraPitch, "BindCameraPitch", SDLK_KP_2, SDLK_KP_8)
AXIS_BINDING(cameraYaw, "BindCameraYaw", SDLK_KP_4, SDLK_KP_6)
AXIS_BINDING(cameraZoom, "BindViewZoom", SDLK_EQUALS, SDLK_MINUS)
KEY_BINDING(frontCamera, "BindFrontCamera", SDLK_KP_8, SDLK_UP)
KEY_BINDING(rearCamera, "BindRearCamera", SDLK_KP_2, SDLK_DOWN)
KEY_BINDING(leftCamera, "BindLeftCamera", SDLK_KP_4, SDLK_LEFT)
KEY_BINDING(rightCamera, "BindRightCamera", SDLK_KP_6, SDLK_RIGHT)
KEY_BINDING(topCamera, "BindTopCamera", SDLK_KP_9, 0)
KEY_BINDING(bottomCamera, "BindBottomCamera", SDLK_KP_3, 0)
KEY_BINDING(resetCamera, "BindResetCamera", SDLK_HOME, 0)
#undef BINDING_GROUP
#undef KEY_BINDING
#undef AXIS_BINDING
}
void ShipViewController::LoadFromJson(const Json &jsonObj)
{
m_internalCameraController->LoadFromJson(jsonObj);
m_externalCameraController->LoadFromJson(jsonObj);
m_siderealCameraController->LoadFromJson(jsonObj);
m_flybyCameraController->LoadFromJson(jsonObj);
}
void ShipViewController::SaveToJson(Json &jsonObj)
{
jsonObj["cam_type"] = int(m_camType);
m_internalCameraController->SaveToJson(jsonObj);
m_externalCameraController->SaveToJson(jsonObj);
m_siderealCameraController->SaveToJson(jsonObj);
m_flybyCameraController->SaveToJson(jsonObj);
}
void ShipViewController::Init()
{
RefCountedPtr<CameraContext> m_cameraContext = parentView->GetCameraContext();
m_internalCameraController.reset(new InternalCameraController(m_cameraContext, Pi::player));
m_externalCameraController.reset(new ExternalCameraController(m_cameraContext, Pi::player));
m_siderealCameraController.reset(new SiderealCameraController(m_cameraContext, Pi::player));
m_flybyCameraController.reset(new FlyByCameraController(m_cameraContext, Pi::player));
SetCamType(m_camType); //set the active camera
}
void ShipViewController::Activated()
{
Pi::input.PushInputFrame(&InputBindings);
m_onMouseWheelCon =
Pi::input.onMouseWheel.connect(sigc::mem_fun(this, &ShipViewController::MouseWheel));
Pi::player->GetPlayerController()->SetMouseForRearView(GetCamType() == CAM_INTERNAL && m_internalCameraController->GetMode() == InternalCameraController::MODE_REAR);
}
void ShipViewController::Deactivated()
{
Pi::input.RemoveInputFrame(&InputBindings);
m_onMouseWheelCon.disconnect();
}
void ShipViewController::SetCamType(enum CamType c)
{
Pi::BoinkNoise();
// don't allow external cameras when docked inside space stations.
// they would clip through the station model
//if (Pi::player->GetFlightState() == Ship::DOCKED && !Pi::player->GetDockedWith()->IsGroundStation())
// c = CAM_INTERNAL;
m_camType = c;
switch (m_camType) {
case CAM_INTERNAL:
m_activeCameraController = m_internalCameraController.get();
Pi::player->OnCockpitActivated();
break;
case CAM_EXTERNAL:
m_activeCameraController = m_externalCameraController.get();
break;
case CAM_SIDEREAL:
m_activeCameraController = m_siderealCameraController.get();
break;
case CAM_FLYBY:
m_activeCameraController = m_flybyCameraController.get();
break;
}
Pi::player->GetPlayerController()->SetMouseForRearView(m_camType == CAM_INTERNAL && m_internalCameraController->GetMode() == InternalCameraController::MODE_REAR);
m_activeCameraController->Reset();
onChangeCamType.emit();
}
void ShipViewController::ChangeInternalCameraMode(InternalCameraController::Mode m)
{
if (m_internalCameraController->GetMode() != m)
Pi::BoinkNoise();
m_internalCameraController->SetMode(m);
Pi::player->GetPlayerController()->SetMouseForRearView(m_camType == CAM_INTERNAL && m_internalCameraController->GetMode() == InternalCameraController::MODE_REAR);
}
void ShipViewController::Update()
{
auto *cam = static_cast<MoveableCameraController *>(m_activeCameraController);
auto frameTime = Pi::GetFrameTime();
vector3d rotate = vector3d(
-InputBindings.cameraPitch->GetValue(),
InputBindings.cameraYaw->GetValue(),
InputBindings.cameraRoll->GetValue());
// Horribly abuse our knowledge of the internals of cam->RotateUp/Down.
if (rotate.x != 0.0) cam->RotateUp(frameTime * rotate.x);
if (rotate.y != 0.0) cam->RotateLeft(frameTime * rotate.y);
if (rotate.z != 0.0) cam->RollLeft(frameTime * rotate.z);
// XXX ugly hack checking for console here
if (!Pi::IsConsoleActive()) {
if (GetCamType() == CAM_INTERNAL) {
if (InputBindings.frontCamera->IsActive())
ChangeInternalCameraMode(InternalCameraController::MODE_FRONT);
else if (InputBindings.rearCamera->IsActive())
ChangeInternalCameraMode(InternalCameraController::MODE_REAR);
else if (InputBindings.leftCamera->IsActive())
ChangeInternalCameraMode(InternalCameraController::MODE_LEFT);
else if (InputBindings.rightCamera->IsActive())
ChangeInternalCameraMode(InternalCameraController::MODE_RIGHT);
else if (InputBindings.topCamera->IsActive())
ChangeInternalCameraMode(InternalCameraController::MODE_TOP);
else if (InputBindings.bottomCamera->IsActive())
ChangeInternalCameraMode(InternalCameraController::MODE_BOTTOM);
} else {
MoveableCameraController *cam = static_cast<MoveableCameraController *>(m_activeCameraController);
vector3d rotate = vector3d(
-InputBindings.cameraPitch->GetValue(),
InputBindings.cameraYaw->GetValue(),
InputBindings.cameraRoll->GetValue());
// Horribly abuse our knowledge of the internals of cam->RotateUp/Down.
if (rotate.x != 0.0) cam->RotateUp(frameTime * rotate.x);
if (rotate.y != 0.0) cam->RotateLeft(frameTime * rotate.y);
if (rotate.z != 0.0) cam->RollLeft(frameTime * rotate.z);
if (InputBindings.cameraZoom->IsActive())
cam->ZoomEvent(-InputBindings.cameraZoom->GetValue() * ZOOM_SPEED * frameTime);
if (InputBindings.resetCamera->IsActive())
cam->Reset();
cam->ZoomEventUpdate(frameTime);
}
}
int mouseMotion[2];
Pi::input.GetMouseMotion(mouseMotion);
// external camera mouselook
if (Pi::input.MouseButtonState(SDL_BUTTON_MIDDLE)) {
const double accel = 0.01; // XXX configurable?
cam->RotateLeft(mouseMotion[0] * accel);
cam->RotateUp(mouseMotion[1] * accel);
}
m_activeCameraController->Update();
}
void ShipViewController::MouseWheel(bool up)
{
if (m_activeCameraController->IsExternal()) {
MoveableCameraController *cam = static_cast<MoveableCameraController *>(m_activeCameraController);
if (!up) // Zoom out
cam->ZoomEvent(ZOOM_SPEED * WHEEL_SENSITIVITY);
else
cam->ZoomEvent(-ZOOM_SPEED * WHEEL_SENSITIVITY);
}
}

View File

@ -0,0 +1,74 @@
#pragma once
#include "CameraController.h"
#include "Input.h"
#include "InteractionController.h"
#include "KeyBindings.h"
#include "utils.h"
class ShipViewController : public InteractionController {
public:
ShipViewController(WorldView *v) :
InteractionController(v),
m_camType(CAM_INTERNAL) {}
virtual void Update();
virtual void Activated();
virtual void Deactivated();
enum CamType {
CAM_INTERNAL,
CAM_EXTERNAL,
CAM_SIDEREAL,
CAM_FLYBY
};
void SetCamType(enum CamType);
enum CamType GetCamType() const { return m_camType; }
CameraController *GetCameraController() const { return m_activeCameraController; }
sigc::signal<void> onChangeCamType;
private:
friend class WorldView;
void ChangeInternalCameraMode(InternalCameraController::Mode m);
enum CamType m_camType;
sigc::connection m_onMouseWheelCon;
std::unique_ptr<InternalCameraController> m_internalCameraController;
std::unique_ptr<ExternalCameraController> m_externalCameraController;
std::unique_ptr<SiderealCameraController> m_siderealCameraController;
std::unique_ptr<FlyByCameraController> m_flybyCameraController;
CameraController *m_activeCameraController; //one of the above
void MouseWheel(bool up);
public:
void Init();
void LoadFromJson(const Json &jsonObj);
void SaveToJson(Json &jsonObj);
static struct InputBinding : public Input::InputFrame {
using Action = KeyBindings::ActionBinding;
using Axis = KeyBindings::AxisBinding;
Axis *cameraYaw;
Axis *cameraPitch;
Axis *cameraRoll;
Axis *cameraZoom;
Action *frontCamera;
Action *rearCamera;
Action *leftCamera;
Action *rightCamera;
Action *topCamera;
Action *bottomCamera;
Action *cycleCameraMode;
Action *resetCamera;
virtual void RegisterBindings();
} InputBindings;
};