2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
4 * All source code herein is the property of Volition, Inc. You may not sell
5 * or otherwise commercially exploit the source or things you created based on
10 * $Logfile: /Freespace2/code/Io/Joy_ff.cpp $
15 * Code for joystick Force Feedback.
18 * Revision 1.3 2002/06/09 04:41:21 relnev
19 * added copyright header
21 * Revision 1.2 2002/05/07 03:16:46 theoddone33
22 * The Great Newline Fix
24 * Revision 1.1.1.1 2002/05/03 03:28:09 root
28 * 4 1/06/99 2:24p Dave
29 * Stubs and release build fixes.
31 * 3 10/09/98 2:57p Dave
32 * Starting splitting up OS stuff.
34 * 2 10/07/98 10:53a Dave
37 * 1 10/07/98 10:49a Dave
39 * 13 5/20/98 5:47p Sandeep
41 * 12 5/20/98 3:52p Allender
42 * fixed compiler warnings
44 * 11 5/20/98 11:06a Hoffoss
47 * 10 5/20/98 10:57a Hoffoss
48 * Made directional hit effect FF a toggle in launcher, and made calibrate
49 * not freeze launcher until process ends.
51 * 9 5/18/98 4:53p Hoffoss
52 * Some force feedback tweaks and pilot initializations there should have
53 * been happening, but weren't, and not are!
55 * 8 5/17/98 6:28p Hoffoss
58 * 7 5/17/98 5:45p Hoffoss
59 * Adjusted ship handling Force Feedback to not be as strong. Poor
60 * Sandeep's wrist can't handle it. :)
62 * 6 5/08/98 5:31p Hoffoss
63 * Isolated the joystick force feedback code more from dependence on other
66 * 5 5/08/98 12:27p Hoffoss
67 * Polished up the error handling and such a bit.
69 * 4 5/08/98 9:54a Hoffoss
70 * Generalized some of the effect stuff to try some swapping of effects.
71 * (going to start next, but checking this in in case I want to revert
72 * back to this state).
74 * 3 5/07/98 12:24a Hoffoss
75 * Finished up sidewinder force feedback support.
77 * 2 5/04/98 11:08p Hoffoss
78 * Expanded on Force Feedback code, and moved it all into Joy_ff.cpp.
79 * Updated references everywhere to it.
86 #include "osregistry.h"
92 DIPERIODIC periodic_struct;
93 DIENVELOPE envelope_struct;
97 } di_periodic_effect_struct;
99 int joy_ff_handling_scaler;
100 int Joy_ff_enabled = 0;
101 int Joy_ff_directional_hit_effect_enabled = 1;
104 LPDIRECTINPUTDEVICE2 pDiDevice;
106 int joy_ff_create_effects();
107 void joy_ff_stop_effects();
109 LPDIRECTINPUTEFFECT pHitEffect1;
110 LPDIRECTINPUTEFFECT pHitEffect2;
111 LPDIRECTINPUTEFFECT pAfterburn1;
112 LPDIRECTINPUTEFFECT pAfterburn2;
113 LPDIRECTINPUTEFFECT pShootEffect;
114 LPDIRECTINPUTEFFECT pSecShootEffect;
115 LPDIRECTINPUTEFFECT pSpring;
116 LPDIRECTINPUTEFFECT pDock;
117 LPDIRECTINPUTEFFECT pDeathroll1;
118 LPDIRECTINPUTEFFECT pDeathroll2;
119 LPDIRECTINPUTEFFECT pExplode;
121 di_periodic_effect_struct Struct_deathroll1;
122 di_periodic_effect_struct Struct_deathroll2;
123 di_periodic_effect_struct Struct_explode;
124 di_periodic_effect_struct Struct_afterburn1;
125 di_periodic_effect_struct Struct_afterburn2;
126 di_periodic_effect_struct Struct_dock;
128 di_condition_effect_struct Spring_cond_effect;
130 void joy_ff_afterburn_off();
131 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);
137 Joy_ff_enabled = 0; // Assume no force feedback
138 ff_enabled = os_config_read_uint(NULL, "EnableJoystickFF", 0);
142 TCHAR g_szOutput[256];
143 TCHAR szCodeString[256];
145 Joy_ff_directional_hit_effect_enabled = os_config_read_uint(NULL, "EnableHitEffect", 1);
147 hr = SWFF_OpenDefaultFFJoystick((HWND) os_get_window(), &pDi, &pDiDevice);
149 nprintf(("Sandeep", "No FF On Joystick, not using FF\n"));
150 SWFF_ErrorCodeToString(hr, &szCodeString[0]);
151 wsprintf(g_szOutput, "Make sure JOYSTICKID1 has Force Feedback\n"
152 "Code = %lx: %s\n", hr, szCodeString);
154 nprintf(("Sandeep", g_szOutput));
159 nprintf(("Sandeep", "There is FF on this joystick! (The peasants cheer)\n"));
160 SWFF_DestroyAllEffects(pDiDevice);
161 if (joy_ff_create_effects())
169 void joy_ff_shutdown()
171 if (Joy_ff_enabled) {
173 joy_ff_stop_effects();
174 SWFF_DestroyAllEffects(pDiDevice);
175 pDiDevice->Unacquire();
176 pDiDevice->Release();
181 HRESULT joy_ff_handle_error(HRESULT hr, char *eff_name = NULL)
184 TCHAR szCodeString[256];
186 SWFF_ErrorCodeToString(hr, szCodeString);
188 nprintf(("Joystick", "FF: Error for %s: %s\n", eff_name, szCodeString));
190 nprintf(("Joystick", "FF: Error: %s\n", szCodeString));
196 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)
198 joy_ff_handle_error(SWFF_CreatePeriodicEffect(pDiDevice, eff, type, dur, per, ang, mag, 0, att, 0, fade, 0, -1));
205 void joy_ff_start_effect(LPDIRECTINPUTEFFECT eff, char *name)
209 nprintf(("Joystick", "FF: Starting effect %s\n", name));
210 hr = joy_ff_handle_error(eff->Start(1, 0));
211 if (hr == DIERR_INPUTLOST) {
213 joy_ff_handle_error(eff->Start(1, 0));
217 int joy_ff_create_effects()
219 joy_ff_handle_error(SWFF_CreateConstantForceEffect(
226 10000, // Attack level
232 nprintf(("Joystick", "FF: Hit effect 1 loaded\n"));
233 else { // bail out early if we can't even load this (because rest probably won't either and failing is slow.
234 nprintf(("Joystick", "FF: Hit effect 1 failed to load\n"));
238 joy_ff_create_std_periodic(
245 100000, // Attack time
246 100000); // Fade time
249 nprintf(("Joystick", "FF: Hit effect 2 loaded\n"));
251 nprintf(("Joystick", "FF: Hit effect 2 failed to load\n"));
253 joy_ff_handle_error(SWFF_CreatePeriodicEffect(
265 0, -1), "ShootEffect");
268 nprintf(("Joystick", "FF: Fire primary effect loaded\n"));
270 nprintf(("Joystick", "FF: Fire primary effect failed to load\n"));
272 joy_ff_handle_error(SWFF_CreateConstantForceEffect(
278 50000, // Attack time
279 10000, // Attack level
282 -1), "SecShootEffect");
285 nprintf(("Joystick", "FF: Fire Secondary effect loaded\n"));
287 nprintf(("Joystick", "FF: Fire Secondary effect failed to load\n"));
289 joy_ff_handle_error(SWFF_CreateConditionEffectStruct(&Spring_cond_effect,
294 100, // X Coefficient
296 100, // Y Coefficient
298 -1), // button play mask
302 nprintf(("Joystick", "FF: Spring effect loaded\n"));
303 joy_ff_start_effect(pSpring, "Spring");
306 nprintf(("Joystick", "FF: Spring effect failed to load\n"));
308 init_periodic_effect_struct(
311 INFINITE, // Duration
316 pDiDevice->CreateEffect(Struct_afterburn1.guid, &Struct_afterburn1.effect, &pAfterburn1, NULL);
318 nprintf(("Joystick", "FF: Afterburner effect 1 loaded\n"));
320 nprintf(("Joystick", "FF: Afterburner effect 1 failed to load\n"));
322 init_periodic_effect_struct(
325 INFINITE, // Duration
330 pDiDevice->CreateEffect(Struct_afterburn2.guid, &Struct_afterburn2.effect, &pAfterburn2, NULL);
332 nprintf(("Joystick", "FF: Afterburner effect 2 loaded\n"));
334 nprintf(("Joystick", "FF: Afterburner effect 2 failed to load\n"));
336 init_periodic_effect_struct(
344 pDiDevice->CreateEffect(Struct_dock.guid, &Struct_dock.effect, &pDock, NULL);
346 nprintf(("Joystick", "FF: Dock effect loaded\n"));
348 nprintf(("Joystick", "FF: Dock effect failed to load\n"));
350 init_periodic_effect_struct(
358 500000); // Fade time
360 pDiDevice->CreateEffect(Struct_explode.guid, &Struct_explode.effect, &pExplode, NULL);
362 nprintf(("Joystick", "FF: Explosion effect loaded\n"));
364 nprintf(("Joystick", "FF: Explosion effect failed to load\n"));
366 init_periodic_effect_struct(
369 INFINITE, // Duration
373 2000000, // Attack time
376 pDiDevice->CreateEffect(Struct_deathroll1.guid, &Struct_deathroll1.effect, &pDeathroll1, NULL);
378 nprintf(("Joystick", "FF: Deathroll effect 1 loaded\n"));
380 nprintf(("Joystick", "FF: Deathroll effect 1 failed to load\n"));
382 init_periodic_effect_struct(
385 INFINITE, // Duration
389 2000000, // Attack time
392 pDiDevice->CreateEffect(Struct_deathroll2.guid, &Struct_deathroll2.effect, &pDeathroll2, NULL);
394 nprintf(("Joystick", "FF: Deathroll effect 2 loaded\n"));
396 nprintf(("Joystick", "FF: Deathroll effect 2 failed to load\n"));
401 void joy_ff_stop_effects()
403 joy_ff_afterburn_off();
406 void joy_ff_mission_init(vector v)
409 // joy_ff_handling_scaler = (int) ((vm_vec_mag(&v) - 1.3f) * 10.5f);
410 joy_ff_handling_scaler = (int) ((vm_vec_mag(&v) + 1.3f) * 5.0f);
411 // joy_ff_handling_scaler = (int) (vm_vec_mag(&v) * 7.5f);
414 void joy_ff_adjust_handling(int speed)
418 v = speed * joy_ff_handling_scaler * 2 / 3;
419 // v += joy_ff_handling_scaler * joy_ff_handling_scaler * 6 / 7 + 250;
420 v += joy_ff_handling_scaler * 45 - 500;
425 if (Spring_cond_effect.DIConditionStruct[0].lPositiveCoefficient != v) {
428 Spring_cond_effect.DIConditionStruct[0].lPositiveCoefficient = v;
429 Spring_cond_effect.DIConditionStruct[0].lNegativeCoefficient = v;
430 Spring_cond_effect.DIConditionStruct[1].lPositiveCoefficient = v;
431 Spring_cond_effect.DIConditionStruct[1].lNegativeCoefficient = v;
432 nprintf(("Joystick", "FF: New handling force = %d\n", v));
434 hr = joy_ff_handle_error(pSpring->SetParameters(&Spring_cond_effect.DIEffectStruct, DIEP_TYPESPECIFICPARAMS), "Spring");
435 if (hr == DIERR_INPUTLOST) {
437 joy_ff_handle_error(pSpring->SetParameters(&Spring_cond_effect.DIEffectStruct, DIEP_TYPESPECIFICPARAMS), "Spring");
443 void joy_ff_change_effect(di_periodic_effect_struct *s, LPDIRECTINPUTEFFECT eff, int gain = -1, int dur = 0, int flags = -1)
447 if ((gain >= 0) && ((int) s->effect.dwGain != gain)) {
448 s->effect.dwGain = gain;
449 nprintf(("Joystick", "FF: Gain reset to %d\n", gain));
453 if (dur && ((int) s->effect.dwDuration != dur)) {
454 s->effect.dwDuration = dur;
455 nprintf(("Joystick", "FF: Duration reset to %d\n", dur));
460 flags = DIEP_DURATION | DIEP_SAMPLEPERIOD | DIEP_GAIN | DIEP_DIRECTION | DIEP_ENVELOPE | DIEP_TYPESPECIFICPARAMS;
461 nprintf(("Joystick", "FF: Doing full reload of effect\n"));
464 if (flags != (DIEP_DURATION | DIEP_GAIN))
470 nprintf(("Joystick", "FF: Swapping in a new effect\n"));
471 hr = joy_ff_handle_error(eff->SetParameters(&s->effect, flags));
472 if (hr == DIERR_INPUTLOST) {
474 joy_ff_handle_error(eff->SetParameters(&s->effect, flags));
478 nprintf(("Joystick", "FF: Swap effect requested, but nothing changed\n"));
481 int joy_ff_effect_playing(LPDIRECTINPUTEFFECT eff)
485 eff->GetEffectStatus(&flags);
486 return (flags & DIEGES_PLAYING);
493 if (joy_ff_handle_error(SWFF_SetGain(pDock, 10000), "Dock") == DIERR_INPUTLOST) {
495 joy_ff_handle_error(SWFF_SetGain(pDock, 10000), "Dock");
498 joy_ff_start_effect(pDock, "Dock");
502 void joy_ff_play_reload_effect()
506 if (joy_ff_handle_error(SWFF_SetGain(pDock, 5000), "Dock (Reload)") == DIERR_INPUTLOST) {
508 joy_ff_handle_error(SWFF_SetGain(pDock, 5000), "Dock (Reload)");
511 joy_ff_start_effect(pDock, "Dock (Reload)");
515 int Joy_ff_afterburning = 0;
517 void joy_ff_afterburn_on()
521 joy_ff_change_effect(&Struct_afterburn1, pAfterburn1, 5000, INFINITE, DIEP_DURATION | DIEP_GAIN);
522 joy_ff_start_effect(pAfterburn1, "Afterburn1");
527 joy_ff_change_effect(&Struct_afterburn2, pAfterburn2, 5000, INFINITE, DIEP_DURATION | DIEP_GAIN);
528 joy_ff_start_effect(pAfterburn2, "Afterburn2");
531 nprintf(("Joystick", "FF: Afterburn started\n"));
532 Joy_ff_afterburning = 1;
535 void joy_ff_afterburn_off()
537 if (!Joy_ff_afterburning)
548 Joy_ff_afterburning = 0;
549 nprintf(("Joystick", "FF: Afterburn stopped\n"));
552 void joy_ff_deathroll()
556 joy_ff_start_effect(pDeathroll1, "Deathroll1");
561 joy_ff_start_effect(pDeathroll2, "Deathroll2");
565 void joy_ff_explode()
575 joy_ff_start_effect(pExplode, "Explode");
579 void joy_ff_fly_by(int mag)
583 if (Joy_ff_afterburning)
586 gain = mag * 120 + 4000;
592 joy_ff_change_effect(&Struct_afterburn1, pAfterburn1, gain, 6000 * mag + 400000, DIEP_DURATION | DIEP_GAIN);
593 joy_ff_start_effect(pAfterburn1, "Afterburn1 (Fly by)");
598 joy_ff_change_effect(&Struct_afterburn2, pAfterburn2, gain, 6000 * mag + 400000, DIEP_DURATION | DIEP_GAIN);
599 joy_ff_start_effect(pAfterburn2, "Afterburn2 (Fly by)");
603 void joy_reacquire_ff()
608 nprintf(("Joystick", "FF: Reacquiring\n"));
609 pDiDevice->Acquire();
610 joy_ff_start_effect(pSpring, "Spring");
613 void joy_unacquire_ff()
617 void joy_ff_play_dir_effect(float x, float y)
625 if (!pHitEffect1 || !pHitEffect2)
628 if (joy_ff_effect_playing(pHitEffect1) || joy_ff_effect_playing(pHitEffect2)) {
629 nprintf(("Joystick", "FF: HitEffect already playing. Skipping\n"));
633 if (Joy_ff_directional_hit_effect_enabled) {
636 else if (x < -8000.0f)
641 else if (y < -8000.0f)
644 imag = (int) fl_sqrt(x * x + y * y);
648 degs = (float)atan2(x, y);
649 idegs = (int) (degs * 18000.0f / PI) + 90;
653 while (idegs >= 36000)
656 if (joy_ff_handle_error(SWFF_SetDirectionGain(pHitEffect1, idegs, imag), "HitEffect1") == DIERR_INPUTLOST) {
658 joy_ff_handle_error(SWFF_SetDirectionGain(pHitEffect1, idegs, imag), "HitEffect1");
665 if (joy_ff_handle_error(SWFF_SetDirectionGain(pHitEffect2, idegs, imag), "HitEffect2") == DIERR_INPUTLOST) {
667 joy_ff_handle_error(SWFF_SetDirectionGain(pHitEffect2, idegs, imag), "HitEffect2");
671 joy_ff_start_effect(pHitEffect1, "HitEffect1");
672 joy_ff_start_effect(pHitEffect2, "HitEffect2");
673 //nprintf(("Joystick", "FF: Dir: %d, Mag = %d\n", idegs, imag));
676 void joy_ff_play_vector_effect(vector *v, float scaler)
681 nprintf(("Joystick", "FF: vec = { %f, %f, %f } s = %f\n", v->x, v->y, v->z, scaler));
682 vm_vec_copy_scale(&vf, v, scaler);
687 y = -vm_vec_mag(&vf);
691 joy_ff_play_dir_effect(-x, -y);
694 static int secondary_ff_level = 0;
696 void joy_ff_play_secondary_shoot(int gain)
701 if (!pSecShootEffect)
704 gain = gain * 100 + 2500;
708 if (gain != secondary_ff_level) {
709 if (joy_ff_handle_error(SWFF_SetGain(pSecShootEffect, gain), "SecShootEffect") == DIERR_INPUTLOST) {
711 joy_ff_handle_error(SWFF_SetGain(pSecShootEffect, gain), "SecShootEffect");
714 if (joy_ff_handle_error(SWFF_SetDuration(pSecShootEffect, 150000 + gain * 25), "SecShootEffect") == DIERR_INPUTLOST) {
716 joy_ff_handle_error(SWFF_SetDuration(pSecShootEffect, 150000 + gain * 25), "SecShootEffect");
719 secondary_ff_level = gain;
720 nprintf(("Joystick", "FF: Secondary force = %d\n", gain));
723 pSecShootEffect->Stop();
724 joy_ff_start_effect(pSecShootEffect, "SecShootEffect");
727 static int primary_ff_level = 0;
729 void joy_ff_play_primary_shoot(int gain)
740 if (gain != primary_ff_level) {
741 if (joy_ff_handle_error(SWFF_SetGain(pShootEffect, gain), "ShootEffect") == DIERR_INPUTLOST) {
743 joy_ff_handle_error(SWFF_SetGain(pShootEffect, gain), "ShootEffect");
746 primary_ff_level = gain;
749 pShootEffect->Stop();
750 joy_ff_start_effect(pShootEffect, "ShootEffect");
753 void init_periodic_effect_struct(di_periodic_effect_struct *effect, int type, int dur, int per, int ang, int mag, int att, int fade)
755 // type-specific stuff
757 GUID guid = GUID_Square;
775 guid = GUID_Triangle;
778 guid = GUID_Triangle;
782 guid = GUID_SawtoothUp;
785 guid = GUID_SawtoothDown;
793 effect->periodic_struct.dwMagnitude = mag;
794 effect->periodic_struct.lOffset = 0;
795 effect->periodic_struct.dwPhase = dwPhase;
796 effect->periodic_struct.dwPeriod = per;
798 effect->envelope_struct.dwSize = sizeof(DIENVELOPE);
799 effect->envelope_struct.dwAttackTime = att;
800 effect->envelope_struct.dwAttackLevel = 0;
801 effect->envelope_struct.dwFadeTime = fade;
802 effect->envelope_struct.dwFadeLevel = 0;
804 effect->axes[0] = DIJOFS_X;
805 effect->axes[1] = DIJOFS_Y;
807 effect->direction[0] = ang;
808 effect->direction[1] = 0;
810 effect->effect.dwSize = sizeof(DIEFFECT);
811 effect->effect.dwFlags = DIEFF_OBJECTOFFSETS | DIEFF_POLAR;
812 effect->effect.dwDuration = dur;
813 effect->effect.dwSamplePeriod = HZ_TO_uS(100);
814 effect->effect.dwGain = 10000;
815 effect->effect.dwTriggerButton = DIEB_NOTRIGGER;
816 effect->effect.dwTriggerRepeatInterval = 0;
817 effect->effect.cAxes = 2;
818 effect->effect.rgdwAxes = effect->axes;
819 effect->effect.rglDirection = effect->direction;
820 effect->effect.lpEnvelope = &effect->envelope_struct;
821 effect->effect.cbTypeSpecificParams = sizeof(effect->periodic_struct);
822 effect->effect.lpvTypeSpecificParams = &effect->periodic_struct;