├── Manual ├── warp.jpg ├── scale.jpg ├── leap_warp.gif ├── menu_tool.png ├── play_tool.png ├── warp_tool.png ├── leap_pinch.gif ├── maestro_tool.png └── laser_pointer.jpg ├── Libs ├── MaestroMode.dll ├── WindowsInput.dll ├── Assembly-CSharp.dll ├── XInputDotNetPure.dll ├── CameraModifications.dll ├── Assembly-CSharp_Studio.dll └── Injector │ ├── IllusionPlugin.dll │ ├── IllusionInjector.dll │ └── IllusionPlugin.XML ├── .gitmodules ├── PlayClubVR ├── Images │ └── icon_maestro.png ├── PlayClubSettings.cs ├── Controller.cs ├── PlayClubVoiceCommands.cs ├── Properties │ └── AssemblyInfo.cs ├── PlayClubVR.cs ├── LookAtMeHandler.cs ├── PlayClubContext.cs ├── PlayClubActor.cs ├── OSP │ └── OSPAudioVolumeManager.cs ├── TransientHead.cs ├── DynamicColliderRegistry.cs ├── MaestroTool.cs ├── PlayClubStandingMode.cs ├── PlayClubVR.csproj ├── PlayClubInterpreter.cs ├── PlayClubSeatedMode.cs ├── PlayTool.cs └── ImpersonationHandler.cs ├── GamePadClub ├── Libs │ └── XInputInterface.dll ├── GamePadHelper.cs ├── GamePadClub.cs ├── Properties │ └── AssemblyInfo.cs ├── GamePadController.cs ├── Handlers │ ├── MouseHandler.cs │ ├── CameraHandler.cs │ └── GameControlHandler.cs └── GamePadClub.csproj ├── PlayClubStudioVR ├── StudioActor.cs ├── StudioSeatedMode.cs ├── StudioStandingMode.cs ├── ShisenCorrecter.cs ├── EyeTarget.cs ├── PlayClubStudioVR.cs ├── StudioInterpreter.cs ├── PlayClubStudioVR.csproj └── IKTool.cs ├── LICENSE ├── PlayClubVR.sln ├── .gitignore ├── README_JA.md └── README.md /Manual/warp.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Eusth/PlayClubVR/HEAD/Manual/warp.jpg -------------------------------------------------------------------------------- /Manual/scale.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Eusth/PlayClubVR/HEAD/Manual/scale.jpg -------------------------------------------------------------------------------- /Libs/MaestroMode.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Eusth/PlayClubVR/HEAD/Libs/MaestroMode.dll -------------------------------------------------------------------------------- /Manual/leap_warp.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Eusth/PlayClubVR/HEAD/Manual/leap_warp.gif -------------------------------------------------------------------------------- /Manual/menu_tool.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Eusth/PlayClubVR/HEAD/Manual/menu_tool.png -------------------------------------------------------------------------------- /Manual/play_tool.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Eusth/PlayClubVR/HEAD/Manual/play_tool.png -------------------------------------------------------------------------------- /Manual/warp_tool.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Eusth/PlayClubVR/HEAD/Manual/warp_tool.png -------------------------------------------------------------------------------- /Libs/WindowsInput.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Eusth/PlayClubVR/HEAD/Libs/WindowsInput.dll -------------------------------------------------------------------------------- /Manual/leap_pinch.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Eusth/PlayClubVR/HEAD/Manual/leap_pinch.gif -------------------------------------------------------------------------------- /Manual/maestro_tool.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Eusth/PlayClubVR/HEAD/Manual/maestro_tool.png -------------------------------------------------------------------------------- /Libs/Assembly-CSharp.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Eusth/PlayClubVR/HEAD/Libs/Assembly-CSharp.dll -------------------------------------------------------------------------------- /Libs/XInputDotNetPure.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Eusth/PlayClubVR/HEAD/Libs/XInputDotNetPure.dll -------------------------------------------------------------------------------- /Manual/laser_pointer.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Eusth/PlayClubVR/HEAD/Manual/laser_pointer.jpg -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "VRGIN"] 2 | path = VRGIN 3 | url = https://github.com/Eusth/VRGIN.Core.git 4 | -------------------------------------------------------------------------------- /Libs/CameraModifications.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Eusth/PlayClubVR/HEAD/Libs/CameraModifications.dll -------------------------------------------------------------------------------- /Libs/Assembly-CSharp_Studio.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Eusth/PlayClubVR/HEAD/Libs/Assembly-CSharp_Studio.dll -------------------------------------------------------------------------------- /Libs/Injector/IllusionPlugin.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Eusth/PlayClubVR/HEAD/Libs/Injector/IllusionPlugin.dll -------------------------------------------------------------------------------- /Libs/Injector/IllusionInjector.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Eusth/PlayClubVR/HEAD/Libs/Injector/IllusionInjector.dll -------------------------------------------------------------------------------- /PlayClubVR/Images/icon_maestro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Eusth/PlayClubVR/HEAD/PlayClubVR/Images/icon_maestro.png -------------------------------------------------------------------------------- /GamePadClub/Libs/XInputInterface.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Eusth/PlayClubVR/HEAD/GamePadClub/Libs/XInputInterface.dll -------------------------------------------------------------------------------- /PlayClubVR/PlayClubSettings.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Xml.Serialization; 6 | using VRGIN.Core; 7 | 8 | namespace PlayClubVR 9 | { 10 | [XmlRoot("Settings")] 11 | public class PlayClubSettings : VRSettings 12 | { 13 | 14 | public bool FullImpersonation { get; set; } 15 | 16 | /// 17 | /// Sets or gets whether or not the girls should look at the player by default. 18 | /// 19 | public bool AutoLookAtMe { get { return _AutoLookAtMe; } set { _AutoLookAtMe = value; TriggerPropertyChanged("AutoLookAtMe"); } } 20 | private bool _AutoLookAtMe = true; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /PlayClubStudioVR/StudioActor.cs: -------------------------------------------------------------------------------- 1 | using PlayClubVR; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using VRGIN.Core; 7 | 8 | namespace PlayClubStudioVR 9 | { 10 | public class StudioActor : PlayClubActor 11 | { 12 | public StudioActor(Human nativeActor) : base(nativeActor) 13 | { 14 | } 15 | 16 | protected override void Initialize(Human actor) 17 | { 18 | base.Initialize(actor); 19 | 20 | if(actor.sex == Human.SEX.FEMALE) 21 | { 22 | var correcter = actor.gameObject.AddComponent(); 23 | correcter.Actor = this; 24 | Logger.Info("Attached Shisen Correcter to {0}", actor.CharaType); 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /PlayClubStudioVR/StudioSeatedMode.cs: -------------------------------------------------------------------------------- 1 | using PlayClubVR; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using VRGIN.Core; 7 | 8 | namespace PlayClubStudioVR 9 | { 10 | class StudioSeatedMode : PlayClubSeatedMode 11 | { 12 | protected override void ChangeMode() 13 | { 14 | VR.Manager.SetMode(); 15 | } 16 | 17 | public override IEnumerable Tools 18 | { 19 | get 20 | { 21 | return base.Tools 22 | // Remove unneeded 23 | .Except((new Type[] { typeof(PlayTool), typeof(MaestroTool) })) 24 | // Add new 25 | .Concat(new Type[] { typeof(IKTool) }); 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /PlayClubVR/Controller.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using UnityEngine; 6 | using VRGIN.Core; 7 | using VRGIN.Controls; 8 | 9 | namespace PlayClubVR 10 | { 11 | public class PlayClubController : Controller 12 | { 13 | public static PlayClubController Create() 14 | { 15 | var leftHand = new GameObject("A Controller").AddComponent(); 16 | 17 | return leftHand; 18 | } 19 | 20 | 21 | //void OnTriggerEnter(Collider collider) 22 | //{ 23 | // var boneCollider = collider.GetComponentInChildren(); 24 | // if(boneCollider) 25 | // { 26 | // boneCollider.Collide() 27 | // } 28 | //} 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /GamePadClub/GamePadHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using XInputDotNetPure; 6 | 7 | namespace GamePadClub 8 | { 9 | public static class GamePadHelper 10 | { 11 | public static bool IsPressDown(ButtonState now, ButtonState before) 12 | { 13 | return now == ButtonState.Pressed && before == ButtonState.Released; 14 | } 15 | 16 | public static bool IsPressUp(ButtonState now, ButtonState before) 17 | { 18 | return now == ButtonState.Released && before == ButtonState.Pressed; 19 | } 20 | 21 | public static bool IsShoulderPressed(GamePadState state) 22 | { 23 | return state.Buttons.LeftShoulder == ButtonState.Pressed || state.Buttons.RightShoulder == ButtonState.Pressed; 24 | } 25 | 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /PlayClubStudioVR/StudioStandingMode.cs: -------------------------------------------------------------------------------- 1 | using PlayClubVR; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using VRGIN.Core; 7 | 8 | namespace PlayClubStudioVR 9 | { 10 | class StudioStandingMode : PlayClubStandingMode 11 | { 12 | protected override void ChangeMode() 13 | { 14 | VR.Manager.SetMode(); 15 | } 16 | 17 | protected override void CreateControllers() 18 | { 19 | base.CreateControllers(); 20 | } 21 | 22 | public override IEnumerable Tools 23 | { 24 | get 25 | { 26 | return base.Tools 27 | // Remove unneeded 28 | .Except((new Type[] { typeof(PlayTool), typeof(MaestroTool) })) 29 | // Add new 30 | .Concat(new Type[] { typeof(IKTool) }); 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Eusth 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /GamePadClub/GamePadClub.cs: -------------------------------------------------------------------------------- 1 | using GamePadClub.Handlers; 2 | using IllusionPlugin; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | 8 | namespace GamePadClub 9 | { 10 | public class GamePadClub : IPlugin 11 | { 12 | public string Name 13 | { 14 | get 15 | { 16 | return "GamePadClub"; 17 | } 18 | } 19 | 20 | public string Version 21 | { 22 | get 23 | { 24 | return "0.1"; 25 | } 26 | } 27 | 28 | public void OnApplicationQuit() 29 | { 30 | } 31 | 32 | public void OnApplicationStart() 33 | { 34 | var instance = GamePadController.Instance; 35 | instance.gameObject.AddComponent(); 36 | instance.gameObject.AddComponent(); 37 | instance.gameObject.AddComponent(); 38 | } 39 | 40 | public void OnFixedUpdate() 41 | { 42 | } 43 | 44 | public void OnLevelWasInitialized(int level) 45 | { 46 | } 47 | 48 | public void OnLevelWasLoaded(int level) 49 | { 50 | } 51 | 52 | public void OnUpdate() 53 | { 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /PlayClubVR/PlayClubVoiceCommands.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using VRGIN.Controls.Speech; 6 | 7 | namespace PlayClubVR 8 | { 9 | public class PlayClubVoiceCommand : VoiceCommand 10 | { 11 | public static readonly VoiceCommand NextAnimation = new PlayClubVoiceCommand("next"); 12 | public static readonly VoiceCommand PreviousAnimation = new PlayClubVoiceCommand("previous"); 13 | public static readonly VoiceCommand StartAnimation = new PlayClubVoiceCommand("start"); 14 | public static readonly VoiceCommand StopAnimation = new PlayClubVoiceCommand("stop"); 15 | public static readonly VoiceCommand Faster = new PlayClubVoiceCommand("faster", "increase speed"); 16 | public static readonly VoiceCommand Slower = new PlayClubVoiceCommand("slower", "decrease speed"); 17 | public static readonly VoiceCommand Climax = new PlayClubVoiceCommand("climax", "come"); 18 | public static readonly VoiceCommand DisableClimax = new PlayClubVoiceCommand("disable climax", "disable orgasm"); 19 | public static readonly VoiceCommand EnableClimax = new PlayClubVoiceCommand("enable climax", "enable orgasm"); 20 | 21 | protected PlayClubVoiceCommand(params string[] texts) : base(texts) 22 | { 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /GamePadClub/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("GamePadClub")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("GamePadClub")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("8cf2e8ae-b1ca-4b00-b997-74622bdaf627")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /PlayClubStudioVR/ShisenCorrecter.cs: -------------------------------------------------------------------------------- 1 | using PlayClubVR; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Reflection; 6 | using System.Text; 7 | using UnityEngine; 8 | using VRGIN.Core; 9 | 10 | namespace PlayClubStudioVR 11 | { 12 | class ShisenCorrecter : MonoBehaviour { 13 | 14 | private Human _Human; 15 | public PlayClubActor Actor; 16 | 17 | void Start() 18 | { 19 | _Human = GetComponent(); 20 | } 21 | 22 | 23 | void Update() 24 | { 25 | if (!_Human) return; 26 | 27 | var eyeLookCtrl = _Human.eyeLook; 28 | var targetCtrl = _Human.bodyRoot.GetComponent(); 29 | 30 | // Make a new EyeTargetController if required 31 | if (targetCtrl == null) 32 | { 33 | targetCtrl = _Human.bodyRoot.gameObject.AddComponent(); 34 | targetCtrl.rootNode = Actor.Eyes; 35 | } 36 | 37 | // Replace camera targets if required 38 | if (eyeLookCtrl.Target.camera == Camera.main) 39 | { 40 | eyeLookCtrl.Change(LookAtRotator.TYPE.TARGET, targetCtrl.Target, true); 41 | } 42 | 43 | var neckLookCtrl = _Human.neckLook; 44 | if (neckLookCtrl.Target.camera == Camera.main) 45 | { 46 | neckLookCtrl.Change(LookAtRotator.TYPE.TARGET, targetCtrl.Target, true); 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /PlayClubVR/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("PlayClubVR")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("PlayClubVR")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("3b4fb983-3996-455b-a81b-d0c023f3fbbc")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /PlayClubStudioVR/EyeTarget.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using UnityEngine; 6 | using VRGIN.Core; 7 | 8 | namespace PlayClubStudioVR 9 | { 10 | /** 11 | * Manages the eye target for a single female. 12 | */ 13 | class EyeTargetController : MonoBehaviour 14 | { 15 | public Transform Target { get; private set; } 16 | public Transform rootNode; 17 | private EyeTarget eyeTarget; 18 | 19 | void Start() 20 | { 21 | Target = new GameObject().transform; 22 | 23 | eyeTarget = Target.gameObject.AddComponent(); 24 | eyeTarget.rootNode = rootNode; 25 | } 26 | 27 | 28 | void OnDestroy() 29 | { 30 | // Character was destroyed, so destroy the created target! 31 | Destroy(Target.gameObject); 32 | } 33 | } 34 | 35 | /// 36 | /// Represents a target that can be looked at. 37 | /// 38 | class EyeTarget : MonoBehaviour 39 | { 40 | /// 41 | /// Offset in meters from the camera. 42 | /// 43 | public float offset = 0.5f; 44 | 45 | /// 46 | /// Origin of the gaze. 47 | /// 48 | public Transform rootNode; 49 | 50 | void Update() 51 | { 52 | if (rootNode != null) 53 | { 54 | var camera = VR.Camera.SteamCam.head.transform; 55 | var dir = (camera.position - rootNode.position).normalized; 56 | 57 | transform.position = camera.position + dir * offset; 58 | } 59 | 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /PlayClubVR/PlayClubVR.cs: -------------------------------------------------------------------------------- 1 | using IllusionPlugin; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using VRGIN.Core; 7 | using VRGIN.Modes; 8 | 9 | namespace PlayClubVR 10 | { 11 | public class PlayClubVR : IEnhancedPlugin 12 | { 13 | public string[] Filter 14 | { 15 | get 16 | { 17 | return new string[] { "PlayClub" }; 18 | } 19 | } 20 | 21 | public string Name 22 | { 23 | get 24 | { 25 | return "PlayClubVR"; 26 | } 27 | } 28 | 29 | public string Version 30 | { 31 | get 32 | { 33 | return "0.5"; 34 | } 35 | } 36 | 37 | public void OnApplicationQuit() 38 | { 39 | } 40 | 41 | public void OnApplicationStart() 42 | { 43 | if (Environment.CommandLine.Contains("--vr")) 44 | { 45 | var manager = VRManager.Create(new PlayClubContext()); 46 | manager.SetMode(); 47 | } 48 | if(Environment.CommandLine.Contains("--verbose")) 49 | { 50 | Logger.Level = Logger.LogMode.Debug; 51 | } 52 | } 53 | 54 | public void OnFixedUpdate() 55 | { 56 | } 57 | 58 | public void OnLateUpdate() 59 | { 60 | } 61 | 62 | public void OnLevelWasInitialized(int level) 63 | { 64 | } 65 | 66 | public void OnLevelWasLoaded(int level) 67 | { 68 | } 69 | 70 | public void OnUpdate() 71 | { 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /PlayClubStudioVR/PlayClubStudioVR.cs: -------------------------------------------------------------------------------- 1 | using IllusionPlugin; 2 | using PlayClubVR; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using VRGIN.Core; 8 | 9 | namespace PlayClubStudioVR 10 | { 11 | public class PlayClubStudioVR : IEnhancedPlugin 12 | { 13 | public string[] Filter 14 | { 15 | get 16 | { 17 | return new string[] { "PlayClubStudio" }; 18 | } 19 | } 20 | 21 | public string Name 22 | { 23 | get 24 | { 25 | return "Play Club Studio VR"; 26 | } 27 | } 28 | 29 | public string Version 30 | { 31 | get 32 | { 33 | return "0.5"; 34 | } 35 | } 36 | 37 | public void OnApplicationQuit() 38 | { 39 | 40 | } 41 | 42 | public void OnApplicationStart() 43 | { 44 | if (Environment.CommandLine.Contains("--vr")) 45 | { 46 | var manager = VRManager.Create(new PlayClubContext()); 47 | manager.SetMode(); 48 | } 49 | if (Environment.CommandLine.Contains("--verbose")) 50 | { 51 | Logger.Level = Logger.LogMode.Debug; 52 | } 53 | } 54 | 55 | public void OnFixedUpdate() 56 | { 57 | } 58 | 59 | public void OnLateUpdate() 60 | { 61 | } 62 | 63 | public void OnLevelWasInitialized(int level) 64 | { 65 | } 66 | 67 | public void OnLevelWasLoaded(int level) 68 | { 69 | } 70 | 71 | public void OnUpdate() 72 | { 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /PlayClubVR/LookAtMeHandler.cs: -------------------------------------------------------------------------------- 1 | using CameraModifications; 2 | using IllusionInjector; 3 | using System; 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using VRGIN.Core; 9 | 10 | namespace PlayClubVR 11 | { 12 | class LookAtMeHandler : ProtectedBehaviour 13 | { 14 | 15 | //CameraModifications.KocchiMitePlugin _KocchitMite; 16 | 17 | protected override void OnStart() 18 | { 19 | base.OnStart(); 20 | if(!PluginManager.Plugins.Any(plugin => plugin.GetType().FullName == "CameraModifications.KocchiMitePlugin")) 21 | { 22 | VRLog.Warn("Didn't find Kocchi Mite plugin"); 23 | 24 | enabled = false; 25 | return; 26 | } 27 | 28 | VRLog.Info("Found Kocchi Mite plugin"); 29 | 30 | } 31 | 32 | protected override void OnLevel(int level) 33 | { 34 | base.OnLevel(level); 35 | 36 | if ((VR.Settings as PlayClubSettings).AutoLookAtMe) { 37 | StartCoroutine(WaitALittle(delegate 38 | { 39 | VRLog.Info("Setting gaze to camera"); 40 | KocchiMite.HeadLookType = LookAtRotator.TYPE.TARGET; 41 | KocchiMite.LookType = LookAtRotator.TYPE.TARGET; 42 | })); 43 | } 44 | } 45 | 46 | private KocchiMitePlugin KocchiMite 47 | { 48 | get 49 | { 50 | return PluginManager.Plugins.OfType().First(); 51 | } 52 | } 53 | 54 | IEnumerator WaitALittle(Action action) 55 | { 56 | yield return null; 57 | yield return null; 58 | 59 | action(); 60 | } 61 | 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /GamePadClub/GamePadController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using UnityEngine; 6 | using XInputDotNetPure; 7 | 8 | namespace GamePadClub 9 | { 10 | public class GamePadController : MonoBehaviour 11 | { 12 | public delegate bool GamePadConsumer(GamePadState nowState, GamePadState prevState); 13 | 14 | private static GamePadController _Instance; 15 | private List _Consumers = new List(); 16 | 17 | private GamePadState? _PrevState; 18 | 19 | public static GamePadController Instance 20 | { 21 | get 22 | { 23 | if (!_Instance) 24 | { 25 | _Instance = new GameObject("GamePadClub_Controller").AddComponent(); 26 | DontDestroyOnLoad(_Instance.gameObject); 27 | } 28 | return _Instance; 29 | } 30 | } 31 | 32 | public void Register(GamePadConsumer consumer) 33 | { 34 | _Consumers.Add(consumer); 35 | } 36 | 37 | public void RegisterAtStart(GamePadConsumer consumer) 38 | { 39 | _Consumers.Insert(0, consumer); 40 | } 41 | public bool Unregister(GamePadConsumer consumer) 42 | { 43 | return _Consumers.Remove(consumer); 44 | } 45 | 46 | 47 | public void FixedUpdate() 48 | { 49 | GamePadState state = GamePad.GetState(PlayerIndex.One); 50 | 51 | if (_PrevState != null && state.IsConnected) 52 | { 53 | foreach (var consumer in _Consumers) 54 | { 55 | if (consumer(state, _PrevState.Value)) 56 | { 57 | break; 58 | } 59 | } 60 | } 61 | 62 | _PrevState = state; 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /GamePadClub/Handlers/MouseHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using UnityEngine; 6 | using WindowsInput; 7 | using XInputDotNetPure; 8 | 9 | namespace GamePadClub.Handlers 10 | { 11 | public class MouseHandler : MonoBehaviour 12 | { 13 | private const float TRIGGER_THRESHOLD = 0.5f; 14 | InputSimulator _Input; 15 | 16 | void Awake() 17 | { 18 | _Input = new InputSimulator(); 19 | } 20 | 21 | void OnEnable() 22 | { 23 | GamePadController.Instance.Register(MoveMouse); 24 | GamePadController.Instance.Register(HandleClicks); 25 | } 26 | 27 | void OnDisable() 28 | { 29 | GamePadController.Instance.Unregister(HandleClicks); 30 | GamePadController.Instance.Unregister(MoveMouse); 31 | } 32 | 33 | bool HandleClicks(GamePadState now, GamePadState before) 34 | { 35 | if (GamePadHelper.IsShoulderPressed(now)) return false; 36 | 37 | // Left click 38 | if (GamePadHelper.IsPressDown(now.Buttons.A, before.Buttons.A)) 39 | { 40 | _Input.Mouse.LeftButtonDown(); 41 | } 42 | else if (GamePadHelper.IsPressUp(now.Buttons.A, before.Buttons.A)) 43 | { 44 | _Input.Mouse.LeftButtonUp(); 45 | } 46 | 47 | // Right click 48 | if (GamePadHelper.IsPressDown(now.Buttons.B, before.Buttons.B)) 49 | { 50 | _Input.Mouse.RightButtonDown(); 51 | } 52 | else if (GamePadHelper.IsPressUp(now.Buttons.B, before.Buttons.B)) 53 | { 54 | _Input.Mouse.RightButtonUp(); 55 | } 56 | 57 | return false; 58 | } 59 | 60 | bool MoveMouse(GamePadState now, GamePadState before) 61 | { 62 | if (now.Triggers.Left < TRIGGER_THRESHOLD && now.Triggers.Right < TRIGGER_THRESHOLD && !GamePadHelper.IsShoulderPressed(now)) 63 | { 64 | // Move cursor 65 | _Input.Mouse.MoveMouseBy((int)(now.ThumbSticks.Left.X * Time.deltaTime * 700), 66 | (int)(-now.ThumbSticks.Left.Y * Time.deltaTime * 700)); 67 | } 68 | 69 | return false; 70 | } 71 | 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /PlayClubVR/PlayClubContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using UnityEngine; 6 | using VRGIN.Controls.Speech; 7 | using VRGIN.Core; 8 | using VRGIN.Visuals; 9 | 10 | namespace PlayClubVR 11 | { 12 | public class PlayClubContext : IVRManagerContext 13 | { 14 | 15 | private DefaultMaterialPalette _Materials; 16 | private VRSettings _Settings; 17 | 18 | public PlayClubContext() 19 | { 20 | _Materials = new DefaultMaterialPalette(); 21 | _Settings = VRSettings.Load("vr_settings.xml"); 22 | 23 | _Materials.StandardShader = Shader.Find("Marmoset/Specular IBL"); 24 | } 25 | 26 | public string GuiLayer 27 | { 28 | get 29 | { 30 | return "Ignore Raycast"; 31 | } 32 | } 33 | 34 | public string InvisibleLayer 35 | { 36 | get 37 | { 38 | return "ScreenShot"; 39 | } 40 | } 41 | 42 | public IMaterialPalette Materials 43 | { 44 | get 45 | { 46 | return _Materials; 47 | } 48 | } 49 | 50 | public Color PrimaryColor 51 | { 52 | get 53 | { 54 | return Color.cyan; 55 | } 56 | } 57 | 58 | public VRSettings Settings 59 | { 60 | get 61 | { 62 | return _Settings; 63 | } 64 | } 65 | 66 | public int UILayerMask 67 | { 68 | get 69 | { 70 | return LayerMask.GetMask(UILayer); 71 | } 72 | } 73 | 74 | public string UILayer 75 | { 76 | get 77 | { 78 | return "UI"; 79 | } 80 | } 81 | 82 | public bool SimulateCursor 83 | { 84 | get 85 | { 86 | return false; 87 | } 88 | } 89 | 90 | public bool GUIAlternativeSortingMode 91 | { 92 | get 93 | { 94 | return true; 95 | } 96 | } 97 | 98 | public Type VoiceCommandType 99 | { 100 | get 101 | { 102 | return typeof(PlayClubVoiceCommand); 103 | } 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /PlayClubStudioVR/StudioInterpreter.cs: -------------------------------------------------------------------------------- 1 | using PlayClubVR; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using UnityEngine; 7 | using VRGIN.Core; 8 | 9 | namespace PlayClubStudioVR 10 | { 11 | public class StudioInterpreter : GameInterpreter 12 | { 13 | public StudioScene _Scene; 14 | 15 | private List _Actors = new List(); 16 | private readonly int pngLayer = LayerMask.NameToLayer("PNG"); 17 | public override IEnumerable Actors 18 | { 19 | get { return _Actors; } 20 | } 21 | 22 | protected override void OnStart() 23 | { 24 | base.OnStart(); 25 | InitScene(); 26 | } 27 | 28 | 29 | protected override void OnLevel(int level) 30 | { 31 | base.OnLevel(level); 32 | InitScene(); 33 | } 34 | 35 | protected override void OnUpdate() 36 | { 37 | base.OnUpdate(); 38 | 39 | if (_Scene) 40 | { 41 | CleanActors(); 42 | } 43 | else 44 | { 45 | _Actors.Clear(); 46 | } 47 | } 48 | 49 | public override IEnumerable FindSubCameras() 50 | { 51 | var subCams = Camera.allCameras.Where( 52 | cam => (cam.cullingMask & VR.Context.UILayerMask) == 0 53 | && cam.clearFlags != CameraClearFlags.Color).ToArray(); 54 | 55 | Logger.Info("Found {0} sub cameras", subCams.Length); 56 | return subCams; 57 | } 58 | 59 | /// 60 | /// Removes dead entries from actors list and adds new ones. 61 | /// 62 | private void CleanActors() 63 | { 64 | _Actors = Actors.Where(a => a.IsValid).ToList(); 65 | 66 | foreach (var member in _Scene.charas.Humans) 67 | { 68 | AddActor(member.human); 69 | } 70 | } 71 | 72 | public override bool IsIgnoredCanvas(Canvas canvas) 73 | { 74 | return canvas.gameObject.layer == pngLayer; 75 | } 76 | 77 | private void AddActor(Human member) 78 | { 79 | if (!member.GetComponent()) 80 | { 81 | _Actors.Add(new StudioActor(member)); 82 | } 83 | } 84 | 85 | private void InitScene() 86 | { 87 | _Scene = GameObject.FindObjectOfType(); 88 | 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /PlayClubVR/PlayClubActor.cs: -------------------------------------------------------------------------------- 1 | using PlayClubVR.OSP; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Reflection; 6 | using System.Text; 7 | using UnityEngine; 8 | using VRGIN.Core; 9 | using VRGIN.Helpers; 10 | 11 | namespace PlayClubVR 12 | { 13 | public class PlayClubActor : DefaultActor 14 | { 15 | 16 | private static FieldInfo _HeartField = typeof(HumanVoice).GetField("heart", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); 17 | private static FieldInfo _VoiceField = typeof(HumanVoice).GetField("voice", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); 18 | private Transform _EyeTransform; 19 | private TransientHead _Head; 20 | 21 | public PlayClubActor(Human nativeActor) : base(nativeActor) 22 | { 23 | } 24 | 25 | public override Transform Eyes 26 | { 27 | get 28 | { 29 | return _EyeTransform; 30 | } 31 | } 32 | 33 | public override bool HasHead 34 | { 35 | get 36 | { 37 | return _Head.Visible; 38 | } 39 | set 40 | { 41 | _Head.Visible = value; 42 | } 43 | } 44 | 45 | protected override void Initialize(Human actor) 46 | { 47 | base.Initialize(actor); 48 | 49 | // Get position of actor's eyes 50 | _Head = Actor.gameObject.AddComponent(); 51 | _EyeTransform = TransientHead.GetEyes(Actor); 52 | 53 | // 3D-ize actor's voice 54 | if (actor.voice) 55 | { 56 | _HeartField.SetValue(actor.voice, OSPAudioVolumeManager.Create(_HeartField.GetValue(actor.voice) as AudioVolumeManager)); 57 | _VoiceField.SetValue(actor.voice, OSPAudioVolumeManager.Create(_VoiceField.GetValue(actor.voice) as AudioVolumeManager)); 58 | } 59 | 60 | // Register bones 61 | foreach (var bone in SoftCustomBones) 62 | { 63 | DynamicColliderRegistry.RegisterDynamicBone(bone); 64 | } 65 | foreach (var bone in SoftBones) 66 | { 67 | DynamicColliderRegistry.RegisterDynamicBone(bone); 68 | } 69 | } 70 | 71 | public IEnumerable SoftBones 72 | { 73 | get 74 | { 75 | return Actor.GetComponentsInChildren(); 76 | } 77 | } 78 | 79 | public IEnumerable SoftCustomBones 80 | { 81 | get 82 | { 83 | return Actor.GetComponentsInChildren(); 84 | } 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /PlayClubVR/OSP/OSPAudioVolumeManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using UnityEngine; 6 | 7 | namespace PlayClubVR.OSP 8 | { 9 | class OSPAudioVolumeManager : AudioVolumeManager 10 | { 11 | private OSPAudioSource ospSource; 12 | 13 | public OSPAudioVolumeManager(AudioSource source, Human human) : base(source, human) 14 | { 15 | } 16 | 17 | public static OSPAudioVolumeManager Create(AudioVolumeManager blueprint) 18 | { 19 | var sourceField = typeof(AudioVolumeManager).GetField("source", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic); 20 | var humanField = typeof(AudioVolumeManager).GetField("human", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic); 21 | 22 | var manager = new OSPAudioVolumeManager(sourceField.GetValue(blueprint) as AudioSource, humanField.GetValue(blueprint) as Human); 23 | manager.ospSource = manager.source.gameObject.AddComponent(); 24 | return manager; 25 | } 26 | 27 | public override void Play(float fade) 28 | { 29 | this.ospSource.Play(); 30 | if (fade == 0f) 31 | { 32 | this.rate = 1f; 33 | this.move = 0f; 34 | } 35 | else 36 | { 37 | this.rate = 0f; 38 | this.move = 1f / fade; 39 | } 40 | this.CalcVolume(); 41 | } 42 | 43 | public override void PlayDelay(float fade, float delay) 44 | { 45 | ospSource.PlayDelayed(delay); 46 | if (fade == 0f) 47 | { 48 | this.rate = 1f; 49 | this.move = 0f; 50 | } 51 | else 52 | { 53 | this.rate = 0f; 54 | this.move = 1f / fade; 55 | } 56 | this.CalcVolume(); 57 | } 58 | 59 | public override void Update() 60 | { 61 | if (this.move != 0f) 62 | { 63 | this.rate += this.move * Time.deltaTime; 64 | if (this.move > 0f && this.rate >= 1f) 65 | { 66 | this.rate = 1f; 67 | this.move = 0f; 68 | } 69 | else if (this.move < 0f && this.rate <= 0f) 70 | { 71 | this.rate = 0f; 72 | this.move = 0f; 73 | this.ospSource.Stop(); 74 | } 75 | } 76 | this.CalcVolume(); 77 | } 78 | 79 | public override void PlayDelay(AudioClip clip, bool loop, float fade, float delay) 80 | { 81 | this.ospSource.Stop(); 82 | this.source.clip = clip; 83 | this.source.loop = loop; 84 | this.ospSource.Stop(); 85 | this.PlayDelay(fade, delay); 86 | } 87 | 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /PlayClubVR.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25123.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PlayClubVR", "PlayClubVR\PlayClubVR.csproj", "{BE0FD119-262B-42C0-BE1F-8BCE0AF022D8}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VRGIN.U46", "VRGIN\VRGIN\VRGIN.U46.csproj", "{8FE87229-42A4-4CB0-846A-7A2726569B17}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PlayClubStudioVR", "PlayClubStudioVR\PlayClubStudioVR.csproj", "{9F6E1BC0-2C48-433E-A8C7-5C21DB635C99}" 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GamePadClub", "GamePadClub\GamePadClub.csproj", "{8CF2E8AE-B1CA-4B00-B997-74622BDAF627}" 13 | EndProject 14 | Global 15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 16 | Debug|Any CPU = Debug|Any CPU 17 | Install|Any CPU = Install|Any CPU 18 | Release|Any CPU = Release|Any CPU 19 | EndGlobalSection 20 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 21 | {BE0FD119-262B-42C0-BE1F-8BCE0AF022D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 22 | {BE0FD119-262B-42C0-BE1F-8BCE0AF022D8}.Debug|Any CPU.Build.0 = Debug|Any CPU 23 | {BE0FD119-262B-42C0-BE1F-8BCE0AF022D8}.Install|Any CPU.ActiveCfg = Install|Any CPU 24 | {BE0FD119-262B-42C0-BE1F-8BCE0AF022D8}.Install|Any CPU.Build.0 = Install|Any CPU 25 | {BE0FD119-262B-42C0-BE1F-8BCE0AF022D8}.Release|Any CPU.ActiveCfg = Release|Any CPU 26 | {BE0FD119-262B-42C0-BE1F-8BCE0AF022D8}.Release|Any CPU.Build.0 = Release|Any CPU 27 | {8FE87229-42A4-4CB0-846A-7A2726569B17}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 28 | {8FE87229-42A4-4CB0-846A-7A2726569B17}.Debug|Any CPU.Build.0 = Debug|Any CPU 29 | {8FE87229-42A4-4CB0-846A-7A2726569B17}.Install|Any CPU.ActiveCfg = Release|Any CPU 30 | {8FE87229-42A4-4CB0-846A-7A2726569B17}.Install|Any CPU.Build.0 = Release|Any CPU 31 | {8FE87229-42A4-4CB0-846A-7A2726569B17}.Release|Any CPU.ActiveCfg = Release|Any CPU 32 | {8FE87229-42A4-4CB0-846A-7A2726569B17}.Release|Any CPU.Build.0 = Release|Any CPU 33 | {9F6E1BC0-2C48-433E-A8C7-5C21DB635C99}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 34 | {9F6E1BC0-2C48-433E-A8C7-5C21DB635C99}.Debug|Any CPU.Build.0 = Debug|Any CPU 35 | {9F6E1BC0-2C48-433E-A8C7-5C21DB635C99}.Install|Any CPU.ActiveCfg = Install|Any CPU 36 | {9F6E1BC0-2C48-433E-A8C7-5C21DB635C99}.Install|Any CPU.Build.0 = Install|Any CPU 37 | {9F6E1BC0-2C48-433E-A8C7-5C21DB635C99}.Release|Any CPU.ActiveCfg = Release|Any CPU 38 | {9F6E1BC0-2C48-433E-A8C7-5C21DB635C99}.Release|Any CPU.Build.0 = Release|Any CPU 39 | {8CF2E8AE-B1CA-4B00-B997-74622BDAF627}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 40 | {8CF2E8AE-B1CA-4B00-B997-74622BDAF627}.Debug|Any CPU.Build.0 = Debug|Any CPU 41 | {8CF2E8AE-B1CA-4B00-B997-74622BDAF627}.Install|Any CPU.ActiveCfg = Release|Any CPU 42 | {8CF2E8AE-B1CA-4B00-B997-74622BDAF627}.Install|Any CPU.Build.0 = Release|Any CPU 43 | {8CF2E8AE-B1CA-4B00-B997-74622BDAF627}.Release|Any CPU.ActiveCfg = Release|Any CPU 44 | {8CF2E8AE-B1CA-4B00-B997-74622BDAF627}.Release|Any CPU.Build.0 = Release|Any CPU 45 | EndGlobalSection 46 | GlobalSection(SolutionProperties) = preSolution 47 | HideSolutionNode = FALSE 48 | EndGlobalSection 49 | EndGlobal 50 | -------------------------------------------------------------------------------- /GamePadClub/Handlers/CameraHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Reflection; 6 | using System.Text; 7 | using UnityEngine; 8 | using WindowsInput; 9 | using XInputDotNetPure; 10 | 11 | namespace GamePadClub.Handlers 12 | { 13 | public class CameraHandler : MonoBehaviour 14 | { 15 | private const float TRIGGER_THRESHOLD = 0.8f; 16 | 17 | private IllusionCamera _Camera; 18 | private FieldInfo _DistanceField = typeof(IllusionCamera).GetField("distance", BindingFlags.NonPublic | BindingFlags.Instance); 19 | private Queue _Messages = new Queue(); 20 | void OnEnable() 21 | { 22 | GamePadController.Instance.Register(MoveCamera); 23 | } 24 | 25 | void OnDisable() 26 | { 27 | GamePadController.Instance.Unregister(MoveCamera); 28 | } 29 | 30 | void Start() 31 | { 32 | StartCoroutine(HandleMessages()); 33 | } 34 | 35 | public void OnLevelWasLoaded() 36 | { 37 | _Camera = FindObjectOfType(); 38 | } 39 | 40 | bool MoveCamera(GamePadState state, GamePadState prevState) 41 | { 42 | if (GamePadHelper.IsShoulderPressed(state)) return false; 43 | 44 | if (_Camera) 45 | { 46 | var leftThumb = new Vector2(state.ThumbSticks.Left.X, state.ThumbSticks.Left.Y); 47 | var rightThumb = new Vector2(state.ThumbSticks.Right.X, state.ThumbSticks.Right.Y); 48 | bool leftTrigger = state.Triggers.Left > TRIGGER_THRESHOLD; 49 | bool rightTrigger = state.Triggers.Right > TRIGGER_THRESHOLD; 50 | 51 | if (leftTrigger || rightTrigger) 52 | { 53 | // Zoom 54 | _DistanceField.SetValue(_Camera, Mathf.Max(0.1f, _Camera.Distance - rightThumb.y * Time.deltaTime * 2)); 55 | 56 | _Messages.Enqueue(() => 57 | { 58 | _Camera.Rotate(new Vector3(0, -rightThumb.x, 0) * Time.deltaTime * 100); 59 | }); 60 | } 61 | if (leftTrigger) 62 | { 63 | // Pan 1 64 | _Camera.Translate(leftThumb * Time.deltaTime * 100, false); 65 | 66 | } 67 | else if (rightTrigger) 68 | { 69 | // Pan 2 70 | _Camera.Translate(new Vector3(leftThumb.x, 0, leftThumb.y) * Time.deltaTime * 100, false); 71 | 72 | } 73 | else 74 | { 75 | // Move 76 | _Messages.Enqueue(() => 77 | { 78 | _Camera.Rotate(new Vector3(rightThumb.y, -rightThumb.x, 0) * Time.deltaTime * 100); 79 | }); 80 | } 81 | } 82 | return false; 83 | } 84 | 85 | IEnumerator HandleMessages() 86 | { 87 | //Console.WriteLine("Starting message queue"); 88 | while(true) 89 | { 90 | //Console.WriteLine("Iterate"); 91 | // This code is executed between Update() and LateUpdate() 92 | // We do this to not interfere with the VR mod 93 | while (_Messages.Count > 0) 94 | { 95 | //Console.WriteLine("Dequeue"); 96 | 97 | _Messages.Dequeue()(); 98 | } 99 | yield return null; 100 | } 101 | } 102 | } 103 | 104 | } 105 | -------------------------------------------------------------------------------- /GamePadClub/GamePadClub.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {8CF2E8AE-B1CA-4B00-B997-74622BDAF627} 8 | Library 9 | Properties 10 | GamePadClub 11 | GamePadClub 12 | v3.5 13 | 512 14 | 15 | 16 | true 17 | full 18 | false 19 | bin\Debug\ 20 | DEBUG;TRACE 21 | prompt 22 | 4 23 | 24 | 25 | pdbonly 26 | true 27 | bin\Release\ 28 | TRACE 29 | prompt 30 | 4 31 | 32 | 33 | 34 | ..\Libs\Assembly-CSharp.dll 35 | False 36 | 37 | 38 | ..\Libs\Injector\IllusionPlugin.dll 39 | False 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | ..\VRGIN\Libs\Unity 4.6\UnityEngine.dll 49 | False 50 | 51 | 52 | ..\VRGIN\Libs\Unity 4.6\UnityEngine.UI.dll 53 | False 54 | 55 | 56 | ..\Libs\WindowsInput.dll 57 | 58 | 59 | ..\Libs\XInputDotNetPure.dll 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | PreserveNewest 75 | 76 | 77 | 78 | 85 | -------------------------------------------------------------------------------- /PlayClubVR/TransientHead.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using UnityEngine; 6 | using VRGIN.Core; 7 | 8 | namespace PlayClubVR 9 | { 10 | public class TransientHead : ProtectedBehaviour 11 | { 12 | private List rendererList = new List(); 13 | private bool hidden = false; 14 | private Transform root; 15 | 16 | private Renderer[] m_tongues; 17 | private Human avatar; 18 | private Transform headTransform; 19 | private Transform eyesTransform; 20 | 21 | public bool Visible 22 | { 23 | get 24 | { 25 | return !hidden; 26 | } 27 | set 28 | { 29 | if (value) 30 | { 31 | Console.WriteLine("SHOW"); 32 | } 33 | else 34 | { 35 | Console.WriteLine("HIDE"); 36 | } 37 | SetVisibility(value); 38 | } 39 | } 40 | 41 | protected override void OnStart() 42 | { 43 | 44 | avatar = GetComponent(); 45 | headTransform = GetHead(avatar); 46 | eyesTransform = GetEyes(avatar); 47 | 48 | root = headTransform.GetComponentsInParent().Last(t => t.name.Contains("body")).parent; 49 | m_tongues = root.GetComponentsInChildren().Where(renderer => renderer.name.StartsWith("cm_O_tang") || renderer.name == "cf_O_tang").Where(tongue => tongue.enabled).ToArray(); 50 | 51 | } 52 | public static Transform GetHead(Human human) 53 | { 54 | return human.headPos.GetComponentsInParent().First(t => t.name.StartsWith("c") && t.name.Contains("J_Head")); 55 | } 56 | 57 | 58 | public static Transform GetEyes(Human human) 59 | { 60 | var eyes = human.headPos.GetComponentsInChildren().FirstOrDefault(t => t.name.StartsWith("c") && t.name.EndsWith("Eye_ty")); 61 | if (!eyes) 62 | { 63 | eyes = new GameObject("cm_Eye_ty").transform; 64 | eyes.SetParent(GetHead(human), false); 65 | eyes.transform.localPosition = new Vector3(0, 0.17f, 0.05f); 66 | } 67 | return eyes; 68 | } 69 | 70 | void SetVisibility(bool visible) 71 | { 72 | if (visible) 73 | { 74 | if (hidden) 75 | { 76 | // enable 77 | //Console.WriteLine("Enabling {0} renderers", rendererList.Count); 78 | foreach (var renderer in rendererList) 79 | { 80 | renderer.enabled = true; 81 | } 82 | foreach (var renderer in m_tongues) 83 | { 84 | renderer.enabled = true; 85 | } 86 | 87 | } 88 | } 89 | else 90 | { 91 | if (!hidden) 92 | { 93 | m_tongues = root.GetComponentsInChildren().Where(renderer => renderer.name.StartsWith("cm_O_tang") || renderer.name == "cf_O_tang").Where(tongue => tongue.enabled).ToArray(); 94 | 95 | // disable 96 | rendererList.Clear(); 97 | foreach (var renderer in headTransform.GetComponentsInChildren().Where(renderer => renderer.enabled)) 98 | { 99 | rendererList.Add(renderer); 100 | renderer.enabled = false; 101 | } 102 | 103 | foreach (var renderer in m_tongues) 104 | { 105 | renderer.enabled = false; 106 | } 107 | } 108 | } 109 | 110 | hidden = !visible; 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /GamePadClub/Handlers/GameControlHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using UnityEngine; 6 | using UnityEngine.EventSystems; 7 | using UnityEngine.UI; 8 | using XInputDotNetPure; 9 | 10 | namespace GamePadClub.Handlers 11 | { 12 | public class GameControlHandler : MonoBehaviour 13 | { 14 | GameControl _GC; 15 | H_Scene _Scene; 16 | 17 | void OnLevelWasLoaded() 18 | { 19 | _GC = FindObjectOfType(); 20 | _Scene = FindObjectOfType(); 21 | } 22 | 23 | void OnEnable() 24 | { 25 | GamePadController.Instance.Register(HandleInput); 26 | } 27 | 28 | void OnDisable() 29 | { 30 | GamePadController.Instance.Unregister(HandleInput); 31 | } 32 | 33 | bool HandleInput(GamePadState now, GamePadState before) 34 | { 35 | if (GamePadHelper.IsShoulderPressed(now)) return false; 36 | 37 | if (_GC && _Scene && now.Buttons.LeftShoulder == ButtonState.Released && now.Buttons.RightShoulder == ButtonState.Released) 38 | { 39 | return HandleGUI(now, before) || HandleSekkusu(now, before); 40 | } 41 | return false; 42 | } 43 | 44 | bool HandleGUI(GamePadState now, GamePadState before) 45 | { 46 | 47 | if (GamePadHelper.IsPressUp(now.Buttons.Start, before.Buttons.Start)) 48 | { 49 | _GC.IsHideUI = !_GC.IsHideUI; 50 | } 51 | return false; 52 | } 53 | 54 | bool HandleSekkusu(GamePadState now, GamePadState before) 55 | { 56 | 57 | // Speed 58 | if (GamePadHelper.IsPressUp(now.DPad.Up, before.DPad.Up)) 59 | { 60 | _Scene.Pad.ChangeSpeed(Mathf.Clamp(_Scene.Pad.speed + 0.6f, _Scene.Pad.SpeedMin, _Scene.Pad.SpeedMax)); 61 | } 62 | else if(GamePadHelper.IsPressUp(now.DPad.Down, before.DPad.Down)) 63 | { 64 | _Scene.Pad.ChangeSpeed(Mathf.Clamp(_Scene.Pad.speed - 0.6f, _Scene.Pad.SpeedMin, _Scene.Pad.SpeedMax)); 65 | } 66 | 67 | // Pose 68 | if (GamePadHelper.IsPressUp(now.DPad.Left, before.DPad.Left)) 69 | { 70 | ChangePose(-1); 71 | } 72 | else if (GamePadHelper.IsPressUp(now.DPad.Right, before.DPad.Right)) 73 | { 74 | ChangePose(1); 75 | } 76 | 77 | // Mode 78 | if (GamePadHelper.IsPressUp(now.Buttons.X, before.Buttons.X)) 79 | { 80 | ToggleGrind(); 81 | } 82 | if (GamePadHelper.IsPressUp(now.Buttons.Y, before.Buttons.Y)) 83 | { 84 | TogglePiston(); 85 | } 86 | 87 | // Ejaculation 88 | if (GamePadHelper.IsPressDown(now.Buttons.RightStick, before.Buttons.RightStick)) 89 | { 90 | Ejaculate(false); 91 | } 92 | 93 | if (GamePadHelper.IsPressDown(now.Buttons.LeftStick, before.Buttons.LeftStick)) 94 | { 95 | Ejaculate(true); 96 | } 97 | 98 | 99 | return false; 100 | } 101 | 102 | 103 | 104 | private void ToggleOrgasmLock() 105 | { 106 | //Console.WriteLine(!_Scene.FemaleGage.Lock ? "Lock" : "Unlock"); 107 | // This also updates the GUI! 108 | GameObject.Find("XtcLock").GetComponent().isOn = !_Scene.FemaleGage.Lock; 109 | //_Scene.FemaleGage.ChangeLock(!_Scene.FemaleGage.Lock); 110 | } 111 | 112 | 113 | private void TogglePiston() 114 | { 115 | _Scene.Pad.pistonToggle.OnPointerClick(new PointerEventData(EventSystem.current)); 116 | 117 | } 118 | 119 | private void ToggleGrind() 120 | { 121 | _Scene.Pad.grindToggle.OnPointerClick(new PointerEventData(EventSystem.current)); 122 | } 123 | 124 | private void SetSpeed(float val) 125 | { 126 | _Scene.Pad.ChangeSpeed(val * 5); 127 | } 128 | 129 | private void Ejaculate(bool outside) 130 | { 131 | if (_Scene.FemaleGage.IsHigh()) 132 | { 133 | _Scene.StateMgr.SetXtc(H_Pad.XTC.SYNC); 134 | } 135 | else if (_Scene.Members[0].sex == Human.SEX.MALE) 136 | { 137 | if (outside) 138 | { 139 | _Scene.StateMgr.SetXtc(H_Pad.XTC.M_OUT); 140 | } else 141 | { 142 | _Scene.StateMgr.SetXtc(H_Pad.XTC.M_IN); 143 | } 144 | } 145 | else 146 | { 147 | _Scene.StateMgr.SetXtc(H_Pad.XTC.F); 148 | } 149 | } 150 | 151 | private void ChangePose(int direction) 152 | { 153 | int currentIndex = _Scene.StyleMgr.StyleList.IndexOf(_Scene.StyleMgr.nowStyle); 154 | int nextIndex = ((currentIndex + _Scene.StyleMgr.StyleList.Count) + direction) % _Scene.StyleMgr.StyleList.Count; 155 | 156 | _Scene.ChangeStyle(_Scene.StyleMgr.StyleList[nextIndex].file); 157 | } 158 | 159 | } 160 | 161 | 162 | } 163 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | artifacts/ 46 | 47 | *_i.c 48 | *_p.c 49 | *_i.h 50 | *.ilk 51 | *.meta 52 | *.obj 53 | *.pch 54 | *.pdb 55 | *.pgc 56 | *.pgd 57 | *.rsp 58 | *.sbr 59 | *.tlb 60 | *.tli 61 | *.tlh 62 | *.tmp 63 | *.tmp_proj 64 | *.log 65 | *.vspscc 66 | *.vssscc 67 | .builds 68 | *.pidb 69 | *.svclog 70 | *.scc 71 | 72 | # Chutzpah Test files 73 | _Chutzpah* 74 | 75 | # Visual C++ cache files 76 | ipch/ 77 | *.aps 78 | *.ncb 79 | *.opendb 80 | *.opensdf 81 | *.sdf 82 | *.cachefile 83 | *.VC.db 84 | *.VC.VC.opendb 85 | 86 | # Visual Studio profiler 87 | *.psess 88 | *.vsp 89 | *.vspx 90 | *.sap 91 | 92 | # TFS 2012 Local Workspace 93 | $tf/ 94 | 95 | # Guidance Automation Toolkit 96 | *.gpState 97 | 98 | # ReSharper is a .NET coding add-in 99 | _ReSharper*/ 100 | *.[Rr]e[Ss]harper 101 | *.DotSettings.user 102 | 103 | # JustCode is a .NET coding add-in 104 | .JustCode 105 | 106 | # TeamCity is a build add-in 107 | _TeamCity* 108 | 109 | # DotCover is a Code Coverage Tool 110 | *.dotCover 111 | 112 | # NCrunch 113 | _NCrunch_* 114 | .*crunch*.local.xml 115 | nCrunchTemp_* 116 | 117 | # MightyMoose 118 | *.mm.* 119 | AutoTest.Net/ 120 | 121 | # Web workbench (sass) 122 | .sass-cache/ 123 | 124 | # Installshield output folder 125 | [Ee]xpress/ 126 | 127 | # DocProject is a documentation generator add-in 128 | DocProject/buildhelp/ 129 | DocProject/Help/*.HxT 130 | DocProject/Help/*.HxC 131 | DocProject/Help/*.hhc 132 | DocProject/Help/*.hhk 133 | DocProject/Help/*.hhp 134 | DocProject/Help/Html2 135 | DocProject/Help/html 136 | 137 | # Click-Once directory 138 | publish/ 139 | 140 | # Publish Web Output 141 | *.[Pp]ublish.xml 142 | *.azurePubxml 143 | # TODO: Comment the next line if you want to checkin your web deploy settings 144 | # but database connection strings (with potential passwords) will be unencrypted 145 | *.pubxml 146 | *.publishproj 147 | 148 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 149 | # checkin your Azure Web App publish settings, but sensitive information contained 150 | # in these scripts will be unencrypted 151 | PublishScripts/ 152 | 153 | # NuGet Packages 154 | *.nupkg 155 | # The packages folder can be ignored because of Package Restore 156 | **/packages/* 157 | # except build/, which is used as an MSBuild target. 158 | !**/packages/build/ 159 | # Uncomment if necessary however generally it will be regenerated when needed 160 | #!**/packages/repositories.config 161 | # NuGet v3's project.json files produces more ignoreable files 162 | *.nuget.props 163 | *.nuget.targets 164 | 165 | # Microsoft Azure Build Output 166 | csx/ 167 | *.build.csdef 168 | 169 | # Microsoft Azure Emulator 170 | ecf/ 171 | rcf/ 172 | 173 | # Windows Store app package directories and files 174 | AppPackages/ 175 | BundleArtifacts/ 176 | Package.StoreAssociation.xml 177 | _pkginfo.txt 178 | 179 | # Visual Studio cache files 180 | # files ending in .cache can be ignored 181 | *.[Cc]ache 182 | # but keep track of directories ending in .cache 183 | !*.[Cc]ache/ 184 | 185 | # Others 186 | ClientBin/ 187 | ~$* 188 | *~ 189 | *.dbmdl 190 | *.dbproj.schemaview 191 | *.pfx 192 | *.publishsettings 193 | node_modules/ 194 | orleans.codegen.cs 195 | 196 | # Since there are multiple workflows, uncomment next line to ignore bower_components 197 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 198 | #bower_components/ 199 | 200 | # RIA/Silverlight projects 201 | Generated_Code/ 202 | 203 | # Backup & report files from converting an old project file 204 | # to a newer Visual Studio version. Backup files are not needed, 205 | # because we have git ;-) 206 | _UpgradeReport_Files/ 207 | Backup*/ 208 | UpgradeLog*.XML 209 | UpgradeLog*.htm 210 | 211 | # SQL Server files 212 | *.mdf 213 | *.ldf 214 | 215 | # Business Intelligence projects 216 | *.rdl.data 217 | *.bim.layout 218 | *.bim_*.settings 219 | 220 | # Microsoft Fakes 221 | FakesAssemblies/ 222 | 223 | # GhostDoc plugin setting file 224 | *.GhostDoc.xml 225 | 226 | # Node.js Tools for Visual Studio 227 | .ntvs_analysis.dat 228 | 229 | # Visual Studio 6 build log 230 | *.plg 231 | 232 | # Visual Studio 6 workspace options file 233 | *.opt 234 | 235 | # Visual Studio LightSwitch build output 236 | **/*.HTMLClient/GeneratedArtifacts 237 | **/*.DesktopClient/GeneratedArtifacts 238 | **/*.DesktopClient/ModelManifest.xml 239 | **/*.Server/GeneratedArtifacts 240 | **/*.Server/ModelManifest.xml 241 | _Pvt_Extensions 242 | 243 | # Paket dependency manager 244 | .paket/paket.exe 245 | paket-files/ 246 | 247 | # FAKE - F# Make 248 | .fake/ 249 | 250 | # JetBrains Rider 251 | .idea/ 252 | *.sln.iml -------------------------------------------------------------------------------- /PlayClubVR/DynamicColliderRegistry.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using UnityEngine; 6 | using VRGIN.Core; 7 | using VRGIN.Helpers; 8 | 9 | namespace PlayClubVR 10 | { 11 | 12 | public static class DynamicColliderRegistry 13 | { 14 | private static IDictionary> _Colliders = new Dictionary>(); 15 | private static IList _Bones = new List(); 16 | 17 | public static IEnumerable Bones { get { return _Bones; } } 18 | 19 | public static void RegisterCollider(DynamicBoneCollider collider, Predicate targetSelector = null) 20 | { 21 | if (targetSelector == null) targetSelector = (bone) => true; 22 | 23 | _Colliders[collider] = targetSelector; 24 | 25 | foreach (var bone in _Bones) 26 | { 27 | Correlate(bone, collider, targetSelector); 28 | } 29 | } 30 | 31 | private static void Correlate(IDynamicBoneWrapper bone, DynamicBoneCollider collider, Predicate targetSelector) 32 | { 33 | if (targetSelector(bone)) 34 | { 35 | if (!bone.Colliders.Contains(collider)) 36 | { 37 | bone.Colliders.Add(collider); 38 | } 39 | } 40 | else 41 | { 42 | bone.Colliders.Remove(collider); 43 | } 44 | } 45 | 46 | private static void Register(IDynamicBoneWrapper wrapper) 47 | { 48 | _Bones.Add(wrapper); 49 | 50 | foreach(var colliderPair in _Colliders) 51 | { 52 | Correlate(wrapper, colliderPair.Key, colliderPair.Value); 53 | } 54 | } 55 | 56 | public static void RegisterDynamicBone(DynamicBone bone) 57 | { 58 | Register(new DynamicBoneWrapper(bone)); 59 | } 60 | 61 | public static void RegisterDynamicBone(DynamicBone_Custom bone) 62 | { 63 | Register(new DynamicBone_CustomWrapper(bone)); 64 | } 65 | 66 | public static void Clear() 67 | { 68 | _Colliders.Clear(); 69 | _Bones.Clear(); 70 | } 71 | 72 | public static Predicate GetCondition(DynamicBoneCollider key) 73 | { 74 | return _Colliders[key]; 75 | } 76 | } 77 | 78 | 79 | public interface IDynamicBoneWrapper 80 | { 81 | MonoBehaviour Bone 82 | { 83 | get; 84 | } 85 | 86 | Transform Root 87 | { 88 | get; 89 | } 90 | 91 | Transform Rim 92 | { 93 | get; 94 | } 95 | 96 | IEnumerable Nodes { get; } 97 | 98 | 99 | List Colliders { get; } 100 | } 101 | 102 | public class DynamicBoneWrapper : IDynamicBoneWrapper 103 | { 104 | public MonoBehaviour Bone 105 | { 106 | get; 107 | private set; 108 | } 109 | 110 | public List Colliders 111 | { 112 | get 113 | { 114 | return (Bone as DynamicBone).m_Colliders; 115 | } 116 | } 117 | 118 | public Transform Root 119 | { 120 | get 121 | { 122 | return (Bone as DynamicBone).m_Root; 123 | } 124 | } 125 | 126 | public Transform Rim 127 | { 128 | get 129 | { 130 | return (Bone as DynamicBone).m_Root.gameObject.Descendants().Last().transform; 131 | } 132 | } 133 | 134 | public IEnumerable Nodes 135 | { 136 | get 137 | { 138 | return Root.gameObject.Descendants().Select(d => d.transform).Concat(new Transform[] { Root }); 139 | } 140 | } 141 | 142 | public DynamicBoneWrapper(DynamicBone bone) 143 | { 144 | Bone = bone; 145 | } 146 | } 147 | 148 | public class DynamicBone_CustomWrapper : IDynamicBoneWrapper 149 | { 150 | public MonoBehaviour Bone 151 | { 152 | get; 153 | private set; 154 | } 155 | 156 | public List Colliders 157 | { 158 | get 159 | { 160 | return (Bone as DynamicBone_Custom).m_Colliders; 161 | } 162 | } 163 | 164 | public Transform Root 165 | { 166 | get 167 | { 168 | return (Bone as DynamicBone_Custom).m_Nodes[0]; 169 | } 170 | } 171 | 172 | public Transform Rim 173 | { 174 | get 175 | { 176 | return (Bone as DynamicBone_Custom).m_Nodes.Last(); 177 | } 178 | } 179 | 180 | public IEnumerable Nodes 181 | { 182 | get 183 | { 184 | return(Bone as DynamicBone_Custom).m_Nodes; 185 | } 186 | } 187 | 188 | public DynamicBone_CustomWrapper(DynamicBone_Custom bone) 189 | { 190 | Bone = bone; 191 | } 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /PlayClubVR/MaestroTool.cs: -------------------------------------------------------------------------------- 1 | using IllusionPlugin; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Diagnostics; 5 | using System.Linq; 6 | using System.Runtime.InteropServices; 7 | using System.Text; 8 | using UnityEngine; 9 | using Valve.VR; 10 | using VRGIN.Core; 11 | using VRGIN.Controls; 12 | using VRGIN.Helpers; 13 | using VRGIN.Native; 14 | using VRGIN.Controls.Tools; 15 | using IllusionInjector; 16 | using MaestroMode; 17 | 18 | namespace PlayClubVR 19 | { 20 | public class MaestroTool : Tool 21 | { 22 | // This is to avoid a dependency to MaestroMode (no instance variables allowed or the application will crash at the absence of the DLL) 23 | object __Handle = null; 24 | private IKHandle _Handle 25 | { 26 | get 27 | { 28 | return __Handle as IKHandle; 29 | } 30 | set 31 | { 32 | __Handle = value; 33 | } 34 | } 35 | Controller _Controller; 36 | Transform _Dummy = null; 37 | private bool _Dragging; 38 | private bool _Focused; 39 | private int _FocusCount; 40 | 41 | private static bool? _Available; 42 | 43 | public override Texture2D Image 44 | { 45 | get 46 | { 47 | return UnityHelper.LoadImage("icon_maestro.png"); 48 | } 49 | } 50 | 51 | /// 52 | /// Gets whether or not the MaestroMode DLL is loaded. 53 | /// 54 | public static bool IsAvailable 55 | { 56 | get 57 | { 58 | return PluginManager.Plugins.Any(plugin => plugin.GetType().FullName == "MaestroMode.MaestroPlugin"); 59 | } 60 | } 61 | 62 | public MaestroPlugin Maestro 63 | { 64 | get 65 | { 66 | return PluginManager.Plugins.OfType().FirstOrDefault(); 67 | } 68 | } 69 | 70 | protected override void OnStart() 71 | { 72 | base.OnStart(); 73 | 74 | _Controller = GetComponent(); 75 | _Dummy = new GameObject().transform; 76 | _Dummy.SetParent(transform, false); 77 | } 78 | 79 | protected override void OnDisable() 80 | { 81 | base.OnDisable(); 82 | _FocusCount = 0; 83 | } 84 | 85 | protected override void OnFixedUpdate() 86 | { 87 | base.OnFixedUpdate(); 88 | 89 | if (_Controller.Tracking.isValid) 90 | { 91 | var device = SteamVR_Controller.Input((int)_Controller.Tracking.index); 92 | 93 | if (device.GetPressDown(EVRButtonId.k_EButton_SteamVR_Trigger)) 94 | { 95 | if(_FocusCount == 0) 96 | { 97 | VRLog.Info("Search for handle"); 98 | _Handle = FindNextHandle(); 99 | } 100 | if (_Handle) 101 | { 102 | _Handle.gameObject.SendMessage("Select"); 103 | _Handle.gameObject.SendMessage("Activate", 1); 104 | 105 | _Handle.MarkDirty(); 106 | 107 | _Dummy.position = _Handle.transform.position; 108 | _Dummy.rotation = _Handle.transform.rotation; 109 | _Dragging = true; 110 | } 111 | } 112 | if (_Handle && _Dragging) 113 | { 114 | if (device.GetPress(EVRButtonId.k_EButton_SteamVR_Trigger)) 115 | { 116 | _Handle.transform.position = _Dummy.position; 117 | _Handle.transform.rotation = _Dummy.rotation; 118 | } 119 | if (device.GetPressUp(EVRButtonId.k_EButton_SteamVR_Trigger)) 120 | { 121 | _Handle.gameObject.SendMessage("Deselect"); 122 | _Dragging = false; 123 | } 124 | } 125 | 126 | if(_Handle && device.GetPressDown(EVRButtonId.k_EButton_SteamVR_Touchpad)) 127 | { 128 | _Handle.gameObject.SendMessage("Reset"); 129 | } 130 | 131 | if(device.GetPressDown(EVRButtonId.k_EButton_Grip)) 132 | { 133 | // Toggle 134 | var prevMode = Maestro.Mode; 135 | Maestro.Mode = MaestroPlugin.MaestroMode.FBBIK; 136 | Maestro.Visible = prevMode == Maestro.Mode ? !Maestro.Visible : true; 137 | } 138 | } 139 | } 140 | 141 | public override List GetHelpTexts() 142 | { 143 | return base.GetHelpTexts(); 144 | } 145 | 146 | private IKHandle FindNextHandle() 147 | { 148 | var handle = Maestro.Handles.OrderBy(h => Vector3.Distance(_Controller.transform.position, h.transform.position)).FirstOrDefault(); 149 | 150 | if(handle && Vector3.Distance(_Controller.transform.position, handle.transform.position) < 0.5f) 151 | { 152 | return handle; 153 | } else 154 | { 155 | return null; 156 | } 157 | 158 | } 159 | 160 | protected override void OnDestroy() 161 | { 162 | } 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /PlayClubStudioVR/PlayClubStudioVR.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {9F6E1BC0-2C48-433E-A8C7-5C21DB635C99} 8 | Library 9 | Properties 10 | PlayClubStudioVR 11 | PlayClubStudioVR 12 | v3.5 13 | 512 14 | 15 | 16 | true 17 | full 18 | false 19 | bin\Debug\ 20 | DEBUG;TRACE 21 | prompt 22 | 4 23 | 24 | 25 | pdbonly 26 | true 27 | bin\Release\ 28 | TRACE 29 | prompt 30 | 4 31 | 32 | 33 | bin\Install\ 34 | TRACE;DEBUG 35 | true 36 | pdbonly 37 | AnyCPU 38 | prompt 39 | MinimumRecommendedRules.ruleset 40 | 0 41 | 42 | 43 | 44 | ..\Libs\Assembly-CSharp_Studio.dll 45 | False 46 | 47 | 48 | ..\Libs\Injector\IllusionPlugin.dll 49 | False 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | ..\VRGIN\Libs\Unity 4.6\UnityEngine.dll 59 | False 60 | 61 | 62 | ..\VRGIN\Libs\Unity 4.6\UnityEngine.UI.dll 63 | False 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | {be0fd119-262b-42c0-be1f-8bce0af022d8} 79 | PlayClubVR 80 | 81 | 82 | {8fe87229-42a4-4cb0-846a-7a2726569b17} 83 | VRGIN.U46 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 100 | 101 | 102 | 103 | $(registry:HKEY_CURRENT_USER\Software\illusion\PlayClub@INSTALLDIR) 104 | $(InstallDir)Plugins\ 105 | $(InstallDir)PlayClubStudio_Data\ 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /PlayClubVR/PlayClubStandingMode.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using UnityEngine; 6 | using VRGIN.Core; 7 | using VRGIN.Controls; 8 | using VRGIN.Helpers; 9 | using VRGIN.Modes; 10 | using VRGIN.Controls.Speech; 11 | using Leap.Unity; 12 | 13 | namespace PlayClubVR 14 | { 15 | public class PlayClubStandingMode : StandingMode 16 | { 17 | private IllusionCamera _IllusionCamera; 18 | 19 | protected virtual void OnEnable() 20 | { 21 | base.OnEnable(); 22 | Logger.Info("Enter standing mode"); 23 | 24 | } 25 | 26 | protected virtual void OnDisable() 27 | { 28 | base.OnDisable(); 29 | Logger.Info("Leave standing mode"); 30 | } 31 | 32 | public override void OnDestroy() 33 | { 34 | base.OnDestroy(); 35 | 36 | DynamicColliderRegistry.Clear(); 37 | } 38 | 39 | protected override void OnStart() 40 | { 41 | base.OnStart(); 42 | _IllusionCamera = FindObjectOfType(); 43 | gameObject.AddComponent(); 44 | } 45 | 46 | protected override void OnLevel(int level) 47 | { 48 | base.OnLevel(level); 49 | _IllusionCamera = FindObjectOfType(); 50 | 51 | } 52 | 53 | public override IEnumerable Tools 54 | { 55 | get 56 | { 57 | var tools = base.Tools.Concat(new Type[] { typeof(PlayTool) }); 58 | if (MaestroTool.IsAvailable) { 59 | tools = tools.Concat(new Type[] { typeof(MaestroTool) }); 60 | } 61 | return tools; 62 | } 63 | } 64 | 65 | protected override IEnumerable CreateShortcuts() 66 | { 67 | var interpreter = VR.Interpreter as PlayClubInterpreter; 68 | 69 | return base.CreateShortcuts().Concat(new IShortcut[] { 70 | new MultiKeyboardShortcut(new KeyStroke("Ctrl + C"), new KeyStroke("Ctrl + C"), ChangeMode ), 71 | 72 | new VoiceShortcut(PlayClubVoiceCommand.StartAnimation, delegate { interpreter.TogglePiston(true); }), 73 | new VoiceShortcut(PlayClubVoiceCommand.StopAnimation, delegate { interpreter.TogglePiston(false); }), 74 | new VoiceShortcut(PlayClubVoiceCommand.Faster, delegate { interpreter.IncreaseSpeed(0.6f); }), 75 | new VoiceShortcut(PlayClubVoiceCommand.Slower, delegate { interpreter.IncreaseSpeed(-0.6f); }), 76 | new VoiceShortcut(PlayClubVoiceCommand.Climax, delegate { interpreter.Ejaculate(); }), 77 | new VoiceShortcut(PlayClubVoiceCommand.NextAnimation, delegate { interpreter.ChangePose(1); }), 78 | new VoiceShortcut(PlayClubVoiceCommand.PreviousAnimation, delegate { interpreter.ChangePose(-1); }), 79 | new VoiceShortcut(PlayClubVoiceCommand.DisableClimax, delegate { interpreter.ToggleOrgasmLock(true); }), 80 | new VoiceShortcut(PlayClubVoiceCommand.EnableClimax, delegate { interpreter.ToggleOrgasmLock(false); }), 81 | }); 82 | } 83 | 84 | protected virtual void ChangeMode() 85 | { 86 | VR.Manager.SetMode(); 87 | } 88 | 89 | protected override void CreateControllers() 90 | { 91 | base.CreateControllers(); 92 | 93 | foreach (var controller in new Controller[] { Left, Right }) 94 | { 95 | var boneCollider = CreateCollider(controller.transform, -0.05f); 96 | boneCollider.m_Center.y = -0.03f; 97 | boneCollider.m_Center.z = 0.01f; 98 | DynamicColliderRegistry.RegisterCollider(boneCollider, (b) => !IsNotBust(b)); 99 | 100 | boneCollider = CreateCollider(controller.transform, 0.01f); 101 | boneCollider.m_Center.y = -0.03f; 102 | boneCollider.m_Center.z = 0.01f; 103 | DynamicColliderRegistry.RegisterCollider(boneCollider, IsNotBust); 104 | } 105 | } 106 | 107 | private DynamicBoneCollider CreateCollider(Transform parent, float radius) 108 | { 109 | var collider = UnityHelper.CreateGameObjectAsChild("Dynamic Collider", parent).gameObject.AddComponent(); 110 | collider.m_Radius = radius; 111 | collider.m_Bound = DynamicBoneCollider.Bound.Outside; 112 | collider.m_Direction = DynamicBoneCollider.Direction.X; 113 | collider.m_Center.y = 0; 114 | collider.m_Center.z = 0; 115 | return collider; 116 | } 117 | 118 | protected override HandAttachments BuildAttachmentHand(Chirality handedness) 119 | { 120 | var hand = base.BuildAttachmentHand(handedness); 121 | 122 | foreach (var sphere in new Transform[] { hand.Thumb, hand.Index, hand.Middle, hand.Ring, hand.Pinky, hand.Palm }) 123 | { 124 | var boneCollider = CreateCollider(sphere, -0.05f); 125 | boneCollider.enabled = false; 126 | DynamicColliderRegistry.RegisterCollider(boneCollider, (b) => !IsNotBust(b)); 127 | 128 | boneCollider = CreateCollider(sphere, 0.01f); 129 | boneCollider.enabled = false; 130 | DynamicColliderRegistry.RegisterCollider(boneCollider, IsNotBust); 131 | 132 | 133 | } 134 | hand.OnBegin += delegate 135 | { 136 | foreach(var collider in hand.GetComponentsInChildren()) 137 | { 138 | collider.enabled = true; 139 | } 140 | }; 141 | 142 | hand.OnFinish += delegate 143 | { 144 | foreach (var collider in hand.GetComponentsInChildren()) 145 | { 146 | collider.enabled = false; 147 | } 148 | }; 149 | 150 | return hand; 151 | } 152 | 153 | private bool IsNotBust(IDynamicBoneWrapper wrapper) 154 | { 155 | return !wrapper.Bone.name.Contains("Bust"); 156 | } 157 | 158 | protected override void SyncCameras() 159 | { 160 | base.SyncCameras(); 161 | if (_IllusionCamera) 162 | { 163 | var my = VR.Camera.SteamCam.head; 164 | 165 | _IllusionCamera.Set( 166 | my.position + my.forward, 167 | Quaternion.LookRotation(my.forward, my.up).eulerAngles, 168 | 1); 169 | } 170 | } 171 | //protected override Controller CreateLeftController() 172 | //{ 173 | // return PlayClubController.Create(); 174 | //} 175 | 176 | //protected override Controller CreateRightController() 177 | //{ 178 | // var controller = PlayClubController.Create(); 179 | // controller.ToolIndex = 1; 180 | // return controller; 181 | //} 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /PlayClubVR/PlayClubVR.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {BE0FD119-262B-42C0-BE1F-8BCE0AF022D8} 8 | Library 9 | Properties 10 | PlayClubVR 11 | PlayClubVR 12 | v3.5 13 | 512 14 | 15 | 16 | true 17 | full 18 | false 19 | bin\Debug\ 20 | DEBUG;TRACE 21 | prompt 22 | 4 23 | 24 | 25 | pdbonly 26 | true 27 | bin\Release\ 28 | TRACE 29 | prompt 30 | 4 31 | 32 | 33 | bin\Install\ 34 | TRACE;DEBUG 35 | true 36 | pdbonly 37 | AnyCPU 38 | prompt 39 | MinimumRecommendedRules.ruleset 40 | 0 41 | 42 | 43 | 44 | ..\Libs\Assembly-CSharp.dll 45 | False 46 | 47 | 48 | ..\Libs\CameraModifications.dll 49 | 50 | 51 | ..\Libs\Injector\IllusionInjector.dll 52 | 53 | 54 | ..\Libs\Injector\IllusionPlugin.dll 55 | False 56 | 57 | 58 | False 59 | ..\VRGIN\Libs\LeapCSharp.NET3.5.dll 60 | 61 | 62 | ..\Libs\MaestroMode.dll 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | ..\VRGIN\Libs\Unity 4.6\UnityEngine.dll 72 | False 73 | 74 | 75 | ..\VRGIN\Libs\Unity 4.6\UnityEngine.UI.dll 76 | False 77 | 78 | 79 | False 80 | ..\VRGIN\Libs\WindowsInput.dll 81 | 82 | 83 | ..\Libs\XInputDotNetPure.dll 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | Always 108 | 109 | 110 | 111 | 112 | {8cf2e8ae-b1ca-4b00-b997-74622bdaf627} 113 | GamePadClub 114 | 115 | 116 | {8fe87229-42a4-4cb0-846a-7a2726569b17} 117 | VRGIN.U46 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 134 | 135 | 136 | 137 | $(registry:HKEY_CURRENT_USER\Software\illusion\PlayClub@INSTALLDIR) 138 | $(InstallDir)Plugins\ 139 | $(InstallDir)PlayClub_Data\ 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | -------------------------------------------------------------------------------- /PlayClubVR/PlayClubInterpreter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using UnityEngine; 6 | using UnityEngine.EventSystems; 7 | using UnityEngine.UI; 8 | using VRGIN.Core; 9 | 10 | namespace PlayClubVR 11 | { 12 | public class PlayClubInterpreter : GameInterpreter 13 | { 14 | /// 15 | /// Gets the current H scene. 16 | /// 17 | public H_Scene Scene { get; private set; } 18 | 19 | private readonly int pngLayer = LayerMask.NameToLayer("PNG"); 20 | private List _Actors = new List(); 21 | public override IEnumerable Actors 22 | { 23 | get { return _Actors; } 24 | } 25 | 26 | protected override void OnStart() 27 | { 28 | base.OnStart(); 29 | 30 | gameObject.AddComponent(); 31 | gameObject.AddComponent(); 32 | 33 | LookForScene(); 34 | 35 | if (IsUIOnlyScene) 36 | { 37 | GameControl ctrl = GameObject.FindObjectOfType(); 38 | ctrl.MapDataCtrl.ChangeMap(ctrl.MapDataCtrl.Datas.ElementAt(1).Value, ctrl, VRCamera.Instance.camera, false, false); 39 | } 40 | 41 | } 42 | 43 | public override bool IsIgnoredCanvas(Canvas canvas) 44 | { 45 | return canvas.gameObject.layer == pngLayer; 46 | } 47 | 48 | 49 | protected override void OnLevel(int level) 50 | { 51 | base.OnLevel(level); 52 | 53 | LookForScene(); 54 | 55 | if(IsUIOnlyScene) 56 | { 57 | GameControl ctrl = GameObject.FindObjectOfType(); 58 | ctrl.MapDataCtrl.ChangeMap(ctrl.MapDataCtrl.Datas.ElementAt(1).Value, ctrl, VRCamera.Instance.camera, false, false); 59 | } 60 | 61 | //var oldShadowCaster = VR.Camera.SteamCam.head.FindChild("ShadowCaster"); 62 | //if (oldShadowCaster) 63 | //{ 64 | // DestroyImmediate(oldShadowCaster); 65 | //} 66 | //var shadowCaster = GameObject.Find("ShadowCaster"); 67 | //if(shadowCaster) 68 | //{ 69 | // VRLog.Info("FOund shadow caster"); 70 | // //shadowCaster.transform.SetParent(VR.Camera.SteamCam.head); 71 | // shadowCaster.transform.GetChild(0).gameObject.AddComponent(); 72 | //} 73 | } 74 | 75 | private bool IsUIOnlyScene 76 | { 77 | get 78 | { 79 | return !GameObject.FindObjectOfType(); 80 | } 81 | } 82 | 83 | protected override void OnUpdate() 84 | { 85 | base.OnUpdate(); 86 | 87 | if (Scene) 88 | { 89 | CleanActors(); 90 | } else 91 | { 92 | _Actors.Clear(); 93 | } 94 | } 95 | 96 | /// 97 | /// Removes dead entries from actors list and adds new ones. 98 | /// 99 | private void CleanActors() 100 | { 101 | _Actors = Actors.Where(a => a.IsValid).ToList(); 102 | 103 | foreach (var member in Scene.Members) 104 | { 105 | AddActor(member); 106 | } 107 | } 108 | 109 | private void AddActor(Human member) 110 | { 111 | if (!member.GetComponent()) 112 | { 113 | _Actors.Add(new PlayClubActor(member)); 114 | } 115 | } 116 | 117 | /// 118 | /// Looks for the scene object. 119 | /// 120 | private void LookForScene() 121 | { 122 | Scene = GameObject.FindObjectOfType(); 123 | 124 | if(!Scene) 125 | { 126 | //// Search for actors 127 | //foreach (var member in GameObject.FindObjectsOfType()) 128 | //{ 129 | // AddActor(member); 130 | //} 131 | } 132 | } 133 | 134 | public override bool IsBody(Collider collider) 135 | { 136 | return collider.gameObject.layer == LayerMask.NameToLayer("ToLiquidCollision") 137 | && collider.transform.parent.name.StartsWith("cf_") || collider.transform.parent.name.StartsWith("cm_"); 138 | } 139 | 140 | 141 | public void ChangePose(int direction) 142 | { 143 | if (!Scene) return; 144 | 145 | int currentIndex = Scene.StyleMgr.StyleList.IndexOf(Scene.StyleMgr.nowStyle); 146 | int nextIndex = ((currentIndex + Scene.StyleMgr.StyleList.Count) + direction) % Scene.StyleMgr.StyleList.Count; 147 | 148 | ChangeStyle(Scene.StyleMgr.StyleList[nextIndex].file); 149 | } 150 | 151 | internal void IncreaseSpeed(float v) 152 | { 153 | Scene.Pad.ChangeSpeed(Mathf.Clamp(Scene.Pad.speed + v, Scene.Pad.SpeedMin, Scene.Pad.SpeedMax)); 154 | } 155 | 156 | public void ChangeStyle(string name) 157 | { 158 | if (!Scene) return; 159 | 160 | Scene.ChangeStyle(name); 161 | Scene.StyleToGUI(name); 162 | Scene.CrossFadeStart(); 163 | } 164 | 165 | public void Ejaculate() 166 | { 167 | if (!Scene) return; 168 | 169 | if (Scene.FemaleGage.IsHigh()) 170 | { 171 | Scene.StateMgr.SetXtc(H_Pad.XTC.SYNC); 172 | } 173 | else if (Scene.Members[0].sex == Human.SEX.MALE) 174 | { 175 | Scene.StateMgr.SetXtc(H_Pad.XTC.M_OUT); 176 | } 177 | else 178 | { 179 | Scene.StateMgr.SetXtc(H_Pad.XTC.F); 180 | } 181 | } 182 | 183 | public void SetSpeed(float val) 184 | { 185 | if (!Scene) return; 186 | 187 | Scene.Pad.ChangeSpeed(val * 5); 188 | } 189 | 190 | public void ToggleOrgasmLock(bool? enabled = null) 191 | { 192 | if (!Scene) return; 193 | //Console.WriteLine(!scene.FemaleGage.Lock ? "Lock" : "Unlock"); 194 | // This also updates the GUI! 195 | var toggle = GameObject.Find("XtcLock").GetComponent(); 196 | 197 | if (enabled == null || (toggle.isOn != enabled.Value)) 198 | toggle.isOn = !Scene.FemaleGage.Lock; 199 | //scene.FemaleGage.ChangeLock(!scene.FemaleGage.Lock); 200 | } 201 | 202 | public void TogglePiston(bool? start = null) 203 | { 204 | if (!Scene) return; 205 | 206 | if (start == null || (Scene.Pad.pistonToggle.isOn != start.Value)) 207 | Scene.Pad.pistonToggle.OnPointerClick(new PointerEventData(EventSystem.current)); 208 | 209 | } 210 | 211 | public void ToggleGrind(bool? start = null) 212 | { 213 | if (!Scene) return; 214 | 215 | if (start == null || (Scene.Pad.grindToggle.isOn != start.Value)) 216 | Scene.Pad.grindToggle.OnPointerClick(new PointerEventData(EventSystem.current)); 217 | } 218 | 219 | 220 | } 221 | } 222 | -------------------------------------------------------------------------------- /PlayClubVR/PlayClubSeatedMode.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using System.Text; 6 | using UnityEngine; 7 | using Valve.VR; 8 | using VRGIN.Core; 9 | using VRGIN.Controls; 10 | using VRGIN.Helpers; 11 | using VRGIN.Modes; 12 | using GamePadClub; 13 | using XInputDotNetPure; 14 | using VRGIN.Controls.Speech; 15 | using Leap.Unity; 16 | 17 | namespace PlayClubVR 18 | { 19 | public class PlayClubSeatedMode : SeatedMode 20 | { 21 | private FieldInfo _IllusionCameraRotation = typeof(IllusionCamera).GetField("rotate", BindingFlags.NonPublic | BindingFlags.Instance); 22 | private IllusionCamera _IllusionCamera; 23 | 24 | protected override void OnEnable() 25 | { 26 | base.OnEnable(); 27 | Logger.Info("Enter seated mode"); 28 | GamePadController.Instance.Register(HandleInput); 29 | 30 | } 31 | protected override void OnDisable() 32 | { 33 | base.OnDisable(); 34 | 35 | Logger.Info("Leave seated mode"); 36 | GamePadController.Instance.Unregister(HandleInput); 37 | 38 | } 39 | 40 | public override void OnDestroy() 41 | { 42 | base.OnDestroy(); 43 | 44 | DynamicColliderRegistry.Clear(); 45 | } 46 | bool HandleInput(GamePadState nowState, GamePadState prevState) 47 | { 48 | if (nowState.Buttons.LeftShoulder == ButtonState.Pressed) 49 | { 50 | if (GamePadHelper.IsPressUp(nowState.Buttons.Start, prevState.Buttons.Start)) 51 | { 52 | Recenter(); 53 | } 54 | 55 | Vector2 rightStick = new Vector2(nowState.ThumbSticks.Right.X, nowState.ThumbSticks.Right.Y); 56 | Vector2 leftStick = new Vector2(nowState.ThumbSticks.Left.X, nowState.ThumbSticks.Left.Y); 57 | if(rightStick.magnitude > 0.1f) 58 | { 59 | VR.Settings.Rotation += rightStick.x * Time.deltaTime * 50f; 60 | VR.Settings.OffsetY += rightStick.y * Time.deltaTime * 0.1f; 61 | 62 | } 63 | if (leftStick.magnitude > 0.1f) 64 | { 65 | VR.Settings.Distance += leftStick.x * Time.deltaTime * 0.1f; 66 | VR.Settings.Angle += leftStick.y * Time.deltaTime * 50f; 67 | } 68 | 69 | if(nowState.DPad.Up == ButtonState.Pressed) 70 | { 71 | VR.Settings.IPDScale += Time.deltaTime * 0.1f; 72 | } else if(nowState.DPad.Down == ButtonState.Pressed) 73 | { 74 | VR.Settings.IPDScale -= Time.deltaTime * 0.1f; 75 | } 76 | 77 | // Impersonate 78 | if (GamePadHelper.IsPressUp(nowState.Buttons.Y, prevState.Buttons.Y)) 79 | { 80 | if (LockTarget == null || !LockTarget.IsValid) { 81 | Impersonate(VR.Interpreter.Actors.FirstOrDefault()); 82 | } else { 83 | Impersonate(null); 84 | } 85 | } 86 | 87 | } 88 | 89 | return false; 90 | } 91 | 92 | protected override void CreateControllers() 93 | { 94 | base.CreateControllers(); 95 | 96 | foreach (var controller in new Controller[] { Left, Right }) 97 | { 98 | var boneCollider = new GameObject("Dynamic Collider").AddComponent(); 99 | boneCollider.transform.SetParent(controller.transform, false); 100 | boneCollider.m_Radius = 0.01f; // Does not seem to have an effect 101 | boneCollider.m_Height = 0f; 102 | boneCollider.m_Center.y = 0; 103 | boneCollider.m_Center.z = 0; 104 | boneCollider.m_Center.x = 0; 105 | 106 | boneCollider.m_Bound = DynamicBoneCollider.Bound.Outside; 107 | DynamicColliderRegistry.RegisterCollider(boneCollider); 108 | 109 | } 110 | } 111 | 112 | public override ETrackingUniverseOrigin TrackingOrigin 113 | { 114 | get 115 | { 116 | return ETrackingUniverseOrigin.TrackingUniverseSeated; 117 | } 118 | } 119 | 120 | 121 | public override IEnumerable Tools 122 | { 123 | get 124 | { 125 | var tools = base.Tools; 126 | if (MaestroTool.IsAvailable) 127 | { 128 | tools = tools.Concat(new Type[] { typeof(MaestroTool) }); 129 | } 130 | return tools; 131 | } 132 | } 133 | 134 | protected override void ChangeModeOnControllersDetected() 135 | { 136 | ChangeMode(); 137 | } 138 | 139 | protected virtual void ChangeMode() 140 | { 141 | VR.Manager.SetMode(); 142 | } 143 | 144 | protected override IEnumerable CreateShortcuts() 145 | { 146 | var interpreter = VR.Interpreter as PlayClubInterpreter; 147 | 148 | return base.CreateShortcuts().Concat(new IShortcut[] { 149 | new MultiKeyboardShortcut(new KeyStroke("Ctrl + C"), new KeyStroke("Ctrl + C"), ChangeMode ), 150 | 151 | new VoiceShortcut(PlayClubVoiceCommand.StartAnimation, delegate { interpreter.TogglePiston(true); }), 152 | new VoiceShortcut(PlayClubVoiceCommand.StopAnimation, delegate { interpreter.TogglePiston(false); }), 153 | new VoiceShortcut(PlayClubVoiceCommand.Faster, delegate { interpreter.IncreaseSpeed(0.6f); }), 154 | new VoiceShortcut(PlayClubVoiceCommand.Slower, delegate { interpreter.IncreaseSpeed(-0.6f); }), 155 | new VoiceShortcut(PlayClubVoiceCommand.Climax, delegate { interpreter.Ejaculate(); }), 156 | new VoiceShortcut(PlayClubVoiceCommand.NextAnimation, delegate { interpreter.ChangePose(1); }), 157 | new VoiceShortcut(PlayClubVoiceCommand.PreviousAnimation, delegate { interpreter.ChangePose(-1); }), 158 | new VoiceShortcut(PlayClubVoiceCommand.DisableClimax, delegate { interpreter.ToggleOrgasmLock(true); }), 159 | new VoiceShortcut(PlayClubVoiceCommand.EnableClimax, delegate { interpreter.ToggleOrgasmLock(false); }), 160 | 161 | }); 162 | } 163 | 164 | protected override void OnStart() 165 | { 166 | base.OnStart(); 167 | _IllusionCamera = FindObjectOfType(); 168 | 169 | } 170 | 171 | protected override void OnLevel(int level) 172 | { 173 | base.OnLevel(level); 174 | _IllusionCamera = FindObjectOfType(); 175 | 176 | } 177 | 178 | protected override void CorrectRotationLock() 179 | { 180 | if (_IllusionCamera) 181 | { 182 | var my = VR.Camera.SteamCam.origin; 183 | 184 | _IllusionCameraRotation.SetValue(_IllusionCamera, my.eulerAngles); 185 | 186 | Vector3 b = my.rotation * (Vector3.back * _IllusionCamera.Distance); 187 | my.position = _IllusionCamera.Focus + b; 188 | } 189 | } 190 | 191 | protected override void SyncCameras() 192 | { 193 | if (_IllusionCamera) 194 | { 195 | var my = VR.Camera.SteamCam.origin; 196 | 197 | _IllusionCamera.Set( 198 | my.position + my.forward, 199 | Quaternion.LookRotation(my.forward, my.up).eulerAngles, 200 | 1); 201 | } 202 | } 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /README_JA.md: -------------------------------------------------------------------------------- 1 | # PlayClubVR 2 | 3 | このMODでプレイクラブがVRで楽しめます。OpenVRを使用しているので、Viveもオキュラスも対応しています。 4 | 5 | 6 | ## 導入方法 7 | 8 | 1. ダウンロードしたファイルを*PlayClub*フォルダーへ解凍 9 | 2. *PlayClubVR.exe*または*PlayClubStudioVR.exe*を起動 10 | 3. それだけでおk 11 | 12 | ※ SteamVRをインストールして起動する必要があります。 13 | 14 | ## モードや操作方法 15 | 16 | PlayClubVRには2つのモードがあります。 17 | 18 | | モード | 解説 | 19 | | ----------- | ------------------- | 20 | | Seated | *デフォルト。*このモードではマウスとキーボードとゲームパッドで入力します。
操作の仕方も大体本ゲームと同じです。UIは大きな画面でユーザーの前で表示されます。| 21 | | Standing | ViveコントローラーやTouchみたいなコントローラーが検出されると自動的にStandingモードに移動します。このモードでは、自由に動けて、コントローラーでイイことが出来ます。| 22 | 23 | ### Seatedモード 24 | 25 | As stated earlier, the controls are basically the same as in the main game with the exception of a few VR-related shortcuts. You are presented with a screen in front of your that replaces your monitor and can be configured via the settings via shortcus (see below). 26 | 27 | #### キーボード 28 | 29 | Keys | Effect 30 | ---- | ------ 31 | Ctrl+C, Ctrl+C | Change to *standing mode*. 32 | Ctrl+C, Ctrl+V | Enable (very experimental) third person camera. [Was used for this video](https://www.youtube.com/watch?v=0klN6gd1ybM). 33 | Alt+S | Save settings (IPD, screen position, etc.). 34 | Alt+L | Load settings (last saved state). 35 | Ctrl+Alt+L | Reset settings to the initial state. 36 | F4 | Switch GUI projection mode (flat, curved, spherical). 37 | F5 | Toggle camera lock (enabled by default). This prevents the camera to *tilt* because such movements are known to cause cyber sickness. 38 | Ctrl+F5 | Apply shaders (only for the brave) 39 | F12 | Recenter 40 | Ctrl+X | Impersonate protagonist. Places the camera at the head position of the protagonist and moves along a little. 41 | Ctrl+Shift+X | Impersonate protagonist. Places the camera at the head position of the protagonist and imitates *all* head movements. 42 | NumPad +
NumPad – | Move GUI up / down. 43 | Ctrl+Shift+NumPad +
Ctrl+Shift+NumPad – | Move GUI left / right 44 | Ctrl+NumPad +
Ctrl+NumPad – | Increase / decrease GUI size. 45 | Alt+NumPad +
Alt+NumPad – | Increase / decrease player scale 46 | Shift+NumPad +
Shift+NumPad – | Increase / decrease GUI distance 47 | 48 | #### Gamepad Bindings 49 | 50 | Keys | Effect 51 | ---- | ------ 52 | Move LS | Control cursor 53 | Move RS | Rotate & zoom 54 | LT+Move LS | Pan left/right, up/down 55 | LT+Move RS | Rotate camera 56 | RT+Move LS | Pan left/right, foward/backward 57 | RT+Move RS | Rotate camera 58 | A | Left click 59 | B | Right click 60 | X | Toggle "grind" animation 61 | Y | Toggle "piston" animation 62 | Back | Toggle GUI 63 | DPad Up / DPad Down | Change animation speed 64 | DPad Left / DPad Right | Change position (prev / next) 65 | Press RS | Come inside 66 | Press LS | Come outside 67 | LB+LS Horizontally | Move GUI left / right 68 | LB+LS Vertically | Move GUI up / down 69 | LB+RS Horizontally | Change GUI distance 70 | LB+RS Vertically | Change GUI width 71 | LB+DPad Up / LB+DPad Down | Change player scale 72 | LB+Y | Impersonate protatonist 73 | 74 | ### Standing Mode 75 | 76 | The *standing mode* is where things start to get interesting. This mode is pretty much disconnected from the usual game in that it comes with its very own controls -- although you can still use your mouse and your keyboard. 77 | 78 | #### キーボード 79 | 80 | Keys | Effect 81 | ---- | ------ 82 | Ctrl+C, Ctrl+C | Change to *standing mode*. 83 | Ctrl+C, Ctrl+V | Enable (very experimental) third person camera. [Was used for this video](https://www.youtube.com/watch?v=0klN6gd1ybM). 84 | Alt+S | Save settings (IPD, screen position, etc.). 85 | Alt+L | Load settings (last saved state). 86 | Ctrl+Alt+L | Reset settings to the initial state. 87 | Ctrl+F5 | Apply shaders (only for the brave) 88 | Alt+NumPad +
Alt+NumPad – | Increase / decrease player scale 89 | 90 | #### 憑依 91 | 92 | *Impersonating* someone is as easy as moving at the position of that character's head. The head will disappear and you can pretend to be him / her. By setting `` you can even control their hands! 93 | 94 | #### Interaction 95 | 96 | Interacting is currently only possible either with the conductor tool (which lets you move around the limbs of characters), or by touching the breasts, hair or skirts of girls. They will then start to wiggle more or less realistically, but it can be fun. 97 | 98 | ## ツール 99 | 100 | These tools are mainly meant to be used in *standing mode* but some of them are also available in *seated mode*. 101 | 102 | ### Menu Tool (seated / standing) 103 | 104 | STUB 105 | 106 | ### Warp Tool (standing) 107 | 108 | STUB 109 | 110 | ### Play Tool (standing) 111 | 112 | STUB 113 | 114 | ### Conductor Tool (seated / standing) 115 | 116 | **Caution:** Requires [Maestro Mode](hongfire.com/forum/showthread.php/440160-%28Illusion%29-Play-Club?p=3667135#post3667135) to work in the main game! 117 | 118 | STUB 119 | 120 | ## 環境設定 121 | 122 | Settings can be changed in the file *vr_settings.xml*, which is generated the first time you start the game. Use `RenderScale` to tweak the resolution, **not** the internal resolution dialog, as that one will currently only change the resolution of the GUI. 123 | 124 | Tag | Default | Effect | Mode 125 | ---- | ------ | ------ | ---- 126 | `` | 0.3 | Sets the distance between the camera and the GUI at `[0,0,0]`. | Seated 127 | `` | 170 | Sets the width of the arc the GUI takes up. | Seated 128 | `` | 1 | Sets the scale of the camera. The higher, the more gigantic the player is. | Seated / Standing 129 | `` | 0 | Sets the vertical offset of the GUI in meters. | Seated 130 | `` | 0 | Sets by how many degrees the GUI is rotated (around the y / up axis) | Seated 131 | `` | True | Sets whether or not rumble is activated. | Seated / Standing 132 | `` | 1 | Sets the render scale of the renderer. Increase for better quality but less performance, decrease for more performance but poor quality. | Seated / Standing 133 | `` | False | Sets whether or not the view should be mirrored in the game window. | Seated / Standing 134 | `` | False | Sets whether or not you should take control over the character's hands whenever you impersonate someone. | Standing 135 | 136 | ## Building PlayClubVR 137 | 138 | PlayClubVR depends on the [VRGIN.Core](https://github.com/Eusth/VRGIN.Core) library which is included as a submodule. It is therefore important that when you clone the project, you clone it recursively. 139 | 140 | ``` 141 | git clone --recursive https://github.com/Eusth/PlayClubVR.git 142 | cd PlayClubVR 143 | ``` 144 | 145 | After cloning the repo and setting up the submodule, you should be able to compile the project by simply opening the *.sln file and building. 146 | 147 | Note that there is a build configuration called "Install" that will extract your Play Club install directory from the registry and copy the files where they belong. 148 | -------------------------------------------------------------------------------- /Libs/Injector/IllusionPlugin.XML: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | IllusionPlugin 5 | 6 | 7 | 8 | 9 | Gets a list of executables this plugin should be excuted on (without the file ending) 10 | 11 | { "PlayClub", "PlayClubStudio" } 12 | 13 | 14 | 15 | Create a New INI file to store or load data 16 | 17 | 18 | 19 | 20 | INIFile Constructor. 21 | 22 | 23 | 24 | 25 | 26 | Write Data to the INI File 27 | 28 | 29 | Section name 30 | 31 | Key Name 32 | 33 | Value Name 34 | 35 | 36 | 37 | Read Data Value From the Ini File 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | Interface for generic Illusion unity plugins. Every class that implements this will be loaded if the DLL is placed at 47 | data/Managed/Plugins. 48 | 49 | 50 | 51 | 52 | Gets the name of the plugin. 53 | 54 | 55 | 56 | 57 | Gets the version of the plugin. 58 | 59 | 60 | 61 | 62 | Gets invoked when the application is started. 63 | 64 | 65 | 66 | 67 | Gets invoked when the application is closed. 68 | 69 | 70 | 71 | 72 | Gets invoked whenever a level is loaded. 73 | 74 | 75 | 76 | 77 | 78 | Gets invoked after the first update cycle after a level was loaded. 79 | 80 | 81 | 82 | 83 | 84 | Gets invoked on every graphic update. 85 | 86 | 87 | 88 | 89 | Gets invoked on ever physics update. 90 | 91 | 92 | 93 | 94 | Allows to get and set preferences for your mod. 95 | 96 | 97 | 98 | 99 | Gets a string from the ini. 100 | 101 | Section of the key. 102 | Name of the key. 103 | Value that should be used when no value is found. 104 | Whether or not the default value should be written if no value is found. 105 | 106 | 107 | 108 | 109 | Gets an int from the ini. 110 | 111 | Section of the key. 112 | Name of the key. 113 | Value that should be used when no value is found. 114 | Whether or not the default value should be written if no value is found. 115 | 116 | 117 | 118 | 119 | Gets a float from the ini. 120 | 121 | Section of the key. 122 | Name of the key. 123 | Value that should be used when no value is found. 124 | Whether or not the default value should be written if no value is found. 125 | 126 | 127 | 128 | 129 | Gets a bool from the ini. 130 | 131 | Section of the key. 132 | Name of the key. 133 | Value that should be used when no value is found. 134 | Whether or not the default value should be written if no value is found. 135 | 136 | 137 | 138 | 139 | Checks whether or not a key exists in the ini. 140 | 141 | Section of the key. 142 | Name of the key. 143 | 144 | 145 | 146 | 147 | Sets a float in the ini. 148 | 149 | Section of the key. 150 | Name of the key. 151 | Value that should be written. 152 | 153 | 154 | 155 | Sets an int in the ini. 156 | 157 | Section of the key. 158 | Name of the key. 159 | Value that should be written. 160 | 161 | 162 | 163 | Sets a string in the ini. 164 | 165 | Section of the key. 166 | Name of the key. 167 | Value that should be written. 168 | 169 | 170 | 171 | Sets a bool in the ini. 172 | 173 | Section of the key. 174 | Name of the key. 175 | Value that should be written. 176 | 177 | 178 | 179 | -------------------------------------------------------------------------------- /PlayClubVR/PlayTool.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using UnityEngine; 6 | using UnityEngine.EventSystems; 7 | using UnityEngine.UI; 8 | using Valve.VR; 9 | using VRGIN.Core; 10 | using VRGIN.Controls; 11 | using VRGIN.Helpers; 12 | using VRGIN.Controls.Tools; 13 | using System.Reflection; 14 | 15 | namespace PlayClubVR 16 | { 17 | public class PlayTool : Tool 18 | { 19 | private readonly FieldInfo _DynamicBoneParticleField = typeof(DynamicBone).GetField("m_Particles", BindingFlags.Instance | BindingFlags.NonPublic); 20 | private readonly FieldInfo _DynamicCustomBoneParticleField = typeof(DynamicBone_Custom).GetField("m_Particles", BindingFlags.Instance | BindingFlags.NonPublic); 21 | 22 | 23 | private const float DOT_PRODUCT_THRESHOLD = 0.7f; 24 | 25 | H_Scene scene; 26 | bool _AlteringSpeed; 27 | bool _IgnoreNextTrigger; 28 | private const float NEAR_THRESHOLD = 0.1f; 29 | 30 | public override Texture2D Image 31 | { 32 | get 33 | { 34 | return UnityHelper.LoadImage("icon_play.png"); 35 | } 36 | } 37 | 38 | protected override void OnStart() 39 | { 40 | base.OnStart(); 41 | 42 | Tracking = GetComponent(); 43 | } 44 | protected override void OnFixedUpdate() 45 | { 46 | scene = (VR.Interpreter as PlayClubInterpreter).Scene; 47 | if (scene && IsTracking) 48 | { 49 | var device = this.Controller; 50 | 51 | var tPadPos = device.GetAxis(); 52 | var tPadClick = device.GetPressUp(EVRButtonId.k_EButton_SteamVR_Touchpad); 53 | var tPadTouch = device.GetTouch(EVRButtonId.k_EButton_SteamVR_Touchpad); 54 | if (tPadClick) 55 | { 56 | if (Vector2.Dot(Vector2.up, tPadPos) > DOT_PRODUCT_THRESHOLD) 57 | { 58 | Logger.Debug("up"); 59 | (VR.Interpreter as PlayClubInterpreter).TogglePiston(); 60 | } 61 | else if (Vector2.Dot(-Vector2.up, tPadPos) > DOT_PRODUCT_THRESHOLD) 62 | { 63 | Logger.Debug("down"); 64 | 65 | (VR.Interpreter as PlayClubInterpreter).ToggleGrind(); 66 | } 67 | else if (Vector2.Dot(-Vector2.right, tPadPos) > DOT_PRODUCT_THRESHOLD) 68 | { 69 | Logger.Debug("left"); 70 | 71 | (VR.Interpreter as PlayClubInterpreter).ChangePose(-1); 72 | } 73 | else if (Vector2.Dot(Vector2.right, tPadPos) > DOT_PRODUCT_THRESHOLD) 74 | { 75 | Logger.Debug("right"); 76 | 77 | (VR.Interpreter as PlayClubInterpreter).ChangePose(1); 78 | } 79 | } 80 | if (device.GetTouchDown(EVRButtonId.k_EButton_Axis0)) 81 | { 82 | if (tPadPos.magnitude < 0.4f) 83 | { 84 | _AlteringSpeed = true; 85 | } 86 | } 87 | 88 | if (_AlteringSpeed && tPadTouch) 89 | { 90 | // Normalize 91 | float val = (tPadPos.y + 1) / 2; 92 | (VR.Interpreter as PlayClubInterpreter).SetSpeed(val); 93 | } 94 | if (device.GetTouchUp(EVRButtonId.k_EButton_Axis0)) 95 | { 96 | _AlteringSpeed = false; 97 | } 98 | 99 | //if (device.GetTouchUp(EVRButtonId.k_EButton_Grip)) 100 | //{ 101 | // ToggleOrgasmLock(); 102 | //} 103 | 104 | if (device.GetPressUp(EVRButtonId.k_EButton_Grip)) 105 | { 106 | if (_IgnoreNextTrigger) 107 | { 108 | _IgnoreNextTrigger = false; 109 | } 110 | else 111 | { 112 | (VR.Interpreter as PlayClubInterpreter).Ejaculate(); 113 | } 114 | } 115 | 116 | if(device.GetPressDown(EVRButtonId.k_EButton_SteamVR_Trigger)) 117 | { 118 | AssociateBoneColliders(FindNearBones()); 119 | } 120 | 121 | if(device.GetPressUp(EVRButtonId.k_EButton_SteamVR_Trigger)) 122 | { 123 | DeassociateBoneColliders(); 124 | } 125 | } 126 | } 127 | 128 | private IDictionary> _ColliderOldSelectorMap = new Dictionary>(); 129 | 130 | private void AssociateBoneColliders(IEnumerable bones) 131 | { 132 | 133 | IDictionary> colliderBoneMap = new Dictionary>(); 134 | 135 | // Find colliders that are responsible for these bones 136 | var colliders = GetComponentsInChildren(); 137 | foreach(var bone in bones) 138 | { 139 | var atari = colliders.FirstOrDefault(collider => bone.Colliders.Contains(collider)); 140 | if(atari) 141 | { 142 | if(!colliderBoneMap.ContainsKey(atari)) 143 | { 144 | colliderBoneMap.Add(atari, new List()); 145 | } 146 | 147 | colliderBoneMap[atari].Add(bone); 148 | atari.m_Bound = DynamicBoneCollider.Bound.Inside; 149 | } 150 | } 151 | 152 | // Override the match condition for the responsible colliders to *only* cover these bones and ignore all other (because we changed the m_Bound to INside) 153 | foreach(var colliderBonePair in colliderBoneMap) 154 | { 155 | _ColliderOldSelectorMap[colliderBonePair.Key] = DynamicColliderRegistry.GetCondition(colliderBonePair.Key); 156 | DynamicColliderRegistry.RegisterCollider(colliderBonePair.Key, (collider) => colliderBonePair.Value.Contains(collider)); 157 | } 158 | } 159 | 160 | 161 | private void DeassociateBoneColliders() 162 | { 163 | // Revert the changes made in AssociateBoneColliders() by setting the m_Bound to Outside and restoring the old conditions 164 | foreach(var colliderConditionPair in _ColliderOldSelectorMap) 165 | { 166 | colliderConditionPair.Key.m_Bound = DynamicBoneCollider.Bound.Outside; 167 | DynamicColliderRegistry.RegisterCollider(colliderConditionPair.Key, colliderConditionPair.Value); 168 | } 169 | 170 | _ColliderOldSelectorMap.Clear(); 171 | } 172 | 173 | private IEnumerable FindNearBones() 174 | { 175 | return DynamicColliderRegistry 176 | .Bones 177 | .Where((bone, i) => 178 | bone.Nodes.Any( n => 179 | Vector3.Distance(transform.position, n.position) < NEAR_THRESHOLD 180 | ) 181 | ); 182 | } 183 | 184 | private void OnImpersonated() 185 | { 186 | _IgnoreNextTrigger = true; 187 | } 188 | 189 | public override List GetHelpTexts() 190 | { 191 | return new List(new HelpText[] { 192 | HelpText.Create("Slide to set speed", FindAttachPosition("trackpad"), new Vector3(0.07f, 0.02f, 0.05f)), 193 | HelpText.Create("Piston", FindAttachPosition("trackpad"), new Vector3(0, 0.02f, 0.05f), new Vector3(0, 0.0f, 0.015f)), 194 | HelpText.Create("Grind", FindAttachPosition("trackpad"), new Vector3(0, 0.02f, -0.05f), new Vector3(0, 0.0f, -0.015f)), 195 | HelpText.Create("Next animation", FindAttachPosition("trackpad"), new Vector3(0.05f, 0.02f, 0), new Vector3(+0.015f, 0.0f, 0)), 196 | HelpText.Create("Prev animation", FindAttachPosition("trackpad"), new Vector3(-0.05f, 0.02f, 0), new Vector3(-0.015f, 0.0f, 0)), 197 | //HelpText.Create("Toggle lock", FindAttachPosition("lgrip"), new Vector3(-0.05f, 0.02f, 0)), 198 | HelpText.Create("Ejaculate", FindAttachPosition("lgrip"), new Vector3(0.05f, 0.02f, -0.05f)), 199 | }); 200 | } 201 | 202 | protected override void OnDestroy() 203 | { 204 | } 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /PlayClubStudioVR/IKTool.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using System.Text; 6 | using UnityEngine; 7 | using Valve.VR; 8 | using VRGIN.Core; 9 | using VRGIN.Controls; 10 | using VRGIN.Helpers; 11 | using VRGIN.Controls.Tools; 12 | using UnityEngine.UI; 13 | 14 | namespace PlayClubStudioVR 15 | { 16 | static class IKCtrl 17 | { 18 | public enum IKVisibility 19 | { 20 | Deactivated, 21 | Hidden, 22 | Visible 23 | } 24 | public enum IKState 25 | { 26 | FK, 27 | IK 28 | } 29 | 30 | public static IKState State { get { return _State; } private set { _State = value; } } 31 | private static IKState _State = IKState.FK; 32 | public static IKVisibility Visibility { get { return _Visibility; } private set { _Visibility = value; } } 33 | private static IKVisibility _Visibility = IKVisibility.Deactivated; 34 | 35 | 36 | public static event EventHandler StateChanged = delegate { }; 37 | 38 | 39 | internal static void Change(IKVisibility _visibility, IKState _state) 40 | { 41 | Visibility = _visibility; 42 | State = _state; 43 | 44 | foreach (var actor in VR.Interpreter.Actors.OfType()) 45 | { 46 | bool ikState = Visibility == IKVisibility.Visible && State == IKState.IK; 47 | bool fkState = Visibility == IKVisibility.Visible && State == IKState.FK; 48 | 49 | bool isActive = actor.Actor.fkCtrl.IsActive; 50 | 51 | if (Visibility == IKVisibility.Visible || Visibility == IKVisibility.Deactivated) 52 | { 53 | actor.Actor.ikCtrl.SetActive(ikState); 54 | actor.Actor.fkCtrl.SetActive(ikState || fkState); 55 | } 56 | 57 | actor.Actor.ikCtrl.SetActiveTargets(ikState); 58 | actor.Actor.fkCtrl.SetActiveTargets(fkState); 59 | 60 | if (ikState) 61 | { 62 | actor.Actor.ikCtrl.SetupIK(); 63 | if (!isActive) 64 | { 65 | actor.Actor.fkCtrl.SetupFK(); 66 | } 67 | } 68 | else if (fkState) 69 | { 70 | actor.Actor.fkCtrl.SetupFK(); 71 | } 72 | } 73 | 74 | StateChanged(null, null); 75 | } 76 | 77 | internal static void Show() 78 | { 79 | if(Visibility == IKVisibility.Hidden) 80 | { 81 | Change(IKVisibility.Visible, State); 82 | } 83 | } 84 | 85 | internal static void Enable() 86 | { 87 | if (Visibility == IKVisibility.Deactivated) 88 | { 89 | Change(IKVisibility.Visible, State); 90 | } 91 | } 92 | 93 | internal static void Disable() 94 | { 95 | if (Visibility > IKVisibility.Deactivated) 96 | { 97 | Change(IKVisibility.Deactivated, State); 98 | } 99 | } 100 | 101 | internal static void Hide() 102 | { 103 | if (Visibility > IKVisibility.Hidden) 104 | { 105 | Change(IKVisibility.Hidden, State); 106 | } 107 | } 108 | 109 | internal static void Toggle() 110 | { 111 | var newState = (IKState)(((int)State + 1) % Enum.GetValues(typeof(IKState)).Length); 112 | Change(Visibility, newState); 113 | } 114 | } 115 | class IKTool : Tool 116 | { 117 | 118 | private class IKStateReactor : ProtectedBehaviour 119 | { 120 | private Text textElement; 121 | protected override void OnAwake() 122 | { 123 | base.OnAwake(); 124 | textElement = GetComponent(); 125 | } 126 | 127 | void OnEnable() 128 | { 129 | IKCtrl.StateChanged += OnStateChanged; 130 | 131 | OnStateChanged(null, null); 132 | } 133 | 134 | void OnDisable() 135 | { 136 | IKCtrl.StateChanged -= OnStateChanged; 137 | } 138 | 139 | private void OnStateChanged(object sender, EventArgs e) 140 | { 141 | textElement.text = IKCtrl.Visibility == IKCtrl.IKVisibility.Visible 142 | ? IKCtrl.State.ToString() 143 | : "-"; 144 | } 145 | } 146 | 147 | private const float MAX_DISTANCE = 0.5f; 148 | 149 | private static string[] BLACKLIST = new string[] { "XY", "YZ", "XZ", "RingGuidZ", "RingGuidX", "RingGuidY" }; 150 | private readonly static int GIZMO_LAYER = LayerMask.NameToLayer("DriveUI"); 151 | private bool _Dragging = false; 152 | private TravelDistanceRumble _Rumble; 153 | private Controller.Lock _Lock; 154 | private Transform _Target; 155 | private FieldInfo _GuideDriveManager = typeof(GuideDrive).GetField("manager", BindingFlags.NonPublic | BindingFlags.Instance); 156 | 157 | private Vector3 _PrevPos; 158 | private Quaternion _PrevRot; 159 | private GuideDriveManager _Manager; 160 | private bool _Hover; 161 | private GameObject _HoverObject; 162 | private Canvas _Canvas; 163 | 164 | public override Texture2D Image 165 | { 166 | get 167 | { 168 | return UnityHelper.LoadImage("icon_maestro.png"); 169 | } 170 | } 171 | 172 | public override List GetHelpTexts() 173 | { 174 | return new List(new HelpText[] { 175 | HelpText.Create("Switch FK/IK", FindAttachPosition("trackpad"), new Vector3(0.07f, 0.02f, 0.05f)), 176 | HelpText.Create("Grab", FindAttachPosition("trigger"), new Vector3(0.06f, 0.04f, -0.05f)), 177 | HelpText.Create("Grab", FindAttachPosition("trigger"), new Vector3(0.06f, 0.04f, -0.05f)), 178 | }); 179 | } 180 | 181 | protected override void OnStart() 182 | { 183 | base.OnStart(); 184 | _Rumble = new TravelDistanceRumble(300, 0.05f, transform); 185 | 186 | CreateIndicatorCanvas(); 187 | } 188 | 189 | protected override void OnDisable() 190 | { 191 | base.OnDisable(); 192 | Owner.StopRumble(_Rumble); 193 | 194 | if(!(Owner.Other.ActiveTool is IKTool)) 195 | { 196 | IKCtrl.Hide(); 197 | } 198 | 199 | if(_Canvas) 200 | { 201 | _Canvas.gameObject.SetActive(false); 202 | } 203 | } 204 | 205 | protected override void OnEnable() 206 | { 207 | base.OnEnable(); 208 | IKCtrl.Show(); 209 | 210 | if (_Canvas) 211 | { 212 | _Canvas.gameObject.SetActive(true); 213 | } 214 | } 215 | 216 | protected override void OnFixedUpdate() 217 | { 218 | base.OnFixedUpdate(); 219 | 220 | if(Tracking.isValid) 221 | { 222 | var device = Controller; 223 | if(device.GetPressDown(EVRButtonId.k_EButton_SteamVR_Trigger)) { 224 | _HoverObject = FindNearestHandle(); 225 | if (!_HoverObject) return; 226 | 227 | _Manager = (_GuideDriveManager.GetValue(_HoverObject.GetComponent()) as GuideDriveManager); 228 | _PrevPos = transform.position; 229 | _PrevRot = transform.rotation; 230 | _Dragging = true; 231 | Owner.StartRumble(_Rumble); 232 | } 233 | if (_HoverObject) 234 | { 235 | if (device.GetPress(EVRButtonId.k_EButton_SteamVR_Trigger)) 236 | { 237 | _Manager.DriveMovePosition(transform.position - _PrevPos); 238 | 239 | var rotDiff = (transform.rotation) * Quaternion.Inverse(_PrevRot); 240 | _Manager.DriveMoveRotation(Quaternion.Inverse(_Manager.rotRoot.rotation) * rotDiff * _Manager.rotRoot.rotation); 241 | 242 | _PrevPos = transform.position; 243 | _PrevRot = transform.rotation; 244 | } 245 | if (device.GetPressUp(EVRButtonId.k_EButton_SteamVR_Trigger)) 246 | { 247 | _Dragging = false; 248 | Release(); 249 | Owner.StopRumble(_Rumble); 250 | } 251 | } 252 | if(device.GetPressDown(EVRButtonId.k_EButton_SteamVR_Touchpad)) 253 | { 254 | if (IKCtrl.Visibility == IKCtrl.IKVisibility.Deactivated) 255 | { 256 | IKCtrl.Enable(); 257 | } 258 | else 259 | { 260 | IKCtrl.Toggle(); 261 | } 262 | } 263 | if (device.GetPressDown(EVRButtonId.k_EButton_Grip)) 264 | { 265 | if (IKCtrl.Visibility == IKCtrl.IKVisibility.Deactivated) 266 | { 267 | IKCtrl.Enable(); 268 | } 269 | else 270 | { 271 | IKCtrl.Disable(); 272 | } 273 | } 274 | } 275 | } 276 | 277 | private GameObject FindNearestHandle() 278 | { 279 | var nearest = GameObject.FindObjectsOfType().OrderBy(g => Vector3.Distance(g.transform.position, transform.position)).FirstOrDefault(); 280 | if(nearest) 281 | { 282 | if (Vector3.Distance(nearest.transform.position, transform.position) <= MAX_DISTANCE) 283 | { 284 | return nearest.gameObject; 285 | } 286 | } 287 | return null; 288 | } 289 | 290 | private void Release() 291 | { 292 | _HoverObject = null; 293 | } 294 | 295 | protected override void OnDestroy() 296 | { 297 | } 298 | 299 | private void CreateIndicatorCanvas() 300 | { 301 | var canvas = _Canvas = UnityHelper.CreateGameObjectAsChild("IKFK", FindAttachPosition("trackpad")).gameObject.AddComponent(); 302 | 303 | 304 | canvas.renderMode = RenderMode.WorldSpace; 305 | 306 | canvas.GetComponent().SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, 100); 307 | canvas.GetComponent().SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, 100); 308 | 309 | canvas.transform.localPosition = new Vector3(0, 0, 0.00327f); 310 | canvas.transform.localRotation = Quaternion.Euler(0, 180, 90); 311 | canvas.transform.localScale = new Vector3(0.0002294636f, 0.0002294636f, 0.0002294636f); 312 | 313 | 314 | var text = UnityHelper.CreateGameObjectAsChild("Text", canvas.transform).gameObject.AddComponent(); 315 | var outline = text.gameObject.AddComponent(); 316 | text.gameObject.AddComponent(); 317 | 318 | // Maximize 319 | text.GetComponent().anchorMin = Vector2.zero; 320 | text.GetComponent().anchorMax = Vector2.one; 321 | text.font = Resources.GetBuiltinResource("Arial.ttf"); 322 | text.alignment = TextAnchor.MiddleCenter; 323 | text.color = Color.white; 324 | text.fontSize = 50; 325 | text.resizeTextForBestFit = false; 326 | } 327 | } 328 | } 329 | -------------------------------------------------------------------------------- /PlayClubVR/ImpersonationHandler.cs: -------------------------------------------------------------------------------- 1 | using RootMotion; 2 | using RootMotion.FinalIK; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using UnityEngine; 8 | using VRGIN.Controls; 9 | using VRGIN.Core; 10 | using VRGIN.Helpers; 11 | 12 | namespace PlayClubVR 13 | { 14 | class ImpersonationHandler : ProtectedBehaviour 15 | { 16 | PlayClubActor _Actor; 17 | Controller.Lock _Lock1 = Controller.Lock.Invalid; 18 | Controller.Lock _Lock2 = Controller.Lock.Invalid; 19 | Transform _GoalLeft; 20 | Transform _GoalRight; 21 | 22 | 23 | private Dictionary IK_MAPPING = new Dictionary() 24 | { 25 | { HumanBodyBones.Head, "Head" }, 26 | { HumanBodyBones.LeftUpperLeg, "LegUp00_L" }, 27 | { HumanBodyBones.LeftLowerLeg, "LegLow01_L"}, 28 | { HumanBodyBones.LeftFoot, "Foot01_L"}, 29 | { HumanBodyBones.RightUpperLeg, "LegUp00_R" }, 30 | { HumanBodyBones.RightLowerLeg, "LegLow01_R"}, 31 | { HumanBodyBones.RightFoot, "Foot01_R"}, 32 | { HumanBodyBones.LeftLowerArm, "ArmLow01_L"}, 33 | { HumanBodyBones.RightLowerArm, "ArmLow01_R"}, 34 | { HumanBodyBones.LeftHand, "Hand_L"}, 35 | { HumanBodyBones.RightHand, "Hand_R"}, 36 | { HumanBodyBones.LeftUpperArm, "ArmUp00_L"}, 37 | { HumanBodyBones.RightUpperArm, "ArmUp00_R"}, 38 | { HumanBodyBones.Hips, "Hips" } 39 | }; 40 | 41 | protected override void OnStart() 42 | { 43 | base.OnStart(); 44 | if(!(VR.Settings as PlayClubSettings).FullImpersonation) 45 | { 46 | DestroyImmediate(this); 47 | } 48 | } 49 | 50 | protected override void OnAwake() 51 | { 52 | base.OnAwake(); 53 | 54 | _GoalLeft = new GameObject().transform; 55 | _GoalRight = new GameObject().transform; 56 | 57 | DontDestroyOnLoad(_GoalLeft); 58 | DontDestroyOnLoad(_GoalRight); 59 | } 60 | 61 | protected virtual void OnDestroy() 62 | { 63 | Destroy(_GoalLeft.gameObject); 64 | Destroy(_GoalRight.gameObject); 65 | } 66 | 67 | protected override void OnUpdate() 68 | { 69 | base.OnUpdate(); 70 | 71 | var impersonatedActor = VR.Interpreter.Actors.FirstOrDefault(a => !a.HasHead) as PlayClubActor; 72 | 73 | if (!_Lock1.IsValid && impersonatedActor != null) { 74 | _Actor = impersonatedActor; 75 | AcquireLocks(); 76 | 77 | Enter(); 78 | } else if(_Lock1.IsValid && impersonatedActor == null) 79 | { 80 | Exit(); 81 | } 82 | } 83 | 84 | private void Enter() 85 | { 86 | VR.Mode.Left.Model.gameObject.SetActive(false); 87 | VR.Mode.Right.Model.gameObject.SetActive(false); 88 | 89 | foreach(var ik in _Actor.Actor.IKs) 90 | { 91 | if(ik.ik) 92 | { 93 | VRLog.Info(ik.ik.name); 94 | } 95 | } 96 | 97 | _Actor.Actor.SetupIK(); 98 | 99 | //var biped = SetUpSimpleBiped(_Actor.Actor); 100 | 101 | //biped.solvers.leftHand.target = VR.Mode.Left.transform; 102 | //biped.solvers.rightHand.target = VR.Mode.Right.transform; 103 | 104 | //left.solver.target = VR.Mode.Left.transform; 105 | //right.solver.target = VR.Mode.Right.transform; 106 | } 107 | 108 | private void Exit() 109 | { 110 | _Lock1.Release(); 111 | _Lock2.Release(); 112 | 113 | VR.Mode.Left.Model.gameObject.SetActive(true); 114 | VR.Mode.Right.Model.gameObject.SetActive(true); 115 | 116 | var scene = GameObject.FindObjectOfType(); 117 | if(scene) 118 | { 119 | scene.UpdateIK(scene.LastAnimeName); 120 | } 121 | } 122 | 123 | protected override void OnLateUpdate() 124 | { 125 | base.OnLateUpdate(); 126 | 127 | if(_Lock1.IsValid) 128 | { 129 | // Update hands! 130 | LeftIK.solver.IKPosition = VR.Mode.Left.transform.position - VR.Mode.Left.transform.forward * 0.1f; 131 | LeftIK.solver.IKRotation = VR.Mode.Left.transform.rotation * Quaternion.Euler(0, 60, 0); 132 | RightIK.solver.IKPosition = VR.Mode.Right.transform.position - VR.Mode.Right.transform.forward * 0.1f; 133 | RightIK.solver.IKRotation = VR.Mode.Right.transform.rotation * Quaternion.Euler(0, -60, 0); 134 | RightIK.solver.bendGoal = _GoalRight; 135 | RightIK.solver.bendModifier = IKSolverLimb.BendModifier.Goal; 136 | RightIK.solver.bendModifierWeight = 1f; 137 | LeftIK.solver.bendModifier = IKSolverLimb.BendModifier.Goal; 138 | LeftIK.solver.bendGoal = _GoalLeft; 139 | LeftIK.solver.bendModifierWeight = 1f; 140 | 141 | _GoalLeft.position = VR.Mode.Left.transform.position - VR.Mode.Left.transform.forward; 142 | _GoalRight.position = VR.Mode.Right.transform.position - VR.Mode.Right.transform.forward; 143 | 144 | _GoalLeft.rotation = VR.Mode.Left.transform.rotation; 145 | _GoalRight.rotation = VR.Mode.Right.transform.rotation; 146 | 147 | } 148 | } 149 | 150 | private LimbIK LeftIK 151 | { 152 | get 153 | { 154 | return _Actor.Actor.IKs[(int)Human.IK_TYPE.ARM_L].ik; 155 | } 156 | } 157 | 158 | private LimbIK RightIK 159 | { 160 | get 161 | { 162 | return _Actor.Actor.IKs[(int)Human.IK_TYPE.ARM_R].ik; 163 | } 164 | } 165 | 166 | private bool AcquireLocks() 167 | { 168 | if(VR.Mode.Left.AcquireFocus(out _Lock1) && VR.Mode.Right.AcquireFocus(out _Lock2)) 169 | { 170 | return true; 171 | } else 172 | { 173 | if (_Lock1.IsValid) _Lock1.Release(); 174 | if (_Lock2.IsValid) _Lock2.Release(); 175 | 176 | return false; 177 | } 178 | 179 | } 180 | 181 | private BipedIK SetUpSimpleBiped(Human member) 182 | { 183 | if (member.gameObject.GetComponent()) 184 | { 185 | return member.gameObject.GetComponent(); 186 | } 187 | var ik = member.gameObject.AddComponent(); 188 | 189 | var root = member.transform.FindChild("cm_body_01"); 190 | if (root == null) root = member.transform.FindChild("cf_body_01"); 191 | if (root == null) 192 | { 193 | VRLog.Info("No entry point found: {0}", member.name); 194 | } 195 | 196 | ik.references = new BipedReferences(); 197 | ik.references.root = root; 198 | 199 | var spines = new List(); 200 | foreach (var transform in root.GetComponentsInChildren()) 201 | { 202 | string name = transform.name; 203 | if (name.Length > 5) 204 | { 205 | name = name.Substring(5); 206 | if (name == IK_MAPPING[HumanBodyBones.Head]) 207 | ik.references.head = transform; 208 | 209 | // LEGS 210 | else if (name == IK_MAPPING[HumanBodyBones.LeftUpperLeg]) 211 | ik.references.leftThigh = transform; 212 | else if (name == IK_MAPPING[HumanBodyBones.LeftLowerLeg]) 213 | ik.references.leftCalf = transform; 214 | else if (name == IK_MAPPING[HumanBodyBones.LeftFoot]) 215 | ik.references.leftFoot = transform; 216 | else if (name == IK_MAPPING[HumanBodyBones.RightUpperLeg]) 217 | ik.references.rightThigh = transform; 218 | else if (name == IK_MAPPING[HumanBodyBones.RightLowerLeg]) 219 | ik.references.rightCalf = transform; 220 | else if (name == IK_MAPPING[HumanBodyBones.RightFoot]) 221 | ik.references.rightFoot = transform; 222 | 223 | // ARMS 224 | else if (name == IK_MAPPING[HumanBodyBones.LeftUpperArm]) 225 | ik.references.leftUpperArm = transform; 226 | else if (name == IK_MAPPING[HumanBodyBones.LeftLowerArm]) 227 | ik.references.leftForearm = transform; 228 | else if (name == IK_MAPPING[HumanBodyBones.LeftHand]) 229 | ik.references.leftHand = transform; 230 | else if (name == IK_MAPPING[HumanBodyBones.RightUpperArm]) 231 | ik.references.rightUpperArm = transform; 232 | else if (name == IK_MAPPING[HumanBodyBones.RightLowerArm]) 233 | ik.references.rightForearm = transform; 234 | else if (name == IK_MAPPING[HumanBodyBones.RightHand]) 235 | ik.references.rightHand = transform; 236 | 237 | // HIPS 238 | else if (name == IK_MAPPING[HumanBodyBones.Hips]) 239 | ik.references.pelvis = transform; 240 | 241 | else if (name == "Spine01" || name == "Spine02" || name == "Spine03") 242 | spines.Add(transform); 243 | 244 | } 245 | } 246 | 247 | ik.references.spine = spines.ToArray(); 248 | 249 | //Console.WriteLine("1: " + ik.references.head); 250 | //Console.WriteLine("2: " + ik.references.leftCalf); 251 | //Console.WriteLine("3: " + ik.references.leftFoot); 252 | //Console.WriteLine("4: " + ik.references.leftForearm); 253 | //Console.WriteLine("5: " + ik.references.leftHand); 254 | //Console.WriteLine("6: " + ik.references.leftThigh); 255 | //Console.WriteLine("7: " + ik.references.leftUpperArm); 256 | //Console.WriteLine("8: " + ik.references.pelvis); 257 | //Console.WriteLine("9: " + ik.references.rightCalf); 258 | //Console.WriteLine("10: " + ik.references.rightFoot); 259 | //Console.WriteLine("11: " + ik.references.rightForearm); 260 | //Console.WriteLine("12: " + ik.references.rightHand); 261 | //Console.WriteLine("13: " + ik.references.rightThigh); 262 | //Console.WriteLine("14: " + ik.references.rightUpperArm); 263 | 264 | ik.SetToDefaults(); 265 | 266 | foreach (var limb in ik.solvers.limbs) 267 | { 268 | limb.IKPositionWeight = 0; 269 | limb.IKRotationWeight = 0; 270 | limb.bendModifier = IKSolverLimb.BendModifier.Goal; 271 | } 272 | 273 | return ik; 274 | } 275 | 276 | 277 | //private LimbIK setUpArmIfNeeded(Human member, bool isLeft) 278 | //{ 279 | // var upperArm = isLeft 280 | // ? member.gameObject.Descendants().First(t => t.name.Substring(5) == IK_MAPPING[HumanBodyBones.LeftUpperArm]).transform 281 | // : member.gameObject.Descendants().First(t => t.name.Substring(5) == IK_MAPPING[HumanBodyBones.RightUpperArm]).transform; 282 | 283 | // var limb = upperArm.GetComponent(); 284 | // if (limb) return limb; 285 | 286 | // limb = upperArm.gameObject.AddComponent(); 287 | // if (isLeft) 288 | // { 289 | // limb.solver.bone2.transform = member.gameObject.Descendants().First(t => t.name.Substring(5) == IK_MAPPING[HumanBodyBones.LeftLowerArm]).transform; 290 | // limb.solver.bone3.transform = member.gameObject.Descendants().First(t => t.name.Substring(5) == IK_MAPPING[HumanBodyBones.LeftHand]).transform; 291 | // } 292 | // else 293 | // { 294 | // limb.solver.bone2.transform = member.gameObject.Descendants().First(t => t.name.Substring(5) == IK_MAPPING[HumanBodyBones.RightLowerArm]).transform; 295 | // limb.solver.bone3.transform = member.gameObject.Descendants().First(t => t.name.Substring(5) == IK_MAPPING[HumanBodyBones.RightHand]).transform; 296 | // } 297 | 298 | // limb.solver.SetChain(limb.solver.bone1.transform, limb.solver.bone2.transform, limb.solver.bone3.transform, member.transform); 299 | 300 | // limb.solver.IKPositionWeight = 1; 301 | // limb.solver.IKRotationWeight = 1; 302 | // // Changing the automatic bend modifier 303 | // //limb.solver.bendModifier = IKSolverLimb.BendModifier.Animation; // Will maintain the bending direction as it is animated. 304 | // //limb.solver.bendModifier = IKSolverLimb.BendModifier.Target; // Will bend the limb with the target rotation 305 | // //limb.solver.bendModifier = IKSolverLimb.BendModifier.Parent; // Will bend the limb with the parent bone (pelvis or shoulder) 306 | // // // Will try to maintain the bend direction in the most biometrically relaxed way for the arms. 307 | // // Will not work for the legs. 308 | // limb.solver.bendModifier = IKSolverLimb.BendModifier.Target; 309 | // return limb; 310 | //} 311 | } 312 | } 313 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PlayClubVR 2 | 3 | This is a mod for Play Club that introduces VR capabilities for both the Vive and the Oculus Rift using OpenVR. It provides both a seated and a standing mode to be usable in any environment. 4 | 5 | 6 | ## Installation 7 | 8 | 1. Download [the latest release](https://github.com/Eusth/PlayClubVR/releases) and extract it into your *PlayClub* directory. 9 | 2. Run *PlayClubVR.exe* or *PlayClubStudioVR.exe* 10 | 3. Enjoy! 11 | 12 | **Caution:** SteamVR needs to be installed, set up, and running! Rift users might otherwise experience a weird "monitor" effect. 13 | 14 | ## Modes & Controls 15 | 16 | PlayClubVR comes in two *modes*: 17 | 18 | | Mode | Description | 19 | | ----------- | ------------------- | 20 | | Seated | *Default mode.* This mode lets you play the game with a mouse, keyboard, or gamepad.
The controls are essentially the same as in the main game. The screen is presented on a big monitor in front of you. | 21 | | Standing | As soon as tracked controllers are registered by the game, it switches into *standing mode*, also called *room scale mode*. In this mode, you can freely move around and use your Vive or Touch controllers to do cool stuff. | 22 | 23 | 24 | ### Seated Mode 25 | 26 | As stated earlier, the controls are basically the same as in the main game with the exception of a few VR-related shortcuts. You are presented with a screen in front of your that replaces your monitor and can be configured via the settings or via shortcuts (see below). 27 | 28 | #### Keyboard Shortcuts 29 | 30 | Keys | Effect 31 | ---- | ------ 32 | Ctrl+C, Ctrl+C | Change to *standing mode*. 33 | Ctrl+C, Ctrl+V | Enable (very experimental) third person camera. [Was used for this video](https://www.youtube.com/watch?v=0klN6gd1ybM). 34 | Alt+S | Save settings (IPD, screen position, etc.). 35 | Alt+L | Load settings (last saved state). 36 | Ctrl+Alt+L | Reset settings to the initial state. 37 | F4 | Switch GUI projection mode (flat, curved, spherical). 38 | F5 | Toggle camera lock (enabled by default). This prevents the camera to *tilt* because such movements are known to cause cyber sickness. 39 | Ctrl+F5 | Apply shaders (only for the brave) 40 | F12 | Recenter 41 | Ctrl+X | Impersonate protagonist. Places the camera at the head position of the protagonist and moves along a little. 42 | Ctrl+Shift+X | Impersonate protagonist. Places the camera at the head position of the protagonist and imitates *all* head movements. 43 | NumPad +
NumPad – | Move GUI up / down. 44 | Ctrl+Shift+NumPad +
Ctrl+Shift+NumPad – | Move GUI left / right 45 | Ctrl+NumPad +
Ctrl+NumPad – | Increase / decrease GUI size. 46 | Alt+NumPad +
Alt+NumPad – | Increase / decrease player scale 47 | Shift+NumPad +
Shift+NumPad – | Increase / decrease GUI distance 48 | 49 | #### Gamepad Bindings 50 | 51 | This mod comes with a plugin that enables XInput devices to work with Play Club. Actually, you can also use most of these key bindings in the game *without* VR! 52 | 53 | If you don't know what those buttons mean, take a look [at this image](https://commons.wikimedia.org/wiki/File:360_controller.svg). 54 | 55 | Buttons | Effect 56 | ---- | ------ 57 | Move LS | Control cursor 58 | Move RS | Rotate & zoom 59 | LT+Move LS | Pan left/right, up/down 60 | LT+Move RS | Rotate camera 61 | RT+Move LS | Pan left/right, foward/backward 62 | RT+Move RS | Rotate camera 63 | A | Left click 64 | B | Right click 65 | X | Toggle "grind" animation 66 | Y | Toggle "piston" animation 67 | Start | Toggle GUI 68 | DPad Up / DPad Down | Change animation speed 69 | DPad Left / DPad Right | Change position (prev / next) 70 | Press RS | Come inside 71 | Press LS | Come outside 72 | LB+LS Horizontally | Move GUI left / right 73 | LB+LS Vertically | Move GUI up / down 74 | LB+RS Horizontally | Change GUI distance 75 | LB+RS Vertically | Change GUI width 76 | LB+DPad Up / LB+DPad Down | Change player scale 77 | LB+Y | Impersonate protatonist 78 | LB+Start | Reset camera 79 | 80 | ### Standing Mode 81 | 82 | The *standing mode* is where things start to get interesting. This mode is pretty much disconnected from the usual game in that it comes with its very own controls -- although you can still use your mouse and your keyboard. 83 | 84 | #### Keyboard Shortcuts 85 | 86 | Keys | Effect 87 | ---- | ------ 88 | Ctrl+C, Ctrl+C | Change to *s mode*. 89 | Ctrl+C, Ctrl+V | Enable (very experimental) third person camera. [Was used for this video](https://www.youtube.com/watch?v=0klN6gd1ybM). 90 | Alt+S | Save settings (IPD, screen position, etc.). 91 | Alt+L | Load settings (last saved state). 92 | Ctrl+Alt+L | Reset settings to the initial state. 93 | Ctrl+F5 | Apply shaders (only for the brave) 94 | Alt+NumPad +
Alt+NumPad – | Increase / decrease player scale 95 | 96 | #### Impersonation 97 | 98 | *Impersonating* someone is as easy as moving at the position of that character's head. The head will disappear and you can pretend to be him / her. By setting `` you can even control their hands! 99 | 100 | #### Interaction 101 | 102 | Interacting is currently only possible either with the conductor tool (which lets you move around the limbs of characters), or by touching the breasts, hair or skirts of girls. They will then start to wiggle more or less realistically, but it can be fun. 103 | 104 | ## Speech Recognition 105 | 106 | PlayClubVR comes with speech recognition, based on Windows built-in speech recognition system. Therefore, any language can be used as long as you have the right language pack installed. 107 | 108 | ### Preparations 109 | 110 | In order to get the speech recognition working, you need to install the appropriate language pack. Chances are that it's already installed, but to make sure follow these steps (on Windows 10): 111 | 112 | 1. Open "Region & language settings" 113 | 2. Click on the language you want to install and hit "Options" 114 | 3. Under "Speech" click "Download" 115 | 116 | ### Configuring 117 | 118 | When you start the game and speech recognition is enabled, it will generate a dictionary file at *UserData/dictionaries/{Locale}.txt* that you can edit. (Locale can be set in *vr_settings.xml*) 119 | 120 | The file is built up like so: 121 | 122 | ``` 123 | [Command1] 124 | Text 1 125 | Text 2 126 | ... 127 | 128 | [Command2] 129 | Text 3 130 | Text 4 131 | ``` 132 | 133 | The commands are used by the game to do things like scaling the camera or controlling the animation. The texts are what you have to say to activate the commands. 134 | 135 | ## Tools 136 | 137 | These tools are mainly meant to be used in *standing mode* but some of them are also available in *seated mode*. By default, your left hand will start with the *menu tool* and your right hand will start with the *warp tool*. In order to change them on either hand, press the *menu button* on your Vive controller. [See here an overview of buttons](https://forums.unrealengine.com/attachment.php?attachmentid=87367&d=1460020388). 138 | 139 | **You can get in-game help any time by holding the menu button!** 140 | 141 | ### Menu Tool (seated / standing) 142 | 143 | 144 | 145 | With the *menu tool* you can interact with the user interface of the game. There are, in fact, two ways you can control the mouse: a two-handed way that makes use of a laser pointer, and a one-handed way that lets you use your trackpad like a ... touchpad! 146 | 147 | #### Laser pointer 148 | 149 | 150 | 151 | To use the laser pointer, simply point the *other controller* at the menu screen. A laser pointer will appear and you can easily interact with the UI. To make a click, press the trigger button. 152 | 153 | #### Trackpad 154 | 155 | To use the trackpad, slide with your thumb over the trackpad and the mouse cursor will move accordingly. To make a click, press the trackpad. 156 | 157 | #### Attaching, Detaching and Resizing the Menu 158 | 159 | 160 | 161 | It's possible to detach and resize the menu you're holding at any point in the game. 162 | 163 | Simply press the grip button to "let go" of the menu screen -- the screen will then stay put where you left it. You can even use other tools and still interact with the screen using the *laser pointer* mechanism. 164 | 165 | Furthermore, it's possible to *resize* the screen. In order to do that, point both your controllers at a screen, press the trigger button, and move the controllers apart. It's also possible to move the screen around like this. 166 | 167 | Lastly, to take control of the screen again, press the grip button once more. 168 | 169 | ### Warp Tool (standing) 170 | 171 | 172 | 173 | The *warp tool* is only available in room scale mode and allows you to jump around in the scene. 174 | 175 | #### Warping 176 | 177 | In order to warp, touch the center of the trackpad, choose your position and press. While touching the trackpad you are able to see: 178 | 179 | 1. Where you will warp to 180 | 2. Your play area 181 | 3. A HMD that further shows where your head will be 182 | 183 | You can also *rotate* your play area while touching the trackpad by drawing circles with your thumb. 184 | 185 | 186 | 187 | #### Impersonation 188 | 189 | With this tool it's also possible to jump to the position of the protagonist: it's as easy as pressing the trigger button. 190 | 191 | However, this will only change your position along the ground plane and *not* adjust your height! If you are too lazy to move into the right position in real life, *hold* the trigger and your height will also be adjusted. 192 | 193 | #### Changing Scale and Height 194 | 195 | It's also possible to change scale and height with this tool, although it's a bit cumbersome at the moment. To do this, hold the trackpad *pressed* before warping. You can now change your future height by moving the Vive controller up and down and your scale by moving back and forth. Note that you can only change one of those two each time. 196 | 197 | By pressing the *grip button* you can reset the scale and height. 198 | 199 | #### Grabbing 200 | 201 | An alternative way to move your position is "grabbing." Just hold the grip button and move the controller around to 202 | move in the opposite direction. At any time, you can rotate your view by pressing the left or right side of your trackpad. 203 | 204 | This also works in conjunction with warping. To get back to ground level, press the grip button once to reset your play area and warp somewhere. 205 | 206 | **Note**: Grabbing is activated if A) you hold the grip button for more than 0.5s or B) you move your controller for more than 3cm. This is to discern between resetting and grabbing. 207 | 208 | ### Play Tool (standing) 209 | 210 | 211 | 212 | The *play tool* is used to interact with the scene. 213 | 214 | Button | Effect 215 | --- | --- 216 | Trackpad up | Start the action (piston movement) 217 | Trackpad down | Start the action (grind movement) 218 | Trackpad left | Previous position 219 | Trackpad right | Next position 220 | Grip | Finish 221 | 222 | You can also change the speed of the animation. In order to do this, touch the center of the trackpad and slide your thumb up or down. 223 | 224 | ### Conductor Tool (seated / standing) 225 | 226 | 227 | 228 | **Caution:** Requires [Maestro Mode](hongfire.com/forum/showthread.php/440160-%28Illusion%29-Play-Club?p=3667135#post3667135) to work in the main game! 229 | 230 | The *conductor tool* lets you pose the guys and gals. The tool works differently in the main game and the studio. 231 | 232 | #### Studio 233 | 234 | The tool is basically a wrapper around the built-in FK/IK functionality. When you switch to this tool, it will be deactivated at first (indicated by a "-" sign). In this state, you can only move the characters as a whole. 235 | 236 | In order to show the handles and move the limbs, activate the tool via the grip button. Press the trackpad to switch between IK and FK modes. 237 | 238 | To interact with the handles, simply grab them using the trigger button. **You do not need to hover over them exactly!** The tool will automatically detect the nearest handle and take control of it! 239 | 240 | #### Main Game 241 | 242 | In the main game you can use this tool if you install the [Maestro Mode](hongfire.com/forum/showthread.php/440160-%28Illusion%29-Play-Club?p=3667135#post3667135) plugin. 243 | 244 | Press the trackpad to toggle *Maestro Mode* on or off. When it's on, you should see blocks and spheres inside the body of your girl. In order to move them around, simply grab them with the *trigger* button. You can also reset their position by pressing the *grip* button. 245 | 246 | **Caution**: For this to work, you mustn't reconfigure the key bindings of Maestro Mode! 247 | 248 | ## Leap Motion 249 | 250 | Leap Motion is somewhat supported by PlayClubVR. To enable this feature, set `` to `True` in your *vr_settings.xml*. 251 | 252 | When the game detects your hands, it will automatically change into room scale mode, so make sure to set up your room correctly. 253 | 254 | ### Using the Menu 255 | 256 | It is possible to use the menu with your hands. Simply pinch your fingers and pull in different directions to draw the interface. To hide it, just keep the area small and it will disappear. 257 | 258 | Use your index finger to control the mouse cursor. 259 | 260 | 261 | 262 | ### Warping 263 | 264 | The system is not yet complete, but you can more or less jump around like so: 265 | 266 | 1. Hold your hand with the palm facing downwards. 267 | 2. Clench your fist. 268 | 3. Hold your hand with the palm facing downwards. 269 | 270 | Your play area should now appear. Keep your hand like this and move it around until you found a good position. 271 | 272 | 4. Clench your fist again. 273 | 274 | 275 | 276 | ## Settings & Tweaks 277 | 278 | Settings can be changed in the file *vr_settings.xml*, which is generated the first time you start the game. Use `RenderScale` to tweak the resolution, **not** the internal resolution dialog, as that one will currently only change the resolution of the GUI. 279 | 280 | Tag | Default | Effect | Mode 281 | ---- | ------ | ------ | ---- 282 | `` | 0.3 | Sets the distance between the camera and the GUI at `[0,0,0]`. | Seated 283 | `` | 170 | Sets the width of the arc the GUI takes up. | Seated 284 | `` | 1 | Sets the scale of the camera. The higher, the more gigantic the player is. | Seated / Standing 285 | `` | 0 | Sets the vertical offset of the GUI in meters. | Seated 286 | `` | 0 | Sets by how many degrees the GUI is rotated (around the y / up axis) | Seated 287 | `` | True | Sets whether or not rumble is activated. | Seated / Standing 288 | `` | 1 | Sets the render scale of the renderer. Increase for better quality but less performance, decrease for more performance but poor quality. | Seated / Standing 289 | `` | False | Sets whether or not the view should be mirrored in the game window. | Seated / Standing 290 | `` | False | Sets whether or not you should take control over the character's hands whenever you impersonate someone. | Standing 291 | `` | True | Sets whether or not rotating around the horizontal axis is allowed. | Seated 292 | `` | Curved | Sets the projection mode of the monitor in seated mode. (Flat / Curved / Spherical) | Seated 293 | `` | False | Sets whether or not speech recognition is enabled. | Seated / Standing 294 | `` | en-US | Sets which locale is used for speech recognition. | Seated / Standing 295 | `` | False | Sets whether or not Leap Motion support is activated. | Seated / Standing 296 | `` | True | Sets whether or not the girls should look at you by default | Seated / Standing 297 | 298 | ## Debugging 299 | 300 | The mod writes all its log output to a file called `vr.log`. In order to display the log at runtime, you can either start PlayClubVR.exe (which, incidentally, is really just [this](https://github.com/Eusth/Illusion-Plugins/tree/master/QuietLauncher)) with the "--verbose" argument or drop [the debug plugin](https://github.com/Eusth/Illusion-Plugins/releases/download/v1.4-Plugin_Architecture/DebugPlugin.zip) into your plugins folder. 301 | 302 | ## Building PlayClubVR 303 | 304 | PlayClubVR depends on the [VRGIN](https://github.com/Eusth/VRGIN) library which is included as a submodule. It is therefore important that when you clone the project, you clone it recursively. 305 | 306 | ``` 307 | git clone --recursive https://github.com/Eusth/PlayClubVR.git 308 | cd PlayClubVR 309 | ``` 310 | 311 | After cloning the repo and setting up the submodule, you should be able to compile the project by simply opening the *.sln file with Visual Studio and building. 312 | 313 | Note that there is a build configuration called "Install" that will extract your Play Club install directory from the registry and copy the files where they belong. 314 | 315 | ## Working with the Code 316 | 317 | Once you have set up your workspace, you can start coding away. The PlayClubVR project only contains the PlayClub-specific code, like the play tool and the boilerplate code. The actual framework code can be found in the VRGIN project. 318 | 319 | ### Initialization 320 | 321 | Your first point of interest will be `PlayClubVR.cs`, which sets the stage for VR. 322 | 323 | ```csharp 324 | var manager = VRManager.Create(new PlayClubContext()); 325 | manager.SetMode(); 326 | ``` 327 | 328 | This snippet shows the four central components utilized in the mod. 329 | 330 | Class | Lifetime | Purpose 331 | ---- | ------ | ------ 332 | `VRManager` | Application | Holds everything together and takes care of the camera generation and mode switching. 333 | `PlayClubInterpeter` | Application | Implements a few game specific methods that are needed by the VRGIN library. For example, finds the actors. 334 | `PlayClubContext` | Application | Simple data holder with a number of configurations, like materials, layers, colors, etc. 335 | `PlayClubSeatedMode` | Mode | The initial mode to start in. Modes contain the actual VR game logic. 336 | 337 | ### Random infos 338 | 339 | - Controls can be found in the `VRGIN.Controls` namespace. 340 | - The camera code is located in `VRGIN.Core.VRCamera`. 341 | - The GUI code is located in `VRGIN.Core.VRGUI` and is rendered by `VRGIN.Visuals.GUIQuad` and `VRGIN.Visuals.GUIMonitor` (curved). 342 | - `VRGIN.Helpers.UnityHelper` has a few useful methods for debugging purposes. Also, use the Ctrl+C, Ctrl+D shortcut to dump the entire scene to `dump.json`. 343 | - If you want to implement your own tool, create a new `Tool` subclass and add it to `PlayClubStandingMode#Tools`. 344 | - If you want to implement a new shortcut, add it to the `CreateShortcuts()` method in the mode where you want it. 345 | --------------------------------------------------------------------------------