From 99dc05be864ba450f81a7b303c1f0c7fa5035ba5 Mon Sep 17 00:00:00 2001 From: Allanis <allanis@saracraft.net> Date: Thu, 29 May 2014 16:36:57 +0100 Subject: [PATCH] [Add] First implementation of force feedback (haptic feedback for joysticks). --- src/joystick.c | 82 +++++++++++++++++++++++++++++------ src/spfx.c | 114 ++++++++++++++++++++++++++++++++++--------------- 2 files changed, 148 insertions(+), 48 deletions(-) diff --git a/src/joystick.c b/src/joystick.c index 60db0e5..e86e57e 100644 --- a/src/joystick.c +++ b/src/joystick.c @@ -4,18 +4,26 @@ * @brief Handles joystick initialization. */ -#include <SDL/SDL.h> #include <string.h> +#include <SDL/SDL.h> +#include <SDL/SDL_joystick.h> +#if SDL_VERSION_ATLEAST(1,3,0) +#include "SDL/SDL_haptic.h" +#endif #include "lephisto.h" #include "log.h" #include "joystick.h" static SDL_Joystick* joystick = NULL; /**< Current joystick in use. */ +#if SDL_VERSION_ATLEAST(1,3,0) +static int has_haptick = 0; /**< Does the player have haptic? */ +SDL_Haptic* haptic = NULL; /**< Current haptic to use, externed in spfx.c */ +unsigned int haptic_query = 0; /**< Properties of the haptic device. */ +#endif +static void joystick_initHaptic(void); /** - * @fn int joystick_get(char* namjoystick) - * * @brief Get the joystick index by name. * @param namjoystick Looks for given string in the joystick name. * @return The index if found, defaults to 0 if it isn't found. @@ -32,21 +40,22 @@ int joystick_get(char* namjoystick) { } /** - * @fn int joystick_use(int indjoystick) - * * @brief Make the game use a joystick by index. * @param indjoystick Index of the joystick to use. * @return 0 on success. */ int joystick_use(int indjoystick) { + /* Check to see if it exists. */ if(indjoystick < 0 || indjoystick >= SDL_NumJoysticks()) { WARN("Joystick of index number %d does not exist. Switching to default (0)", indjoystick); indjoystick = 0; } - if(joystick) - /* Might as well close it if it is open already. */ + /* Close if already open. */ + if(joystick != NULL) { SDL_JoystickClose(joystick); + joystick = NULL; + } /* Start using the joystick. */ joystick = SDL_JoystickOpen(indjoystick); if(joystick == NULL) { @@ -55,16 +64,51 @@ int joystick_use(int indjoystick) { return -1; } LOG("Using joystick %d - %s", indjoystick, SDL_JoystickName(indjoystick)); - DEBUG("\t\tWith %d axes, %d buttons, %d balls, and %d hats\n", + DEBUG(" With %d axes, %d buttons, %d balls, and %d hats\n", SDL_JoystickNumAxes(joystick), SDL_JoystickNumButtons(joystick), SDL_JoystickNumBalls(joystick), SDL_JoystickNumHats(joystick)); + /* Initialize the haptic if possible. */ + joystick_initHaptic(); + + /* For style purposes. */ + DEBUG(); + return 0; } /** - * @fn int joystick_init(void) - * + * @brief Initializes force feedback for the loaded device. + */ +static void joystick_initHaptic(void) { +#if SDL_VERSION_ATLEAST(1,3,0) + if(has_haptic && SDL_JoystickIsHaptic(joystick)) { + /* Close haptic if already open. */ + if(haptic != NULL) { + SDL_HapticClose(haptic); + haptic = NULL; + } + + /* Try to create haptic device. */ + haptic = SDL_HapticOpenFromJoystick(joystick); + if(haptic == NULL) { + WARN("Unable to initialize force feedback."); + return; + } + + /* Check to see what it supports. */ + haptic_query = SDL_HapticQuery(haptic); + if(!(haptic_query & SDL_HAPTIC_SINE)) { + SDL_HapticClose(haptic); + haptic = NULL; + return; + } + DEBUG(" force feedback enabled"); + } +#endif +} + +/** * @brief Initializes the joystick subsystem. * @return 0 on success. */ @@ -77,6 +121,11 @@ int joystick_init(void) { return -1; } +#if SDL_VERSION_ATLEAST(1,3,0) + if(SDL_InitSubSystem(SDL_INIT_HAPTIC) == 0) + has_haptic = 1; +#endif + /* Figure out how many joysticks there are. */ numjoysticks = SDL_NumJoysticks(); LOG("%d joystick%s detected", numjoysticks, (numjoysticks==1)?"":"s"); @@ -90,11 +139,18 @@ int joystick_init(void) { } /** - * @fn void joystick_exit(void) - * * @brief Exit the joystick subsystem. */ void joystick_exit(void) { - SDL_JoystickClose(joystick); +#if SDL_VERSION_ATLEAST(1,3,0) + if(haptic != NULL) { + SDL_HapticClose(haptic); + haptic = NULL; + } +#endif + if(joystick != NULL) { + SDL_JoystickClose(joystick); + joystick = NULL; + } } diff --git a/src/spfx.c b/src/spfx.c index 441d0d9..a1afa08 100644 --- a/src/spfx.c +++ b/src/spfx.c @@ -4,8 +4,11 @@ * @brief Handle the specific effects. */ -#include <SDL.h> - +#include <inttypes.h> +#include <SDL/SDL.h> +#if SDL_VERSION_ATLEAST(1,3,0) +#include "SDL_haptic.h" +#endif #include "lephisto.h" #include "log.h" #include "pilot.h" @@ -27,6 +30,13 @@ Vec2 shake_pos = { .x = 0., .y = 0. }; /**< Current shake pos. Used in nebula static Vec2 shake_vel = { .x = 0., .y = 0. }; /**< Current shake vel. */ static int shake_off = 1; /**< 1 if shake is not active. */ +#if SDL_VERSION_ATLEAST(1,3,0) +extern SDL_Haptic* haptic; /**< From joystick.c */ +extern unsigned int haptic_query; /**< From joystick.c */ +static int haptic_rumble = -1; /**< Haptic rumble effect ID. */ +static SDL_HapticEffect haptic_rumbleEffect; /**< Haptic rumble effect. */ +#endif + /** * @struct SPFX_Base * @@ -67,14 +77,16 @@ static SPFX* spfx_stack_back = NULL; /**< Back special effect layer. */ static int spfx_nstack_back = 0; /**< Number of special effects in the back. */ static int spfx_mstack_back = 0; /**< Memory allocated for special effects in back. */ +/* General. */ static int spfx_base_load(char* name, int ttl, int anim, char* gfx, int sx, int sy); static void spfx_base_free(SPFX_Base* effect); static void spfx_destroy(SPFX* layer, int* nlayer, int spfx); static void spfx_update_layer(SPFX* layer, int* nlayer, const double dt); +/* Haptic. */ +static int spfx_hapticInit(void); +static void spfx_hapticRumble(void); /** - * @fn static int spfx_base_load(char* name, int ttl, int anim, char* gfx, int sx, int sy) - * * @brief Load an SPFX_Base into the stack based on some params. * @param name Name of the spfx. * @param ttl Time to live of the spfx. @@ -102,8 +114,6 @@ static int spfx_base_load(char* name, int ttl, int anim, char* gfx, int sx, int } /** - * @fn static void spfx_base_free(SPFX_Base* effect) - * * @brief Free an SPFX_Base. * @param effect SPFX_Base to free. */ @@ -113,8 +123,6 @@ static void spfx_base_free(SPFX_Base* effect) { } /** - * @fn int spfx_get(char* name) - * * @brief Get the id of an spfx based on name. * @param name Name to match. * @return ID of the special effect or -1 on error. @@ -129,8 +137,6 @@ int spfx_get(char* name) { } /** - * @fn int spfx_load(void) - * * @brief Load the spfx stack. * @return 0 on success. * @@ -152,12 +158,13 @@ int spfx_load(void) { /* Plasma hits. */ spfx_base_load("PlaS", 400, 400, "plas.png", 6, 5); spfx_base_load("PlaM", 450, 450, "plam.png", 6, 5); + + spfx_hapticInit(); + return 0; } /** - * @fn void spfx_free(void) - * * @brief Free the spfx stack. */ void spfx_free(void) { @@ -180,11 +187,6 @@ void spfx_free(void) { } /** - * @fn void spfx_add(int effect, - * const double px, const double py, - * const double vx, const double vy, - * const int layer) { - * * @brief Create a new special effect. * @param effect Base effect identifier to use. * @param px X position of the effect. @@ -242,8 +244,6 @@ void spfx_add(int effect, } /** - * @fn void spfx_clear(void) - * * @brief Clear all the currently running effects. */ void spfx_clear(void) { @@ -264,8 +264,6 @@ void spfx_clear(void) { } /** - * @fn static void spfx_destroy(SPFX* layer, int* nlayer, int spfx) - * * @brief Destroys an active spfx. * @param layer Layer the spfx is on. * @param nlayer Pointer to the number of elements in the layer. @@ -277,8 +275,6 @@ static void spfx_destroy(SPFX* layer, int* nlayer, int spfx) { } /** - * @fn void spfx_update(const double dt) - * * @brief Update all the spfx. * @param dt Current delta tick. */ @@ -288,8 +284,6 @@ void spfx_update(const double dt) { } /** - * @fn static void spfx_update_layer(SPFX* layer, int* nlayer, const double dt) - * * @brief Update an individual spfx. * @param layer Layer the spfx is on. * @param nlayer Pointer to the assosiated nlayer. @@ -313,8 +307,6 @@ static void spfx_update_layer(SPFX* layer, int* nlayer, const double dt) { } /** - * @fn void spfx_start(double dt) - * * @brief Prepare the rendering for the special effects. * * Should be called at the beginning of the rendering loop. @@ -360,24 +352,78 @@ void spfx_start(const double dt) { } /** - * @fn void spfx_shake(double mod) - * * @brief Increases the current rumble level. * * Rumble will decay over time. * @param mod Modifier to increase levely by. */ void spfx_shake(double mod) { + /* Add the modifier. */ shake_rad += mod; if(shake_rad > SHAKE_MAX) shake_rad = SHAKE_MAX; - shake_off = 0; - vect_pset(&shake_vel, SHAKE_VEL_MOD*shake_rad, RNGF() * 2. * M_PI); + + /* Rumble if it wasn't rumbling before. */ + if(shake_off == 1) + spfx_hapticRumble(); + + /* Notify that rumble is active. */ + shake_off = 0; +} + +/** + * @brief Initializes the rumble effect. + * @return 0 on success. + */ +static int spfx_hapticInit(void) { +#if SDL_VERSION_ATLEAST(1,3,0) + SDL_HapticEffect* efx; + + efx = &haptic_rumbleEffect; + memset(efx, 0, sizeof(SDL_HapticEffect)); + efx->type = SDL_HAPTIC_SINE; + efx->periodic.direction.type = SDL_HAPTIC_POLAR; + efx->periodic.length = 1000; + efx->periodic.period = 200; + efx->periodic.magnitude = 0x4000; + efx->periodic.fade_length = 1000; + efx->periodic.fade_level = 0; + + haptic_rumble = SDL_HapticNewEffect(haptic, efx); + if(haptic_rumble < 0) { + WARN("Unable to upload haptic effect: %s.", SDL_GetError()); + return -1; + } +#endif + return 0; +} + +/** + * @brief Run a rumble effect. + */ +static void spfx_hapticRumble(void) { +#if SDL_VERSION_ATLEAST(1,3,0) + SDL_HapticEffect* efx; + + if(haptic_rumble >= 0) { + /* Stop the effect if it was playing. */ + SDL_HapticStopEffect(haptic, haptic_rumble); + + /* Update the effect. */ + efx = &haptic_rumbleEffect; + efx->periodic.length = (uint32_t)(1000.*shake_rad / SHAKE_DECAY); + if(SDL_HapticUpdateEffect(haptic, haptic_rumble, &haptic_rumbleEffect) < 0) { + WARN("Failed to update effect: %s.", SDL_GetError()); + return; + } + + /* Run the new effect. */ + SDL_HapticRunEffect(haptic, haptic_rumble, 1); + } +#endif } /** - * @fn void spfx_cinematic(void) - * * @brief Set the cinematic mode. * * Should be run at the end of the render loop if needed. @@ -403,8 +449,6 @@ void spfx_cinematic(void) { } /** - * @fn void spfx_render(const int layer) - * * @brief Render the entire spfx layer. * @param layer Layer to render. */