• Content count

  • Joined

  • Last visited

Community Reputation

1064 Excellent


  1. The simplest way is the same way parts are loaded, by using PartTools to export to a .mu file. That'll be loaded by the game into GameDatabase.Instance.databaseModel. Parts are created when an appropriate config refers to the model in this list but you can use it for whatever purpose. The format does have some limitations though, so if you find you need more capability you can use an AssetBundle instead. In that case, you might find this thread to be a useful read
  2. I'm around if help is needed but it's fairly straightforward. Only gotcha was (iirc) that the IVA NavBall is or was flipped horizontally for some reason There isn't a collar object so it's not quite that easy, it's mixed in with the body mesh. But actually it's not too hard to just extract the collar triangles and create a new collar GO which you then treat exactly like that. The transparent texture idea would probably work but it's going to result in some side effects and is probably just more effort overall It would probably be wise to stitch the top of the suit closed as a next step though. You can see (in Val's case especially) that her suit doesn't quite meet her neck so the effect is somewhat spoiled
  3. The TextMeshPro asset from the store has a different assembly name that the one KSPAssetCompiler references so it's failing to resolve. You can fix that pretty easily with a little script. Create a new Editor folder somewhere appropriate (I use Plugins/KSPAssets/Editor), create a new script TextMeshProResolver and paste this into it: using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using ReeperCommon.Logging; using UnityEditor; using UnityEngine; namespace Assets.Plugins.KSPAssets.Editor { [InitializeOnLoad] static class TextMeshProResolver { private const string TextMeshPro = "TextMesh Pro/Plugins"; private const string TextMeshProDllFilter = "TextMeshPro*.dll"; static TextMeshProResolver() { AppDomain.CurrentDomain.AssemblyResolve += CurrentDomainOnAssemblyResolve; } private static Assembly CurrentDomainOnAssemblyResolve(object sender, ResolveEventArgs args) { if (!args.Name.StartsWith("TextMeshPro")) return null; Assembly result; if (FindLoadedTMP(out result)) { return result; } string path; if (!GetPathToTMP(out path)) Debug.LogError("need to install TextMeshPro"); else Debug.LogWarning("modify this function to load the DLL ourselves"); // seems to always be loaded by the time we get there, but include a reminder // on how to fix it in case this is ever not the case return null; } private static string SanitizePath(string path) { return path.Replace('\\', Path.DirectorySeparatorChar).Replace('/', Path.DirectorySeparatorChar); } // ReSharper disable once InconsistentNaming private static bool GetPathToTMP(out string path) { path = string.Empty; var tmpDir = SanitizePath(Path.Combine(Application.dataPath, TextMeshPro)); if (!Directory.Exists(tmpDir)) return false; var possibleDlls = Directory.GetFiles(tmpDir, TextMeshProDllFilter); if (possibleDlls.Length == 0) { Debug.LogError("could not find TextMeshPro dll!"); return false; } else if (possibleDlls.Length > 1) { Debug.LogError("multiple dlls found for TextMeshPro. Did you install multiple versions?"); return false; } var possiblePath = SanitizePath(Path.Combine(tmpDir, possibleDlls.Single())); if (!File.Exists(possiblePath)) return false; path = possiblePath; return true; } // ReSharper disable once InconsistentNaming private static bool FindLoadedTMP(out Assembly tmpAssembly) { string path; tmpAssembly = null; if (!GetPathToTMP(out path)) return false; foreach (var a in AppDomain.CurrentDomain.GetAssemblies() .Where(a => a.GetName().Name.StartsWith("TextMeshPro"))) { if (0 != string.Compare(SanitizePath(a.Location), path, StringComparison.Ordinal)) continue; tmpAssembly = a; return true; } return false; } } }
  4. You're almost there. RemoveAllListeners only removes non-persistent listeners. You can disable the persistent ones with onClick.SetPersistentListenerState. You might also want to be more discerning with your GameObject.Find call. You can just grab the right transform directly: CrewAssignmentDialog.Instance.transform.Find("VL/Buttons/Button Fill"); // <-- GetComponent<Button> on this if found
  5. Can you clarify how you're testing this? OnLoad will only run in the editor if the parts are being loaded from disk or the editor cache so when you say "every time" I'm suspicious. It certainly won't be running when you pick a part from the parts list and then place it onto the ship, for example
  6. Odd, your example works fine on my system. Do you end up triggering any texture rebuilds while running yours? Are you testing with the pre-release or the current version? Win7 GTX 960 Current version (not localization pre-release) Installed fonts according to Font.GetOSInstalledFontNames:
  7. That mod only. Yes I knew it's pretty ugly code, it was just slapped together to check things out
  8. @Steven Mading 3 and 4 are for rendering done in OnGUI. Those lines are for 1 and 2 to match the order of initialization you specified in the OP
  9. Something sounds fishy here. Are you editing the default GUI.skin? Why? I couldn't reproduce the problem with the following: abstract class TestTextRenderer { private Rect _rect = new Rect(0f, 0f, 300f, 300f); private GUISkin _skin; private string _title; private string _text; public virtual void Initialize() { _skin = GetSkin(); _title = GetTitle(); _rect.center = new Vector2(Screen.width * 0.5f, Screen.height * 0.5f); _text = Enumerable.Range(0, 255).Select(idx => ((char)idx).ToString()).Aggregate((s1, s2) => s1 + s2); _text += _text += _text; // increase length a bit Log.Normal(_title + " font: " + _skin.font.name + ": " + string.Join(",", _skin.font.fontNames)); } protected abstract GUISkin GetSkin(); protected abstract string GetTitle(); public void DrawWindow() { GUI.skin = _skin; _rect = KSPUtil.ClampRectToScreen(GUILayout.Window(_title.GetHashCode(), _rect, DoWindow, _title)); } private void DoWindow(int winid) { GUILayout.TextArea(_text); GUI.DragWindow(); } } class DynamicTextRenderer : TestTextRenderer { private const string FontName = "Arial Bold"; private const int FontSize = 16; protected override GUISkin GetSkin() { var cloneSkin = UnityEngine.Object.Instantiate(HighLogic.Skin); var dynFont = Font.CreateDynamicFontFromOSFont(FontName, FontSize); cloneSkin.font = dynFont; return cloneSkin; } protected override string GetTitle() { return "Dynamic Text"; } } class ResourceTextRenderer : TestTextRenderer { private readonly Font[] _resourceFonts; private const string FontName = "Arial"; public ResourceTextRenderer([NotNull] Font[] resourceFonts) { if (resourceFonts == null) throw new ArgumentNullException("resourceFonts"); _resourceFonts = resourceFonts; } protected override GUISkin GetSkin() { var cloneSkin = UnityEngine.Object.Instantiate(HighLogic.Skin); cloneSkin.font = _resourceFonts.First(f => f.fontNames.Contains(FontName)); return cloneSkin; } protected override string GetTitle() { return "Resource-loaded Font"; } } // controls ordering of text initialization and rendering // 1. init resource font // 2. init dyn font // 3. make sure dyn font renders first // 4. res font renders second [KSPAddon(KSPAddon.Startup.MainMenu, true)] public class RenderOrderController : MonoBehaviour { private DynamicTextRenderer _dynText = null; private ResourceTextRenderer _resText = null; private void Awake() { Font.textureRebuilt += FontOnTextureRebuilt; _resText = new ResourceTextRenderer(Resources.FindObjectsOfTypeAll<Font>()); _resText.Initialize(); _dynText = new DynamicTextRenderer(); _dynText.Initialize(); } private void OnDestroy() { Font.textureRebuilt -= FontOnTextureRebuilt; } private void FontOnTextureRebuilt(Font font) { Log.Warning("Font rebuilt: {0}; {1}", font.name, string.Join(",", font.fontNames)); } private void OnGUI() { var origSkin = GUI.skin; _dynText.DrawWindow(); GUI.skin = origSkin; _resText.DrawWindow(); } }
  10. You could get the instance IDs of textures, too. This would fail if they update their image by changing UVs. But why copy the texture? You can just grab a reference to it and use it directly. I thought you were only interested in the buttons themselves, which contain everything you need to render a duplicate version (if I understand what you're attempting to do correctly) by themselves. That's not the expensive call. If the texture is readable, a copy of it exists in system memory so Unity won't stall the GPU to read pixels back from it. It's ReadPixels that's terribly slow (if you haven't got any unreadable textures, this isn't hurting you so far). The problem with that one is simply that every time you call it, an array allocation will be made. Why do you need a clone of the texture? Are you modifying it later?
  11. Every UnityEngine.Object has a unique identifier you can get with GetInstanceID(). One simple fix is to look at the instance IDs of the buttons themselves. If you want max efficiency, you could eliminate the constant rechecking and comparisons entirely by setting up a little tracking MonoBehaviour that sets off an event whenever a button is created or destroyed (or whatever else you'd like) public static class ToolbarEvents { public enum ChangeType { Added, Removed } // just like GameEvents public static readonly EventData<ApplicationLauncherButton, ChangeType> ButtonChange = new EventData<ApplicationLauncherButton, ChangeType>("AppLauncher_ButtonEvent"); [KSPAddon(KSPAddon.Startup.MainMenu, true)] private class Install : MonoBehaviour { private IEnumerator Start() { while (ApplicationLauncher.Instance == null) yield return null; var al = ApplicationLauncher.Instance; ButtonEventDispatcher.AddToPrefab(al.listItemPrefab); foreach (var b in al.GetComponentsInChildren<ApplicationLauncherButton>(true)) ButtonChange.Fire(b, ChangeType.Added); Destroy(gameObject); } } private class ButtonEventDispatcher : MonoBehaviour { [SerializeField] private ApplicationLauncherButton _ourButton; // Unity instantiation serialization will make sure this points // to the live button when created private static ApplicationLauncherButton _buttonPrefab; // this used to prevent Unity from unloading prefab from memory // should it stop being referenced at some point public static void AddToPrefab(ApplicationLauncherButton prefab) { var ourPrefab = prefab.gameObject.AddComponent<ButtonEventDispatcher>(); ourPrefab._ourButton = prefab; _buttonPrefab = prefab; } private void Start() { ButtonChange.Fire(_ourButton, ChangeType.Added); } private void OnDestroy() { ButtonChange.Fire(_ourButton, ChangeType.Removed); } // can also do events for hiding/showing if desired } }
  12. This is the worst one. GetPixels32() as well. This code will also potentially be very costly to run, potentially framerate-killing, because it'll stall the graphics pipeline every time you ReadPixels. I assume the error catch here is a fallback for unreadable textures As a final punch in the pants, it'll quickly consume all available heap mem because you never destroy the Texture2D. Eventually Unity will force a cleanup and the unreferenced ones will be destroyed. What is the purpose of this code? You seem to know that you can just read a texture directly from one of the components used in ApplicationLauncher buttons. If you want to copy the texture to manipulate it, instantiate a copy instead. I don't see where it's ultimately manipulated on a first glance though
  13. Flood gates have been open for literal years. I personally never received a complaint about ScienceAlert's animated button before. Ultimately it's up to the modder @linuxgurugamerThere's an overload of the AddModApplication method that accepts an Animator. Set your button up inside Unity (Animator + Sprite Renderer, scale 100,100,100, layer "UI", renderer sorting layer "Apps", frame size 38x38). Create a prefab out of it, load it inside KSP via AssetBundle, instantiate an instance and pass it as an argument. If you need more detail, let me know and I'll put together a complete example
  14. I've seen the NullRef in ReplaceTextures before when the main texture on the flag transform renderer material wasn't set to anything (ex: mk1pod). Any unset material texture on any renderer in the part would theoretically [haven't tested it, but logically] cause that method to throw if you attempted to replace any of the part's textures, unwinding all the way back to PartLoader and causing the part to fail to compile. If you have a zip of this particular part somewhere, it would be helpful to track down whatever the problem is if it isn't the unset texture thing