diff --git a/Assets/LUXPiano.meta b/Assets/LUXPiano.meta new file mode 100644 index 0000000..2a4df58 --- /dev/null +++ b/Assets/LUXPiano.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f3012d801cb94df46953c3c3552d01ee +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/LUXPiano/Art.meta b/Assets/LUXPiano/Art.meta new file mode 100644 index 0000000..c4308b0 --- /dev/null +++ b/Assets/LUXPiano/Art.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b7e15f73887071743b53ca20adbfd493 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/LUXPiano/Art/Black Key.mat b/Assets/LUXPiano/Art/Black Key.mat new file mode 100644 index 0000000..f40c3d8 --- /dev/null +++ b/Assets/LUXPiano/Art/Black Key.mat @@ -0,0 +1,76 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 6 + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_Name: Black Key + m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0} + m_ShaderKeywords: + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: {} + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailAlbedoMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailMask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailNormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _EmissionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MetallicGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _OcclusionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _ParallaxMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Floats: + - _BumpScale: 1 + - _Cutoff: 0.5 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _GlossMapScale: 1 + - _Glossiness: 0.5 + - _GlossyReflections: 1 + - _Metallic: 0 + - _Mode: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _UVSec: 0 + - _ZWrite: 1 + m_Colors: + - _Color: {r: 0.122641504, g: 0.122641504, b: 0.122641504, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} diff --git a/Assets/LUXPiano/Art/Black Key.mat.meta b/Assets/LUXPiano/Art/Black Key.mat.meta new file mode 100644 index 0000000..e45e184 --- /dev/null +++ b/Assets/LUXPiano/Art/Black Key.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d6d5818e4a8fcec48a4cf66657ee4b35 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/LUXPiano/Art/White Key.mat b/Assets/LUXPiano/Art/White Key.mat new file mode 100644 index 0000000..0a429f5 --- /dev/null +++ b/Assets/LUXPiano/Art/White Key.mat @@ -0,0 +1,76 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 6 + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_Name: White Key + m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0} + m_ShaderKeywords: + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: {} + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailAlbedoMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailMask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailNormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _EmissionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MetallicGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _OcclusionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _ParallaxMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Floats: + - _BumpScale: 1 + - _Cutoff: 0.5 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _GlossMapScale: 1 + - _Glossiness: 0.5 + - _GlossyReflections: 1 + - _Metallic: 0 + - _Mode: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _UVSec: 0 + - _ZWrite: 1 + m_Colors: + - _Color: {r: 1, g: 1, b: 1, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} diff --git a/Assets/LUXPiano/Art/White Key.mat.meta b/Assets/LUXPiano/Art/White Key.mat.meta new file mode 100644 index 0000000..d435b0d --- /dev/null +++ b/Assets/LUXPiano/Art/White Key.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 10a49312282010d4381356c509b81eb3 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/MidiJack.meta b/Assets/LUXPiano/MidiJack.meta similarity index 100% rename from Assets/MidiJack.meta rename to Assets/LUXPiano/MidiJack.meta diff --git a/Assets/MidiJack/Editor.meta b/Assets/LUXPiano/MidiJack/Editor.meta similarity index 100% rename from Assets/MidiJack/Editor.meta rename to Assets/LUXPiano/MidiJack/Editor.meta diff --git a/Assets/MidiJack/Editor/MidiJackWindow.cs b/Assets/LUXPiano/MidiJack/Editor/MidiJackWindow.cs similarity index 100% rename from Assets/MidiJack/Editor/MidiJackWindow.cs rename to Assets/LUXPiano/MidiJack/Editor/MidiJackWindow.cs diff --git a/Assets/MidiJack/Editor/MidiJackWindow.cs.meta b/Assets/LUXPiano/MidiJack/Editor/MidiJackWindow.cs.meta similarity index 100% rename from Assets/MidiJack/Editor/MidiJackWindow.cs.meta rename to Assets/LUXPiano/MidiJack/Editor/MidiJackWindow.cs.meta diff --git a/Assets/MidiJack/Midi.cs b/Assets/LUXPiano/MidiJack/Midi.cs similarity index 100% rename from Assets/MidiJack/Midi.cs rename to Assets/LUXPiano/MidiJack/Midi.cs diff --git a/Assets/MidiJack/Midi.cs.meta b/Assets/LUXPiano/MidiJack/Midi.cs.meta similarity index 100% rename from Assets/MidiJack/Midi.cs.meta rename to Assets/LUXPiano/MidiJack/Midi.cs.meta diff --git a/Assets/MidiJack/MidiDriver.cs b/Assets/LUXPiano/MidiJack/MidiDriver.cs similarity index 100% rename from Assets/MidiJack/MidiDriver.cs rename to Assets/LUXPiano/MidiJack/MidiDriver.cs diff --git a/Assets/MidiJack/MidiDriver.cs.meta b/Assets/LUXPiano/MidiJack/MidiDriver.cs.meta similarity index 100% rename from Assets/MidiJack/MidiDriver.cs.meta rename to Assets/LUXPiano/MidiJack/MidiDriver.cs.meta diff --git a/Assets/MidiJack/MidiMaster.cs b/Assets/LUXPiano/MidiJack/MidiMaster.cs similarity index 100% rename from Assets/MidiJack/MidiMaster.cs rename to Assets/LUXPiano/MidiJack/MidiMaster.cs diff --git a/Assets/MidiJack/MidiMaster.cs.meta b/Assets/LUXPiano/MidiJack/MidiMaster.cs.meta similarity index 100% rename from Assets/MidiJack/MidiMaster.cs.meta rename to Assets/LUXPiano/MidiJack/MidiMaster.cs.meta diff --git a/Assets/MidiJack/MidiStateUpdater.cs b/Assets/LUXPiano/MidiJack/MidiStateUpdater.cs similarity index 100% rename from Assets/MidiJack/MidiStateUpdater.cs rename to Assets/LUXPiano/MidiJack/MidiStateUpdater.cs diff --git a/Assets/MidiJack/MidiStateUpdater.cs.meta b/Assets/LUXPiano/MidiJack/MidiStateUpdater.cs.meta similarity index 100% rename from Assets/MidiJack/MidiStateUpdater.cs.meta rename to Assets/LUXPiano/MidiJack/MidiStateUpdater.cs.meta diff --git a/Assets/MidiJack/Plugins.meta b/Assets/LUXPiano/MidiJack/Plugins.meta similarity index 100% rename from Assets/MidiJack/Plugins.meta rename to Assets/LUXPiano/MidiJack/Plugins.meta diff --git a/Assets/MidiJack/Plugins/MidiJackPlugin.bundle.meta b/Assets/LUXPiano/MidiJack/Plugins/MidiJackPlugin.bundle.meta similarity index 100% rename from Assets/MidiJack/Plugins/MidiJackPlugin.bundle.meta rename to Assets/LUXPiano/MidiJack/Plugins/MidiJackPlugin.bundle.meta diff --git a/Assets/MidiJack/Plugins/MidiJackPlugin.bundle/Contents.meta b/Assets/LUXPiano/MidiJack/Plugins/MidiJackPlugin.bundle/Contents.meta similarity index 100% rename from Assets/MidiJack/Plugins/MidiJackPlugin.bundle/Contents.meta rename to Assets/LUXPiano/MidiJack/Plugins/MidiJackPlugin.bundle/Contents.meta diff --git a/Assets/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/Info.plist b/Assets/LUXPiano/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/Info.plist similarity index 100% rename from Assets/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/Info.plist rename to Assets/LUXPiano/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/Info.plist diff --git a/Assets/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/Info.plist.meta b/Assets/LUXPiano/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/Info.plist.meta similarity index 100% rename from Assets/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/Info.plist.meta rename to Assets/LUXPiano/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/Info.plist.meta diff --git a/Assets/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/MacOS.meta b/Assets/LUXPiano/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/MacOS.meta similarity index 100% rename from Assets/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/MacOS.meta rename to Assets/LUXPiano/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/MacOS.meta diff --git a/Assets/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/MacOS/MidiJackPlugin b/Assets/LUXPiano/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/MacOS/MidiJackPlugin similarity index 100% rename from Assets/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/MacOS/MidiJackPlugin rename to Assets/LUXPiano/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/MacOS/MidiJackPlugin diff --git a/Assets/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/MacOS/MidiJackPlugin.meta b/Assets/LUXPiano/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/MacOS/MidiJackPlugin.meta similarity index 100% rename from Assets/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/MacOS/MidiJackPlugin.meta rename to Assets/LUXPiano/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/MacOS/MidiJackPlugin.meta diff --git a/Assets/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/Resources.meta b/Assets/LUXPiano/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/Resources.meta similarity index 100% rename from Assets/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/Resources.meta rename to Assets/LUXPiano/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/Resources.meta diff --git a/Assets/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/Resources/en.lproj.meta b/Assets/LUXPiano/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/Resources/en.lproj.meta similarity index 100% rename from Assets/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/Resources/en.lproj.meta rename to Assets/LUXPiano/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/Resources/en.lproj.meta diff --git a/Assets/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/Resources/en.lproj/InfoPlist.strings b/Assets/LUXPiano/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/Resources/en.lproj/InfoPlist.strings similarity index 100% rename from Assets/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/Resources/en.lproj/InfoPlist.strings rename to Assets/LUXPiano/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/Resources/en.lproj/InfoPlist.strings diff --git a/Assets/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/Resources/en.lproj/InfoPlist.strings.meta b/Assets/LUXPiano/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/Resources/en.lproj/InfoPlist.strings.meta similarity index 100% rename from Assets/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/Resources/en.lproj/InfoPlist.strings.meta rename to Assets/LUXPiano/MidiJack/Plugins/MidiJackPlugin.bundle/Contents/Resources/en.lproj/InfoPlist.strings.meta diff --git a/Assets/MidiJack/Plugins/x64.meta b/Assets/LUXPiano/MidiJack/Plugins/x64.meta similarity index 100% rename from Assets/MidiJack/Plugins/x64.meta rename to Assets/LUXPiano/MidiJack/Plugins/x64.meta diff --git a/Assets/MidiJack/Plugins/x64/MidiJackPlugin.dll b/Assets/LUXPiano/MidiJack/Plugins/x64/MidiJackPlugin.dll similarity index 100% rename from Assets/MidiJack/Plugins/x64/MidiJackPlugin.dll rename to Assets/LUXPiano/MidiJack/Plugins/x64/MidiJackPlugin.dll diff --git a/Assets/MidiJack/Plugins/x64/MidiJackPlugin.dll.meta b/Assets/LUXPiano/MidiJack/Plugins/x64/MidiJackPlugin.dll.meta similarity index 100% rename from Assets/MidiJack/Plugins/x64/MidiJackPlugin.dll.meta rename to Assets/LUXPiano/MidiJack/Plugins/x64/MidiJackPlugin.dll.meta diff --git a/Assets/MidiJack/Plugins/x86.meta b/Assets/LUXPiano/MidiJack/Plugins/x86.meta similarity index 100% rename from Assets/MidiJack/Plugins/x86.meta rename to Assets/LUXPiano/MidiJack/Plugins/x86.meta diff --git a/Assets/MidiJack/Plugins/x86/MidiJackPlugin.dll b/Assets/LUXPiano/MidiJack/Plugins/x86/MidiJackPlugin.dll similarity index 100% rename from Assets/MidiJack/Plugins/x86/MidiJackPlugin.dll rename to Assets/LUXPiano/MidiJack/Plugins/x86/MidiJackPlugin.dll diff --git a/Assets/MidiJack/Plugins/x86/MidiJackPlugin.dll.meta b/Assets/LUXPiano/MidiJack/Plugins/x86/MidiJackPlugin.dll.meta similarity index 100% rename from Assets/MidiJack/Plugins/x86/MidiJackPlugin.dll.meta rename to Assets/LUXPiano/MidiJack/Plugins/x86/MidiJackPlugin.dll.meta diff --git a/Assets/LUXPiano/Plugins.meta b/Assets/LUXPiano/Plugins.meta new file mode 100644 index 0000000..611e67c --- /dev/null +++ b/Assets/LUXPiano/Plugins.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 74ab43bd66b4741499c17f218d56c5f3 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/LUXPiano/Plugins/Editor.meta b/Assets/LUXPiano/Plugins/Editor.meta new file mode 100644 index 0000000..0d148fe --- /dev/null +++ b/Assets/LUXPiano/Plugins/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 95004c6a58042874e98835292d89675a +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/LUXPiano/Plugins/Editor/JetBrains.meta b/Assets/LUXPiano/Plugins/Editor/JetBrains.meta new file mode 100644 index 0000000..b4cab5e --- /dev/null +++ b/Assets/LUXPiano/Plugins/Editor/JetBrains.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 61a19c79d2c13dc41b1c275ab74ef889 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/LUXPiano/Plugins/Editor/JetBrains/Unity3DRider.cs b/Assets/LUXPiano/Plugins/Editor/JetBrains/Unity3DRider.cs new file mode 100644 index 0000000..6c6fed0 --- /dev/null +++ b/Assets/LUXPiano/Plugins/Editor/JetBrains/Unity3DRider.cs @@ -0,0 +1,1363 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Version: 2.1.3.5034 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ +using Application = UnityEngine.Application; +using Debug = UnityEngine.Debug; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Net; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text.RegularExpressions; +using System.Text; +using System.Xml.Linq; +using System; +using UnityEditor; +using UnityEngine; + +namespace Plugins.Editor.JetBrains +{ + public class RiderAssetPostprocessor : AssetPostprocessor + { + public static void OnGeneratedCSProjectFiles() + { + if (!RiderPlugin.Enabled) + return; + var currentDirectory = Directory.GetCurrentDirectory(); + var projectFiles = Directory.GetFiles(currentDirectory, "*.csproj"); + + foreach (var file in projectFiles) + { + UpgradeProjectFile(file); + } + + var slnFile = RiderPlugin.SlnFile; + if (string.IsNullOrEmpty(slnFile)) + return; + + RiderPlugin.Log(RiderPlugin.LoggingLevel.Verbose, string.Format("Post-processing {0}", slnFile)); + string slnAllText = File.ReadAllText(slnFile); + const string unityProjectGuid = @"Project(""{E097FAD1-6243-4DAD-9C02-E9B9EFC3FFC1}"")"; + if (!slnAllText.Contains(unityProjectGuid)) + { + string matchGUID = @"Project\(\""\{[A-Z0-9]{8}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{12}\}\""\)"; + // Unity may put a random guid, unityProjectGuid will help VSTU recognize Rider-generated projects + slnAllText = Regex.Replace(slnAllText, matchGUID, unityProjectGuid); + } + + var lines = slnAllText.Split(new[] {Environment.NewLine}, StringSplitOptions.RemoveEmptyEntries); + var sb = new StringBuilder(); + foreach (var line in lines) + { + if (line.StartsWith("Project(")) + { + MatchCollection mc = Regex.Matches(line, "\"([^\"]*)\""); + //RiderPlugin.Log(RiderPlugin.LoggingLevel.Info, "mc[1]: "+mc[1].Value); + //RiderPlugin.Log(RiderPlugin.LoggingLevel.Info, "mc[2]: "+mc[2].Value); + var to = GetFileNameWithoutExtension(mc[2].Value.Substring(1, mc[2].Value.Length-1)); // remove quotes + //RiderPlugin.Log(RiderPlugin.LoggingLevel.Info, "to:" + to); + //RiderPlugin.Log(RiderPlugin.LoggingLevel.Info, line); + var newLine = line.Substring(0, mc[1].Index + 1) + to + line.Substring(mc[1].Index + mc[1].Value.Length - 1); + sb.Append(newLine); + //RiderPlugin.Log(RiderPlugin.LoggingLevel.Info, newLine); + } + else + { + sb.Append(line); + } + sb.Append(Environment.NewLine); + } + File.WriteAllText(slnFile, sb.ToString()); + } + + private static string GetFileNameWithoutExtension(string path) + { + if (string.IsNullOrEmpty(path)) + return null; + int length; + return (length = path.LastIndexOf('.')) == -1 ? path : path.Substring(0, length); + } + + private static void UpgradeProjectFile(string projectFile) + { + RiderPlugin.Log(RiderPlugin.LoggingLevel.Verbose, string.Format("Post-processing {0}", projectFile)); + var doc = XDocument.Load(projectFile); + var projectContentElement = doc.Root; + XNamespace xmlns = projectContentElement.Name.NamespaceName; // do not use var + + FixTargetFrameworkVersion(projectContentElement, xmlns); + FixSystemXml(projectContentElement, xmlns); + SetLangVersion(projectContentElement, xmlns); + // Unity_5_6_OR_NEWER switched to nunit 3.5 +#if UNITY_5_6_OR_NEWER + ChangeNunitReference(projectContentElement, xmlns); +#endif + +#if !UNITY_2017_1_OR_NEWER // Unity 2017.1 and later has this features by itself + SetManuallyDefinedComilingSettings(projectFile, projectContentElement, xmlns); +#endif + SetXCodeDllReference("UnityEditor.iOS.Extensions.Xcode.dll", xmlns, projectContentElement); + SetXCodeDllReference("UnityEditor.iOS.Extensions.Common.dll", xmlns, projectContentElement); + ApplyManualCompilingSettingsReferences(projectContentElement, xmlns); + doc.Save(projectFile); + } + + private static void FixSystemXml(XElement projectContentElement, XNamespace xmlns) + { + var el = projectContentElement + .Elements(xmlns+"ItemGroup") + .Elements(xmlns+"Reference") + .FirstOrDefault(a => a.Attribute("Include").Value=="System.XML"); + if (el != null) + { + el.Attribute("Include").Value = "System.Xml"; + } + } + + private static void ChangeNunitReference(XElement projectContentElement, XNamespace xmlns) + { + var el = projectContentElement + .Elements(xmlns+"ItemGroup") + .Elements(xmlns+"Reference") + .FirstOrDefault(a => a.Attribute("Include").Value=="nunit.framework"); + if (el != null) + { + var hintPath = el.Elements(xmlns + "HintPath").FirstOrDefault(); + if (hintPath != null) + { + var path = Path.GetFullPath("Library/resharper-unity-libs/nunit3.5.0/nunit.framework.dll"); + if (new FileInfo(path).Exists) + hintPath.Value = path; + } + } + } + + private static readonly string PROJECT_MANUAL_CONFIG_ABSOLUTE_FILE_PATH = Path.GetFullPath("Assets/mcs.rsp"); +#if !UNITY_2017_1_OR_NEWER // Unity 2017.1 and later has this features by itself + private const string UNITY_PLAYER_PROJECT_NAME = "Assembly-CSharp.csproj"; + private const string UNITY_EDITOR_PROJECT_NAME = "Assembly-CSharp-Editor.csproj"; + private const string UNITY_UNSAFE_KEYWORD = "-unsafe"; + private const string UNITY_DEFINE_KEYWORD = "-define:"; + private static readonly string PLAYER_PROJECT_MANUAL_CONFIG_ABSOLUTE_FILE_PATH = Path.GetFullPath("Assets/smcs.rsp"); + private static readonly string EDITOR_PROJECT_MANUAL_CONFIG_ABSOLUTE_FILE_PATH = Path.GetFullPath("Assets/gmcs.rsp"); + + private static void SetManuallyDefinedComilingSettings(string projectFile, XElement projectContentElement, XNamespace xmlns) + { + string configPath = null; + + if (IsPlayerProjectFile(projectFile) || IsEditorProjectFile(projectFile)) + { + //Prefer mcs.rsp if it exists + if (File.Exists(PROJECT_MANUAL_CONFIG_ABSOLUTE_FILE_PATH)) + { + configPath = PROJECT_MANUAL_CONFIG_ABSOLUTE_FILE_PATH; + } + else + { + if (IsPlayerProjectFile(projectFile)) + configPath = PLAYER_PROJECT_MANUAL_CONFIG_ABSOLUTE_FILE_PATH; + else if (IsEditorProjectFile(projectFile)) + configPath = EDITOR_PROJECT_MANUAL_CONFIG_ABSOLUTE_FILE_PATH; + } + } + + if(!string.IsNullOrEmpty(configPath)) + ApplyManualCompilingSettings(configPath + , projectContentElement + , xmlns); + } + + private static void ApplyManualCompilingSettings(string configFilePath, XElement projectContentElement, XNamespace xmlns) + { + if (File.Exists(configFilePath)) + { + var configText = File.ReadAllText(configFilePath); + if (configText.Contains(UNITY_UNSAFE_KEYWORD)) + { + // Add AllowUnsafeBlocks to the .csproj. Unity doesn't generate it (although VSTU does). + // Strictly necessary to compile unsafe code + ApplyAllowUnsafeBlocks(projectContentElement, xmlns); + } + if (configText.Contains(UNITY_DEFINE_KEYWORD)) + { + // defines could be + // 1) -define:DEFINE1,DEFINE2 + // 2) -define:DEFINE1;DEFINE2 + // 3) -define:DEFINE1 -define:DEFINE2 + // 4) -define:DEFINE1,DEFINE2;DEFINE3 + // tested on "-define:DEF1;DEF2 -define:DEF3,DEF4;DEFFFF \n -define:DEF5" + // result: DEF1, DEF2, DEF3, DEF4, DEFFFF, DEF5 + + var definesList = new List(); + var compileFlags = configText.Split(' ', '\n'); + foreach (var flag in compileFlags) + { + var f = flag.Trim(); + if (f.Contains(UNITY_DEFINE_KEYWORD)) + { + var defineEndPos = f.IndexOf(UNITY_DEFINE_KEYWORD) + UNITY_DEFINE_KEYWORD.Length; + var definesSubString = f.Substring(defineEndPos,f.Length - defineEndPos); + definesSubString = definesSubString.Replace(";", ","); + definesList.AddRange(definesSubString.Split(',')); + } + } + + ApplyCustomDefines(definesList.ToArray(), projectContentElement, xmlns); + } + } + } + + private static void ApplyCustomDefines(string[] customDefines, XElement projectContentElement, XNamespace xmlns) + { + var definesString = string.Join(";", customDefines); + + var DefineConstants = projectContentElement + .Elements(xmlns+"PropertyGroup") + .Elements(xmlns+"DefineConstants") + .FirstOrDefault(definesConsts=> !string.IsNullOrEmpty(definesConsts.Value)); + + if (DefineConstants != null) + { + DefineConstants.SetValue(DefineConstants.Value + ";" + definesString); + } + } + + private static void ApplyAllowUnsafeBlocks(XElement projectContentElement, XNamespace xmlns) + { + projectContentElement.AddFirst( + new XElement(xmlns + "PropertyGroup", new XElement(xmlns + "AllowUnsafeBlocks", true))); + } + + private static bool IsPlayerProjectFile(string projectFile) + { + return Path.GetFileName(projectFile) == UNITY_PLAYER_PROJECT_NAME; + } + + private static bool IsEditorProjectFile(string projectFile) + { + return Path.GetFileName(projectFile) == UNITY_EDITOR_PROJECT_NAME; + } +#endif + private static void SetXCodeDllReference(string name, XNamespace xmlns, XElement projectContentElement) + { + string unityAppBaseFolder = Path.GetDirectoryName(EditorApplication.applicationPath); + + var xcodeDllPath = Path.Combine(unityAppBaseFolder, Path.Combine("Data/PlaybackEngines/iOSSupport", name)); + if (!File.Exists(xcodeDllPath)) + xcodeDllPath = Path.Combine(unityAppBaseFolder, Path.Combine("PlaybackEngines/iOSSupport", name)); + + if (File.Exists(xcodeDllPath)) + { + var itemGroup = new XElement(xmlns + "ItemGroup"); + var reference = new XElement(xmlns + "Reference"); + reference.Add(new XAttribute("Include", Path.GetFileNameWithoutExtension(xcodeDllPath))); + reference.Add(new XElement(xmlns + "HintPath", xcodeDllPath)); + itemGroup.Add(reference); + projectContentElement.Add(itemGroup); + } + } + + private const string UNITY_REFERENCE_KEYWORD = "-r:"; + /// + /// Handles custom references -r: in "mcs.rsp" + /// + /// + /// + private static void ApplyManualCompilingSettingsReferences(XElement projectContentElement, XNamespace xmlns) + { + if (!File.Exists(PROJECT_MANUAL_CONFIG_ABSOLUTE_FILE_PATH)) + return; + + var configFilePath = PROJECT_MANUAL_CONFIG_ABSOLUTE_FILE_PATH; + + if (File.Exists(configFilePath)) + { + var configText = File.ReadAllText(configFilePath); + if (configText.Contains(UNITY_REFERENCE_KEYWORD)) + { + var referenceList = new List(); + var compileFlags = configText.Split(' ', '\n'); + foreach (var flag in compileFlags) + { + var f = flag.Trim(); + if (f.Contains(UNITY_REFERENCE_KEYWORD)) + { + var defineEndPos = f.IndexOf(UNITY_REFERENCE_KEYWORD) + UNITY_REFERENCE_KEYWORD.Length; + var definesSubString = f.Substring(defineEndPos,f.Length - defineEndPos); + definesSubString = definesSubString.Replace(";", ","); + referenceList.AddRange(definesSubString.Split(',')); + } + } + + foreach (var referenceName in referenceList) + { + ApplyCustomReference(referenceName, projectContentElement, xmlns); + } + } + } + } + + private static void ApplyCustomReference(string name, XElement projectContentElement, XNamespace xmlns) + { + var itemGroup = new XElement(xmlns + "ItemGroup"); + var reference = new XElement(xmlns + "Reference"); + reference.Add(new XAttribute("Include", Path.GetFileNameWithoutExtension(name))); + itemGroup.Add(reference); + projectContentElement.Add(itemGroup); + } + + // Set appropriate version + private static void FixTargetFrameworkVersion(XElement projectElement, XNamespace xmlns) + { + var targetFrameworkVersion = projectElement.Elements(xmlns + "PropertyGroup") + .Elements(xmlns + "TargetFrameworkVersion") + .FirstOrDefault(); // Processing csproj files, which are not Unity-generated #56 + if (targetFrameworkVersion != null) + { + int scriptingRuntime = 0; // legacy runtime + try + { + var property = typeof(EditorApplication).GetProperty("scriptingRuntimeVersion"); + scriptingRuntime = (int)property.GetValue(null, null); + if (scriptingRuntime>0) + RiderPlugin.Log(RiderPlugin.LoggingLevel.Verbose, "Latest runtime detected."); + } + catch(Exception){} + + if (scriptingRuntime>0) + targetFrameworkVersion.SetValue("v"+RiderPlugin.TargetFrameworkVersion); + else + targetFrameworkVersion.SetValue("v"+RiderPlugin.TargetFrameworkVersionOldMono); + } + } + + private static void SetLangVersion(XElement projectElement, XNamespace xmlns) + { + // Add LangVersion to the .csproj. Unity doesn't generate it (although VSTU does). + // Not strictly necessary, as the Unity plugin for Rider will work it out, but setting + // it makes Rider work if it's not installed. + var langVersion = projectElement.Elements(xmlns + "PropertyGroup").Elements(xmlns + "LangVersion") + .FirstOrDefault(); // Processing csproj files, which are not Unity-generated #56 + if (langVersion != null) + { + langVersion.SetValue(GetLanguageLevel()); + } + else + { + projectElement.AddFirst(new XElement(xmlns + "PropertyGroup", + new XElement(xmlns + "LangVersion", GetLanguageLevel()))); + } + } + + private static string GetLanguageLevel() + { + // https://bitbucket.org/alexzzzz/unity-c-5.0-and-6.0-integration/src + if (Directory.Exists(Path.GetFullPath("CSharp70Support"))) + return "7"; + if (Directory.Exists(Path.GetFullPath("CSharp60Support"))) + return "6"; + + // Unity 5.5 supports C# 6, but only when targeting .NET 4.6. The enum doesn't exist pre Unity 5.5 +#if !UNITY_5_6_OR_NEWER + if ((int)PlayerSettings.apiCompatibilityLevel >= 3) + #else + if ((int) PlayerSettings.GetApiCompatibilityLevel(EditorUserBuildSettings.selectedBuildTargetGroup) >= 3) +#endif + return "6"; + + return "4"; + } + + private static Type ourPdb2MdbDriver; + private static Type Pdb2MdbDriver + { + get + { + if (ourPdb2MdbDriver != null) + return ourPdb2MdbDriver; + Assembly assembly; + try + { + var path = Path.GetFullPath(@"Library\resharper-unity-libs\pdb2mdb.exe"); + var bytes = File.ReadAllBytes(path); + assembly = Assembly.Load(bytes); + } + catch (Exception) + { + RiderPlugin.Log(RiderPlugin.LoggingLevel.Verbose, "Loading pdb2mdb failed."); + assembly = null; + } + + if (assembly == null) + return null; + var type = assembly.GetType("Pdb2Mdb.Driver"); + if (type == null) + return null; + return ourPdb2MdbDriver = type; + } + } + + public static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromPath) + { + if (!RiderPlugin.Enabled) + return; + var toBeConverted = importedAssets.Where(a => + a.EndsWith(".dll", StringComparison.OrdinalIgnoreCase) && + importedAssets.Any(a1 => a1 == Path.ChangeExtension(a, ".pdb")) && + importedAssets.All(b => b != Path.ChangeExtension(a, ".dll.mdb"))) + .ToArray(); + foreach (var asset in toBeConverted) + { + var pdb = Path.ChangeExtension(asset, ".pdb"); + if (!IsPortablePdb(pdb)) + ConvertSymbolsForAssembly(asset); + else + RiderPlugin.Log(RiderPlugin.LoggingLevel.Verbose, string.Format("mdb generation for Portable pdb is not supported. {0}", pdb)); + } + } + + private static void ConvertSymbolsForAssembly(string asset) + { + if (Pdb2MdbDriver == null) + { + RiderPlugin.Log(RiderPlugin.LoggingLevel.Verbose, "FailedToConvertDebugSymbolsNoPdb2mdb."); + return; + } + + var method = Pdb2MdbDriver.GetMethod("Main", BindingFlags.Static | BindingFlags.NonPublic); + if (method == null) + { + RiderPlugin.Log(RiderPlugin.LoggingLevel.Verbose, "WarningFailedToConvertDebugSymbolsPdb2mdbMainIsNull."); + return; + } + + var strArray = new[] { Path.GetFullPath(asset) }; + method.Invoke(null, new object[] { strArray }); + } + + //https://github.com/xamarin/xamarin-android/commit/4e30546f + const uint ppdb_signature = 0x424a5342; + public static bool IsPortablePdb(string filename) + { + try + { + using (var fs = new FileStream(filename, FileMode.Open, FileAccess.Read)) + { + using (var br = new BinaryReader(fs)) + { + return br.ReadUInt32() == ppdb_signature; + } + } + } + catch + { + return false; + } + } + } +} + +namespace Plugins.Editor.JetBrains +{ + [InitializeOnLoad] + public static class RiderPlugin + { + private static bool Initialized; + internal static string SlnFile; + + public static void Log(LoggingLevel level, string initialText) + { + if (level < SelectedLoggingLevel) return; + + var text = "[Rider] "+DateTime.Now.ToString("HH:mm:ss:ff")+" [" + level + "] " + initialText; + + switch (level) + { + case LoggingLevel.Warning: + Debug.LogWarning(text); + break; + default: + Debug.Log(text); + break; + } + } + + private static string GetDefaultApp() + { + var allFoundPaths = GetAllRiderPaths().Select(a=>new FileInfo(a).FullName).ToArray(); + var externalEditor = GetExternalScriptEditor(); + if (!string.IsNullOrEmpty(externalEditor)) + { + var alreadySetPath = new FileInfo(externalEditor).FullName; + if (RiderPathExist(alreadySetPath)) + { + if (!allFoundPaths.Any() || allFoundPaths.Any() && allFoundPaths.Contains(alreadySetPath)) + { + RiderPath = alreadySetPath; + return alreadySetPath; + } + } + } + if (!string.IsNullOrEmpty(RiderPath) && allFoundPaths.Contains(new FileInfo(RiderPath).FullName)) {} + else + RiderPath = allFoundPaths.FirstOrDefault(); + + return RiderPath; + } + + private static string[] GetAllRiderPaths() + { + switch (SystemInfoRiderPlugin.operatingSystemFamily) + { + case OperatingSystemFamily.Windows: + string[] folders = + { + @"C:\ProgramData\Microsoft\Windows\Start Menu\Programs\JetBrains", Path.Combine( + Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), + @"Microsoft\Windows\Start Menu\Programs\JetBrains Toolbox") + }; + + var newPathLnks = folders.Select(b => new DirectoryInfo(b)).Where(a => a.Exists) + .SelectMany(c => c.GetFiles("*Rider*.lnk")).ToArray(); + if (newPathLnks.Any()) + { + var newPaths = newPathLnks + .Select(newPathLnk => new FileInfo(ShortcutResolver.Resolve(newPathLnk.FullName))) + .Where(fi => File.Exists(fi.FullName)) + .ToArray() + .OrderByDescending(fi => FileVersionInfo.GetVersionInfo(fi.FullName).ProductVersion) + .Select(a => a.FullName).ToArray(); + + return newPaths; + } + break; + + case OperatingSystemFamily.MacOSX: + // "/Applications/*Rider*.app" + //"~/Applications/JetBrains Toolbox/*Rider*.app" + string[] foldersMac = + { + "/Applications", Path.Combine(Environment.GetEnvironmentVariable("HOME"), "Applications/JetBrains Toolbox") + }; + var newPathsMac = foldersMac.Select(b => new DirectoryInfo(b)).Where(a => a.Exists) + .SelectMany(c => c.GetDirectories("*Rider*.app")) + .Select(a => a.FullName).ToArray(); + return newPathsMac; + } + return new string[0]; + } + + private static string GetTargetFrameworkVersionDefault(string defaultValue) + { + if (SystemInfoRiderPlugin.operatingSystemFamily == OperatingSystemFamily.Windows) + { + var dir = new DirectoryInfo(@"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework"); + if (dir.Exists) + { + var availableVersions = dir.GetDirectories("v*").Select(a => a.Name.Substring(1)) + .Where(v => TryCatch(v, s => { })).ToArray(); + if (availableVersions.Any() && !availableVersions.Contains(defaultValue)) + { + defaultValue = availableVersions.OrderBy(a => new Version(a)).Last(); + } + } + } + return defaultValue; + } + + + public static string TargetFrameworkVersion + { + get + { + return EditorPrefs.GetString("Rider_TargetFrameworkVersion", GetTargetFrameworkVersionDefault("4.6")); + } + set + { + TryCatch(value, val => + { + EditorPrefs.SetString("Rider_TargetFrameworkVersion", val); + }); + } + } + + public static string TargetFrameworkVersionOldMono + { + get + { + return EditorPrefs.GetString("Rider_TargetFrameworkVersionOldMono", GetTargetFrameworkVersionDefault("3.5")); + } + set + { + TryCatch(value, val => + { + EditorPrefs.SetString("Rider_TargetFrameworkVersionOldMono", val); + }); + } + } + + private static bool TryCatch(string value, Action action) + { + try + { + new Version(value); // mono 2.6 doesn't support Version.TryParse + action(value); + return true; + } + catch (ArgumentException) + { + } // can't put loggin here because ot fire on every symbol + catch (FormatException) + { + } + return false; + } + + public static string RiderPath + { + get { return EditorPrefs.GetString("Rider_RiderPath", GetAllRiderPaths().FirstOrDefault()); } + set { EditorPrefs.SetString("Rider_RiderPath", value); } + } + + public enum LoggingLevel + { + Verbose = 0, + Info = 1, + Warning = 2 + } + + public static LoggingLevel SelectedLoggingLevel + { + get { return (LoggingLevel) EditorPrefs.GetInt("Rider_SelectedLoggingLevel", 1); } + set { EditorPrefs.SetInt("Rider_SelectedLoggingLevel", (int) value); } + } + + public static bool RiderInitializedOnce + { + get { return EditorPrefs.GetBool("RiderInitializedOnce", false); } + set { EditorPrefs.SetBool("RiderInitializedOnce", value); } + } + + internal static bool Enabled + { + get + { + var defaultApp = GetExternalScriptEditor(); + return !string.IsNullOrEmpty(defaultApp) && Path.GetFileName(defaultApp).ToLower().Contains("rider"); + } + } + + static RiderPlugin() + { + var riderPath = GetDefaultApp(); + if (!RiderPathExist(riderPath)) + return; + + AddRiderToRecentlyUsedScriptApp(riderPath, "RecentlyUsedScriptApp"); + if (!RiderInitializedOnce) + { + SetExternalScriptEditor(riderPath); + RiderInitializedOnce = true; + } + if (Enabled) + { + InitRiderPlugin(); + } + } + + private static void InitRiderPlugin() + { + var projectDirectory = Directory.GetParent(Application.dataPath).FullName; + + var projectName = Path.GetFileName(projectDirectory); + SlnFile = Path.GetFullPath(string.Format("{0}.sln", projectName)); + + InitializeEditorInstanceJson(); + + RiderAssetPostprocessor.OnGeneratedCSProjectFiles(); + + Log(LoggingLevel.Info, "Rider plugin initialized. You may change the amount of Rider Debug output via Edit -> Preferences -> Rider -> Logging Level"); + Initialized = true; + } + + private static void AddRiderToRecentlyUsedScriptApp(string userAppPath, string recentAppsKey) + { + for (int index = 0; index < 10; ++index) + { + string path = EditorPrefs.GetString(recentAppsKey + (object) index); + if (File.Exists(path) && Path.GetFileName(path).ToLower().Contains("rider")) + return; + } + EditorPrefs.SetString(recentAppsKey + 9, userAppPath); + } + + private static string GetExternalScriptEditor() + { + return EditorPrefs.GetString("kScriptsDefaultApp"); + } + + private static void SetExternalScriptEditor(string path) + { + EditorPrefs.SetString("kScriptsDefaultApp", path); + } + + private static bool RiderPathExist(string path) + { + if (string.IsNullOrEmpty(path)) + return false; + // windows or mac + var fileInfo = new FileInfo(path); + if (!fileInfo.Name.ToLower().Contains("rider")) + return false; + var directoryInfo = new DirectoryInfo(path); + return fileInfo.Exists || (SystemInfoRiderPlugin.operatingSystemFamily == OperatingSystemFamily.MacOSX && + directoryInfo.Exists); + } + + /// + /// Creates and deletes Library/EditorInstance.json containing info about unity instance + /// + private static void InitializeEditorInstanceJson() + { + Log(LoggingLevel.Verbose, "Writing Library/EditorInstance.json"); + + var editorInstanceJsonPath = Path.GetFullPath("Library/EditorInstance.json"); + + File.WriteAllText(editorInstanceJsonPath, string.Format(@"{{ + ""process_id"": {0}, + ""version"": ""{1}"", + ""app_path"": ""{2}"", + ""app_contents_path"": ""{3}"", + ""attach_allowed"": ""{4}"" +}}", Process.GetCurrentProcess().Id, Application.unityVersion, + EditorApplication.applicationPath, + EditorApplication.applicationContentsPath, + EditorPrefs.GetBool("AllowAttachedDebuggingOfEditor", true) + )); + + AppDomain.CurrentDomain.DomainUnload += (sender, args) => + { + Log(LoggingLevel.Verbose, "Deleting Library/EditorInstance.json"); + File.Delete(editorInstanceJsonPath); + }; + } + + /// + /// Asset Open Callback (from Unity) + /// + /// + /// Called when Unity is about to open an asset. + /// + [UnityEditor.Callbacks.OnOpenAssetAttribute()] + static bool OnOpenedAsset(int instanceID, int line) + { + if (Enabled) + { + if (!Initialized) + { + // make sure the plugin was initialized first. + // this can happen in case "Rider" was set as the default scripting app only after this plugin was imported. + InitRiderPlugin(); + } + + // determine asset that has been double clicked in the project view + var selected = EditorUtility.InstanceIDToObject(instanceID); + + var assetFilePath = Path.GetFullPath(AssetDatabase.GetAssetPath(selected)); + if (!(selected.GetType().ToString() == "UnityEditor.MonoScript" || + selected.GetType().ToString() == "UnityEngine.Shader" || + (selected.GetType().ToString() == "UnityEngine.TextAsset" && +#if UNITY_5 || UNITY_5_5_OR_NEWER + EditorSettings.projectGenerationUserExtensions.Contains(Path.GetExtension(assetFilePath).Substring(1)) +#else + EditorSettings.externalVersionControl.Contains(Path.GetExtension(assetFilePath).Substring(1)) +#endif + ))) + return false; + + SyncSolution(); // added to handle opening file, which was just recently created. + if (DetectPortAndOpenFile(line, assetFilePath, SystemInfoRiderPlugin.operatingSystemFamily == OperatingSystemFamily.Windows)) + return true; + var args = string.Format("{0}{1}{0} --line {2} {0}{3}{0}", "\"", SlnFile, line, assetFilePath); + return CallRider(args); + } + + return false; + } + + + private static bool DetectPortAndOpenFile(int line, string filePath, bool isWindows) + { + if (SystemInfoRiderPlugin.operatingSystemFamily == OperatingSystemFamily.Windows) + { + var process = GetRiderProcess(); + if (process == null) + return false; + } + + var ports = Enumerable.Range(63342, 20); + var res = ports.Any(port => + { + var aboutUrl = string.Format("http://localhost:{0}/api/about/", port); + var aboutUri = new Uri(aboutUrl); + + using (var client = new WebClient()) + { + client.Headers.Add("origin", string.Format("http://localhost:{0}", port)); + client.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded"; + + try + { + var responce = CallHttpApi(aboutUri, client); + if (responce.ToLower().Contains("rider")) + { + return HttpOpenFile(line, filePath, isWindows, port, client); + } + } + catch (Exception e) + { + Log(LoggingLevel.Verbose, string.Format("Exception in DetectPortAndOpenFile: {0}", e)); + } + } + return false; + }); + return res; + } + + private static bool HttpOpenFile(int line, string filePath, bool isWindows, int port, WebClient client) + { + var url = string.Format("http://localhost:{0}/api/file?file={1}{2}", port, filePath, + line < 0 + ? "&p=0" + : "&line=" + line); // &p is needed to workaround https://youtrack.jetbrains.com/issue/IDEA-172350 + if (isWindows) + url = string.Format(@"http://localhost:{0}/api/file/{1}{2}", port, filePath, line < 0 ? "" : ":" + line); + + var uri = new Uri(url); + Log(LoggingLevel.Verbose, string.Format("HttpRequestOpenFile({0})", uri.AbsoluteUri)); + + CallHttpApi(uri, client); + ActivateWindow(); + return true; + } + + private static string CallHttpApi(Uri uri, WebClient client) + { + var responseString = client.DownloadString(uri.AbsoluteUri); + Log(LoggingLevel.Verbose, string.Format("CallHttpApi {0} response: {1}", uri.AbsoluteUri, responseString)); + return responseString; + } + + private static bool CallRider(string args) + { + var defaultApp = GetDefaultApp(); + if (!RiderPathExist(defaultApp)) + { + return false; + } + + var proc = new Process(); + if (SystemInfoRiderPlugin.operatingSystemFamily == OperatingSystemFamily.MacOSX) + { + proc.StartInfo.FileName = "open"; + proc.StartInfo.Arguments = string.Format("-n {0}{1}{0} --args {2}", "\"", "/" + defaultApp, args); + Log(LoggingLevel.Verbose, string.Format("{0} {1}", proc.StartInfo.FileName, proc.StartInfo.Arguments)); + } + else + { + proc.StartInfo.FileName = defaultApp; + proc.StartInfo.Arguments = args; + Log(LoggingLevel.Verbose, string.Format("{2}{0}{2}" + " {1}", proc.StartInfo.FileName, proc.StartInfo.Arguments, "\"")); + } + + proc.StartInfo.UseShellExecute = false; + proc.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; + proc.StartInfo.CreateNoWindow = true; + proc.StartInfo.RedirectStandardOutput = true; + proc.Start(); + + ActivateWindow(); + return true; + } + + private static void ActivateWindow() + { + if (SystemInfoRiderPlugin.operatingSystemFamily == OperatingSystemFamily.Windows) + { + try + { + var process = GetRiderProcess(); + if (process != null) + { + // Collect top level windows + var topLevelWindows = User32Dll.GetTopLevelWindowHandles(); + // Get process main window title + var windowHandle = topLevelWindows.FirstOrDefault(hwnd => User32Dll.GetWindowProcessId(hwnd) == process.Id); + Log(LoggingLevel.Info, string.Format("ActivateWindow: {0} {1}", process.Id, windowHandle)); + if (windowHandle != IntPtr.Zero) + { + //User32Dll.ShowWindow(windowHandle, 9); //SW_RESTORE = 9 + User32Dll.SetForegroundWindow(windowHandle); + } + } + } + catch (Exception e) + { + Log(LoggingLevel.Warning, "Exception on ActivateWindow: " + e); + } + } + } + + private static Process GetRiderProcess() + { + var process = Process.GetProcesses().FirstOrDefault(p => + { + string processName; + try + { + processName = + p.ProcessName; // some processes like kaspersky antivirus throw exception on attempt to get ProcessName + } + catch (Exception) + { + return false; + } + + return !p.HasExited && processName.ToLower().Contains("rider"); + }); + return process; + } + + // The default "Open C# Project" menu item will use the external script editor to load the .sln + // file, but unless Unity knows the external script editor can properly load solutions, it will + // also launch MonoDevelop (or the OS registered app for .sln files). This menu item side steps + // that issue, and opens the solution in Rider without opening MonoDevelop as well. + // Unity 2017.1 and later recognise Rider as an app that can load solutions, so this menu isn't + // needed in newer versions. + [MenuItem("Assets/Open C# Project in Rider", false, 1000)] + static void MenuOpenProject() + { + // Force the project files to be sync + SyncSolution(); + + // Load Project + CallRider(string.Format("{0}{1}{0}", "\"", SlnFile)); + } + + [MenuItem("Assets/Open C# Project in Rider", true, 1000)] + static bool ValidateMenuOpenProject() + { + return Enabled; + } + + /// + /// Force Unity To Write Project File + /// + private static void SyncSolution() + { + System.Type T = System.Type.GetType("UnityEditor.SyncVS,UnityEditor"); + System.Reflection.MethodInfo SyncSolution = T.GetMethod("SyncSolution", + System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static); + SyncSolution.Invoke(null, null); + } + + /// + /// JetBrains Rider Integration Preferences Item + /// + /// + /// Contains all 3 toggles: Enable/Disable; Debug On/Off; Writing Launch File On/Off + /// + [PreferenceItem("Rider")] + static void RiderPreferencesItem() + { + EditorGUILayout.BeginVertical(); + EditorGUI.BeginChangeCheck(); + + var alternatives = GetAllRiderPaths(); + if (alternatives.Any()) + { + int index = Array.IndexOf(alternatives, RiderPath); + var alts = alternatives.Select(s => s.Replace("/", ":")) + .ToArray(); // hack around https://fogbugz.unity3d.com/default.asp?940857_tirhinhe3144t4vn + RiderPath = alternatives[EditorGUILayout.Popup("Rider executable:", index == -1 ? 0 : index, alts)]; + if (EditorGUILayout.Toggle(new GUIContent("Rider is default editor"), Enabled)) + { + SetExternalScriptEditor(RiderPath); + EditorGUILayout.HelpBox("Unckecking will restore default external editor.", MessageType.None); + } + else + { + SetExternalScriptEditor(string.Empty); + EditorGUILayout.HelpBox("Checking will set Rider as default external editor", MessageType.None); + } + } + + GUILayout.BeginVertical(); + string status = "TargetFrameworkVersion for Runtime"; + EditorGUILayout.TextArea(status, EditorStyles.boldLabel); + var help = @"TargetFramework >= 4.5 is recommended."; + TargetFrameworkVersion = + EditorGUILayout.TextField( + new GUIContent("NET 4.6", + help), TargetFrameworkVersion); + EditorGUILayout.HelpBox(help, MessageType.None); + var helpOldMono = @"TargetFramework = 3.5 is recommended. + - With 4.5 Rider may show ambiguous references in UniRx."; + + TargetFrameworkVersionOldMono = + EditorGUILayout.TextField( + new GUIContent("NET 3.5", + helpOldMono), TargetFrameworkVersionOldMono); + EditorGUILayout.HelpBox(helpOldMono, MessageType.None); + + GUILayout.EndVertical(); + + EditorGUI.EndChangeCheck(); + + EditorGUI.BeginChangeCheck(); + + var loggingMsg = + @"Sets the amount of Rider Debug output. If you are about to report an issue, please select Verbose logging level and attach Unity console output to the issue."; + SelectedLoggingLevel = (LoggingLevel) EditorGUILayout.EnumPopup(new GUIContent("Logging Level", loggingMsg), SelectedLoggingLevel); + EditorGUILayout.HelpBox(loggingMsg, MessageType.None); + + EditorGUI.EndChangeCheck(); + + var url = "https://github.com/JetBrains/resharper-unity"; + LinkButton(url, url); + +/* if (GUILayout.Button("reset RiderInitializedOnce = false")) + { + RiderInitializedOnce = false; + }*/ + + EditorGUILayout.EndVertical(); + } + + private static void LinkButton(string caption, string url) + { + var style = GUI.skin.label; + style.richText = true; + caption = string.Format("{0}", caption); + + bool bClicked = GUILayout.Button(caption, style); + + var rect = GUILayoutUtility.GetLastRect(); + rect.width = style.CalcSize(new GUIContent(caption)).x; + EditorGUIUtility.AddCursorRect(rect, MouseCursor.Link); + + if (bClicked) + Application.OpenURL(url); + } + + #region SystemInfoRiderPlugin + + private static class SystemInfoRiderPlugin + { + public static OperatingSystemFamily operatingSystemFamily + { + get + { +#if UNITY_5_5_OR_NEWER +return SystemInfo.operatingSystemFamily; +#else + if (SystemInfo.operatingSystem.StartsWith("Mac", StringComparison.InvariantCultureIgnoreCase)) + { + return OperatingSystemFamily.MacOSX; + } + if (SystemInfo.operatingSystem.StartsWith("Win", StringComparison.InvariantCultureIgnoreCase)) + { + return OperatingSystemFamily.Windows; + } + if (SystemInfo.operatingSystem.StartsWith("Lin", StringComparison.InvariantCultureIgnoreCase)) + { + return OperatingSystemFamily.Linux; + } + return OperatingSystemFamily.Other; +#endif + } + } + } +#if !UNITY_5_5_OR_NEWER + enum OperatingSystemFamily + { + Other, + MacOSX, + Windows, + Linux, + } +#endif + #endregion + + static class User32Dll + { + + /// + /// Gets the ID of the process that owns the window. + /// Note that creating a wrapper for that is very expensive because it causes an enumeration of all the system processes to happen. + /// + public static int GetWindowProcessId(IntPtr hwnd) + { + uint dwProcessId; + GetWindowThreadProcessId(hwnd, out dwProcessId); + return unchecked((int) dwProcessId); + } + + /// + /// Lists the handles of all the top-level windows currently available in the system. + /// + public static List GetTopLevelWindowHandles() + { + var retval = new List(); + EnumWindowsProc callback = (hwnd, param) => + { + retval.Add(hwnd); + return 1; + }; + EnumWindows(Marshal.GetFunctionPointerForDelegate(callback), IntPtr.Zero); + GC.KeepAlive(callback); + return retval; + } + + public delegate Int32 EnumWindowsProc(IntPtr hwnd, IntPtr lParam); + + [DllImport("user32.dll", CharSet = CharSet.Unicode, PreserveSig = true, SetLastError = true, + ExactSpelling = true)] + public static extern Int32 EnumWindows(IntPtr lpEnumFunc, IntPtr lParam); + + [DllImport("user32.dll", SetLastError = true)] + static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); + + [DllImport("user32.dll", CharSet = CharSet.Unicode, PreserveSig = true, SetLastError = true, + ExactSpelling = true)] + public static extern Int32 SetForegroundWindow(IntPtr hWnd); + + [DllImport("user32.dll", CharSet = CharSet.Unicode, PreserveSig = true, SetLastError = true, + ExactSpelling = true)] + public static extern UInt32 ShowWindow(IntPtr hWnd, Int32 nCmdShow); + } + + static class ShortcutResolver + { + #region Signitures imported from http://pinvoke.net + + [DllImport("shfolder.dll", CharSet = CharSet.Auto)] + internal static extern int SHGetFolderPath(IntPtr hwndOwner, int nFolder, IntPtr hToken, int dwFlags, StringBuilder lpszPath); + + [Flags()] + enum SLGP_FLAGS + { + /// Retrieves the standard short (8.3 format) file name + SLGP_SHORTPATH = 0x1, + + /// Retrieves the Universal Naming Convention (UNC) path name of the file + SLGP_UNCPRIORITY = 0x2, + + /// Retrieves the raw path name. A raw path is something that might not exist and may include environment variables that need to be expanded + SLGP_RAWPATH = 0x4 + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + struct WIN32_FIND_DATAW + { + public uint dwFileAttributes; + public long ftCreationTime; + public long ftLastAccessTime; + public long ftLastWriteTime; + public uint nFileSizeHigh; + public uint nFileSizeLow; + public uint dwReserved0; + public uint dwReserved1; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] public string cFileName; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)] public string cAlternateFileName; + } + + [Flags()] + enum SLR_FLAGS + { + /// + /// Do not display a dialog box if the link cannot be resolved. When SLR_NO_UI is set, + /// the high-order word of fFlags can be set to a time-out value that specifies the + /// maximum amount of time to be spent resolving the link. The function returns if the + /// link cannot be resolved within the time-out duration. If the high-order word is set + /// to zero, the time-out duration will be set to the default value of 3,000 milliseconds + /// (3 seconds). To specify a value, set the high word of fFlags to the desired time-out + /// duration, in milliseconds. + /// + SLR_NO_UI = 0x1, + + /// Obsolete and no longer used + SLR_ANY_MATCH = 0x2, + + /// If the link object has changed, update its path and list of identifiers. + /// If SLR_UPDATE is set, you do not need to call IPersistFile::IsDirty to determine + /// whether or not the link object has changed. + SLR_UPDATE = 0x4, + + /// Do not update the link information + SLR_NOUPDATE = 0x8, + + /// Do not execute the search heuristics + SLR_NOSEARCH = 0x10, + + /// Do not use distributed link tracking + SLR_NOTRACK = 0x20, + + /// Disable distributed link tracking. By default, distributed link tracking tracks + /// removable media across multiple devices based on the volume name. It also uses the + /// Universal Naming Convention (UNC) path to track remote file systems whose drive letter + /// has changed. Setting SLR_NOLINKINFO disables both types of tracking. + SLR_NOLINKINFO = 0x40, + + /// Call the Microsoft Windows Installer + SLR_INVOKE_MSI = 0x80 + } + + + /// The IShellLink interface allows Shell links to be created, modified, and resolved + [ComImport(), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("000214F9-0000-0000-C000-000000000046")] + interface IShellLinkW + { + /// Retrieves the path and file name of a Shell link object + [MethodImpl (MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)] + void GetPath([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, int cchMaxPath, out WIN32_FIND_DATAW pfd, SLGP_FLAGS fFlags); + + /// Retrieves the list of item identifiers for a Shell link object + [MethodImpl (MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)] + void GetIDList(out IntPtr ppidl); + + /// Sets the pointer to an item identifier list (PIDL) for a Shell link object. + [MethodImpl (MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)] + void SetIDList(IntPtr pidl); + + /// Retrieves the description string for a Shell link object + [MethodImpl (MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)] + void GetDescription([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszName, int cchMaxName); + + /// Sets the description for a Shell link object. The description can be any application-defined string + [MethodImpl (MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)] + void SetDescription([MarshalAs(UnmanagedType.LPWStr)] string pszName); + + /// Retrieves the name of the working directory for a Shell link object + [MethodImpl (MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)] + void GetWorkingDirectory([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszDir, int cchMaxPath); + + /// Sets the name of the working directory for a Shell link object + [MethodImpl (MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)] + void SetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] string pszDir); + + /// Retrieves the command-line arguments associated with a Shell link object + [MethodImpl (MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)] + void GetArguments([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszArgs, int cchMaxPath); + + /// Sets the command-line arguments for a Shell link object + [MethodImpl (MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)] + void SetArguments([MarshalAs(UnmanagedType.LPWStr)] string pszArgs); + + /// Retrieves the hot key for a Shell link object + [MethodImpl (MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)] + void GetHotkey(out short pwHotkey); + + /// Sets a hot key for a Shell link object + [MethodImpl (MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)] + void SetHotkey(short wHotkey); + + /// Retrieves the show command for a Shell link object + [MethodImpl (MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)] + void GetShowCmd(out int piShowCmd); + + /// Sets the show command for a Shell link object. The show command sets the initial show state of the window. + [MethodImpl (MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)] + void SetShowCmd(int iShowCmd); + + /// Retrieves the location (path and index) of the icon for a Shell link object + [MethodImpl (MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)] + void GetIconLocation([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszIconPath, int cchIconPath, out int piIcon); + + /// Sets the location (path and index) of the icon for a Shell link object + [MethodImpl (MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)] + void SetIconLocation([MarshalAs(UnmanagedType.LPWStr)] string pszIconPath, int iIcon); + + /// Sets the relative path to the Shell link object + [MethodImpl (MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)] + void SetRelativePath([MarshalAs(UnmanagedType.LPWStr)] string pszPathRel, int dwReserved); + + /// Attempts to find the target of a Shell link, even if it has been moved or renamed + [MethodImpl (MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)] + void Resolve(IntPtr hwnd, SLR_FLAGS fFlags); + + /// Sets the path and file name of a Shell link object + [MethodImpl (MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)] + void SetPath([MarshalAs(UnmanagedType.LPWStr)] string pszFile); + } + + [ComImport, Guid("0000010c-0000-0000-c000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IPersist + { + [MethodImpl (MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)] + void GetClassID(out Guid pClassID); + } + + + [ComImport, Guid("0000010b-0000-0000-C000-000000000046"), + InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IPersistFile : IPersist + { + [MethodImpl(MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)] + new void GetClassID(out Guid pClassID); + + [MethodImpl(MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)] + int IsDirty(); + + [MethodImpl(MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)] + void Load([In, MarshalAs(UnmanagedType.LPWStr)] string pszFileName, uint dwMode); + + [MethodImpl (MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)] + void Save([In, MarshalAs(UnmanagedType.LPWStr)] string pszFileName, [In, MarshalAs(UnmanagedType.Bool)] bool fRemember); + + [MethodImpl (MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)] + void SaveCompleted([In, MarshalAs(UnmanagedType.LPWStr)] string pszFileName); + + [MethodImpl (MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)] + void GetCurFile([In, MarshalAs(UnmanagedType.LPWStr)] string ppszFileName); + } + + const uint STGM_READ = 0; + const int MAX_PATH = 260; + + // CLSID_ShellLink from ShlGuid.h + [ + ComImport(), + Guid("00021401-0000-0000-C000-000000000046") + ] + public class ShellLink + { + } + + #endregion + + public static string Resolve(string filename) + { + ShellLink link = new ShellLink(); + ((IPersistFile) link).Load(filename, STGM_READ); + // If I can get hold of the hwnd call resolve first. This handles moved and renamed files. + // ((IShellLinkW)link).Resolve(hwnd, 0) + StringBuilder sb = new StringBuilder(MAX_PATH); + WIN32_FIND_DATAW data = new WIN32_FIND_DATAW(); + ((IShellLinkW) link).GetPath(sb, sb.Capacity, out data, 0); + return sb.ToString(); + } + } + } +} + +// Developed using JetBrains Rider =) diff --git a/Assets/LUXPiano/Plugins/Editor/JetBrains/Unity3DRider.cs.meta b/Assets/LUXPiano/Plugins/Editor/JetBrains/Unity3DRider.cs.meta new file mode 100644 index 0000000..632830b --- /dev/null +++ b/Assets/LUXPiano/Plugins/Editor/JetBrains/Unity3DRider.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1abb4305d6e736848a2adccf4fc0d572 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/LUXPiano/Prefabs.meta b/Assets/LUXPiano/Prefabs.meta new file mode 100644 index 0000000..dff65f6 --- /dev/null +++ b/Assets/LUXPiano/Prefabs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 414407c70752f284ea4b510eac98e7f8 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/LUXPiano/Prefabs/LUXKey (Sharp).prefab b/Assets/LUXPiano/Prefabs/LUXKey (Sharp).prefab new file mode 100644 index 0000000..4c9a9e0 --- /dev/null +++ b/Assets/LUXPiano/Prefabs/LUXKey (Sharp).prefab @@ -0,0 +1,142 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1001 &100100000 +Prefab: + m_ObjectHideFlags: 1 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 0} + m_Modifications: [] + m_RemovedComponents: [] + m_ParentPrefab: {fileID: 0} + m_RootGameObject: {fileID: 1510759480481440} + m_IsPrefabParent: 1 +--- !u!1 &1510759480481440 +GameObject: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + serializedVersion: 5 + m_Component: + - component: {fileID: 4735443396113658} + - component: {fileID: 114350742191996926} + m_Layer: 0 + m_Name: LUXKey (Sharp) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &1702084857732414 +GameObject: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + serializedVersion: 5 + m_Component: + - component: {fileID: 4430957499096772} + - component: {fileID: 33275080848273190} + - component: {fileID: 23951006577316942} + - component: {fileID: 65020162253031530} + m_Layer: 0 + m_Name: Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &4430957499096772 +Transform: + m_ObjectHideFlags: 1 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1702084857732414} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -0.01, y: 0.0088, z: -0.06} + m_LocalScale: {x: 0.015, y: 0.02, z: 0.12} + m_Children: [] + m_Father: {fileID: 4735443396113658} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &4735443396113658 +Transform: + m_ObjectHideFlags: 1 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1510759480481440} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0.01, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 4430957499096772} + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!23 &23951006577316942 +MeshRenderer: + m_ObjectHideFlags: 1 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1702084857732414} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RenderingLayerMask: 4294967295 + m_Materials: + - {fileID: 2100000, guid: d6d5818e4a8fcec48a4cf66657ee4b35, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 +--- !u!33 &33275080848273190 +MeshFilter: + m_ObjectHideFlags: 1 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1702084857732414} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!65 &65020162253031530 +BoxCollider: + m_ObjectHideFlags: 1 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1702084857732414} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!114 &114350742191996926 +MonoBehaviour: + m_ObjectHideFlags: 1 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1510759480481440} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: d8b41fbaae706404aaabf9d10d07f453, type: 3} + m_Name: + m_EditorClassIdentifier: + thisWidth: 0 + pressTraversalDegrees: {x: -3.5, y: 0, z: 0} + isPressed: 0 diff --git a/Assets/LUXPiano/Prefabs/LUXKey (Sharp).prefab.meta b/Assets/LUXPiano/Prefabs/LUXKey (Sharp).prefab.meta new file mode 100644 index 0000000..5d4c94c --- /dev/null +++ b/Assets/LUXPiano/Prefabs/LUXKey (Sharp).prefab.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 634a1eebb2a2dbe48b124f7fd80ff3f2 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 100100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/LUXPiano/Prefabs/LUXKey.prefab b/Assets/LUXPiano/Prefabs/LUXKey.prefab new file mode 100644 index 0000000..0bfd167 --- /dev/null +++ b/Assets/LUXPiano/Prefabs/LUXKey.prefab @@ -0,0 +1,142 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1001 &100100000 +Prefab: + m_ObjectHideFlags: 1 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 0} + m_Modifications: [] + m_RemovedComponents: [] + m_ParentPrefab: {fileID: 0} + m_RootGameObject: {fileID: 1510759480481440} + m_IsPrefabParent: 1 +--- !u!1 &1510759480481440 +GameObject: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + serializedVersion: 5 + m_Component: + - component: {fileID: 4735443396113658} + - component: {fileID: 114350742191996926} + m_Layer: 0 + m_Name: LUXKey + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &1702084857732414 +GameObject: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + serializedVersion: 5 + m_Component: + - component: {fileID: 4430957499096772} + - component: {fileID: 33275080848273190} + - component: {fileID: 23951006577316942} + - component: {fileID: 65020162253031530} + m_Layer: 0 + m_Name: Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &4430957499096772 +Transform: + m_ObjectHideFlags: 1 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1702084857732414} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: -0.08} + m_LocalScale: {x: 0.02, y: 0.02, z: 0.16} + m_Children: [] + m_Father: {fileID: 4735443396113658} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &4735443396113658 +Transform: + m_ObjectHideFlags: 1 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1510759480481440} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 4430957499096772} + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!23 &23951006577316942 +MeshRenderer: + m_ObjectHideFlags: 1 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1702084857732414} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RenderingLayerMask: 4294967295 + m_Materials: + - {fileID: 2100000, guid: 10a49312282010d4381356c509b81eb3, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 +--- !u!33 &33275080848273190 +MeshFilter: + m_ObjectHideFlags: 1 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1702084857732414} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!65 &65020162253031530 +BoxCollider: + m_ObjectHideFlags: 1 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1702084857732414} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!114 &114350742191996926 +MonoBehaviour: + m_ObjectHideFlags: 1 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1510759480481440} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: d8b41fbaae706404aaabf9d10d07f453, type: 3} + m_Name: + m_EditorClassIdentifier: + thisWidth: 0.021 + pressTraversalDegrees: {x: -3.5, y: 0, z: 0} + isPressed: 0 diff --git a/Assets/LUXPiano/Prefabs/LUXKey.prefab.meta b/Assets/LUXPiano/Prefabs/LUXKey.prefab.meta new file mode 100644 index 0000000..a8d99e7 --- /dev/null +++ b/Assets/LUXPiano/Prefabs/LUXKey.prefab.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 6ca8c4fefd0d74a45b81aa29f12d62af +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 100100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/LUXPiano/Prefabs/LUXPiano.prefab b/Assets/LUXPiano/Prefabs/LUXPiano.prefab new file mode 100644 index 0000000..68e7444 --- /dev/null +++ b/Assets/LUXPiano/Prefabs/LUXPiano.prefab @@ -0,0 +1,105 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1001 &100100000 +Prefab: + m_ObjectHideFlags: 1 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 0} + m_Modifications: [] + m_RemovedComponents: [] + m_ParentPrefab: {fileID: 0} + m_RootGameObject: {fileID: 1126565012880550} + m_IsPrefabParent: 1 +--- !u!1 &1114720910048750 +GameObject: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + serializedVersion: 5 + m_Component: + - component: {fileID: 4215781214681260} + - component: {fileID: 114262336673180654} + m_Layer: 0 + m_Name: Keys + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &1126565012880550 +GameObject: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + serializedVersion: 5 + m_Component: + - component: {fileID: 4743298813287564} + - component: {fileID: 114532666944635662} + m_Layer: 0 + m_Name: LUXPiano + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &4215781214681260 +Transform: + m_ObjectHideFlags: 1 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1114720910048750} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 4743298813287564} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &4743298813287564 +Transform: + m_ObjectHideFlags: 1 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1126565012880550} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 4215781214681260} + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &114262336673180654 +MonoBehaviour: + m_ObjectHideFlags: 1 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1114720910048750} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: a79ff9d0da3ab8d4eb2cd0859ed0f175, type: 3} + m_Name: + m_EditorClassIdentifier: + keyPrefab: {fileID: 114350742191996926, guid: 6ca8c4fefd0d74a45b81aa29f12d62af, + type: 2} + keyPrefabSharp: {fileID: 114350742191996926, guid: 634a1eebb2a2dbe48b124f7fd80ff3f2, + type: 2} + spawnKeysOnEnable: 1 + startNote: 21 + endNote: 108 +--- !u!114 &114532666944635662 +MonoBehaviour: + m_ObjectHideFlags: 1 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1126565012880550} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 0df5f5e10f781924d8cb1bb0a4074b30, type: 3} + m_Name: + m_EditorClassIdentifier: + logEvents: 0 + resizeKeysToRange: 1 + minKey: 2147483647 + maxKey: -2147483648 diff --git a/Assets/LUXPiano/Prefabs/LUXPiano.prefab.meta b/Assets/LUXPiano/Prefabs/LUXPiano.prefab.meta new file mode 100644 index 0000000..b8729cc --- /dev/null +++ b/Assets/LUXPiano/Prefabs/LUXPiano.prefab.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4924853a019ee5a4dbd90e8ef957eff9 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 100100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/LUXPiano/Scripts.meta b/Assets/LUXPiano/Scripts.meta new file mode 100644 index 0000000..19d61bd --- /dev/null +++ b/Assets/LUXPiano/Scripts.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 6cc6e59a13c08a542974e0cc0e25b5fc +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/LUXPiano/Scripts/Key.cs b/Assets/LUXPiano/Scripts/Key.cs new file mode 100644 index 0000000..bf5003e --- /dev/null +++ b/Assets/LUXPiano/Scripts/Key.cs @@ -0,0 +1,60 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +namespace LUX +{ + public class Key : MonoBehaviour + { + [Tooltip("Used for spacing against other keys")] + public float thisWidth; + + [Tooltip("How many degrees should the key move when pressed?")] + public Vector3 pressTraversalDegrees = new Vector3(-3.5f, 0, 0); + + [Tooltip("Is the key currently being held?")] [ReadOnly] + public bool isPressed = false; + + // Store the rotation vectors locally: + protected Vector3 staticRotation; + + public int note { get; protected set; } + + public void SetNote(int to) + { + note = to; + gameObject.name = "LUX.Key (" + ((Note) Notes.GetNoteFromOctive(note)).ToString() + ")"; + + // Set the rotation here because this is after instantiation/setup. Keypress could still happen this tick. + staticRotation = transform.localEulerAngles; + } + + public void OnPressed(float velocity) + { + //Debug.Log(gameObject.name + " Pressed, velocity: "+velocity); + if (isPressed) + { + // Debug.LogWarning("Key can't be double pressed.", gameObject); + // nb: This can happen if you have more than one keyboard plugged in so COULD happen. let's just ignore. + return; + } + + isPressed = true; + transform.localEulerAngles = staticRotation + pressTraversalDegrees; + } + + public void OnReleased() + { + //Debug.Log(gameObject.name + " Released"); + if (!isPressed) + { + //Debug.LogWarning("Key can't be double unpressed."); + // nb: This can happen if you have more than one keyboard plugged in so COULD happen. let's just ignore. + return; + } + + isPressed = false; + transform.localEulerAngles = staticRotation; + } + } +} diff --git a/Assets/LUXPiano/Scripts/Key.cs.meta b/Assets/LUXPiano/Scripts/Key.cs.meta new file mode 100644 index 0000000..47f26f9 --- /dev/null +++ b/Assets/LUXPiano/Scripts/Key.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d8b41fbaae706404aaabf9d10d07f453 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/LUXPiano/Scripts/Keys.cs b/Assets/LUXPiano/Scripts/Keys.cs new file mode 100644 index 0000000..b582d71 --- /dev/null +++ b/Assets/LUXPiano/Scripts/Keys.cs @@ -0,0 +1,130 @@ +using System.Collections; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using UnityEngine; + +namespace LUX +{ + public class Keys : MonoBehaviour + { + [Tooltip("Prefab for spawning piano keys")] + public Key keyPrefab; + public Key keyPrefabSharp; + + public bool spawnKeysOnEnable = true; + + [Tooltip("MIDI specified note numbers for keyboard layout. eg: Middle C is 60")] + // http://computermusicresource.com/midikeys.html + public int startNote = 21; + public int endNote = 108; + + public int numKeys + { + get + { + if (startNote > endNote) + { + return 0; + } + return endNote - startNote + 1; + } + } + + protected Dictionary keys = new Dictionary(); + + protected void OnEnable() + { + if (spawnKeysOnEnable) + { + CreateKeys(); + } + } + + /// + /// Destroys all keys that we've previously created. + /// + public void DestroyKeys() + { + foreach (KeyValuePair entry in keys) + { + Destroy(entry.Value.gameObject); + } + + keys.Clear(); + } + + /// + /// Creates a whole new set of keys. + /// + public void CreateKeys() + { + // clear out the old: + DestroyKeys(); + + if (numKeys <= 0) + { + Debug.LogError("Can't make <= 0 keys."); + return; + } + + int note = startNote; + Vector3 spawnPosition = transform.position; + Key lastKey = null; + while (note <= endNote) + { + if (lastKey != null) + { + // black keys have a thisWidth of zero. + spawnPosition += transform.right * lastKey.thisWidth; + } + lastKey = CreateKey(note, spawnPosition, transform.rotation); + note++; + } + } + + private Key CreateKey(int note, Vector3 position, Quaternion rotation) + { + Key prefab; + if (Notes.IsSharp(note)) + { + prefab = keyPrefabSharp; + } + else + { + prefab = keyPrefab; + } + + Key key = Instantiate(prefab, position, rotation); + key.transform.SetParent(transform, true); + key.SetNote(note); + keys.Add(note, key); + return key; + } + + public void OnKeyPressed(int note, float velocity) + { + if (keys.ContainsKey(note)) + { + OnKeyPressed(keys[note], velocity); + } + } + + public void OnKeyPressed(Key key, float velocity) + { + key.OnPressed(velocity); + } + + public void OnKeyReleased(int note) + { + if (keys.ContainsKey(note)) + { + OnKeyReleased(keys[note]); + } + } + + public void OnKeyReleased(Key key) + { + key.OnReleased(); + } + } +} diff --git a/Assets/LUXPiano/Scripts/Keys.cs.meta b/Assets/LUXPiano/Scripts/Keys.cs.meta new file mode 100644 index 0000000..f8179cb --- /dev/null +++ b/Assets/LUXPiano/Scripts/Keys.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a79ff9d0da3ab8d4eb2cd0859ed0f175 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/LUXPiano/Scripts/MidiInput.cs b/Assets/LUXPiano/Scripts/MidiInput.cs new file mode 100644 index 0000000..766d9d1 --- /dev/null +++ b/Assets/LUXPiano/Scripts/MidiInput.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using MidiJack; +using UnityEngine; + +namespace LUX +{ + public class MidiInput : MonoBehaviour + { + public bool logEvents = false; + public bool resizeKeysToRange = false; + + protected Keys luxKeys; + [ReadOnly] public int minKey = int.MaxValue; + [ReadOnly] public int maxKey = int.MinValue; + + protected void OnEnable() + { + MidiMaster.noteOnDelegate += OnNoteOn; + MidiMaster.noteOffDelegate += OnNoteOff; + luxKeys = GetComponentInChildren(); + } + + protected void OnDisable() + { + MidiMaster.noteOnDelegate -= OnNoteOn; + MidiMaster.noteOffDelegate -= OnNoteOff; + } + + private void OnNoteOn(MidiChannel channel, int note, float velocity) + { + Log("[NoteOn] channel: " + channel.ToString() + " note: " + note + " velocity: " + velocity); + CheckNoteRange(note); + luxKeys.OnKeyPressed(note, velocity); + } + + private void OnNoteOff(MidiChannel channel, int note) + { + Log("[NoteOff] channel: " + channel.ToString() + " note: " + note); + CheckNoteRange(note); + luxKeys.OnKeyReleased(note); + } + + private void CheckNoteRange(int note) + { + bool rangeChanged = false; + if (note < minKey) + { + minKey = note; + rangeChanged = true; + } + + if (note > maxKey) + { + maxKey = note; + rangeChanged = true; + } + + if (rangeChanged) + { + if (resizeKeysToRange) + { + luxKeys.startNote = minKey; + luxKeys.endNote = maxKey; + luxKeys.CreateKeys(); + } + } + } + + protected void Log(string message) + { + if (logEvents) + { + Debug.Log(message); + } + } + + } +} diff --git a/Assets/LUXPiano/Scripts/MidiInput.cs.meta b/Assets/LUXPiano/Scripts/MidiInput.cs.meta new file mode 100644 index 0000000..6a8175c --- /dev/null +++ b/Assets/LUXPiano/Scripts/MidiInput.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0df5f5e10f781924d8cb1bb0a4074b30 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/LUXPiano/Scripts/Notes.cs b/Assets/LUXPiano/Scripts/Notes.cs new file mode 100644 index 0000000..206c664 --- /dev/null +++ b/Assets/LUXPiano/Scripts/Notes.cs @@ -0,0 +1,65 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +namespace LUX +{ + public enum Note : int + { + C = 0, + Cs = 1, + D = 2, + Ds = 3, + E = 4, + F = 5, + Fs = 6, + G = 7, + Gs = 8, + A = 9, + As = 10, + B = 11 + } + + public class Notes + { + public static bool IsSharp(int note) + { + if (IsNote(note, Note.Cs) || + IsNote(note, Note.Ds) || + IsNote(note, Note.Fs) || + IsNote(note, Note.Gs) || + IsNote(note, Note.As)) + { + return true; + } + + return false; + } + + public static bool IsNotSharp(int note) + { + return !IsSharp(note); + } + + public static bool IsNote(int note, Note comparitive) + { + note = GetNoteFromOctive(note); + int compareValue = (int) comparitive; + if (compareValue == note) + { + return true; + } + return false; + } + + public static int GetNoteFromOctive(int note) + { + while (note >= 12) + { + note -= 12; + } + + return note; + } + } +} diff --git a/Assets/LUXPiano/Scripts/Notes.cs.meta b/Assets/LUXPiano/Scripts/Notes.cs.meta new file mode 100644 index 0000000..91c793d --- /dev/null +++ b/Assets/LUXPiano/Scripts/Notes.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 87177767b4ad192468fcd7517303145e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/LUXPiano/Scripts/ReadOnlyAttribute.cs b/Assets/LUXPiano/Scripts/ReadOnlyAttribute.cs new file mode 100644 index 0000000..8fccb68 --- /dev/null +++ b/Assets/LUXPiano/Scripts/ReadOnlyAttribute.cs @@ -0,0 +1,33 @@ +using UnityEngine; +#if UNITY_EDITOR +using UnityEditor; +#endif + +namespace LUX +{ + public class ReadOnly : PropertyAttribute + { + + } + +#if UNITY_EDITOR + [CustomPropertyDrawer(typeof(ReadOnly))] + public class ReadOnlyDrawer : PropertyDrawer + { + public override float GetPropertyHeight(SerializedProperty property, + GUIContent label) + { + return EditorGUI.GetPropertyHeight(property, label, true); + } + + public override void OnGUI(Rect position, + SerializedProperty property, + GUIContent label) + { + GUI.enabled = false; + EditorGUI.PropertyField(position, property, label, true); + GUI.enabled = true; + } + } +#endif +} \ No newline at end of file diff --git a/Assets/LUXPiano/Scripts/ReadOnlyAttribute.cs.meta b/Assets/LUXPiano/Scripts/ReadOnlyAttribute.cs.meta new file mode 100644 index 0000000..7231659 --- /dev/null +++ b/Assets/LUXPiano/Scripts/ReadOnlyAttribute.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 170b28fee6effce4091e9ab67d61f14b +timeCreated: 1467488107 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Piano.cs b/Assets/Piano.cs new file mode 100644 index 0000000..946221c --- /dev/null +++ b/Assets/Piano.cs @@ -0,0 +1,16 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +public class Piano : MonoBehaviour { + + // Use this for initialization + void Start () { + + } + + // Update is called once per frame + void Update () { + + } +} diff --git a/Assets/Piano.cs.meta b/Assets/Piano.cs.meta new file mode 100644 index 0000000..0b71e5f --- /dev/null +++ b/Assets/Piano.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b0bd47d019258fd41a80b6321ed368df +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scenes/SampleScene.unity b/Assets/Scenes/Example Scene.unity similarity index 77% rename from Assets/Scenes/SampleScene.unity rename to Assets/Scenes/Example Scene.unity index 6dcbeae..9a14acd 100644 --- a/Assets/Scenes/SampleScene.unity +++ b/Assets/Scenes/Example Scene.unity @@ -179,13 +179,13 @@ Transform: m_PrefabParentObject: {fileID: 0} m_PrefabInternal: {fileID: 0} m_GameObject: {fileID: 282840810} - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: 0, y: 1, z: -10} + m_LocalRotation: {x: 0.20551528, y: -0, z: -0, w: 0.97865397} + m_LocalPosition: {x: 0, y: 17.39, z: -27.44} m_LocalScale: {x: 1, y: 1, z: 1} m_Children: [] m_Father: {fileID: 0} m_RootOrder: 0 - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_LocalEulerAnglesHint: {x: 23.719002, y: 0, z: 0} --- !u!1 &1526980365 GameObject: m_ObjectHideFlags: 0 @@ -251,3 +251,45 @@ Transform: m_Father: {fileID: 0} m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0} +--- !u!1001 &1779513520 +Prefab: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 0} + m_Modifications: + - target: {fileID: 4743298813287564, guid: 4924853a019ee5a4dbd90e8ef957eff9, type: 2} + propertyPath: m_LocalPosition.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4743298813287564, guid: 4924853a019ee5a4dbd90e8ef957eff9, type: 2} + propertyPath: m_LocalPosition.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4743298813287564, guid: 4924853a019ee5a4dbd90e8ef957eff9, type: 2} + propertyPath: m_LocalPosition.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4743298813287564, guid: 4924853a019ee5a4dbd90e8ef957eff9, type: 2} + propertyPath: m_LocalRotation.x + value: -0 + objectReference: {fileID: 0} + - target: {fileID: 4743298813287564, guid: 4924853a019ee5a4dbd90e8ef957eff9, type: 2} + propertyPath: m_LocalRotation.y + value: -0 + objectReference: {fileID: 0} + - target: {fileID: 4743298813287564, guid: 4924853a019ee5a4dbd90e8ef957eff9, type: 2} + propertyPath: m_LocalRotation.z + value: -0 + objectReference: {fileID: 0} + - target: {fileID: 4743298813287564, guid: 4924853a019ee5a4dbd90e8ef957eff9, type: 2} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 4743298813287564, guid: 4924853a019ee5a4dbd90e8ef957eff9, type: 2} + propertyPath: m_RootOrder + value: 2 + objectReference: {fileID: 0} + m_RemovedComponents: [] + m_ParentPrefab: {fileID: 100100000, guid: 4924853a019ee5a4dbd90e8ef957eff9, type: 2} + m_IsPrefabParent: 0 diff --git a/Assets/Scenes/SampleScene.unity.meta b/Assets/Scenes/Example Scene.unity.meta similarity index 100% rename from Assets/Scenes/SampleScene.unity.meta rename to Assets/Scenes/Example Scene.unity.meta diff --git a/project.sln.DotSettings b/project.sln.DotSettings new file mode 100644 index 0000000..fd88f79 --- /dev/null +++ b/project.sln.DotSettings @@ -0,0 +1,80 @@ + + False + True + DO_NOT_SHOW + DO_NOT_SHOW + WARNING + WARNING + WARNING + WARNING + WARNING + WARNING + WARNING + WARNING + DO_NOT_SHOW + DO_NOT_SHOW + WARNING + + + Required + Required + Required + + + Required + 3 + 0 + END_OF_LINE + END_OF_LINE + True + True + True + True + True + True + True + True + True + True + True + True + END_OF_LINE + END_OF_LINE + TOGETHER_SAME_LINE + + + NO_INDENT + Tab + True + True + True + True + True + True + END_OF_LINE + END_OF_LINE + True + True + END_OF_LINE + NEVER + END_OF_LINE + False + UseExplicitType + UseExplicitType + UseExplicitType + <Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + True + True + True + True + True + True + True + True \ No newline at end of file