2 * $Logfile: /Freespace2/code/Io/Joy_ff.cpp $
7 * Code for joystick Force Feedback.
10 * Revision 1.1 2002/05/03 03:28:09 root
14 * 4 1/06/99 2:24p Dave
15 * Stubs and release build fixes.
17 * 3 10/09/98 2:57p Dave
18 * Starting splitting up OS stuff.
20 * 2 10/07/98 10:53a Dave
23 * 1 10/07/98 10:49a Dave
25 * 13 5/20/98 5:47p Sandeep
27 * 12 5/20/98 3:52p Allender
28 * fixed compiler warnings
30 * 11 5/20/98 11:06a Hoffoss
33 * 10 5/20/98 10:57a Hoffoss
34 * Made directional hit effect FF a toggle in launcher, and made calibrate
35 * not freeze launcher until process ends.
37 * 9 5/18/98 4:53p Hoffoss
38 * Some force feedback tweaks and pilot initializations there should have
39 * been happening, but weren't, and not are!
41 * 8 5/17/98 6:28p Hoffoss
44 * 7 5/17/98 5:45p Hoffoss
45 * Adjusted ship handling Force Feedback to not be as strong. Poor
46 * Sandeep's wrist can't handle it. :)
48 * 6 5/08/98 5:31p Hoffoss
49 * Isolated the joystick force feedback code more from dependence on other
52 * 5 5/08/98 12:27p Hoffoss
53 * Polished up the error handling and such a bit.
55 * 4 5/08/98 9:54a Hoffoss
56 * Generalized some of the effect stuff to try some swapping of effects.
57 * (going to start next, but checking this in in case I want to revert
58 * back to this state).
60 * 3 5/07/98 12:24a Hoffoss
61 * Finished up sidewinder force feedback support.
63 * 2 5/04/98 11:08p Hoffoss
64 * Expanded on Force Feedback code, and moved it all into Joy_ff.cpp.
65 * Updated references everywhere to it.
72 #include "osregistry.h"
78 DIPERIODIC periodic_struct;
79 DIENVELOPE envelope_struct;
83 } di_periodic_effect_struct;
85 int joy_ff_handling_scaler;
86 int Joy_ff_enabled = 0;
87 int Joy_ff_directional_hit_effect_enabled = 1;
90 LPDIRECTINPUTDEVICE2 pDiDevice;
92 int joy_ff_create_effects();
93 void joy_ff_stop_effects();
95 LPDIRECTINPUTEFFECT pHitEffect1;
96 LPDIRECTINPUTEFFECT pHitEffect2;
97 LPDIRECTINPUTEFFECT pAfterburn1;
98 LPDIRECTINPUTEFFECT pAfterburn2;
99 LPDIRECTINPUTEFFECT pShootEffect;
100 LPDIRECTINPUTEFFECT pSecShootEffect;
101 LPDIRECTINPUTEFFECT pSpring;
102 LPDIRECTINPUTEFFECT pDock;
103 LPDIRECTINPUTEFFECT pDeathroll1;
104 LPDIRECTINPUTEFFECT pDeathroll2;
105 LPDIRECTINPUTEFFECT pExplode;
107 di_periodic_effect_struct Struct_deathroll1;
108 di_periodic_effect_struct Struct_deathroll2;
109 di_periodic_effect_struct Struct_explode;
110 di_periodic_effect_struct Struct_afterburn1;
111 di_periodic_effect_struct Struct_afterburn2;
112 di_periodic_effect_struct Struct_dock;
114 di_condition_effect_struct Spring_cond_effect;
116 void joy_ff_afterburn_off();
117 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);
123 Joy_ff_enabled = 0; // Assume no force feedback
124 ff_enabled = os_config_read_uint(NULL, "EnableJoystickFF", 0);
128 TCHAR g_szOutput[256];
129 TCHAR szCodeString[256];
131 Joy_ff_directional_hit_effect_enabled = os_config_read_uint(NULL, "EnableHitEffect", 1);
133 hr = SWFF_OpenDefaultFFJoystick((HWND) os_get_window(), &pDi, &pDiDevice);
135 nprintf(("Sandeep", "No FF On Joystick, not using FF\n"));
136 SWFF_ErrorCodeToString(hr, &szCodeString[0]);
137 wsprintf(g_szOutput, "Make sure JOYSTICKID1 has Force Feedback\n"
138 "Code = %lx: %s\n", hr, szCodeString);
140 nprintf(("Sandeep", g_szOutput));
145 nprintf(("Sandeep", "There is FF on this joystick! (The peasants cheer)\n"));
146 SWFF_DestroyAllEffects(pDiDevice);
147 if (joy_ff_create_effects())
155 void joy_ff_shutdown()
157 if (Joy_ff_enabled) {
159 joy_ff_stop_effects();
160 SWFF_DestroyAllEffects(pDiDevice);
161 pDiDevice->Unacquire();
162 pDiDevice->Release();
167 HRESULT joy_ff_handle_error(HRESULT hr, char *eff_name = NULL)
170 TCHAR szCodeString[256];
172 SWFF_ErrorCodeToString(hr, szCodeString);
174 nprintf(("Joystick", "FF: Error for %s: %s\n", eff_name, szCodeString));
176 nprintf(("Joystick", "FF: Error: %s\n", szCodeString));
182 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)
184 joy_ff_handle_error(SWFF_CreatePeriodicEffect(pDiDevice, eff, type, dur, per, ang, mag, 0, att, 0, fade, 0, -1));
191 void joy_ff_start_effect(LPDIRECTINPUTEFFECT eff, char *name)
195 nprintf(("Joystick", "FF: Starting effect %s\n", name));
196 hr = joy_ff_handle_error(eff->Start(1, 0));
197 if (hr == DIERR_INPUTLOST) {
199 joy_ff_handle_error(eff->Start(1, 0));
203 int joy_ff_create_effects()
205 joy_ff_handle_error(SWFF_CreateConstantForceEffect(
212 10000, // Attack level
218 nprintf(("Joystick", "FF: Hit effect 1 loaded\n"));
219 else { // bail out early if we can't even load this (because rest probably won't either and failing is slow.
220 nprintf(("Joystick", "FF: Hit effect 1 failed to load\n"));
224 joy_ff_create_std_periodic(
231 100000, // Attack time
232 100000); // Fade time
235 nprintf(("Joystick", "FF: Hit effect 2 loaded\n"));
237 nprintf(("Joystick", "FF: Hit effect 2 failed to load\n"));
239 joy_ff_handle_error(SWFF_CreatePeriodicEffect(
251 0, -1), "ShootEffect");
254 nprintf(("Joystick", "FF: Fire primary effect loaded\n"));
256 nprintf(("Joystick", "FF: Fire primary effect failed to load\n"));
258 joy_ff_handle_error(SWFF_CreateConstantForceEffect(
264 50000, // Attack time
265 10000, // Attack level
268 -1), "SecShootEffect");
271 nprintf(("Joystick", "FF: Fire Secondary effect loaded\n"));
273 nprintf(("Joystick", "FF: Fire Secondary effect failed to load\n"));
275 joy_ff_handle_error(SWFF_CreateConditionEffectStruct(&Spring_cond_effect,
280 100, // X Coefficient
282 100, // Y Coefficient
284 -1), // button play mask
288 nprintf(("Joystick", "FF: Spring effect loaded\n"));
289 joy_ff_start_effect(pSpring, "Spring");
292 nprintf(("Joystick", "FF: Spring effect failed to load\n"));
294 init_periodic_effect_struct(
297 INFINITE, // Duration
302 pDiDevice->CreateEffect(Struct_afterburn1.guid, &Struct_afterburn1.effect, &pAfterburn1, NULL);
304 nprintf(("Joystick", "FF: Afterburner effect 1 loaded\n"));
306 nprintf(("Joystick", "FF: Afterburner effect 1 failed to load\n"));
308 init_periodic_effect_struct(
311 INFINITE, // Duration
316 pDiDevice->CreateEffect(Struct_afterburn2.guid, &Struct_afterburn2.effect, &pAfterburn2, NULL);
318 nprintf(("Joystick", "FF: Afterburner effect 2 loaded\n"));
320 nprintf(("Joystick", "FF: Afterburner effect 2 failed to load\n"));
322 init_periodic_effect_struct(
330 pDiDevice->CreateEffect(Struct_dock.guid, &Struct_dock.effect, &pDock, NULL);
332 nprintf(("Joystick", "FF: Dock effect loaded\n"));
334 nprintf(("Joystick", "FF: Dock effect failed to load\n"));
336 init_periodic_effect_struct(
344 500000); // Fade time
346 pDiDevice->CreateEffect(Struct_explode.guid, &Struct_explode.effect, &pExplode, NULL);
348 nprintf(("Joystick", "FF: Explosion effect loaded\n"));
350 nprintf(("Joystick", "FF: Explosion effect failed to load\n"));
352 init_periodic_effect_struct(
355 INFINITE, // Duration
359 2000000, // Attack time
362 pDiDevice->CreateEffect(Struct_deathroll1.guid, &Struct_deathroll1.effect, &pDeathroll1, NULL);
364 nprintf(("Joystick", "FF: Deathroll effect 1 loaded\n"));
366 nprintf(("Joystick", "FF: Deathroll effect 1 failed to load\n"));
368 init_periodic_effect_struct(
371 INFINITE, // Duration
375 2000000, // Attack time
378 pDiDevice->CreateEffect(Struct_deathroll2.guid, &Struct_deathroll2.effect, &pDeathroll2, NULL);
380 nprintf(("Joystick", "FF: Deathroll effect 2 loaded\n"));
382 nprintf(("Joystick", "FF: Deathroll effect 2 failed to load\n"));
387 void joy_ff_stop_effects()
389 joy_ff_afterburn_off();
392 void joy_ff_mission_init(vector v)
395 // joy_ff_handling_scaler = (int) ((vm_vec_mag(&v) - 1.3f) * 10.5f);
396 joy_ff_handling_scaler = (int) ((vm_vec_mag(&v) + 1.3f) * 5.0f);
397 // joy_ff_handling_scaler = (int) (vm_vec_mag(&v) * 7.5f);
400 void joy_ff_adjust_handling(int speed)
404 v = speed * joy_ff_handling_scaler * 2 / 3;
405 // v += joy_ff_handling_scaler * joy_ff_handling_scaler * 6 / 7 + 250;
406 v += joy_ff_handling_scaler * 45 - 500;
411 if (Spring_cond_effect.DIConditionStruct[0].lPositiveCoefficient != v) {
414 Spring_cond_effect.DIConditionStruct[0].lPositiveCoefficient = v;
415 Spring_cond_effect.DIConditionStruct[0].lNegativeCoefficient = v;
416 Spring_cond_effect.DIConditionStruct[1].lPositiveCoefficient = v;
417 Spring_cond_effect.DIConditionStruct[1].lNegativeCoefficient = v;
418 nprintf(("Joystick", "FF: New handling force = %d\n", v));
420 hr = joy_ff_handle_error(pSpring->SetParameters(&Spring_cond_effect.DIEffectStruct, DIEP_TYPESPECIFICPARAMS), "Spring");
421 if (hr == DIERR_INPUTLOST) {
423 joy_ff_handle_error(pSpring->SetParameters(&Spring_cond_effect.DIEffectStruct, DIEP_TYPESPECIFICPARAMS), "Spring");
429 void joy_ff_change_effect(di_periodic_effect_struct *s, LPDIRECTINPUTEFFECT eff, int gain = -1, int dur = 0, int flags = -1)
433 if ((gain >= 0) && ((int) s->effect.dwGain != gain)) {
434 s->effect.dwGain = gain;
435 nprintf(("Joystick", "FF: Gain reset to %d\n", gain));
439 if (dur && ((int) s->effect.dwDuration != dur)) {
440 s->effect.dwDuration = dur;
441 nprintf(("Joystick", "FF: Duration reset to %d\n", dur));
446 flags = DIEP_DURATION | DIEP_SAMPLEPERIOD | DIEP_GAIN | DIEP_DIRECTION | DIEP_ENVELOPE | DIEP_TYPESPECIFICPARAMS;
447 nprintf(("Joystick", "FF: Doing full reload of effect\n"));
450 if (flags != (DIEP_DURATION | DIEP_GAIN))
456 nprintf(("Joystick", "FF: Swapping in a new effect\n"));
457 hr = joy_ff_handle_error(eff->SetParameters(&s->effect, flags));
458 if (hr == DIERR_INPUTLOST) {
460 joy_ff_handle_error(eff->SetParameters(&s->effect, flags));
464 nprintf(("Joystick", "FF: Swap effect requested, but nothing changed\n"));
467 int joy_ff_effect_playing(LPDIRECTINPUTEFFECT eff)
471 eff->GetEffectStatus(&flags);
472 return (flags & DIEGES_PLAYING);
479 if (joy_ff_handle_error(SWFF_SetGain(pDock, 10000), "Dock") == DIERR_INPUTLOST) {
481 joy_ff_handle_error(SWFF_SetGain(pDock, 10000), "Dock");
484 joy_ff_start_effect(pDock, "Dock");
488 void joy_ff_play_reload_effect()
492 if (joy_ff_handle_error(SWFF_SetGain(pDock, 5000), "Dock (Reload)") == DIERR_INPUTLOST) {
494 joy_ff_handle_error(SWFF_SetGain(pDock, 5000), "Dock (Reload)");
497 joy_ff_start_effect(pDock, "Dock (Reload)");
501 int Joy_ff_afterburning = 0;
503 void joy_ff_afterburn_on()
507 joy_ff_change_effect(&Struct_afterburn1, pAfterburn1, 5000, INFINITE, DIEP_DURATION | DIEP_GAIN);
508 joy_ff_start_effect(pAfterburn1, "Afterburn1");
513 joy_ff_change_effect(&Struct_afterburn2, pAfterburn2, 5000, INFINITE, DIEP_DURATION | DIEP_GAIN);
514 joy_ff_start_effect(pAfterburn2, "Afterburn2");
517 nprintf(("Joystick", "FF: Afterburn started\n"));
518 Joy_ff_afterburning = 1;
521 void joy_ff_afterburn_off()
523 if (!Joy_ff_afterburning)
534 Joy_ff_afterburning = 0;
535 nprintf(("Joystick", "FF: Afterburn stopped\n"));
538 void joy_ff_deathroll()
542 joy_ff_start_effect(pDeathroll1, "Deathroll1");
547 joy_ff_start_effect(pDeathroll2, "Deathroll2");
551 void joy_ff_explode()
561 joy_ff_start_effect(pExplode, "Explode");
565 void joy_ff_fly_by(int mag)
569 if (Joy_ff_afterburning)
572 gain = mag * 120 + 4000;
578 joy_ff_change_effect(&Struct_afterburn1, pAfterburn1, gain, 6000 * mag + 400000, DIEP_DURATION | DIEP_GAIN);
579 joy_ff_start_effect(pAfterburn1, "Afterburn1 (Fly by)");
584 joy_ff_change_effect(&Struct_afterburn2, pAfterburn2, gain, 6000 * mag + 400000, DIEP_DURATION | DIEP_GAIN);
585 joy_ff_start_effect(pAfterburn2, "Afterburn2 (Fly by)");
589 void joy_reacquire_ff()
594 nprintf(("Joystick", "FF: Reacquiring\n"));
595 pDiDevice->Acquire();
596 joy_ff_start_effect(pSpring, "Spring");
599 void joy_unacquire_ff()
603 void joy_ff_play_dir_effect(float x, float y)
611 if (!pHitEffect1 || !pHitEffect2)
614 if (joy_ff_effect_playing(pHitEffect1) || joy_ff_effect_playing(pHitEffect2)) {
615 nprintf(("Joystick", "FF: HitEffect already playing. Skipping\n"));
619 if (Joy_ff_directional_hit_effect_enabled) {
622 else if (x < -8000.0f)
627 else if (y < -8000.0f)
630 imag = (int) fl_sqrt(x * x + y * y);
634 degs = (float)atan2(x, y);
635 idegs = (int) (degs * 18000.0f / PI) + 90;
639 while (idegs >= 36000)
642 if (joy_ff_handle_error(SWFF_SetDirectionGain(pHitEffect1, idegs, imag), "HitEffect1") == DIERR_INPUTLOST) {
644 joy_ff_handle_error(SWFF_SetDirectionGain(pHitEffect1, idegs, imag), "HitEffect1");
651 if (joy_ff_handle_error(SWFF_SetDirectionGain(pHitEffect2, idegs, imag), "HitEffect2") == DIERR_INPUTLOST) {
653 joy_ff_handle_error(SWFF_SetDirectionGain(pHitEffect2, idegs, imag), "HitEffect2");
657 joy_ff_start_effect(pHitEffect1, "HitEffect1");
658 joy_ff_start_effect(pHitEffect2, "HitEffect2");
659 //nprintf(("Joystick", "FF: Dir: %d, Mag = %d\n", idegs, imag));
662 void joy_ff_play_vector_effect(vector *v, float scaler)
667 nprintf(("Joystick", "FF: vec = { %f, %f, %f } s = %f\n", v->x, v->y, v->z, scaler));
668 vm_vec_copy_scale(&vf, v, scaler);
673 y = -vm_vec_mag(&vf);
677 joy_ff_play_dir_effect(-x, -y);
680 static int secondary_ff_level = 0;
682 void joy_ff_play_secondary_shoot(int gain)
687 if (!pSecShootEffect)
690 gain = gain * 100 + 2500;
694 if (gain != secondary_ff_level) {
695 if (joy_ff_handle_error(SWFF_SetGain(pSecShootEffect, gain), "SecShootEffect") == DIERR_INPUTLOST) {
697 joy_ff_handle_error(SWFF_SetGain(pSecShootEffect, gain), "SecShootEffect");
700 if (joy_ff_handle_error(SWFF_SetDuration(pSecShootEffect, 150000 + gain * 25), "SecShootEffect") == DIERR_INPUTLOST) {
702 joy_ff_handle_error(SWFF_SetDuration(pSecShootEffect, 150000 + gain * 25), "SecShootEffect");
705 secondary_ff_level = gain;
706 nprintf(("Joystick", "FF: Secondary force = %d\n", gain));
709 pSecShootEffect->Stop();
710 joy_ff_start_effect(pSecShootEffect, "SecShootEffect");
713 static int primary_ff_level = 0;
715 void joy_ff_play_primary_shoot(int gain)
726 if (gain != primary_ff_level) {
727 if (joy_ff_handle_error(SWFF_SetGain(pShootEffect, gain), "ShootEffect") == DIERR_INPUTLOST) {
729 joy_ff_handle_error(SWFF_SetGain(pShootEffect, gain), "ShootEffect");
732 primary_ff_level = gain;
735 pShootEffect->Stop();
736 joy_ff_start_effect(pShootEffect, "ShootEffect");
739 void init_periodic_effect_struct(di_periodic_effect_struct *effect, int type, int dur, int per, int ang, int mag, int att, int fade)
741 // type-specific stuff
743 GUID guid = GUID_Square;
761 guid = GUID_Triangle;
764 guid = GUID_Triangle;
768 guid = GUID_SawtoothUp;
771 guid = GUID_SawtoothDown;
779 effect->periodic_struct.dwMagnitude = mag;
780 effect->periodic_struct.lOffset = 0;
781 effect->periodic_struct.dwPhase = dwPhase;
782 effect->periodic_struct.dwPeriod = per;
784 effect->envelope_struct.dwSize = sizeof(DIENVELOPE);
785 effect->envelope_struct.dwAttackTime = att;
786 effect->envelope_struct.dwAttackLevel = 0;
787 effect->envelope_struct.dwFadeTime = fade;
788 effect->envelope_struct.dwFadeLevel = 0;
790 effect->axes[0] = DIJOFS_X;
791 effect->axes[1] = DIJOFS_Y;
793 effect->direction[0] = ang;
794 effect->direction[1] = 0;
796 effect->effect.dwSize = sizeof(DIEFFECT);
797 effect->effect.dwFlags = DIEFF_OBJECTOFFSETS | DIEFF_POLAR;
798 effect->effect.dwDuration = dur;
799 effect->effect.dwSamplePeriod = HZ_TO_uS(100);
800 effect->effect.dwGain = 10000;
801 effect->effect.dwTriggerButton = DIEB_NOTRIGGER;
802 effect->effect.dwTriggerRepeatInterval = 0;
803 effect->effect.cAxes = 2;
804 effect->effect.rgdwAxes = effect->axes;
805 effect->effect.rglDirection = effect->direction;
806 effect->effect.lpEnvelope = &effect->envelope_struct;
807 effect->effect.cbTypeSpecificParams = sizeof(effect->periodic_struct);
808 effect->effect.lpvTypeSpecificParams = &effect->periodic_struct;