2 * $Logfile: /Freespace2/code/Io/Joy_ff.cpp $
7 * Code for joystick Force Feedback.
10 * Revision 1.2 2002/05/07 03:16:46 theoddone33
11 * The Great Newline Fix
13 * Revision 1.1.1.1 2002/05/03 03:28:09 root
17 * 4 1/06/99 2:24p Dave
18 * Stubs and release build fixes.
20 * 3 10/09/98 2:57p Dave
21 * Starting splitting up OS stuff.
23 * 2 10/07/98 10:53a Dave
26 * 1 10/07/98 10:49a Dave
28 * 13 5/20/98 5:47p Sandeep
30 * 12 5/20/98 3:52p Allender
31 * fixed compiler warnings
33 * 11 5/20/98 11:06a Hoffoss
36 * 10 5/20/98 10:57a Hoffoss
37 * Made directional hit effect FF a toggle in launcher, and made calibrate
38 * not freeze launcher until process ends.
40 * 9 5/18/98 4:53p Hoffoss
41 * Some force feedback tweaks and pilot initializations there should have
42 * been happening, but weren't, and not are!
44 * 8 5/17/98 6:28p Hoffoss
47 * 7 5/17/98 5:45p Hoffoss
48 * Adjusted ship handling Force Feedback to not be as strong. Poor
49 * Sandeep's wrist can't handle it. :)
51 * 6 5/08/98 5:31p Hoffoss
52 * Isolated the joystick force feedback code more from dependence on other
55 * 5 5/08/98 12:27p Hoffoss
56 * Polished up the error handling and such a bit.
58 * 4 5/08/98 9:54a Hoffoss
59 * Generalized some of the effect stuff to try some swapping of effects.
60 * (going to start next, but checking this in in case I want to revert
61 * back to this state).
63 * 3 5/07/98 12:24a Hoffoss
64 * Finished up sidewinder force feedback support.
66 * 2 5/04/98 11:08p Hoffoss
67 * Expanded on Force Feedback code, and moved it all into Joy_ff.cpp.
68 * Updated references everywhere to it.
75 #include "osregistry.h"
81 DIPERIODIC periodic_struct;
82 DIENVELOPE envelope_struct;
86 } di_periodic_effect_struct;
88 int joy_ff_handling_scaler;
89 int Joy_ff_enabled = 0;
90 int Joy_ff_directional_hit_effect_enabled = 1;
93 LPDIRECTINPUTDEVICE2 pDiDevice;
95 int joy_ff_create_effects();
96 void joy_ff_stop_effects();
98 LPDIRECTINPUTEFFECT pHitEffect1;
99 LPDIRECTINPUTEFFECT pHitEffect2;
100 LPDIRECTINPUTEFFECT pAfterburn1;
101 LPDIRECTINPUTEFFECT pAfterburn2;
102 LPDIRECTINPUTEFFECT pShootEffect;
103 LPDIRECTINPUTEFFECT pSecShootEffect;
104 LPDIRECTINPUTEFFECT pSpring;
105 LPDIRECTINPUTEFFECT pDock;
106 LPDIRECTINPUTEFFECT pDeathroll1;
107 LPDIRECTINPUTEFFECT pDeathroll2;
108 LPDIRECTINPUTEFFECT pExplode;
110 di_periodic_effect_struct Struct_deathroll1;
111 di_periodic_effect_struct Struct_deathroll2;
112 di_periodic_effect_struct Struct_explode;
113 di_periodic_effect_struct Struct_afterburn1;
114 di_periodic_effect_struct Struct_afterburn2;
115 di_periodic_effect_struct Struct_dock;
117 di_condition_effect_struct Spring_cond_effect;
119 void joy_ff_afterburn_off();
120 void init_periodic_effect_struct(di_periodic_effect_struct *effect, int type, int dur, int per, int ang = 0, int mag = 10000, int att = 0, int fade = 0);
126 Joy_ff_enabled = 0; // Assume no force feedback
127 ff_enabled = os_config_read_uint(NULL, "EnableJoystickFF", 0);
131 TCHAR g_szOutput[256];
132 TCHAR szCodeString[256];
134 Joy_ff_directional_hit_effect_enabled = os_config_read_uint(NULL, "EnableHitEffect", 1);
136 hr = SWFF_OpenDefaultFFJoystick((HWND) os_get_window(), &pDi, &pDiDevice);
138 nprintf(("Sandeep", "No FF On Joystick, not using FF\n"));
139 SWFF_ErrorCodeToString(hr, &szCodeString[0]);
140 wsprintf(g_szOutput, "Make sure JOYSTICKID1 has Force Feedback\n"
141 "Code = %lx: %s\n", hr, szCodeString);
143 nprintf(("Sandeep", g_szOutput));
148 nprintf(("Sandeep", "There is FF on this joystick! (The peasants cheer)\n"));
149 SWFF_DestroyAllEffects(pDiDevice);
150 if (joy_ff_create_effects())
158 void joy_ff_shutdown()
160 if (Joy_ff_enabled) {
162 joy_ff_stop_effects();
163 SWFF_DestroyAllEffects(pDiDevice);
164 pDiDevice->Unacquire();
165 pDiDevice->Release();
170 HRESULT joy_ff_handle_error(HRESULT hr, char *eff_name = NULL)
173 TCHAR szCodeString[256];
175 SWFF_ErrorCodeToString(hr, szCodeString);
177 nprintf(("Joystick", "FF: Error for %s: %s\n", eff_name, szCodeString));
179 nprintf(("Joystick", "FF: Error: %s\n", szCodeString));
185 int joy_ff_create_std_periodic(LPDIRECTINPUTEFFECT *eff, int type, int dur, int per, int ang = 0, int mag = 10000, int att = 0, int fade = 0)
187 joy_ff_handle_error(SWFF_CreatePeriodicEffect(pDiDevice, eff, type, dur, per, ang, mag, 0, att, 0, fade, 0, -1));
194 void joy_ff_start_effect(LPDIRECTINPUTEFFECT eff, char *name)
198 nprintf(("Joystick", "FF: Starting effect %s\n", name));
199 hr = joy_ff_handle_error(eff->Start(1, 0));
200 if (hr == DIERR_INPUTLOST) {
202 joy_ff_handle_error(eff->Start(1, 0));
206 int joy_ff_create_effects()
208 joy_ff_handle_error(SWFF_CreateConstantForceEffect(
215 10000, // Attack level
221 nprintf(("Joystick", "FF: Hit effect 1 loaded\n"));
222 else { // bail out early if we can't even load this (because rest probably won't either and failing is slow.
223 nprintf(("Joystick", "FF: Hit effect 1 failed to load\n"));
227 joy_ff_create_std_periodic(
234 100000, // Attack time
235 100000); // Fade time
238 nprintf(("Joystick", "FF: Hit effect 2 loaded\n"));
240 nprintf(("Joystick", "FF: Hit effect 2 failed to load\n"));
242 joy_ff_handle_error(SWFF_CreatePeriodicEffect(
254 0, -1), "ShootEffect");
257 nprintf(("Joystick", "FF: Fire primary effect loaded\n"));
259 nprintf(("Joystick", "FF: Fire primary effect failed to load\n"));
261 joy_ff_handle_error(SWFF_CreateConstantForceEffect(
267 50000, // Attack time
268 10000, // Attack level
271 -1), "SecShootEffect");
274 nprintf(("Joystick", "FF: Fire Secondary effect loaded\n"));
276 nprintf(("Joystick", "FF: Fire Secondary effect failed to load\n"));
278 joy_ff_handle_error(SWFF_CreateConditionEffectStruct(&Spring_cond_effect,
283 100, // X Coefficient
285 100, // Y Coefficient
287 -1), // button play mask
291 nprintf(("Joystick", "FF: Spring effect loaded\n"));
292 joy_ff_start_effect(pSpring, "Spring");
295 nprintf(("Joystick", "FF: Spring effect failed to load\n"));
297 init_periodic_effect_struct(
300 INFINITE, // Duration
305 pDiDevice->CreateEffect(Struct_afterburn1.guid, &Struct_afterburn1.effect, &pAfterburn1, NULL);
307 nprintf(("Joystick", "FF: Afterburner effect 1 loaded\n"));
309 nprintf(("Joystick", "FF: Afterburner effect 1 failed to load\n"));
311 init_periodic_effect_struct(
314 INFINITE, // Duration
319 pDiDevice->CreateEffect(Struct_afterburn2.guid, &Struct_afterburn2.effect, &pAfterburn2, NULL);
321 nprintf(("Joystick", "FF: Afterburner effect 2 loaded\n"));
323 nprintf(("Joystick", "FF: Afterburner effect 2 failed to load\n"));
325 init_periodic_effect_struct(
333 pDiDevice->CreateEffect(Struct_dock.guid, &Struct_dock.effect, &pDock, NULL);
335 nprintf(("Joystick", "FF: Dock effect loaded\n"));
337 nprintf(("Joystick", "FF: Dock effect failed to load\n"));
339 init_periodic_effect_struct(
347 500000); // Fade time
349 pDiDevice->CreateEffect(Struct_explode.guid, &Struct_explode.effect, &pExplode, NULL);
351 nprintf(("Joystick", "FF: Explosion effect loaded\n"));
353 nprintf(("Joystick", "FF: Explosion effect failed to load\n"));
355 init_periodic_effect_struct(
358 INFINITE, // Duration
362 2000000, // Attack time
365 pDiDevice->CreateEffect(Struct_deathroll1.guid, &Struct_deathroll1.effect, &pDeathroll1, NULL);
367 nprintf(("Joystick", "FF: Deathroll effect 1 loaded\n"));
369 nprintf(("Joystick", "FF: Deathroll effect 1 failed to load\n"));
371 init_periodic_effect_struct(
374 INFINITE, // Duration
378 2000000, // Attack time
381 pDiDevice->CreateEffect(Struct_deathroll2.guid, &Struct_deathroll2.effect, &pDeathroll2, NULL);
383 nprintf(("Joystick", "FF: Deathroll effect 2 loaded\n"));
385 nprintf(("Joystick", "FF: Deathroll effect 2 failed to load\n"));
390 void joy_ff_stop_effects()
392 joy_ff_afterburn_off();
395 void joy_ff_mission_init(vector v)
398 // joy_ff_handling_scaler = (int) ((vm_vec_mag(&v) - 1.3f) * 10.5f);
399 joy_ff_handling_scaler = (int) ((vm_vec_mag(&v) + 1.3f) * 5.0f);
400 // joy_ff_handling_scaler = (int) (vm_vec_mag(&v) * 7.5f);
403 void joy_ff_adjust_handling(int speed)
407 v = speed * joy_ff_handling_scaler * 2 / 3;
408 // v += joy_ff_handling_scaler * joy_ff_handling_scaler * 6 / 7 + 250;
409 v += joy_ff_handling_scaler * 45 - 500;
414 if (Spring_cond_effect.DIConditionStruct[0].lPositiveCoefficient != v) {
417 Spring_cond_effect.DIConditionStruct[0].lPositiveCoefficient = v;
418 Spring_cond_effect.DIConditionStruct[0].lNegativeCoefficient = v;
419 Spring_cond_effect.DIConditionStruct[1].lPositiveCoefficient = v;
420 Spring_cond_effect.DIConditionStruct[1].lNegativeCoefficient = v;
421 nprintf(("Joystick", "FF: New handling force = %d\n", v));
423 hr = joy_ff_handle_error(pSpring->SetParameters(&Spring_cond_effect.DIEffectStruct, DIEP_TYPESPECIFICPARAMS), "Spring");
424 if (hr == DIERR_INPUTLOST) {
426 joy_ff_handle_error(pSpring->SetParameters(&Spring_cond_effect.DIEffectStruct, DIEP_TYPESPECIFICPARAMS), "Spring");
432 void joy_ff_change_effect(di_periodic_effect_struct *s, LPDIRECTINPUTEFFECT eff, int gain = -1, int dur = 0, int flags = -1)
436 if ((gain >= 0) && ((int) s->effect.dwGain != gain)) {
437 s->effect.dwGain = gain;
438 nprintf(("Joystick", "FF: Gain reset to %d\n", gain));
442 if (dur && ((int) s->effect.dwDuration != dur)) {
443 s->effect.dwDuration = dur;
444 nprintf(("Joystick", "FF: Duration reset to %d\n", dur));
449 flags = DIEP_DURATION | DIEP_SAMPLEPERIOD | DIEP_GAIN | DIEP_DIRECTION | DIEP_ENVELOPE | DIEP_TYPESPECIFICPARAMS;
450 nprintf(("Joystick", "FF: Doing full reload of effect\n"));
453 if (flags != (DIEP_DURATION | DIEP_GAIN))
459 nprintf(("Joystick", "FF: Swapping in a new effect\n"));
460 hr = joy_ff_handle_error(eff->SetParameters(&s->effect, flags));
461 if (hr == DIERR_INPUTLOST) {
463 joy_ff_handle_error(eff->SetParameters(&s->effect, flags));
467 nprintf(("Joystick", "FF: Swap effect requested, but nothing changed\n"));
470 int joy_ff_effect_playing(LPDIRECTINPUTEFFECT eff)
474 eff->GetEffectStatus(&flags);
475 return (flags & DIEGES_PLAYING);
482 if (joy_ff_handle_error(SWFF_SetGain(pDock, 10000), "Dock") == DIERR_INPUTLOST) {
484 joy_ff_handle_error(SWFF_SetGain(pDock, 10000), "Dock");
487 joy_ff_start_effect(pDock, "Dock");
491 void joy_ff_play_reload_effect()
495 if (joy_ff_handle_error(SWFF_SetGain(pDock, 5000), "Dock (Reload)") == DIERR_INPUTLOST) {
497 joy_ff_handle_error(SWFF_SetGain(pDock, 5000), "Dock (Reload)");
500 joy_ff_start_effect(pDock, "Dock (Reload)");
504 int Joy_ff_afterburning = 0;
506 void joy_ff_afterburn_on()
510 joy_ff_change_effect(&Struct_afterburn1, pAfterburn1, 5000, INFINITE, DIEP_DURATION | DIEP_GAIN);
511 joy_ff_start_effect(pAfterburn1, "Afterburn1");
516 joy_ff_change_effect(&Struct_afterburn2, pAfterburn2, 5000, INFINITE, DIEP_DURATION | DIEP_GAIN);
517 joy_ff_start_effect(pAfterburn2, "Afterburn2");
520 nprintf(("Joystick", "FF: Afterburn started\n"));
521 Joy_ff_afterburning = 1;
524 void joy_ff_afterburn_off()
526 if (!Joy_ff_afterburning)
537 Joy_ff_afterburning = 0;
538 nprintf(("Joystick", "FF: Afterburn stopped\n"));
541 void joy_ff_deathroll()
545 joy_ff_start_effect(pDeathroll1, "Deathroll1");
550 joy_ff_start_effect(pDeathroll2, "Deathroll2");
554 void joy_ff_explode()
564 joy_ff_start_effect(pExplode, "Explode");
568 void joy_ff_fly_by(int mag)
572 if (Joy_ff_afterburning)
575 gain = mag * 120 + 4000;
581 joy_ff_change_effect(&Struct_afterburn1, pAfterburn1, gain, 6000 * mag + 400000, DIEP_DURATION | DIEP_GAIN);
582 joy_ff_start_effect(pAfterburn1, "Afterburn1 (Fly by)");
587 joy_ff_change_effect(&Struct_afterburn2, pAfterburn2, gain, 6000 * mag + 400000, DIEP_DURATION | DIEP_GAIN);
588 joy_ff_start_effect(pAfterburn2, "Afterburn2 (Fly by)");
592 void joy_reacquire_ff()
597 nprintf(("Joystick", "FF: Reacquiring\n"));
598 pDiDevice->Acquire();
599 joy_ff_start_effect(pSpring, "Spring");
602 void joy_unacquire_ff()
606 void joy_ff_play_dir_effect(float x, float y)
614 if (!pHitEffect1 || !pHitEffect2)
617 if (joy_ff_effect_playing(pHitEffect1) || joy_ff_effect_playing(pHitEffect2)) {
618 nprintf(("Joystick", "FF: HitEffect already playing. Skipping\n"));
622 if (Joy_ff_directional_hit_effect_enabled) {
625 else if (x < -8000.0f)
630 else if (y < -8000.0f)
633 imag = (int) fl_sqrt(x * x + y * y);
637 degs = (float)atan2(x, y);
638 idegs = (int) (degs * 18000.0f / PI) + 90;
642 while (idegs >= 36000)
645 if (joy_ff_handle_error(SWFF_SetDirectionGain(pHitEffect1, idegs, imag), "HitEffect1") == DIERR_INPUTLOST) {
647 joy_ff_handle_error(SWFF_SetDirectionGain(pHitEffect1, idegs, imag), "HitEffect1");
654 if (joy_ff_handle_error(SWFF_SetDirectionGain(pHitEffect2, idegs, imag), "HitEffect2") == DIERR_INPUTLOST) {
656 joy_ff_handle_error(SWFF_SetDirectionGain(pHitEffect2, idegs, imag), "HitEffect2");
660 joy_ff_start_effect(pHitEffect1, "HitEffect1");
661 joy_ff_start_effect(pHitEffect2, "HitEffect2");
662 //nprintf(("Joystick", "FF: Dir: %d, Mag = %d\n", idegs, imag));
665 void joy_ff_play_vector_effect(vector *v, float scaler)
670 nprintf(("Joystick", "FF: vec = { %f, %f, %f } s = %f\n", v->x, v->y, v->z, scaler));
671 vm_vec_copy_scale(&vf, v, scaler);
676 y = -vm_vec_mag(&vf);
680 joy_ff_play_dir_effect(-x, -y);
683 static int secondary_ff_level = 0;
685 void joy_ff_play_secondary_shoot(int gain)
690 if (!pSecShootEffect)
693 gain = gain * 100 + 2500;
697 if (gain != secondary_ff_level) {
698 if (joy_ff_handle_error(SWFF_SetGain(pSecShootEffect, gain), "SecShootEffect") == DIERR_INPUTLOST) {
700 joy_ff_handle_error(SWFF_SetGain(pSecShootEffect, gain), "SecShootEffect");
703 if (joy_ff_handle_error(SWFF_SetDuration(pSecShootEffect, 150000 + gain * 25), "SecShootEffect") == DIERR_INPUTLOST) {
705 joy_ff_handle_error(SWFF_SetDuration(pSecShootEffect, 150000 + gain * 25), "SecShootEffect");
708 secondary_ff_level = gain;
709 nprintf(("Joystick", "FF: Secondary force = %d\n", gain));
712 pSecShootEffect->Stop();
713 joy_ff_start_effect(pSecShootEffect, "SecShootEffect");
716 static int primary_ff_level = 0;
718 void joy_ff_play_primary_shoot(int gain)
729 if (gain != primary_ff_level) {
730 if (joy_ff_handle_error(SWFF_SetGain(pShootEffect, gain), "ShootEffect") == DIERR_INPUTLOST) {
732 joy_ff_handle_error(SWFF_SetGain(pShootEffect, gain), "ShootEffect");
735 primary_ff_level = gain;
738 pShootEffect->Stop();
739 joy_ff_start_effect(pShootEffect, "ShootEffect");
742 void init_periodic_effect_struct(di_periodic_effect_struct *effect, int type, int dur, int per, int ang, int mag, int att, int fade)
744 // type-specific stuff
746 GUID guid = GUID_Square;
764 guid = GUID_Triangle;
767 guid = GUID_Triangle;
771 guid = GUID_SawtoothUp;
774 guid = GUID_SawtoothDown;
782 effect->periodic_struct.dwMagnitude = mag;
783 effect->periodic_struct.lOffset = 0;
784 effect->periodic_struct.dwPhase = dwPhase;
785 effect->periodic_struct.dwPeriod = per;
787 effect->envelope_struct.dwSize = sizeof(DIENVELOPE);
788 effect->envelope_struct.dwAttackTime = att;
789 effect->envelope_struct.dwAttackLevel = 0;
790 effect->envelope_struct.dwFadeTime = fade;
791 effect->envelope_struct.dwFadeLevel = 0;
793 effect->axes[0] = DIJOFS_X;
794 effect->axes[1] = DIJOFS_Y;
796 effect->direction[0] = ang;
797 effect->direction[1] = 0;
799 effect->effect.dwSize = sizeof(DIEFFECT);
800 effect->effect.dwFlags = DIEFF_OBJECTOFFSETS | DIEFF_POLAR;
801 effect->effect.dwDuration = dur;
802 effect->effect.dwSamplePeriod = HZ_TO_uS(100);
803 effect->effect.dwGain = 10000;
804 effect->effect.dwTriggerButton = DIEB_NOTRIGGER;
805 effect->effect.dwTriggerRepeatInterval = 0;
806 effect->effect.cAxes = 2;
807 effect->effect.rgdwAxes = effect->axes;
808 effect->effect.rglDirection = effect->direction;
809 effect->effect.lpEnvelope = &effect->envelope_struct;
810 effect->effect.cbTypeSpecificParams = sizeof(effect->periodic_struct);
811 effect->effect.lpvTypeSpecificParams = &effect->periodic_struct;