1 /*---------------------------------------------------------------------------*
2   Project:  Dolphin Game Pad(Controller) API
3   File:     Padclamp.c
4 
5   Copyright (C) 1998-2006 Nintendo.  All rights reserved.
6 
7   These coded instructions, statements, and computer programs contain
8   proprietary information of Nintendo of America Inc. and/or Nintendo
9   Company Ltd., and are protected by Federal copyright law.  They may
10   not be disclosed to third parties or copied or duplicated in any form,
11   in whole or in part, without the prior written consent of Nintendo.
12 
13   $Log: Padclamp.c,v $
14   Revision 1.2  02/03/2006 00:31:59  yasuh-to
15   Changed include path for RevolutionSDK
16 
17   Revision 1.1.1.1  2005/12/29 06:53:27  hiratsu
18   Initial import.
19 
20     7     8/20/02 10:28:00 Shiki
21     Implemented PADClampCircle() based on Steve's code.
22 
23     6     01/06/18 20:44 Shiki
24     Modified ClampRegion values.
25 
26     5     01/06/18 16:29 Shiki
27     Updated for production controllers.
28 
29     4     01/03/21 18:59 Shiki
30     Revised analog trigger clamping code to support DS4 and later.
31 
32     3     6/13/00 7:28p Shiki
33     Added analog trigger clamping.
34 
35     2     2/24/00 11:32p Shiki
36     Re-implemented for dolphin controller.
37 
38     6     9/23/99 4:57p Shiki
39     Renamed 'errno' of PADStatus to 'err'.
40 
41     4     5/11/99 4:39p Shiki
42     Refreshed include tree.
43 
44     3     5/07/99 9:15p Shiki
45     Fixed dead-zone clamping.
46 
47     2     5/06/99 8:18p Shiki
48     Renamed PADNormalize() to PADClamp()
49 
50     1     5/06/99 7:04p Shiki
51 
52   $NoKeywords: $
53  *---------------------------------------------------------------------------*/
54 
55 #include <math.h>
56 #include <revolution/os.h>
57 #include <revolution/pad.h>
58 
59 // Parameters for Dolphin controller
60 
61 typedef struct PADClampRegion
62 {
63     u8  minTrigger;     // the play of the trigger
64     u8  maxTrigger;
65     s8  minStick;       // the play of the control stick
66     s8  maxStick;
67     s8  xyStick;        // max on x = y
68     s8  minSubstick;    // the play of the C stick
69     s8  maxSubstick;
70     s8  xySubstick;
71 
72     s8  radStick;       // max radius of the control stick
73     s8  radSubstick;    // max radius of the C stick
74 } PADClampRegion;
75 
76 #ifndef EMU
77 
78 // GameCube Standard Controller
79 static const PADClampRegion ClampRegion =
80 {
81     30,     // minTrigger
82     180,    // maxTrigger
83 
84     15,     // minStick
85     72,     // maxStick
86     40,     // xyStick
87 
88     15,     // minSubstick
89     59,     // maxSubstick
90     31,     // xySubstick
91 
92     56,
93     44,
94 };
95 
96 #else   // EMU
97 
98 // Mac emulator
99 static const PADClampRegion ClampRegion =
100 {
101     40,     // minTrigger
102     190,    // maxTrigger
103 
104     35,     // minStick
105     79,     // maxStick
106     47,     // xyStick
107 
108     35,     // minSubstick
109     66,     // maxSubstick
110     38,     // xySubstick
111 
112     56,
113     44
114 };
115 
116 #endif  // EMU
117 
118 /*---------------------------------------------------------------------------*
119   Name:         ClampStick
120 
121   Description:  Adjusts stick movement data within the octagon, or clamps
122                 the data to the origin if stick is close to the origin as
123                 the play.
124 
125   Arguments:    px          pointer to movement data in terms of x-axis
126                 py          pointer to movement data in terms of y-axis
127                 max         max on y = 0
128                 xy          max on x = y
129                 min         deadzone
130 
131   Returns:      None.
132  *---------------------------------------------------------------------------*/
ClampStick(s8 * px,s8 * py,s8 max,s8 xy,s8 min)133 static void ClampStick(s8* px, s8* py, s8 max, s8 xy, s8 min)
134 {
135     int x = *px;
136     int y = *py;
137     int signX;
138     int signY;
139     int d;
140 
141     if (0 <= x)
142     {
143         signX = 1;
144     }
145     else
146     {
147         signX = -1;
148         x = -x;
149     }
150 
151     if (0 <= y)
152     {
153         signY = 1;
154     }
155     else
156     {
157         signY = -1;
158         y = -y;
159     }
160 
161     // Clamp dead zone
162     if (x <= min)
163     {
164         x = 0;
165     }
166     else
167     {
168         x -= min;
169     }
170     if (y <= min)
171     {
172         y = 0;
173     }
174     else
175     {
176         y -= min;
177     }
178 
179     if (x == 0 && y == 0)
180     {
181         *px = *py = 0;
182         return;
183         // NOT REACHED HERE
184     }
185 
186     // Clamp outer octagon
187     if (xy * y <= xy * x)
188     {
189         d = xy * x + (max - xy) * y;
190         if (xy * max < d)
191         {
192             x = (s8) (xy * max * x / d);
193             y = (s8) (xy * max * y / d);
194         }
195     }
196     else
197     {
198         d = xy * y + (max - xy) * x;
199         if (xy * max < d)
200         {
201             x = (s8) (xy * max * x / d);
202             y = (s8) (xy * max * y / d);
203         }
204     }
205 
206     *px = (s8) (signX * x);
207     *py = (s8) (signY * y);
208 }
209 
210 /*---------------------------------------------------------------------------*
211   Name:         ClampCircle
212 
213   Description:  Adjusts stick movement data.
214 
215   Arguments:    px          pointer to movement data in terms of x-axis
216                 py          pointer to movement data in terms of y-axis
217                 radius      max valid radius
218                 min         deadzone
219 
220   Returns:      None.
221  *---------------------------------------------------------------------------*/
ClampCircle(s8 * px,s8 * py,s8 radius,s8 min)222 static void ClampCircle(s8* px, s8* py, s8 radius, s8 min)
223 {
224     int x = *px;
225     int y = *py;
226     int squared;
227     int length;
228 
229     // Remove vertical zone
230     if (-min < x && x < min)
231     {
232         x = 0;
233     }
234     else if (0 < x)
235     {
236         x -= min;
237     }
238     else
239     {
240         x += min;
241     }
242 
243     // Remove horizontal zone
244     if (-min < y && y < min)
245     {
246         y = 0;
247     }
248     else if (0 < y)
249     {
250         y -= min;
251     }
252     else
253     {
254         y += min;
255     }
256 
257     // Clamp input to unit circle of radius
258     squared = x*x + y*y;
259     if (radius*radius < squared)
260     {
261         // Vector too long - clamp
262         length = (int) sqrtf(squared);
263         x = (x * radius) / length;
264         y = (y * radius) / length;
265     }
266 
267     *px = (s8)x;
268     *py = (s8)y;
269 }
270 
271 /*---------------------------------------------------------------------------*
272   Name:         ClampTrigger
273 
274   Description:  Adjusts trigger movement data
275 
276   Arguments:    trigger     trigger magnitude
277                 min         minimum valid value for trigger
278                 max         maximum valid value for trigger
279 
280   Returns:      None.
281  *---------------------------------------------------------------------------*/
ClampTrigger(u8 * trigger,u8 min,u8 max)282 static void ClampTrigger(u8* trigger, u8 min, u8 max)
283 {
284     if (*trigger <= min)
285     {
286         *trigger = 0;
287     }
288     else
289     {
290         if (max < *trigger)
291         {
292             *trigger = max;
293         }
294         *trigger -= min;
295     }
296 }
297 
298 /*---------------------------------------------------------------------------*
299   Name:         PADClamp
300 
301   Description:  Clamps game pad status.
302 
303   Arguments:    status      array[PAD_MAX_CONTROLLERS] of PADStatus to be
304                             clamped.
305 
306   Returns:      None.
307  *---------------------------------------------------------------------------*/
PADClamp(PADStatus * status)308 void PADClamp(PADStatus* status)
309 {
310     int i;
311 
312     for (i = 0; i < PAD_MAX_CONTROLLERS; i++, status++)
313     {
314         if (status->err != PAD_ERR_NONE)
315         {
316             continue;
317         }
318         ClampStick(&status->stickX,
319                    &status->stickY,
320                    ClampRegion.maxStick,
321                    ClampRegion.xyStick,
322                    ClampRegion.minStick);
323         ClampStick(&status->substickX,
324                    &status->substickY,
325                    ClampRegion.maxSubstick,
326                    ClampRegion.xySubstick,
327                    ClampRegion.minSubstick);
328         ClampTrigger(&status->triggerLeft,
329                      ClampRegion.minTrigger,
330                      ClampRegion.maxTrigger);
331         ClampTrigger(&status->triggerRight,
332                      ClampRegion.minTrigger,
333                      ClampRegion.maxTrigger);
334     }
335 }
336 
337 /*---------------------------------------------------------------------------*
338   Name:         PADClampCircle
339 
340   Description:  Clamps game pad status. Sticks get clamped to a circle.
341 
342   Arguments:    status      array[PAD_MAX_CONTROLLERS] of PADStatus to be
343                             clamped.
344 
345   Returns:      None.
346  *---------------------------------------------------------------------------*/
PADClampCircle(PADStatus * status)347 void PADClampCircle(PADStatus* status)
348 {
349     int i;
350 
351     for (i = 0; i < PAD_MAX_CONTROLLERS; i++, status++)
352     {
353         if (status->err != PAD_ERR_NONE)
354         {
355             continue;
356         }
357         ClampCircle(&status->stickX, &status->stickY,
358                     ClampRegion.radStick,
359                     ClampRegion.minStick);
360         ClampCircle(&status->substickX, &status->substickY,
361                     ClampRegion.radSubstick,
362                     ClampRegion.minSubstick);
363         ClampTrigger(&status->triggerLeft,
364                      ClampRegion.minTrigger,
365                      ClampRegion.maxTrigger);
366         ClampTrigger(&status->triggerRight,
367                      ClampRegion.minTrigger,
368                      ClampRegion.maxTrigger);
369     }
370 }
371