mirror of
				https://github.com/dolphin-emu/dolphin.git
				synced 2025-10-25 01:19:19 +00:00 
			
		
		
		
	git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@5835 8ced0084-cf51-0410-be5f-012b33b47a6e
		
			
				
	
	
		
			433 lines
		
	
	
	
		
			13 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			433 lines
		
	
	
	
		
			13 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| // Copyright (C) 2003 Dolphin Project.
 | |
| 
 | |
| // This program is free software: you can redistribute it and/or modify
 | |
| // it under the terms of the GNU General Public License as published by
 | |
| // the Free Software Foundation, version 2.0.
 | |
| 
 | |
| // This program is distributed in the hope that it will be useful,
 | |
| // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
| // GNU General Public License 2.0 for more details.
 | |
| 
 | |
| // A copy of the GPL 2.0 should have been included with the program.
 | |
| // If not, see http://www.gnu.org/licenses/
 | |
| 
 | |
| // Official SVN repository and contact information can be found at
 | |
| // http://code.google.com/p/dolphin-emu/
 | |
| 
 | |
| #include <vector>
 | |
| #include <string>
 | |
| 
 | |
| 
 | |
| #include "Common.h" // Common
 | |
| #include "MathUtil.h"
 | |
| #include "StringUtil.h" // for ArrayToString()
 | |
| #include "IniFile.h"
 | |
| #include "pluginspecs_wiimote.h"
 | |
| 
 | |
| #include "EmuDefinitions.h" // Local
 | |
| #include "main.h" 
 | |
| #include "wiimote_hid.h"
 | |
| #include "EmuSubroutines.h"
 | |
| #include "EmuMain.h"
 | |
| #include "Encryption.h" // for extension encryption
 | |
| #include "Config.h" // for g_Config
 | |
| 
 | |
| 
 | |
| namespace WiiMoteEmu
 | |
| {
 | |
| 
 | |
| //******************************************************************************
 | |
| // Accelerometer functions
 | |
| //******************************************************************************
 | |
| 
 | |
| // Wiimote accelerometer
 | |
| /* The accelerometer x, y and z values range from 0x00 to 0xff with the default
 | |
|    netural values being [y = 0x84, x = 0x84, z = 0x9f] according to a
 | |
|    source. The extremes are 0x00 for (-) and 0xff for (+). It's important that
 | |
|    all values are not 0x80, the mouse pointer can disappear from the screen
 | |
|    permanently then, until z is adjusted back. This is because the game detects
 | |
|    a steep pitch of the Wiimote then.
 | |
| 
 | |
| Wiimote Accelerometer Axes
 | |
| 
 | |
| + (- -- X -- +)
 | |
| |      ___  
 | |
| |     |   |\  -
 | |
| |     | + ||   \ 
 | |
|       | . ||    \
 | |
| Y     |. .||     Z
 | |
|       | . ||      \
 | |
| |     | . ||       \
 | |
| |     |___||        +
 | |
| -       ---
 | |
| 
 | |
| */
 | |
| 
 | |
| // Single shake step of all three directions
 | |
| void ShakeToAccelerometer(int &_x, int &_y, int &_z, STiltData &_TiltData)
 | |
| {
 | |
| 	switch(_TiltData.Shake)
 | |
| 	{
 | |
| 	case 0:
 | |
| 		_TiltData.Shake = -1;
 | |
| 		break;
 | |
| 	case 1:
 | |
| 	case 3:
 | |
| 		_x = g_wm.cal_zero.x / 2;
 | |
| 		_y = g_wm.cal_zero.y / 2;
 | |
| 		_z = g_wm.cal_zero.z / 2;
 | |
| 		break;
 | |
| 	case 5:
 | |
| 	case 7:
 | |
| 		_x = (0xFF - g_wm.cal_zero.x ) / 2;
 | |
| 		_y = (0xFF - g_wm.cal_zero.y ) / 2;
 | |
| 		_z = (0xFF - g_wm.cal_zero.z ) / 2;
 | |
| 		break;
 | |
| 	case 2:
 | |
| 		_x = 0x00;
 | |
| 		_y = 0x00;
 | |
| 		_z = 0x00;
 | |
| 		break;
 | |
| 	case 6:
 | |
| 		_x = 0xFF;
 | |
| 		_y = 0xFF;
 | |
| 		_z = 0xFF;
 | |
| 		break;
 | |
| 	case 4:
 | |
| 		_x = 0x80;
 | |
| 		_y = 0x80;
 | |
| 		_z = 0x80;
 | |
| 		break;
 | |
| 	default:
 | |
| 		_TiltData.Shake = -1;
 | |
| 		break;
 | |
| 	}
 | |
| 	_TiltData.Shake++;
 | |
| 	
 | |
| 	if (_TiltData.Shake != 0)
 | |
| 	{
 | |
| 		DEBUG_LOG(WIIMOTE, "Shake: %i - 0x%02x, 0x%02x, 0x%02x", _TiltData.Shake, _x, _y, _z);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /* Tilting by gamepad. We can guess that the game will calculate
 | |
|    roll and pitch and use them as measures of the tilting. We are
 | |
|    interested in this tilting range 90 to -90*/
 | |
| void TiltByGamepad(STiltData &_TiltData, int Type)
 | |
| {
 | |
| 	// Return if we have no pads
 | |
| 	if (NumGoodPads == 0) return;
 | |
| 
 | |
| 	/* Adjust the pad state values, including a downscaling from the original
 | |
| 	   0x8000 size values to 0x80. The only reason we do this is that the code
 | |
| 	   below crrently assume that the range is 0 to 255 for all axes. If we
 | |
| 	   lose any precision by doing this we could consider not doing this
 | |
| 	   adjustment. And instead for example upsize the XInput trigger from 0x80
 | |
| 	   to 0x8000. */
 | |
| 	int Lx = WiiMapping[g_ID].AxisState.Lx;
 | |
| 	int Ly = WiiMapping[g_ID].AxisState.Ly;
 | |
| 	int Rx = WiiMapping[g_ID].AxisState.Rx;
 | |
| 	int Ry = WiiMapping[g_ID].AxisState.Ry;
 | |
| 	int Tl = WiiMapping[g_ID].AxisState.Tl;
 | |
| 	int Tr = WiiMapping[g_ID].AxisState.Tr;
 | |
| 
 | |
| 	// Save the Range in degrees, 45 and 90 are good values in some games
 | |
| 	int RollRange = WiiMapping[g_ID].Tilt.RollRange;
 | |
| 	int PitchRange = WiiMapping[g_ID].Tilt.PitchRange;
 | |
| 
 | |
| 	// The trigger currently only controls pitch, no roll, no free swing
 | |
| 	if (Type == FROM_TRIGGER)
 | |
| 	{
 | |
| 		// Make the range the same dimension as the analog stick
 | |
| 		Tl = Tl / 2;
 | |
| 		Tr = Tr / 2;
 | |
| 		// Invert
 | |
| 		if (WiiMapping[g_ID].Tilt.PitchInvert) { Tl = -Tl; Tr = -Tr; }
 | |
| 		// The final value
 | |
| 		_TiltData.Pitch = (int)((float)PitchRange * ((float)(Tl - Tr) / 128.0f));
 | |
| 	}
 | |
| 
 | |
| 	/* For the analog stick roll is by default set to the X-axis, pitch is by
 | |
| 	   default set to the Y-axis.  By changing the axis mapping and the invert
 | |
| 	   options this can be altered in any way */
 | |
| 	else if (Type == FROM_ANALOG1)
 | |
| 	{
 | |
| 		// Adjust the trigger to go between negative and positive values
 | |
| 		Lx = Lx - 0x80;
 | |
| 		Ly = Ly - 0x80;
 | |
| 		// Invert
 | |
| 		if (WiiMapping[g_ID].Tilt.RollInvert) Lx = -Lx; // else Tr = -Tr;
 | |
| 		if (WiiMapping[g_ID].Tilt.PitchInvert) Ly = -Ly; // else Tr = -Tr;
 | |
| 		// Produce the final value
 | |
| 		_TiltData.Roll = (int)((RollRange) ? (float)RollRange * ((float)Lx / 128.0f) : Lx);
 | |
| 		_TiltData.Pitch = (int)((PitchRange) ? (float)PitchRange * ((float)Ly / 128.0f) : Ly);
 | |
| 	}
 | |
| 	// Otherwise we are using ANALOG2
 | |
| 	else
 | |
| 	{
 | |
| 		// Adjust the trigger to go between negative and positive values
 | |
| 		Rx = Rx - 0x80;
 | |
| 		Ry = Ry - 0x80;
 | |
| 		// Invert
 | |
| 		if (WiiMapping[g_ID].Tilt.RollInvert) Rx = -Rx; // else Tr = -Tr;
 | |
| 		if (WiiMapping[g_ID].Tilt.PitchInvert) Ry = -Ry; // else Tr = -Tr;
 | |
| 		// Produce the final value
 | |
| 		_TiltData.Roll = (int)((RollRange) ? (float)RollRange * ((float)Rx / 128.0f) : Rx);
 | |
| 		_TiltData.Pitch = (int)((PitchRange) ? (float)PitchRange * ((float)Ry / 128.0f) : Ry);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // Tilting by keyboard
 | |
| void TiltByKeyboard(STiltData &_TiltData, int Type)
 | |
| {
 | |
| #if defined(HAVE_WX) && HAVE_WX
 | |
| 	int _ROLL_LEFT_ = (Type) ? ENC_ROLL_L : EWM_ROLL_L;
 | |
| 	int _ROLL_RIGHT_ = (Type) ? ENC_ROLL_R : EWM_ROLL_R;
 | |
| 	int _PITCH_UP_ = (Type) ? ENC_PITCH_U : EWM_PITCH_U;
 | |
| 	int _PITCH_DOWN_ = (Type) ? ENC_PITCH_D : EWM_PITCH_D;
 | |
| 
 | |
| 	// Do roll/pitch or free swing
 | |
| 
 | |
| 	if (IsKey(_ROLL_LEFT_))
 | |
| 	{
 | |
| 		if (WiiMapping[g_ID].Tilt.RollRange)
 | |
| 		{
 | |
| 			// Stop at the lower end of the range
 | |
| 			if (_TiltData.Roll > -WiiMapping[g_ID].Tilt.RollRange)
 | |
| 				_TiltData.Roll -= 3; // aim left
 | |
| 		}
 | |
| 		else // Free swing
 | |
| 		{
 | |
| 			_TiltData.Roll = -0x80 / 2;
 | |
| 		}
 | |
| 	}
 | |
| 	else if (IsKey(_ROLL_RIGHT_))
 | |
| 	{
 | |
| 		if (WiiMapping[g_ID].Tilt.RollRange)
 | |
| 		{
 | |
| 			// Stop at the upper end of the range
 | |
| 			if (_TiltData.Roll < WiiMapping[g_ID].Tilt.RollRange)
 | |
| 				_TiltData.Roll += 3; // aim right
 | |
| 		}
 | |
| 		else // Free swing
 | |
| 		{
 | |
| 			_TiltData.Roll = 0x80 / 2;
 | |
| 		}
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		_TiltData.Roll = 0;
 | |
| 	}
 | |
| 	if (IsKey(_PITCH_UP_))
 | |
| 	{
 | |
| 		if (WiiMapping[g_ID].Tilt.PitchRange)
 | |
| 		{
 | |
| 			// Stop at the lower end of the range
 | |
| 			if (_TiltData.Pitch > -WiiMapping[g_ID].Tilt.PitchRange)
 | |
| 				_TiltData.Pitch -= 3; // aim down
 | |
| 		}
 | |
| 		else // Free swing
 | |
| 		{
 | |
| 			_TiltData.Pitch = -0x80 / 2;
 | |
| 		}
 | |
| 	}
 | |
| 	else if (IsKey(_PITCH_DOWN_))
 | |
| 	{
 | |
| 		if (WiiMapping[g_ID].Tilt.PitchRange)
 | |
| 		{
 | |
| 			// Stop at the upper end of the range
 | |
| 			if (_TiltData.Pitch < WiiMapping[g_ID].Tilt.PitchRange)
 | |
| 				_TiltData.Pitch += 3; // aim up
 | |
| 		}
 | |
| 		else // Free swing
 | |
| 		{
 | |
| 			_TiltData.Pitch = 0x80 / 2;
 | |
| 		}
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		_TiltData.Pitch = 0;
 | |
| 	}
 | |
| #endif
 | |
| }
 | |
| 
 | |
| // Tilting Wiimote (Wario Land aiming, Mario Kart steering and other things)
 | |
| void TiltWiimote(int &_x, int &_y, int &_z)
 | |
| {
 | |
| 	// Select input method and return the x, y, x values
 | |
| 	if ((WiiMapping[g_ID].UDPWM.instance)&&(WiiMapping[g_ID].UDPWM.enableAccel))
 | |
| 	{
 | |
| 		float x,y,z;
 | |
| 		WiiMapping[g_ID].UDPWM.instance->getAccel(x,y,z);
 | |
| 		float xg = WiiMoteEmu::g_wm.cal_g.x;
 | |
| 		float yg = WiiMoteEmu::g_wm.cal_g.y;
 | |
| 		float zg = WiiMoteEmu::g_wm.cal_g.z;
 | |
| 		_x = WiiMoteEmu::g_wm.cal_zero.x + (int)(xg * x);
 | |
| 		_y = WiiMoteEmu::g_wm.cal_zero.y + (int)(yg * y);
 | |
| 		_z = WiiMoteEmu::g_wm.cal_zero.z + (int)(zg * z);
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		if (WiiMapping[g_ID].Tilt.InputWM == FROM_KEYBOARD)
 | |
| 			TiltByKeyboard(WiiMapping[g_ID].Motion.TiltWM, 0);
 | |
| 		else
 | |
| 			TiltByGamepad(WiiMapping[g_ID].Motion.TiltWM, WiiMapping[g_ID].Tilt.InputWM);
 | |
| 
 | |
| 		// Adjust angles, it's only needed if both roll and pitch is used together
 | |
| 		if (WiiMapping[g_ID].Tilt.RollRange && WiiMapping[g_ID].Tilt.PitchRange)
 | |
| 			AdjustAngles(WiiMapping[g_ID].Motion.TiltWM.Roll, WiiMapping[g_ID].Motion.TiltWM.Pitch);
 | |
| 
 | |
| 		// Calculate the accelerometer value from this tilt angle
 | |
| 		TiltToAccelerometer(_x, _y, _z,WiiMapping[g_ID].Motion.TiltWM);
 | |
| 	}
 | |
| 	//DEBUG_LOG(WIIMOTE, "Roll:%i, Pitch:%i, _x:%u, _y:%u, _z:%u", g_Wiimote_kbd.TiltData.Roll, g_Wiimote_kbd.TiltData.Pitch, _x, _y, _z);
 | |
| }
 | |
| 
 | |
| // Tilting Nunchuck (Mad World, Dead Space and other things)
 | |
| void TiltNunchuck(int &_x, int &_y, int &_z)
 | |
| {
 | |
| 	// Select input method and return the x, y, x values
 | |
| 	if (WiiMapping[g_ID].Tilt.InputNC == FROM_KEYBOARD)
 | |
| 		TiltByKeyboard(WiiMapping[g_ID].Motion.TiltNC, 1);
 | |
| 	else
 | |
| 		TiltByGamepad(WiiMapping[g_ID].Motion.TiltNC, WiiMapping[g_ID].Tilt.InputNC);
 | |
| 
 | |
| 	// Adjust angles, it's only needed if both roll and pitch is used together
 | |
| 	if (WiiMapping[g_ID].Tilt.RollRange && WiiMapping[g_ID].Tilt.PitchRange)
 | |
| 		AdjustAngles(WiiMapping[g_ID].Motion.TiltNC.Roll, WiiMapping[g_ID].Motion.TiltNC.Pitch);
 | |
| 
 | |
| 	// Calculate the accelerometer value from this tilt angle
 | |
| 	TiltToAccelerometer(_x, _y, _z, WiiMapping[g_ID].Motion.TiltNC);
 | |
| 
 | |
| 	//DEBUG_LOG(WIIMOTE, "Roll:%i, Pitch:%i, _x:%u, _y:%u, _z:%u", g_NunchuckExt.TiltData.Roll, g_NunchuckExt.TiltData.Pitch, _x, _y, _z);
 | |
| }
 | |
| 
 | |
| /* Angles adjustment for the upside down state when both roll and pitch is
 | |
|    used. When the absolute values of the angles go over 90 the Wiimote is
 | |
|    upside down and these adjustments are needed. */
 | |
| void AdjustAngles(int &Roll, int &Pitch)
 | |
| {
 | |
| 	int OldPitch = Pitch;
 | |
| 
 | |
| 	if (abs(Roll) > 90)
 | |
| 	{
 | |
| 		if (Pitch >= 0)
 | |
| 			Pitch = 180 - Pitch; // 15 to 165
 | |
| 		else
 | |
| 			Pitch = -180 - Pitch; // -15 to -165
 | |
| 	}	
 | |
| 
 | |
| 	if (abs(OldPitch) > 90)
 | |
| 	{
 | |
| 		if (Roll >= 0)
 | |
| 			Roll = 180 - Roll; // 15 to 165
 | |
| 		else		
 | |
| 			Roll = -180 - Roll; // -15 to -165
 | |
| 	}	
 | |
| }
 | |
| 
 | |
| 
 | |
| // Angles to accelerometer values
 | |
| void TiltToAccelerometer(int &_x, int &_y, int &_z, STiltData &_TiltData)
 | |
| {
 | |
| 	if (_TiltData.Roll == 0 && _TiltData.Pitch == 0)
 | |
| 		return;
 | |
| 
 | |
| 	// We need radiands for the math functions
 | |
| 	float Roll = InputCommon::Deg2Rad((float)_TiltData.Roll);
 | |
| 	float Pitch = InputCommon::Deg2Rad((float)_TiltData.Pitch);
 | |
| 	// We need float values
 | |
| 	float x = 0.0f, y = 0.0f, z = 1.0f; // Gravity
 | |
| 
 | |
| 	// In these cases we can use the simple and accurate formula
 | |
| 	if(WiiMapping[g_ID].Tilt.RollRange && !WiiMapping[g_ID].Tilt.PitchRange)
 | |
| 	{
 | |
| 		x = sin(Roll);
 | |
| 		z = cos(Roll);
 | |
| 	}
 | |
| 	else if (WiiMapping[g_ID].Tilt.PitchRange && !WiiMapping[g_ID].Tilt.RollRange)
 | |
| 	{
 | |
| 		y = sin(Pitch);
 | |
| 		z = cos(Pitch);
 | |
| 	}
 | |
| 	else if(WiiMapping[g_ID].Tilt.RollRange && WiiMapping[g_ID].Tilt.PitchRange)
 | |
| 	{
 | |
| 		// ====================================================
 | |
| 		/* This seems to always produce the exact same combination of x, y, z
 | |
| 		   and Roll and Pitch that the real Wiimote produce. There is an
 | |
| 		   unlimited amount of x, y, z combinations for any combination of Roll
 | |
| 		   and Pitch. But if we select a Z from the smallest of the absolute
 | |
| 		   value of cos(Roll) and cos (Pitch) we get the right values. */
 | |
| 		// ---------	
 | |
| 		if (abs(cos(Roll)) < abs(cos(Pitch)))
 | |
| 			z = cos(Roll);
 | |
| 		else
 | |
| 			z = cos(Pitch);
 | |
| 		/* I got these from reversing the calculation in
 | |
| 		   PitchAccelerometerToDegree() in a math program. */
 | |
| 		float x_num = 2 * tanf(0.5f * Roll) * z;
 | |
| 		float x_den = pow2f(tanf(0.5f * Roll)) - 1;
 | |
| 		x = - (x_num / x_den);
 | |
| 		float y_num = 2 * tanf(0.5f * Pitch) * z;
 | |
| 		float y_den = pow2f(tanf(0.5f * Pitch)) - 1;
 | |
| 		y = - (y_num / y_den);
 | |
| 	}
 | |
| 
 | |
| 	// Multiply with neutral value and its g
 | |
| 	float xg = g_wm.cal_g.x;
 | |
| 	float yg = g_wm.cal_g.y;
 | |
| 	float zg = g_wm.cal_g.z;
 | |
| 	int ix = g_wm.cal_zero.x + (int)(xg * x);
 | |
| 	int iy = g_wm.cal_zero.y + (int)(yg * y);
 | |
| 	int iz = g_wm.cal_zero.z + (int)(zg * z);
 | |
| 
 | |
| 	if (!WiiMapping[g_ID].bUpright)
 | |
| 	{
 | |
| 		if(WiiMapping[g_ID].Tilt.RollRange) _x = ix;
 | |
| 		if(WiiMapping[g_ID].Tilt.PitchRange) _y = iy;
 | |
| 		_z = iz;
 | |
| 	}
 | |
| 	else	// Upright wiimote
 | |
| 	{
 | |
| 		if(WiiMapping[g_ID].Tilt.RollRange) _x = ix;
 | |
| 		if(WiiMapping[g_ID].Tilt.PitchRange) _z = iy;
 | |
| 		_y = 0xFF - iz;
 | |
| 	}
 | |
| 
 | |
| 	// Direct mapping for swing, from analog stick to accelerometer
 | |
| 	if (!WiiMapping[g_ID].Tilt.RollRange)
 | |
| 	{
 | |
| 		_x -= _TiltData.Roll;
 | |
| 	}
 | |
| 	if (!WiiMapping[g_ID].Tilt.PitchRange)
 | |
| 	{
 | |
| 		if (!WiiMapping[g_ID].bUpright)
 | |
| 			_z -= _TiltData.Pitch;
 | |
| 		else	// Upright wiimote
 | |
| 			_y += _TiltData.Pitch;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // Rotate IR dot when rolling Wiimote
 | |
| void RotateIRDot(int &_x, int &_y, STiltData &_TiltData)
 | |
| {
 | |
| 	if (!WiiMapping[g_ID].Tilt.RollRange || !_TiltData.Roll)
 | |
| 		return;
 | |
| 
 | |
| 	// The IR camera resolution is 1023x767
 | |
| 	float dot_x = _x - 1023.0f / 2;
 | |
| 	float dot_y = _y - 767.0f / 2;
 | |
| 
 | |
| 	float radius = sqrt(pow(dot_x, 2) + pow(dot_y, 2));
 | |
| 	float radian = atan2(dot_y, dot_x);
 | |
| 
 | |
| 	_x = (int)(radius * cos(radian + InputCommon::Deg2Rad((float)_TiltData.Roll)) + 1023.0f / 2);
 | |
| 	_y = (int)(radius * sin(radian + InputCommon::Deg2Rad((float)_TiltData.Roll)) + 767.0f / 2);
 | |
| 
 | |
| 	// Out of sight check
 | |
| 	if (_x < 0 || _x > 1023) _x = 0xFFFF;
 | |
| 	if (_y < 0 || _y > 767) _y = 0xFFFF;
 | |
| }
 | |
| 
 | |
| } // WiiMoteEmu
 |