From 816f3bf2136e6edc8cbf94df2db03785bae15882 Mon Sep 17 00:00:00 2001 From: Nicholas Ewalt Date: Sun, 17 Jul 2016 23:34:58 -0700 Subject: [PATCH] Experimental Emote Support, Configurable Background Alpha Emote support is a little messy right now. Something about Unity's TextMesh Quads causes a crash when running the Release version, but no errors are being reported in the Editor. For now I'm going to release a Development version which won't crash when it happens, but instead will spam some errors to the Development Console which you can just Close and ignore until the errors go away (after you receive enough messages to clear the buffer). Some emotes are still not being displayed properly (seems random) but you should be able to use up to 7 unique emotes per line, with no specific limit on the number of emotes per message or at any one time, aside from the 7 unique emotes per line limit which Unity imposes. I'm having difficulty pinning down the exact cause of the crash, but it definitely has something to do with Unity's TextMesh and lots of Quads, so I may have to work up a more elegant solution. For now, this version works fine in the Editor and works okay in Development Mode but some messages bug out :(. Also in this version, I added a configurable Background Alpha Slider to the RGB Sliders. This Alpha only controls the Background Alpha, so it is now possible to have a nearly completely transparent background, with fully opaque text, if desired. It seems like SteamVR prevents me from having a fully transparent overlay, but I'll have to look into that when I get a chance :P I also updated to the latest Unity SteamVR Plugin files. I don't think anything changed, but I was having issues until I reimported it for some reason, so I went through and stripped the files we didn't need again, and it seems to be working fine so w/e. The controller manager seems to have changed, but GitHub isn't being really helpful with the file comparison so I'm not sure what changed exactly. Speaking of the controller manager, I updated the TrackedDeviceManager so it won't spam the chat when the controllers aren't detected, it will just notify you once. I also made it possible for multiple instances of the Overlay program to be running simultaneously. You can now correctly run the exe multiple times and configure them separately such that you can have multiple Twitch Chat's running/visible at the same time. I really wanted to wait until Emote support was more mature, but I haven't updated in a while and I suspect some people are getting impatient for basic Emote support :o The Emote support is a bit inefficient but only adds an extra 1% average CPU usage on my PC, so I think the inefficiencies are acceptable until I can push out a more optimized version. I recommend sticking with a previous version unless you really want the Emotes, because this version has a few bugs to work out. --- .../HOTK_TwitchDemoScene.unity | Bin 287052 -> 292892 bytes .../UI Scripts/DropdownSaveLoadController.cs | 15 +- .../UI Scripts/MaterialColorMatchSlider.cs | 20 +- Assets/HOTK/HOTK_Overlay.cs | 5 +- Assets/HOTK/HOTK_TrackedDeviceManager.cs | 6 +- Assets/HOTK/Twitch/TwitchChatBGMat.mat | Bin 4900 -> 5120 bytes Assets/HOTK/Twitch/TwitchChatTester.cs | 442 ++++++++++++++--- .../Twitch/TwitchEmoteMaterialRecycler.cs | 93 ++++ .../TwitchEmoteMaterialRecycler.cs.meta | 12 + Assets/HOTK/Twitch/TwitchIRC.cs | 42 +- Assets/HOTK/Twitch/TwitchSettingsSaver.cs | 4 +- Assets/SteamVR.meta | 2 +- Assets/SteamVR/Editor.meta | 2 +- Assets/SteamVR/Prefabs.meta | 2 +- Assets/SteamVR/Resources.meta | 2 +- .../Resources/SteamVR_Blit.shader.meta | 5 +- .../Resources/SteamVR_BlitFlip.shader.meta | 6 +- Assets/SteamVR/Scripts.meta | 2 +- .../Scripts/SteamVR_ControllerManager.cs | 448 +++++++++--------- .../Scripts/SteamVR_ControllerManager.cs.meta | 6 +- Assets/SteamVR/Scripts/SteamVR_Overlay.cs | 2 +- .../SteamVR/Scripts/SteamVR_Overlay.cs.meta | 3 - Assets/SteamVR/Scripts/SteamVR_Render.cs | 8 +- Assets/SteamVR/Textures.meta | 2 +- Assets/SteamVR/Textures/arrow.png | Bin 0 -> 5292 bytes Assets/SteamVR/Textures/arrow.png.meta | 52 ++ Assets/SteamVR/Textures/background.png | Bin 0 -> 11479 bytes Assets/SteamVR/Textures/background.png.meta | 52 ++ 28 files changed, 886 insertions(+), 347 deletions(-) create mode 100644 Assets/HOTK/Twitch/TwitchEmoteMaterialRecycler.cs create mode 100644 Assets/HOTK/Twitch/TwitchEmoteMaterialRecycler.cs.meta create mode 100644 Assets/SteamVR/Textures/arrow.png create mode 100644 Assets/SteamVR/Textures/arrow.png.meta create mode 100644 Assets/SteamVR/Textures/background.png create mode 100644 Assets/SteamVR/Textures/background.png.meta diff --git a/Assets/HOTK/Example Content/HOTK_TwitchDemoScene.unity b/Assets/HOTK/Example Content/HOTK_TwitchDemoScene.unity index e9e61ff2131c1f56c571efa2bac1e7822ef34fe2..c42ded2fb824934fb7947b61b81228168cac0a36 100644 GIT binary patch delta 17229 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 delta 15198 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 diff --git a/Assets/HOTK/Example Content/UI Scripts/DropdownSaveLoadController.cs b/Assets/HOTK/Example Content/UI Scripts/DropdownSaveLoadController.cs index 6431c5a..8a52308 100644 --- a/Assets/HOTK/Example Content/UI Scripts/DropdownSaveLoadController.cs +++ b/Assets/HOTK/Example Content/UI Scripts/DropdownSaveLoadController.cs @@ -31,6 +31,7 @@ public class DropdownSaveLoadController : MonoBehaviour public MaterialColorMatchSlider RSlider; public MaterialColorMatchSlider GSlider; public MaterialColorMatchSlider BSlider; + public MaterialColorMatchSlider ASlider; public InputField AlphaStartField; public InputField AlphaEndField; @@ -154,6 +155,7 @@ public void OnLoadPressed() // Load an existing save RSlider.Slider.value = settings.BackgroundR; GSlider.Slider.value = settings.BackgroundG; BSlider.Slider.value = settings.BackgroundB; + ASlider.Slider.value = (settings.SaveFileVersion >= 3 ? settings.BackgroundA : 1.0f); // Save File compatability AlphaStartField.text = settings.AlphaStart.ToString(); AlphaEndField.text = settings.AlphaEnd.ToString(); @@ -197,9 +199,11 @@ public void OnSavePressed() settings.Point = OverlayToSave.AnchorPoint; settings.Animation = OverlayToSave.AnimateOnGaze; - settings.BackgroundR = BackgroundMaterial.color.r; - settings.BackgroundG = BackgroundMaterial.color.g; - settings.BackgroundB = BackgroundMaterial.color.b; + var backgroundColor = GetMaterialTexture().GetPixel(0, 0); + settings.BackgroundR = backgroundColor.r; + settings.BackgroundG = backgroundColor.g; + settings.BackgroundB = backgroundColor.b; + settings.BackgroundA = backgroundColor.a; settings.AlphaStart = OverlayToSave.Alpha; settings.AlphaEnd = OverlayToSave.Alpha2; settings.AlphaSpeed = OverlayToSave.AlphaSpeed; settings.ScaleStart = OverlayToSave.Scale; settings.ScaleEnd = OverlayToSave.Scale2; settings.ScaleSpeed = OverlayToSave.ScaleSpeed; @@ -207,6 +211,11 @@ public void OnSavePressed() } } + private Texture2D GetMaterialTexture() + { + return (Texture2D)(BackgroundMaterial.mainTexture ?? (BackgroundMaterial.mainTexture = TwitchChatTester.GenerateBaseTexture())); + } + public void OnSaveNewPressed() { if (string.IsNullOrEmpty(SaveName.text) || TwitchSettingsSaver.SavedSettings.ContainsKey(SaveName.text)) return; diff --git a/Assets/HOTK/Example Content/UI Scripts/MaterialColorMatchSlider.cs b/Assets/HOTK/Example Content/UI Scripts/MaterialColorMatchSlider.cs index b87e549..1aa66ea 100644 --- a/Assets/HOTK/Example Content/UI Scripts/MaterialColorMatchSlider.cs +++ b/Assets/HOTK/Example Content/UI Scripts/MaterialColorMatchSlider.cs @@ -17,7 +17,7 @@ public Slider Slider public void OnEnable() { if (Material == null) return; - var c = Material.color; + var c = GetMaterialTexture().GetPixel(0,0); switch (Setting) { case ColorSetting.R: @@ -29,15 +29,24 @@ public void OnEnable() case ColorSetting.B: Slider.value = c.b; break; + case ColorSetting.A: + Slider.value = c.a; + break; default: throw new ArgumentOutOfRangeException(); } } + private Texture2D GetMaterialTexture() + { + return (Texture2D) (Material.mainTexture ?? (Material.mainTexture = TwitchChatTester.GenerateBaseTexture())); + } + public void OnValueChanges() { if (Material == null) return; - var c = Material.color; + var tex = GetMaterialTexture(); + var c = tex.GetPixel(0, 0); switch (Setting) { case ColorSetting.R: @@ -49,11 +58,15 @@ public void OnValueChanges() case ColorSetting.B: c.b = Slider.value; break; + case ColorSetting.A: + c.a = Slider.value; + break; default: throw new ArgumentOutOfRangeException(); } - Material.color = c; + tex.SetPixel(0, 0, c); + tex.Apply(); } public enum ColorSetting @@ -61,5 +74,6 @@ public enum ColorSetting R, G, B, + A, } } diff --git a/Assets/HOTK/HOTK_Overlay.cs b/Assets/HOTK/HOTK_Overlay.cs index 1cf4a16..17b2b56 100644 --- a/Assets/HOTK/HOTK_Overlay.cs +++ b/Assets/HOTK/HOTK_Overlay.cs @@ -2,6 +2,7 @@ using System.Linq; using UnityEngine; using Valve.VR; +using Random = System.Random; public class HOTK_Overlay : MonoBehaviour { @@ -49,8 +50,10 @@ public class HOTK_Overlay : MonoBehaviour #endregion #region Interal Vars + + public static Random rand = new Random(); public static HOTK_Overlay HighQualityOverlay; // Only one Overlay can be HQ at a time - public static string Key { get { return "unity:" + Application.companyName + "." + Application.productName; } } + public static string Key { get { return "unity:" + Application.companyName + "." + Application.productName + "." + rand.Next(); } } public static GameObject ZeroReference; // Used to get a reference to the world 0, 0, 0 point public GameObject OverlayReference; // Used to get a reference for the Overlay's transform diff --git a/Assets/HOTK/HOTK_TrackedDeviceManager.cs b/Assets/HOTK/HOTK_TrackedDeviceManager.cs index 71efc3b..c8814a6 100644 --- a/Assets/HOTK/HOTK_TrackedDeviceManager.cs +++ b/Assets/HOTK/HOTK_TrackedDeviceManager.cs @@ -130,7 +130,7 @@ public void FindControllers() return; } - Log("Searching for Controllers.."); + if (_noControllersCount == 0) Log("Searching for Controllers.."); _leftIndex = system.GetTrackedDeviceIndexForControllerRole(ETrackedControllerRole.LeftHand); CallIndexChanged(ETrackedControllerRole.LeftHand, _leftIndex); _rightIndex = system.GetTrackedDeviceIndexForControllerRole(ETrackedControllerRole.RightHand); @@ -176,7 +176,7 @@ public void FindControllers() } else if (_leftIndex == OpenVR.k_unTrackedDeviceIndexInvalid && _rightIndex == OpenVR.k_unTrackedDeviceIndexInvalid) // Both controllers are unassigned { - LogWarning("SteamVR Reports No Assigned Controllers..! Searching.."); + if (_noControllersCount == 0) LogWarning("SteamVR Reports No Assigned Controllers..! Searching.."); var foundUnassigned = 0; var slots = new uint[2]; // Sort through all the devices until we find two controllers @@ -237,7 +237,7 @@ public void FindControllers() } break; case 0: - LogWarning("Couldn't Find Any Unassigned Controllers!"); + if (_noControllersCount == 0) LogWarning("Couldn't Find Any Unassigned Controllers!"); _noControllersCount++; if (_noControllersCount >= 10) { diff --git a/Assets/HOTK/Twitch/TwitchChatBGMat.mat b/Assets/HOTK/Twitch/TwitchChatBGMat.mat index 5c9797880fc0931f9490b9e320985ff869f7475b..958531bb806ba561eca1dc36b405c99a134b4892 100644 GIT binary patch delta 285 zcmZ3Y)}X<`z`${kfkA{}BS#Ad69dcUK8|JVLjN=w;D8m#+QGfqO<)ltqv+(9g7yLd znH8xy!Fh=VP&JcPg!a?}4PuD*%qw$COi5*6Xs`$KgG*BLk~4Ea>Uco%P`-11E=aE< z7m}Di)HDGkv7pq%6yL<+YzBsUXF(+K@S@C;REPvnwB8wLjuu0_bAC>K5lpneksqWX xv^X`?(=n$YBhfi06Ji7N()); } } - private static TwitchChatTester _instance; - public struct TwitchChat - { - public readonly string Name; - public readonly string Color; - public readonly string Message; - - public TwitchChat(string name, string color, string message) - { - Name = name; - Color = color; - Message = message; - } - } + public int ChatLineCount = 27; // Max line count for our display public InputField UsernameBox; public InputField OAuthBox; @@ -37,15 +24,23 @@ public TwitchChat(string name, string color, string message) public Button ConnectButton; public Text ConnectButtonText; - public TextMesh TextMesh + public TextMesh TextMesh // Used to check each message as it's being built to WordWrap each message into lines { get { return _textMesh ?? (_textMesh = GetComponent()); } } private TextMesh _textMesh; + public GameObject TextMeshBase; // Cloned into ChatTextMeshes + + private bool _hasGeneratedTextMeshes = false; + public TextMesh[] ChatTextMeshes; // Each one of these is a line on our Chat Display + public Renderer[] ChatTextRenderers; // Each one of these is a line on our Chat Display + + // These are used to display ViewerCount and ChannelName when connected public TextMesh ViewerCountTextMesh; public TextMesh ChannelNameTextMesh; + // These are used to play message sounds public AudioSource IncomingMessageSoundSource1; public AudioSource IncomingMessageSoundSource2; public AudioSource IncomingMessageSoundSource3; @@ -59,7 +54,11 @@ public TwitchIRC IRC } private TwitchIRC _irc; - private readonly List _userChat = new List(); + private readonly List _userChat = new List(); // Used to store the currect messages on the Chat Display + + private readonly Dictionary _emoteMap = new Dictionary(); // Used temporarily to store the currect emotes on a given line + + private readonly Stopwatch _messageSoundStopwatch = new Stopwatch(); // Used to prevent message sound spamming public bool Connected { @@ -69,6 +68,7 @@ public bool Connected public void Awake() { _instance = this; + GenChatTexts(); } public void Start() @@ -76,8 +76,6 @@ public void Start() ClearViewerCountAndChannelName("Disconnected"); } - private readonly Stopwatch _messageSoundStopwatch = new Stopwatch(); // Used to prevent message sound spamming - public void ToggleConnect() { if (!Connected) @@ -131,6 +129,7 @@ public void ToggleConnect() } } + // Update the view count as often as possible IEnumerator UpdateViews() { while (Connected && IRC.ChannelName.Length > 0) @@ -168,29 +167,22 @@ IEnumerator UpdateViews() } } + // Reset the channel and viewer text private void ClearViewerCountAndChannelName(string channelText = null) { - if (ChannelNameTextMesh != null) ChannelNameTextMesh.text = (channelText ?? ""); if (ViewerCountTextMesh != null) ViewerCountTextMesh.text = ""; } + // Process a given message and pass it down the correct channels private void OnChatMsg(TwitchIRC.TwitchMessage message) { - if (message.Emotes != null) - { - if (message.Emotes.Count > 0) - { - Debug.Log("Caught " + message.Emotes.Count + " Emotes!"); - } - } - var msg = message.Message; - var cmd = msg.Split(' '); + var cmd = message.Message.Split(' '); var nickname = cmd[0].Split('!')[0].Substring(1); var mode = cmd[1]; var channel = cmd[2].Substring(1); var len = cmd[0].Length + cmd[1].Length + cmd[2].Length + 4; - var chat = msg.Substring(len); + var chat = message.Message.Substring(len); switch (mode) { @@ -228,7 +220,7 @@ private void OnChatMsg(TwitchIRC.TwitchMessage message) } break; case "PRIVMSG": - AddMsg(FirstLetterToUpper(nickname), TwitchIRC.GetUserColor(nickname), chat); + AddMsg(FirstLetterToUpper(nickname), TwitchIRC.GetUserColor(nickname), chat, message.Emotes); PlayMessageSound(); break; } @@ -276,6 +268,7 @@ public void SetMessageVolume(float volume) IncomingMessageSoundSource6.volume = volume; } + // Play a sound on the next free sound player public void PlayMessageSound() { // Prevent the message sound from spamming too rapidly @@ -297,49 +290,302 @@ public void PlayMessageSound() else if (IncomingMessageSoundSource6 != null && IncomingMessageSoundSource6.clip != null) IncomingMessageSoundSource6.Play(); } + // Add a system message to the chat display public void AddSystemNotice(string msgIn, TwitchIRC.NoticeColor colorEnum = TwitchIRC.NoticeColor.Blue) { OnChatMsg(new TwitchIRC.TwitchMessage(TwitchIRC.ToNotice("System", msgIn, colorEnum))); } - private void AddMsg(string nickname, string color, string chat) + // Add a given message to our list of messages, pushing them to the chat display + private void AddMsg(string nickname, string color, string chat, List emotes = null ) { - _userChat.Add(new TwitchChat(nickname, color, chat)); + _userChat.Add(new TwitchChat(nickname, color, chat, emotes ?? new List())); - while (_userChat.Count > 27) + // Remove excess messages + while (_userChat.Count > ChatLineCount) _userChat.RemoveAt(0); - - WordWrapText(_userChat); + + StartCoroutine(TwitchEmoteMaterialRecycler.Instance.UpdateEmoteMaterials(this, new TwitchChatUpdate(_userChat.ToArray(), _userChat.SelectMany(d => d.Emotes).ToList().Select(d => d.EmoteId).ToArray()))); + } + + // Generate the TextMeshes if required and wordwrap the given + // messages such that they _should_ fit into lines for our TextMeshes + public void SetChatMessages(TwitchChatUpdate chatUpdate, TwitchEmoteMaterialRecycler.EmoteMaterial[] mats) + { + GenChatTexts(); + WordWrapText(chatUpdate.Messages.ToList(), mats); } - private void WordWrapText(List messages) + private static readonly System.Object BuilderLocker = new System.Object(); + // Build the given messages and materials into a list of lines for our TextMeshes + private void WordWrapText(List messages, TwitchEmoteMaterialRecycler.EmoteMaterial[] mats) // TODO: Don't rebuild the #$%^ing list every time we get a message, just push the data along and rebuild the newest line { - var lines = new List(); - TextMesh.text = ""; - var ren = TextMesh.GetComponent(); - var rowLimit = 0.975f; //find the sweet spot - foreach (var m in messages) + try { - TextMesh.text = string.Format("{1}: ", m.Color, m.Name); - var builder = ""; - var parts = m.Message.Split(' '); - foreach (var t in parts) + lock (BuilderLocker) { - builder = TextMesh.text; - TextMesh.text += t + " "; - if (ren.bounds.extents.x > rowLimit) + mats = mats.DistinctBy(p => p.Id).ToArray(); + _emoteMap.Clear(); + for (var i = 0; i < mats.Length; i++) { - lines.Add(builder.TrimEnd() + System.Environment.NewLine); - TextMesh.text = t + " "; + _emoteMap.Add(mats[i].Id, mats[i].Material); + } + const int maxEmotesPerLine = 7; + var lines = new List(); + TextMesh.text = ""; + var ren = TextMesh.GetComponent(); + const float rowLimit = 0.975f; //find the sweet spot + var messageEmotes = new List[messages.Count]; + for (var mi = 0; mi < messages.Count; mi++) + { + messageEmotes[mi] = new List(); + } + for (var mi = 0; mi < messages.Count; mi++) + { + var m = messages[mi]; + TextMesh.text = string.Format("{1}: ", m.Color, m.Name); + var builder = ""; + var message = m.Message; + + // Insert the Emotes for this message + if (m.Emotes != null && m.Emotes.Count > 0) + { + var indexIncrease = 0; + var nextIndex = 0; + foreach (var key in m.Emotes) + { + // Cache the emote list so we can have seven unique emotes per message + Material mat; + if (!_emoteMap.TryGetValue(key.EmoteId, out mat)) continue; + var ind = 0; + var foundKey = false; + foreach (var matPair in messageEmotes[mi].Where(matPair => key.EmoteId == matPair.EmoteId)) + { + foundKey = true; + ind = matPair.Index; + break; + } + if (!foundKey) + { + ind = nextIndex; + messageEmotes[mi].Add(new MaterialIndexPair(ind, key.EmoteId, mat)); + nextIndex++; + } + + var text = string.Format("", ind); + message = message.Insert(key.EmoteStart + indexIncrease, text); + indexIncrease += text.Length; + } + } + + // Convert this message into Lines, such that they do not exceed the bounds of the chat box + var buildingQuad = false; + var buildingQuadNow = false; + var quadBuilder = ""; + var parts = message.Split(' '); + var lineEmotes = new List(); + var lineEmoteCount = 0; + var emotes = messageEmotes[mi].ToArray(); + var emoteMap = new Dictionary(); + var currentIndex = -1; + foreach (var t in parts) + { + builder = TextMesh.text; + if (t == " rowLimit) + { + lines.Add(new LineEmotePair(builder.TrimEnd(), lineEmotes.ToArray())); + lineEmotes.Clear(); + emoteMap.Clear(); + lineEmoteCount = 0; + TextMesh.text = t + " "; + } + builder = TextMesh.text; + } + else + { + if (buildingQuadNow || lineEmoteCount < maxEmotesPerLine) + { + buildingQuadNow = true; // Allow an emoji to finish building even if it exceeds the limits + // Here we are constructing the quad used in the TextMesh so that it will display an Emoji + string te; + if (t.StartsWith("material=")) + { + // Remap the materials for this line + var index = int.Parse(t.Substring(9, 1)); + currentIndex = index; + int ind; + if (emoteMap.TryGetValue(index, out ind)) + { + // This emote was already used on this line + te = "material=" + ind; + } + else + { + // This emote is new to this line, we must map it for future use on this line + lineEmoteCount++; + lineEmotes.Add(new MaterialIndexPair(lineEmoteCount, emotes[index].EmoteId, emotes[index].Material)); + te = "material=" + lineEmoteCount; + emoteMap.Add(index, lineEmoteCount); + } + } + else te = t; + + quadBuilder += te + " "; + if (t != "/>") continue; + buildingQuad = false; + buildingQuadNow = false; + TextMesh.text += quadBuilder.TrimEnd() + " "; + + if (currentIndex == -1) + { + Debug.LogWarning("This shouldn't happen.."); + continue; + } + if (ren.bounds.extents.x > rowLimit) + { + // This Emoji violates the line's bounds + // Check if this material belongs on the next line only + var curId = lineEmotes[lineEmoteCount - 1].EmoteId; + var count = 0; + foreach (var emote in lineEmotes.Where(emote => emote.EmoteId == curId)) + { + count++; + if (count >= 2) + break; + } + if (count == 1) lineEmotes.RemoveAt(lineEmoteCount - 1); // Remove the last material if it only belongs to this emote + lines.Add(new LineEmotePair(builder.TrimEnd(), lineEmotes.ToArray())); // Assign the previous working buffer to the last line + // Push this Emoji to the next line and reconstruct it there + lineEmotes.Clear(); + emoteMap.Clear(); + + var nextQuad = ""; + var nexQuadParts = quadBuilder.Split(' '); + + lineEmoteCount = 1; + // Reconstruct this Emoji for the next line + foreach (var tt in nexQuadParts) + { + string tte; + if (tt.StartsWith("material=")) + { + tte = "material=" + lineEmoteCount; + } + else tte = tt; + + nextQuad += tte + " "; + } + // Update the Emoji map with the material for the Emoji being pushed to the next line + lineEmotes.Add(new MaterialIndexPair(lineEmoteCount, emotes[currentIndex].EmoteId, emotes[currentIndex].Material)); + emoteMap.Add(currentIndex, lineEmoteCount); + currentIndex = -1; + TextMesh.text = " " + nextQuad.Trim() + " "; + } + // Clear the working buffers + builder = TextMesh.text; + quadBuilder = ""; + } + else // Too many emotes on this line, ignore this quad + { + if (t != "/>") continue; + buildingQuad = false; + } + } + } + lines.Add(new LineEmotePair(builder.TrimEnd(), lineEmotes.ToArray())); // Add the final line from the builder to the list of lines + } + // Clear the builder's text + TextMesh.text = ""; + + // Remove excess lines + while (lines.Count > ChatLineCount) + lines.RemoveAt(0); + + // Set the Emote materials and TextMesh texts + var offset = ChatLineCount - lines.Count; + for (var i = 0; i < ChatLineCount; i++) + { + if (i >= lines.Count) continue; + SetMaterialSize(ChatTextRenderers[i + offset], lines[i].EmoteList.Length + 1); + for (var j = 0; j < lines[i].EmoteList.Length; j++) + { + SetMaterial(ChatTextRenderers[i + offset], lines[i].EmoteList[j].Index, lines[i].EmoteList[j].Material); + } + ChatTextMeshes[i + offset].text = lines[i].LineText; + } + + // Refresh the texts to force them to display correctly + foreach (TextMesh t in ChatTextMeshes) + { + t.anchor = TextAnchor.UpperLeft; + t.anchor = TextAnchor.LowerLeft; } - builder = TextMesh.text; } - lines.Add(builder.TrimEnd() + System.Environment.NewLine); } - - TextMesh.text = lines.Aggregate("", (current, t) => current + t); + catch (Exception e) + { + Debug.LogException(e); + } + } + + // Assign a material to a given renderer (used to set Emoji materials) + public static void SetMaterial(Renderer ren, int index, Material material) + { + var mats = ren.sharedMaterials; + if (index < 0 || index >= mats.Length) return; + mats[index] = material; + ren.sharedMaterials = mats; } + // Set the size of the Materials Array on a given Renderer (used to set Emoji materials) + public static void SetMaterialSize(Renderer ren, int count) + { + if (count < 1) return; + // Make new Materials Array + var mats = ren.sharedMaterials; + var newMats = new Material[count]; + + // Copy old Materials Array + for (var i = 0; i < Mathf.Min(mats.Length, count); i++) + { + newMats[i] = mats[i]; + } + + // Apply new Materials Array + ren.sharedMaterials = newMats; + } + + private static readonly System.Object GenTextMeshLocker = new System.Object(); + // Generate our Text Meshes and ensure they are only generated once + private void GenChatTexts() + { + lock (GenTextMeshLocker) + { + if (_hasGeneratedTextMeshes) return; + ChatTextMeshes = new TextMesh[ChatLineCount]; + ChatTextRenderers = new Renderer[ChatLineCount]; + for (var i = 0; i < ChatLineCount; i++) + { + var obj = GameObject.Instantiate(TextMeshBase); + ChatTextRenderers[i] = obj.GetComponent(); + ChatTextMeshes[i] = obj.GetComponent(); + ChatTextMeshes[i].text = ""; + obj.transform.parent = gameObject.transform.parent; + obj.transform.localScale = new Vector3(0.005f, 0.005f, 1f); + obj.transform.localPosition = new Vector3(-0.5f, 0.465f - (0.0355f * i), -1f); + obj.SetActive(true); + } + _hasGeneratedTextMeshes = true; + } + } + + // Convert the first letter of the given string to a Capital Letter public static string FirstLetterToUpper(string str) { if (str == null) @@ -351,6 +597,8 @@ public static string FirstLetterToUpper(string str) return str.ToUpper(); } + // Convert the first letter, and every first letter after an underscore to a Capital Letter + // This looks a bit nicer before we have the proper format for this channel name public static string ChannelFirstLetterToUpper(string str) { if (str == null) @@ -368,9 +616,75 @@ public static string ChannelFirstLetterToUpper(string str) return st; } -// These are filled by JsonUtility so the compiler is confused + internal static Texture2D GenerateBaseTexture() + { + var tex = new Texture2D(1, 1); + tex.SetPixel(0, 0, new Color(0.45f, 0.2f, 0.75f)); + tex.Apply(); + return tex; + } + + // Contains a single chat message + public struct TwitchChat + { + public readonly string Name; + public readonly string Color; + public readonly string Message; + public readonly List Emotes; + + public TwitchChat(string name, string color, string message, List emotes) + { + Name = name; + Color = color; + Message = message; + Emotes = emotes; + } + } + + // Contains a group of chat messages + public struct TwitchChatUpdate + { + public readonly TwitchChat[] Messages; + public readonly int[] EmoteIds; + + public TwitchChatUpdate(TwitchChat[] messages, int[] emoteIds) + { + Messages = messages; + EmoteIds = emoteIds; + } + } + + // Passes material/emoteid information for a given message/line + public struct MaterialIndexPair + { + public readonly int Index; + public readonly int EmoteId; + public readonly Material Material; + + public MaterialIndexPair(int index, int emoteId, Material material) + { + Index = index; + EmoteId = emoteId; + Material = material; + } + } + + // Passes material/text information for a given line + public struct LineEmotePair + { + public readonly string LineText; + public readonly MaterialIndexPair[] EmoteList; + + public LineEmotePair(string lineText, MaterialIndexPair[] emoteList) + { + LineText = lineText; + EmoteList = emoteList; + } + } + + // These are filled by JsonUtility so the compiler is confused #pragma warning disable 649 -// ReSharper disable InconsistentNaming + // ReSharper disable InconsistentNaming [Serializable] private class ChannelDataFull { @@ -460,3 +774,19 @@ private class StreamLinksData #pragma warning restore 649 // ReSharper restore InconsistentNaming } + +public static class LinqExtensions +{ + public static IEnumerable DistinctBy + (this IEnumerable source, Func keySelector) + { + HashSet seenKeys = new HashSet(); + foreach (TSource element in source) + { + if (seenKeys.Add(keySelector(element))) + { + yield return element; + } + } + } +} \ No newline at end of file diff --git a/Assets/HOTK/Twitch/TwitchEmoteMaterialRecycler.cs b/Assets/HOTK/Twitch/TwitchEmoteMaterialRecycler.cs new file mode 100644 index 0000000..43bf4bf --- /dev/null +++ b/Assets/HOTK/Twitch/TwitchEmoteMaterialRecycler.cs @@ -0,0 +1,93 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; + +public class TwitchEmoteMaterialRecycler : MonoBehaviour +{ + public static TwitchEmoteMaterialRecycler Instance + { + get { return _instance ?? (_instance = new GameObject("TwitchEmoteMaterialRecycler", typeof(TwitchEmoteMaterialRecycler)) { hideFlags = HideFlags.HideInHierarchy }.GetComponent()); } + } + + private static TwitchEmoteMaterialRecycler _instance; + + private readonly List _activeEmoteMaterials = new List(); + + public Material GenerateEmoteMaterial() + { + var material = new Material(Shader.Find("Unlit/Transparent")) + { + name = "Emote Material" + }; + return material; + } + + private static readonly System.Object ActiveEmoteLocker = new System.Object(); + public IEnumerator UpdateEmoteMaterials(TwitchChatTester chat, TwitchChatTester.TwitchChatUpdate chatUpdate) + { + List list; + var newList = new List(); + lock (ActiveEmoteLocker) + { + // Generate a list of Materials no longer being used + var rem = (from mat in _activeEmoteMaterials where !chatUpdate.EmoteIds.Contains(mat.Id) select mat.Id).ToList(); + // Remove materials that are no longer being used + foreach (var r in rem) + { + for (var i = 0; i < _activeEmoteMaterials.Count; i ++) + { + if (_activeEmoteMaterials[i].Id != r) continue; + //Debug.Log("Not using " + _activeEmoteMaterials[i].Id); + _activeEmoteMaterials.RemoveAt(i); + break; + } + } + list = _activeEmoteMaterials.ToArray().ToList(); + // Add materials that are now being used + foreach (var mat in (from newId in chatUpdate.EmoteIds.ToList().Distinct() let found = list.Any(existing => existing.Id == newId) where !found select newId).Select(newId => new EmoteMaterial(newId, GenerateEmoteMaterial()))) + { + _activeEmoteMaterials.Add(mat); + newList.Add(mat); + } + } + + foreach (var mat in newList) + { + yield return StartCoroutine(LoadEmote(mat)); + list.Add(mat); + } + + chat.SetChatMessages(chatUpdate, list.ToArray()); + } + + IEnumerator LoadEmote(EmoteMaterial emote) + { + var tex = new Texture2D(4, 4, TextureFormat.DXT1, false); + emote.Material.mainTexture = tex; + var www = new WWW(string.Format("http://static-cdn.jtvnw.net/emoticons/v1/{0}/1.0", emote.Id)); + //Debug.Log("Loading " + emote.Id); + yield return www; + try + { + www.LoadImageIntoTexture(tex); + } + catch (Exception e) + { + Debug.LogError(e); + } + } + + public struct EmoteMaterial + { + public readonly int Id; + public readonly Material Material; + + public EmoteMaterial(int id, Material material) + { + Id = id; + Material = material; + } + } +} diff --git a/Assets/HOTK/Twitch/TwitchEmoteMaterialRecycler.cs.meta b/Assets/HOTK/Twitch/TwitchEmoteMaterialRecycler.cs.meta new file mode 100644 index 0000000..bfc8bb5 --- /dev/null +++ b/Assets/HOTK/Twitch/TwitchEmoteMaterialRecycler.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 3bd5a462dccb24549a72b081853aabcd +timeCreated: 1467698705 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/HOTK/Twitch/TwitchIRC.cs b/Assets/HOTK/Twitch/TwitchIRC.cs index ef23517..2f9c1cb 100644 --- a/Assets/HOTK/Twitch/TwitchIRC.cs +++ b/Assets/HOTK/Twitch/TwitchIRC.cs @@ -2,6 +2,7 @@ using UnityEngine; using System.Collections; using System.Collections.Generic; +using System.Linq; using System.Threading; public class TwitchIRC : MonoBehaviour @@ -93,7 +94,7 @@ private void IRCInputProcedure(System.IO.TextReader input, System.Net.Sockets.Ne _buffer = input.ReadLine(); if (_buffer == null) continue; - Debug.Log(_buffer); + //Debug.Log(_buffer); string[] tokens; string message; @@ -133,23 +134,7 @@ private void IRCInputProcedure(System.IO.TextReader input, System.Net.Sockets.Ne else if (key.StartsWith("emotes=")) { var emotes = key.Substring(7).Split('/'); - foreach (var emote in emotes) - { - var emoteSplit = emote.IndexOf(":"); - if (emoteSplit == -1) continue; // No emotes sent - var emoteId = int.Parse(emote.Substring(0, emoteSplit)); - var emotePos = emote.Substring(emoteSplit + 1); - var emotePoses = emotePos.Split(','); - foreach (var emoteP in emotePoses) - { - var emoteTokens = emoteP.Split('-'); - var emoteStart = int.Parse(emoteTokens[0]); - var emoteEnd = int.Parse(emoteTokens[1]) + 1; - - emoteKeys.Add(new EmoteKey(emoteId, emoteStart, emoteEnd)); - Debug.Log(string.Format("Used emote {0} from {1} to {2}", emoteId, emoteStart, emoteEnd)); - } - } + emoteKeys.AddRange(from emote in emotes let emoteSplit = emote.IndexOf(":") where emoteSplit != -1 let emoteId = int.Parse(emote.Substring(0, emoteSplit)) let emotePos = emote.Substring(emoteSplit + 1) let emotePoses = emotePos.Split(',') from emoteP in emotePoses let emoteTokens = emoteP.Split('-') let emoteStart = int.Parse(emoteTokens[0]) let emoteLen = (int.Parse(emoteTokens[1]) - emoteStart) + 1 select new EmoteKey(emoteId, emoteStart, emoteLen)); // LINQ master :P } } @@ -160,23 +145,18 @@ private void IRCInputProcedure(System.IO.TextReader input, System.Net.Sockets.Ne var messageSplit = message.Substring(1).IndexOf(':'); var messageHeader = message.Substring(0, messageSplit); var messageBody = message.Substring(messageSplit + 2); - - Debug.Log("Header: " + messageHeader); - Debug.Log("Body: " + messageBody); + for (var i = 0; i < emoteKeys.Count; i++) { - var len = emoteKeys[i].EmoteEnd - emoteKeys[i].EmoteStart; - Debug.Log(string.Format("Removing {0} to {1} from {2}", emoteKeys[i].EmoteStart, len, messageBody)); - messageBody = messageBody.Remove(emoteKeys[i].EmoteStart, len); - for (var j = i; j < emoteKeys.Count; j++) + messageBody = messageBody.Remove(emoteKeys[i].EmoteStart, emoteKeys[i].EmoteLength); + for (var j = i + 1; j < emoteKeys.Count; j++) { - emoteKeys[j].EmoteStart -= len; - emoteKeys[j].EmoteEnd -= len; + emoteKeys[j].EmoteStart -= emoteKeys[i].EmoteLength; + //Debug.Log("Adjusting Emote Length"); } } message = messageHeader + " :" + messageBody; - Debug.Log(message); } } else @@ -403,13 +383,13 @@ public class EmoteKey { public readonly int EmoteId; public int EmoteStart; - public int EmoteEnd; + public int EmoteLength; - public EmoteKey(int id, int start, int end) + public EmoteKey(int id, int start, int length) { EmoteId = id; EmoteStart = start; - EmoteEnd = end; + EmoteLength = length; } } } diff --git a/Assets/HOTK/Twitch/TwitchSettingsSaver.cs b/Assets/HOTK/Twitch/TwitchSettingsSaver.cs index a4a5973..522c57d 100644 --- a/Assets/HOTK/Twitch/TwitchSettingsSaver.cs +++ b/Assets/HOTK/Twitch/TwitchSettingsSaver.cs @@ -6,7 +6,7 @@ [System.Serializable] public class TwitchSettings { - public const uint CurrentSaveVersion = 2; + public const uint CurrentSaveVersion = 3; public uint SaveFileVersion; @@ -20,7 +20,7 @@ public class TwitchSettings public HOTK_Overlay.AttachmentPoint Point; public HOTK_Overlay.AnimationType Animation; - public float BackgroundR, BackgroundG, BackgroundB; + public float BackgroundR, BackgroundG, BackgroundB, BackgroundA; public float AlphaStart, AlphaEnd, AlphaSpeed; public float ScaleStart, ScaleEnd, ScaleSpeed; diff --git a/Assets/SteamVR.meta b/Assets/SteamVR.meta index a17b64e..47746a2 100644 --- a/Assets/SteamVR.meta +++ b/Assets/SteamVR.meta @@ -1,7 +1,7 @@ fileFormatVersion: 2 guid: f0032bd9499af5e4aa85ea41615843a0 folderAsset: yes -timeCreated: 1465633285 +timeCreated: 1468737963 licenseType: Free DefaultImporter: userData: diff --git a/Assets/SteamVR/Editor.meta b/Assets/SteamVR/Editor.meta index e2f41ed..a4121d9 100644 --- a/Assets/SteamVR/Editor.meta +++ b/Assets/SteamVR/Editor.meta @@ -1,7 +1,7 @@ fileFormatVersion: 2 guid: 941dde9a9ca65d74489c024e76c8bfe8 folderAsset: yes -timeCreated: 1465633285 +timeCreated: 1468815268 licenseType: Free DefaultImporter: userData: diff --git a/Assets/SteamVR/Prefabs.meta b/Assets/SteamVR/Prefabs.meta index b857624..2619977 100644 --- a/Assets/SteamVR/Prefabs.meta +++ b/Assets/SteamVR/Prefabs.meta @@ -1,7 +1,7 @@ fileFormatVersion: 2 guid: f4d909dcb749c45459770e058a2efac3 folderAsset: yes -timeCreated: 1465633285 +timeCreated: 1468815268 licenseType: Free DefaultImporter: userData: diff --git a/Assets/SteamVR/Resources.meta b/Assets/SteamVR/Resources.meta index de3d65c..575f69e 100644 --- a/Assets/SteamVR/Resources.meta +++ b/Assets/SteamVR/Resources.meta @@ -1,7 +1,7 @@ fileFormatVersion: 2 guid: 128449277d8f96b46a3816056eee4067 folderAsset: yes -timeCreated: 1465633285 +timeCreated: 1468737963 licenseType: Free DefaultImporter: userData: diff --git a/Assets/SteamVR/Resources/SteamVR_Blit.shader.meta b/Assets/SteamVR/Resources/SteamVR_Blit.shader.meta index 98528ca..b04569b 100644 --- a/Assets/SteamVR/Resources/SteamVR_Blit.shader.meta +++ b/Assets/SteamVR/Resources/SteamVR_Blit.shader.meta @@ -1,9 +1,6 @@ fileFormatVersion: 2 -guid: 021228b76f139394fae4afb56cd6bf29 -timeCreated: 1465931332 -licenseType: Free +guid: 6403027b84bd2824dafa520459aa107d ShaderImporter: defaultTextures: [] userData: assetBundleName: - assetBundleVariant: diff --git a/Assets/SteamVR/Resources/SteamVR_BlitFlip.shader.meta b/Assets/SteamVR/Resources/SteamVR_BlitFlip.shader.meta index 1b6b751..ba44dba 100644 --- a/Assets/SteamVR/Resources/SteamVR_BlitFlip.shader.meta +++ b/Assets/SteamVR/Resources/SteamVR_BlitFlip.shader.meta @@ -1,7 +1,7 @@ fileFormatVersion: 2 -guid: 7f38d614fcfa3b541bde2acda92af7ca -timeCreated: 1465931347 -licenseType: Free +guid: 2adcf81282c27bc4098ee69d2419bc48 +timeCreated: 1430872950 +licenseType: Store ShaderImporter: defaultTextures: [] userData: diff --git a/Assets/SteamVR/Scripts.meta b/Assets/SteamVR/Scripts.meta index 3e07a77..dee0db1 100644 --- a/Assets/SteamVR/Scripts.meta +++ b/Assets/SteamVR/Scripts.meta @@ -1,7 +1,7 @@ fileFormatVersion: 2 guid: 95553d89c282bf141b499a612fd87915 folderAsset: yes -timeCreated: 1465633285 +timeCreated: 1468737963 licenseType: Free DefaultImporter: userData: diff --git a/Assets/SteamVR/Scripts/SteamVR_ControllerManager.cs b/Assets/SteamVR/Scripts/SteamVR_ControllerManager.cs index ad3b116..c2537b0 100644 --- a/Assets/SteamVR/Scripts/SteamVR_ControllerManager.cs +++ b/Assets/SteamVR/Scripts/SteamVR_ControllerManager.cs @@ -1,4 +1,4 @@ -//======= Copyright (c) Valve Corporation, All rights reserved. =============== +//========= Copyright 2016, Valve Corporation, All rights reserved. =========== // // Purpose: Enables/disables objects based on connectivity and assigned roles. // @@ -10,228 +10,228 @@ public class SteamVR_ControllerManager : MonoBehaviour { - public GameObject left, right; - public GameObject[] objects; // populate with objects you want to assign to additional controllers - - uint[] indices; // assigned - bool[] connected = new bool[OpenVR.k_unMaxTrackedDeviceCount]; // controllers only - - // cached roles - may or may not be connected - uint leftIndex = OpenVR.k_unTrackedDeviceIndexInvalid; - uint rightIndex = OpenVR.k_unTrackedDeviceIndexInvalid; - - void Awake() - { - // Add left and right entries to the head of the list so we only have to operate on the list itself. - var additional = (this.objects != null) ? this.objects.Length : 0; - var objects = new GameObject[2 + additional]; - indices = new uint[2 + additional]; - objects[0] = right; - indices[0] = OpenVR.k_unTrackedDeviceIndexInvalid; - objects[1] = left; - indices[1] = OpenVR.k_unTrackedDeviceIndexInvalid; - for (int i = 0; i < additional; i++) - { - objects[2 + i] = this.objects[i]; - indices[2 + i] = OpenVR.k_unTrackedDeviceIndexInvalid; - } - this.objects = objects; - } - - void OnEnable() - { - for (int i = 0; i < objects.Length; i++) - { - var obj = objects[i]; - if (obj != null) - obj.SetActive(false); - } - - OnTrackedDeviceRoleChanged(); - - for (int i = 0; i < SteamVR.connected.Length; i++) - if (SteamVR.connected[i]) - OnDeviceConnected(i, true); - - SteamVR_Utils.Event.Listen("input_focus", OnInputFocus); - SteamVR_Utils.Event.Listen("device_connected", OnDeviceConnected); - SteamVR_Utils.Event.Listen("TrackedDeviceRoleChanged", OnTrackedDeviceRoleChanged); - } - - void OnDisable() - { - SteamVR_Utils.Event.Remove("input_focus", OnInputFocus); - SteamVR_Utils.Event.Remove("device_connected", OnDeviceConnected); - SteamVR_Utils.Event.Remove("TrackedDeviceRoleChanged", OnTrackedDeviceRoleChanged); - } - - static string[] labels = { "left", "right" }; - - // Hide controllers when the dashboard is up. - private void OnInputFocus(params object[] args) - { - bool hasFocus = (bool)args[0]; - if (hasFocus) - { - for (int i = 0; i < objects.Length; i++) - { - var obj = objects[i]; - if (obj != null) - { - var label = (i < 2) ? labels[i] : (i - 1).ToString(); - ShowObject(obj.transform, "hidden (" + label + ")"); - } - } - } - else - { - for (int i = 0; i < objects.Length; i++) - { - var obj = objects[i]; - if (obj != null) - { - var label = (i < 2) ? labels[i] : (i - 1).ToString(); - HideObject(obj.transform, "hidden (" + label + ")"); - } - } - } - } - - // Reparents to a new object and deactivates that object (this allows - // us to call SetActive in OnDeviceConnected independently. - private void HideObject(Transform t, string name) - { - var hidden = new GameObject(name).transform; - hidden.parent = t.parent; - t.parent = hidden; - hidden.gameObject.SetActive(false); - } - private void ShowObject(Transform t, string name) - { - var hidden = t.parent; - if (hidden.gameObject.name != name) - return; - t.parent = hidden.parent; - Destroy(hidden.gameObject); - } - - private void SetTrackedDeviceIndex(int objectIndex, uint trackedDeviceIndex) - { - // First make sure no one else is already using this index. - if (trackedDeviceIndex != OpenVR.k_unTrackedDeviceIndexInvalid) - { - for (int i = 0; i < objects.Length; i++) - { - if (i != objectIndex && indices[i] == trackedDeviceIndex) - { - var obj = objects[i]; - if (obj != null) - obj.SetActive(false); - - indices[i] = OpenVR.k_unTrackedDeviceIndexInvalid; - } - } - } - - // Only set when changed. - if (trackedDeviceIndex != indices[objectIndex]) - { - indices[objectIndex] = trackedDeviceIndex; - - var obj = objects[objectIndex]; - if (obj != null) - { - if (trackedDeviceIndex == OpenVR.k_unTrackedDeviceIndexInvalid) - obj.SetActive(false); - else - { - obj.SetActive(true); - obj.BroadcastMessage("SetDeviceIndex", (int)trackedDeviceIndex, SendMessageOptions.DontRequireReceiver); - } - } - } - } - - // Keep track of assigned roles. - private void OnTrackedDeviceRoleChanged(params object[] args) - { - Refresh(); - } - - // Keep track of connected controller indices. - private void OnDeviceConnected(params object[] args) - { - var index = (uint)(int)args[0]; - bool changed = this.connected[index]; - this.connected[index] = false; - - var connected = (bool)args[1]; - if (connected) - { - var system = OpenVR.System; - if (system != null && system.GetTrackedDeviceClass(index) == ETrackedDeviceClass.Controller) - { - this.connected[index] = true; - changed = !changed; // if we clear and set the same index, nothing has changed - } - } - - if (changed) - Refresh(); - } - - public void Refresh() - { - int objectIndex = 0; - - var system = OpenVR.System; - if (system != null) - { - leftIndex = system.GetTrackedDeviceIndexForControllerRole(ETrackedControllerRole.LeftHand); - rightIndex = system.GetTrackedDeviceIndexForControllerRole(ETrackedControllerRole.RightHand); - } - - // If neither role has been assigned yet, try hooking up at least the right controller. - if (leftIndex == OpenVR.k_unTrackedDeviceIndexInvalid && rightIndex == OpenVR.k_unTrackedDeviceIndexInvalid) - { - for (uint deviceIndex = 0; deviceIndex < connected.Length; deviceIndex++) - { - if (connected[deviceIndex]) - { - SetTrackedDeviceIndex(objectIndex++, deviceIndex); - break; - } - } - } - else - { - SetTrackedDeviceIndex(objectIndex++, (rightIndex < connected.Length && connected[rightIndex]) ? rightIndex : OpenVR.k_unTrackedDeviceIndexInvalid); - SetTrackedDeviceIndex(objectIndex++, (leftIndex < connected.Length && connected[leftIndex]) ? leftIndex : OpenVR.k_unTrackedDeviceIndexInvalid); - - // Assign out any additional controllers only after both left and right have been assigned. - if (leftIndex != OpenVR.k_unTrackedDeviceIndexInvalid && rightIndex != OpenVR.k_unTrackedDeviceIndexInvalid) - { - for (uint deviceIndex = 0; deviceIndex < connected.Length; deviceIndex++) - { - if (objectIndex >= objects.Length) - break; - - if (!connected[deviceIndex]) - continue; - - if (deviceIndex != leftIndex && deviceIndex != rightIndex) - { - SetTrackedDeviceIndex(objectIndex++, deviceIndex); - } - } - } - } - - // Reset the rest. - while (objectIndex < objects.Length) - { - SetTrackedDeviceIndex(objectIndex++, OpenVR.k_unTrackedDeviceIndexInvalid); - } - } + public GameObject left, right; + public GameObject[] objects; // populate with objects you want to assign to additional controllers + + uint[] indices; // assigned + bool[] connected = new bool[OpenVR.k_unMaxTrackedDeviceCount]; // controllers only + + // cached roles - may or may not be connected + uint leftIndex = OpenVR.k_unTrackedDeviceIndexInvalid; + uint rightIndex = OpenVR.k_unTrackedDeviceIndexInvalid; + + void Awake() + { + // Add left and right entries to the head of the list so we only have to operate on the list itself. + var additional = (this.objects != null) ? this.objects.Length : 0; + var objects = new GameObject[2 + additional]; + indices = new uint[2 + additional]; + objects[0] = right; + indices[0] = OpenVR.k_unTrackedDeviceIndexInvalid; + objects[1] = left; + indices[1] = OpenVR.k_unTrackedDeviceIndexInvalid; + for (int i = 0; i < additional; i++) + { + objects[2 + i] = this.objects[i]; + indices[2 + i] = OpenVR.k_unTrackedDeviceIndexInvalid; + } + this.objects = objects; + } + + void OnEnable() + { + for (int i = 0; i < objects.Length; i++) + { + var obj = objects[i]; + if (obj != null) + obj.SetActive(false); + } + + OnTrackedDeviceRoleChanged(); + + for (int i = 0; i < SteamVR.connected.Length; i++) + if (SteamVR.connected[i]) + OnDeviceConnected(i, true); + + SteamVR_Utils.Event.Listen("input_focus", OnInputFocus); + SteamVR_Utils.Event.Listen("device_connected", OnDeviceConnected); + SteamVR_Utils.Event.Listen("TrackedDeviceRoleChanged", OnTrackedDeviceRoleChanged); + } + + void OnDisable() + { + SteamVR_Utils.Event.Remove("input_focus", OnInputFocus); + SteamVR_Utils.Event.Remove("device_connected", OnDeviceConnected); + SteamVR_Utils.Event.Remove("TrackedDeviceRoleChanged", OnTrackedDeviceRoleChanged); + } + + static string[] labels = { "left", "right" }; + + // Hide controllers when the dashboard is up. + private void OnInputFocus(params object[] args) + { + bool hasFocus = (bool)args[0]; + if (hasFocus) + { + for (int i = 0; i < objects.Length; i++) + { + var obj = objects[i]; + if (obj != null) + { + var label = (i < 2) ? labels[i] : (i - 1).ToString(); + ShowObject(obj.transform, "hidden (" + label + ")"); + } + } + } + else + { + for (int i = 0; i < objects.Length; i++) + { + var obj = objects[i]; + if (obj != null) + { + var label = (i < 2) ? labels[i] : (i - 1).ToString(); + HideObject(obj.transform, "hidden (" + label + ")"); + } + } + } + } + + // Reparents to a new object and deactivates that object (this allows + // us to call SetActive in OnDeviceConnected independently. + private void HideObject(Transform t, string name) + { + var hidden = new GameObject(name).transform; + hidden.parent = t.parent; + t.parent = hidden; + hidden.gameObject.SetActive(false); + } + private void ShowObject(Transform t, string name) + { + var hidden = t.parent; + if (hidden.gameObject.name != name) + return; + t.parent = hidden.parent; + Destroy(hidden.gameObject); + } + + private void SetTrackedDeviceIndex(int objectIndex, uint trackedDeviceIndex) + { + // First make sure no one else is already using this index. + if (trackedDeviceIndex != OpenVR.k_unTrackedDeviceIndexInvalid) + { + for (int i = 0; i < objects.Length; i++) + { + if (i != objectIndex && indices[i] == trackedDeviceIndex) + { + var obj = objects[i]; + if (obj != null) + obj.SetActive(false); + + indices[i] = OpenVR.k_unTrackedDeviceIndexInvalid; + } + } + } + + // Only set when changed. + if (trackedDeviceIndex != indices[objectIndex]) + { + indices[objectIndex] = trackedDeviceIndex; + + var obj = objects[objectIndex]; + if (obj != null) + { + if (trackedDeviceIndex == OpenVR.k_unTrackedDeviceIndexInvalid) + obj.SetActive(false); + else + { + obj.SetActive(true); + obj.BroadcastMessage("SetDeviceIndex", (int)trackedDeviceIndex, SendMessageOptions.DontRequireReceiver); + } + } + } + } + + // Keep track of assigned roles. + private void OnTrackedDeviceRoleChanged(params object[] args) + { + Refresh(); + } + + // Keep track of connected controller indices. + private void OnDeviceConnected(params object[] args) + { + var index = (uint)(int)args[0]; + bool changed = this.connected[index]; + this.connected[index] = false; + + var connected = (bool)args[1]; + if (connected) + { + var system = OpenVR.System; + if (system != null && system.GetTrackedDeviceClass(index) == ETrackedDeviceClass.Controller) + { + this.connected[index] = true; + changed = !changed; // if we clear and set the same index, nothing has changed + } + } + + if (changed) + Refresh(); + } + + public void Refresh() + { + int objectIndex = 0; + + var system = OpenVR.System; + if (system != null) + { + leftIndex = system.GetTrackedDeviceIndexForControllerRole(ETrackedControllerRole.LeftHand); + rightIndex = system.GetTrackedDeviceIndexForControllerRole(ETrackedControllerRole.RightHand); + } + + // If neither role has been assigned yet, try hooking up at least the right controller. + if (leftIndex == OpenVR.k_unTrackedDeviceIndexInvalid && rightIndex == OpenVR.k_unTrackedDeviceIndexInvalid) + { + for (uint deviceIndex = 0; deviceIndex < connected.Length; deviceIndex++) + { + if (connected[deviceIndex]) + { + SetTrackedDeviceIndex(objectIndex++, deviceIndex); + break; + } + } + } + else + { + SetTrackedDeviceIndex(objectIndex++, (rightIndex < connected.Length && connected[rightIndex]) ? rightIndex : OpenVR.k_unTrackedDeviceIndexInvalid); + SetTrackedDeviceIndex(objectIndex++, (leftIndex < connected.Length && connected[leftIndex]) ? leftIndex : OpenVR.k_unTrackedDeviceIndexInvalid); + + // Assign out any additional controllers only after both left and right have been assigned. + if (leftIndex != OpenVR.k_unTrackedDeviceIndexInvalid && rightIndex != OpenVR.k_unTrackedDeviceIndexInvalid) + { + for (uint deviceIndex = 0; deviceIndex < connected.Length; deviceIndex++) + { + if (objectIndex >= objects.Length) + break; + + if (!connected[deviceIndex]) + continue; + + if (deviceIndex != leftIndex && deviceIndex != rightIndex) + { + SetTrackedDeviceIndex(objectIndex++, deviceIndex); + } + } + } + } + + // Reset the rest. + while (objectIndex < objects.Length) + { + SetTrackedDeviceIndex(objectIndex++, OpenVR.k_unTrackedDeviceIndexInvalid); + } + } } diff --git a/Assets/SteamVR/Scripts/SteamVR_ControllerManager.cs.meta b/Assets/SteamVR/Scripts/SteamVR_ControllerManager.cs.meta index cfd6376..8f4c60b 100644 --- a/Assets/SteamVR/Scripts/SteamVR_ControllerManager.cs.meta +++ b/Assets/SteamVR/Scripts/SteamVR_ControllerManager.cs.meta @@ -1,7 +1,7 @@ fileFormatVersion: 2 -guid: 1edaba0e604d1244a9245cd9a8306816 -timeCreated: 1465931117 -licenseType: Free +guid: e3b47c2980b93bc48844a54641dab5b8 +timeCreated: 1437430318 +licenseType: Store MonoImporter: serializedVersion: 2 defaultReferences: [] diff --git a/Assets/SteamVR/Scripts/SteamVR_Overlay.cs b/Assets/SteamVR/Scripts/SteamVR_Overlay.cs index b0e02ea..42bd035 100644 --- a/Assets/SteamVR/Scripts/SteamVR_Overlay.cs +++ b/Assets/SteamVR/Scripts/SteamVR_Overlay.cs @@ -30,7 +30,7 @@ public class SteamVR_Overlay : MonoBehaviour private ulong handle = OpenVR.k_ulOverlayHandleInvalid; - void OnEnable() + void OnEnable() { var overlay = OpenVR.Overlay; if (overlay != null) diff --git a/Assets/SteamVR/Scripts/SteamVR_Overlay.cs.meta b/Assets/SteamVR/Scripts/SteamVR_Overlay.cs.meta index e2c9383..21fe1d6 100644 --- a/Assets/SteamVR/Scripts/SteamVR_Overlay.cs.meta +++ b/Assets/SteamVR/Scripts/SteamVR_Overlay.cs.meta @@ -1,7 +1,5 @@ fileFormatVersion: 2 guid: 46fe9e0b23166454c8cb73040321d78c -timeCreated: 1466224777 -licenseType: Free MonoImporter: serializedVersion: 2 defaultReferences: [] @@ -9,4 +7,3 @@ MonoImporter: icon: {instanceID: 0} userData: assetBundleName: - assetBundleVariant: diff --git a/Assets/SteamVR/Scripts/SteamVR_Render.cs b/Assets/SteamVR/Scripts/SteamVR_Render.cs index cff356e..6230de7 100644 --- a/Assets/SteamVR/Scripts/SteamVR_Render.cs +++ b/Assets/SteamVR/Scripts/SteamVR_Render.cs @@ -170,10 +170,10 @@ private IEnumerator RenderLoop() #if (UNITY_5_3 || UNITY_5_2 || UNITY_5_1 || UNITY_5_0) if (cameras.Length == 0) continue; SteamVR_Utils.QueueEventOnRenderThread(SteamVR.Unity.k_nRenderEventID_WaitGetPoses); - // Hack to flush render event that was queued in Update (this ensures WaitGetPoses has returned before we grab the new values). - SteamVR.Unity.EventWriteString("[UnityMain] GetNativeTexturePtr - Begin"); - SteamVR_Camera.GetSceneTexture(cameras[0].GetComponent().hdr).GetNativeTexturePtr(); - SteamVR.Unity.EventWriteString("[UnityMain] GetNativeTexturePtr - End"); + // Hack to flush render event that was queued in Update (this ensures WaitGetPoses has returned before we grab the new values). + SteamVR.Unity.EventWriteString("[UnityMain] GetNativeTexturePtr - Begin"); + SteamVR_Camera.GetSceneTexture(cameras[0].GetComponent().hdr).GetNativeTexturePtr(); + SteamVR.Unity.EventWriteString("[UnityMain] GetNativeTexturePtr - End"); compositor.GetLastPoses(poses, gamePoses); SteamVR_Utils.Event.Send("new_poses", poses); diff --git a/Assets/SteamVR/Textures.meta b/Assets/SteamVR/Textures.meta index 043ec8d..4c08081 100644 --- a/Assets/SteamVR/Textures.meta +++ b/Assets/SteamVR/Textures.meta @@ -1,7 +1,7 @@ fileFormatVersion: 2 guid: 003cb17d6edd28b4d88df6f35f9c03ab folderAsset: yes -timeCreated: 1465633285 +timeCreated: 1468737963 licenseType: Free DefaultImporter: userData: diff --git a/Assets/SteamVR/Textures/arrow.png b/Assets/SteamVR/Textures/arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..4693d064907092ad1d33b8ac0958b8af74dc2119 GIT binary patch literal 5292 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 literal 0 HcmV?d00001 diff --git a/Assets/SteamVR/Textures/background.png.meta b/Assets/SteamVR/Textures/background.png.meta new file mode 100644 index 0000000..e49c481 --- /dev/null +++ b/Assets/SteamVR/Textures/background.png.meta @@ -0,0 +1,52 @@ +fileFormatVersion: 2 +guid: bb00cc87e146a414fbf2c4d3c0d31151 +TextureImporter: + fileIDToRecycleName: {} + serializedVersion: 2 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + linearTexture: 1 + correctGamma: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: .25 + normalMapFilter: 0 + isReadable: 0 + grayScaleToAlpha: 0 + generateCubemap: 0 + cubemapConvolution: 0 + cubemapConvolutionSteps: 8 + cubemapConvolutionExponent: 1.5 + seamlessCubemap: 0 + textureFormat: -1 + maxTextureSize: 1024 + textureSettings: + filterMode: -1 + aniso: 1 + mipBias: -1 + wrapMode: 1 + nPOTScale: 0 + lightmap: 0 + rGBM: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: .5, y: .5} + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spritePixelsToUnits: 100 + alphaIsTransparency: 1 + textureType: 2 + buildTargetSettings: [] + spriteSheet: + sprites: [] + spritePackingTag: + userData: + assetBundleName: