[Add] First implementation of force feedback (haptic feedback for

joysticks).
This commit is contained in:
Allanis 2014-05-29 16:36:57 +01:00
parent b36afb79da
commit 99dc05be86
2 changed files with 148 additions and 48 deletions

View File

@ -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) {
#if SDL_VERSION_ATLEAST(1,3,0)
if(haptic != NULL) {
SDL_HapticClose(haptic);
haptic = NULL;
}
#endif
if(joystick != NULL) {
SDL_JoystickClose(joystick);
joystick = NULL;
}
}

View File

@ -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.
*/