mirror of
https://github.com/muskit/H3VR-TNH-Quality-of-Life-Improvements.git
synced 2026-06-02 20:24:26 -07:00
Initial commit
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_PrefabParentObject: {fileID: 0}
|
||||
m_PrefabInternal: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: afed54a239594b8abd631a8047a54987, type: 3}
|
||||
m_Name: BuildProfile
|
||||
m_EditorClassIdentifier:
|
||||
PackageName: TNH_Quality_of_Life_Improvements
|
||||
Author: muskit
|
||||
Version: 1.0.0
|
||||
Icon: {fileID: 2800000, guid: f999bddab1821b94691466e97cc6891f, type: 3}
|
||||
ReadMe: {fileID: 102900000, guid: ab1d6dea017447a48ac348db588a6f35, type: 3}
|
||||
WebsiteURL:
|
||||
Description: Quality of life improvements to the Take and Hold experience.
|
||||
AdditionalDependencies:
|
||||
- BepInEx-BepInExPack_H3VR-5.4.1700
|
||||
StripNamespaces: 1
|
||||
AdditionalNamespaces:
|
||||
- TNHQoLImprovements
|
||||
BuildItems:
|
||||
- {fileID: 11400000, guid: ccaff18373cd99848b344316974e6d46, type: 2}
|
||||
BundleCompressionType: 2
|
||||
BuildAction: 2
|
||||
OutputProfile:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b545caf63c55b0143bd5c4ce2e5eef7c
|
||||
timeCreated: 1642332594
|
||||
licenseType: Free
|
||||
NativeFormatImporter:
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 875f8d04396f408f8bd11e474bb41f3a
|
||||
timeCreated: 1617325310
|
||||
@@ -0,0 +1,126 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
|
||||
namespace MeatKit
|
||||
{
|
||||
public static partial class MeatKit
|
||||
{
|
||||
private const string EditorAssemblyPath = "Library/ScriptAssemblies/";
|
||||
public const string BundleOutputPath = "AssetBundles/";
|
||||
|
||||
private static void ExportEditorAssembly(string folder)
|
||||
{
|
||||
if (!File.Exists(EditorAssemblyPath + AssemblyName + ".dll")) return;
|
||||
|
||||
// Delete the old file
|
||||
var settings = BuildWindow.SelectedProfile;
|
||||
var exportPath = folder + settings.PackageName + ".dll";
|
||||
if (File.Exists(exportPath)) File.Delete(exportPath);
|
||||
|
||||
var rParams = new ReaderParameters
|
||||
{
|
||||
AssemblyResolver =
|
||||
new RedirectedAssemblyResolver(Path.GetDirectoryName(typeof(UnityEngine.Object).Assembly.Location))
|
||||
};
|
||||
|
||||
// Get the MeatKitPlugin class and rename it
|
||||
string tempFile = Path.GetTempFileName();
|
||||
File.Copy(EditorAssemblyPath + AssemblyName + ".dll", tempFile, true);
|
||||
using (var asm = AssemblyDefinition.ReadAssembly(tempFile, rParams))
|
||||
{
|
||||
var plugin = asm.MainModule.GetType("MeatKitPlugin");
|
||||
plugin.Name = settings.PackageName + "Plugin";
|
||||
|
||||
// This is some quantum bullshit.
|
||||
// If you don't enumerate the constructor arguments for attributes their values aren't updated correctly.
|
||||
foreach (var x in GetAllCustomAttributes(asm).SelectMany(a => a.ConstructorArguments))
|
||||
{
|
||||
}
|
||||
|
||||
// Get the BepInPlugin attribute and replace the values in it with our own
|
||||
var str = asm.MainModule.TypeSystem.String;
|
||||
var guid = settings.Author + "." + settings.PackageName;
|
||||
var pluginAttribute = plugin.CustomAttributes.First(a => a.AttributeType.Name == "BepInPlugin");
|
||||
pluginAttribute.ConstructorArguments[0] = new CustomAttributeArgument(str, guid);
|
||||
pluginAttribute.ConstructorArguments[1] = new CustomAttributeArgument(str, settings.PackageName);
|
||||
pluginAttribute.ConstructorArguments[2] = new CustomAttributeArgument(str, settings.Version);
|
||||
|
||||
// Get the LoadAssets method and make a new body for it
|
||||
var loadAssetsMethod = plugin.Methods.First(m => m.Name == "LoadAssets");
|
||||
loadAssetsMethod.Body = new MethodBody(loadAssetsMethod);
|
||||
var il = loadAssetsMethod.Body.GetILProcessor();
|
||||
|
||||
// Let any build items insert their own code in here
|
||||
foreach (var item in settings.BuildItems)
|
||||
item.GenerateLoadAssets(plugin, il);
|
||||
|
||||
// Insert a ret at the end so it's valid
|
||||
il.Emit(OpCodes.Ret);
|
||||
|
||||
// Module name needs to be changed away from Assembly-CSharp.dll because it is a reserved name.
|
||||
asm.Name = new AssemblyNameDefinition(settings.PackageName, asm.Name.Version);
|
||||
asm.MainModule.Name = settings.PackageName + ".dll";
|
||||
|
||||
// References to renamed unity code must be swapped out.
|
||||
foreach (var ii in asm.MainModule.AssemblyReferences)
|
||||
switch (ii.Name)
|
||||
{
|
||||
case AssemblyRename:
|
||||
ii.Name = AssemblyName;
|
||||
break;
|
||||
case AssemblyFirstpassRename:
|
||||
ii.Name = AssemblyFirstpassName;
|
||||
break;
|
||||
}
|
||||
|
||||
if (BuildWindow.SelectedProfile.StripNamespaces)
|
||||
{
|
||||
// Remove types not in an allowed namespace or the global namespace
|
||||
string[] allowedNamespaces = BuildWindow.SelectedProfile.GetAllAllowedNamespaces();
|
||||
List<TypeDefinition> typesToRemove = new List<TypeDefinition>();
|
||||
foreach (var type in asm.MainModule.Types)
|
||||
{
|
||||
if (type.Namespace == "" || allowedNamespaces.Any(x => type.Namespace.Contains(x)))
|
||||
continue;
|
||||
typesToRemove.Add(type);
|
||||
}
|
||||
|
||||
foreach (var type in typesToRemove) asm.MainModule.Types.Remove(type);
|
||||
}
|
||||
|
||||
// Remove the same types we didn't want to import. This cannot be skipped.
|
||||
foreach (var type in StripAssemblyTypes
|
||||
.Select(x => asm.MainModule.GetType(x))
|
||||
.Where(x => x != null))
|
||||
asm.MainModule.Types.Remove(type);
|
||||
|
||||
try
|
||||
{
|
||||
// Save it
|
||||
asm.Write(exportPath);
|
||||
}
|
||||
catch (ArgumentException e)
|
||||
{
|
||||
throw new MeatKitBuildException("Unable to write exported scripts file. This is likely due to namespace stripping being enabled and a required namespace is not whitelisted.", e);
|
||||
}
|
||||
}
|
||||
|
||||
// Delete temp file now that we're done.
|
||||
File.Delete(tempFile);
|
||||
}
|
||||
|
||||
private static IEnumerable<CustomAttribute> GetAllCustomAttributes(AssemblyDefinition asm)
|
||||
{
|
||||
foreach (var type in asm.MainModule.Types)
|
||||
{
|
||||
foreach (var attrib in type.CustomAttributes) yield return attrib;
|
||||
foreach (CustomAttribute attrib in type.Methods.SelectMany(method => method.CustomAttributes))
|
||||
yield return attrib;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0c6f57f9c2dd40b8a7eabb4f85e2e283
|
||||
timeCreated: 1617344435
|
||||
@@ -0,0 +1,260 @@
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using Mono.Cecil;
|
||||
using NStrip;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MeatKit
|
||||
{
|
||||
/// <summary>
|
||||
/// Assembly importer class to get the managed assemblies from the game into the Unity editor without
|
||||
/// the editor wanting to crash itself. Original implementation by Nolenz.
|
||||
/// https://github.com/WurstModders/WurstMod-Reloaded/blob/2e33e83284b3a9f39c8df210ad907925d1d7d9d8/WMRWorkbench/Assets/Editor/Manglers/AssemblyMangler.cs
|
||||
/// </summary>
|
||||
public static partial class MeatKit
|
||||
{
|
||||
private const string AssemblyName = "Assembly-CSharp";
|
||||
private const string AssemblyRename = "H3VRCode-CSharp";
|
||||
private const string AssemblyFirstpassName = "Assembly-CSharp-firstpass";
|
||||
private const string AssemblyFirstpassRename = "H3VRCode-CSharp-passfirst";
|
||||
|
||||
// Types we want to strip from the main Unity assembly
|
||||
private static readonly string[] StripAssemblyTypes =
|
||||
{
|
||||
// Alloy classes
|
||||
"MaterialMapChannelPackerDefinition",
|
||||
"Alloy.PackedMapDefinition",
|
||||
"Alloy.BaseTextureChannelMapping",
|
||||
"Alloy.MapChannel",
|
||||
"Alloy.TextureValueChannelMode",
|
||||
"Alloy.NormalMapChannelTextureChannelMapping",
|
||||
"Alloy.TextureImportConfig",
|
||||
"Alloy.MapTextureChannelMapping",
|
||||
"AlloyUtils",
|
||||
"Alloy.EnumExtension",
|
||||
"MinValueAttribute",
|
||||
"MaxValueAttribute",
|
||||
"AlloyEffectsManager",
|
||||
"Alloy.EnumFlagsAttribute",
|
||||
|
||||
// Bakery MonoBehaviours
|
||||
"BakeryAlwaysRender",
|
||||
"BakeryDirectLight",
|
||||
"BakeryLightmapGroup",
|
||||
"BakeryLightmapGroupSelector",
|
||||
"BakeryLightmappedPrefab",
|
||||
"BakeryLightMesh",
|
||||
"BakeryPointLight",
|
||||
"BakerySkyLight",
|
||||
"BakeryVolume",
|
||||
"BakeryVolumeReceiver",
|
||||
"BakeryVolumeTrigger",
|
||||
"BakeryProjectSettings",
|
||||
"VolumeTestScene2",
|
||||
"BakeryPackAsSingleSquare",
|
||||
"BakerySector",
|
||||
"BakerySectorCapture",
|
||||
"ftGlobalStorage",
|
||||
"ftLightmaps",
|
||||
"ftLightmapsStorage",
|
||||
"ftLocalStorage",
|
||||
|
||||
// Bakery supporting types
|
||||
"ftUniqueIDRegistry",
|
||||
"BakeryLightmapGroupPlain",
|
||||
|
||||
//Editor Tool Scripts
|
||||
"IconCamera"
|
||||
};
|
||||
|
||||
// Array of the extra assemblies that need to come with the main Unity assemblies
|
||||
private static readonly string[] ExtraAssemblies =
|
||||
{
|
||||
"DinoFracture.dll",
|
||||
"ES2.dll"
|
||||
};
|
||||
|
||||
|
||||
private static void ImportAssemblies(string assembliesDirectory, string destinationDirectory)
|
||||
{
|
||||
// Remove whatever was there before and make the folder again
|
||||
if (!Directory.Exists(destinationDirectory)) Directory.CreateDirectory(destinationDirectory);
|
||||
|
||||
// Load all of our modifiers
|
||||
var editors = Extensions.GetAllInstances<AssemblyModifier>();
|
||||
foreach (var editor in editors) editor.Applied = false;
|
||||
|
||||
// We need a custom assembly resolver that sometimes points to different directories.
|
||||
var rParams = new ReaderParameters
|
||||
{
|
||||
AssemblyResolver = new RedirectedAssemblyResolver(assembliesDirectory, destinationDirectory)
|
||||
};
|
||||
|
||||
// Rename the game's firstpass assembly
|
||||
{
|
||||
var firstpassAssembly =
|
||||
AssemblyDefinition.ReadAssembly(Path.Combine(assembliesDirectory, AssemblyFirstpassName + ".dll"));
|
||||
firstpassAssembly.Name =
|
||||
new AssemblyNameDefinition(AssemblyFirstpassRename, firstpassAssembly.Name.Version);
|
||||
firstpassAssembly.MainModule.Name = AssemblyFirstpassRename + ".dll";
|
||||
|
||||
// Apply modifications
|
||||
foreach (var editor in editors) editor.ApplyModification(firstpassAssembly);
|
||||
|
||||
// Publicize Assembly
|
||||
AssemblyStripper.MakePublic(firstpassAssembly, new string[0], false, false);
|
||||
|
||||
firstpassAssembly.Write(Path.Combine(destinationDirectory, AssemblyFirstpassRename + ".dll"));
|
||||
firstpassAssembly.Dispose();
|
||||
}
|
||||
|
||||
// Main assembly
|
||||
{
|
||||
// Rename the main assembly
|
||||
var mainAssembly =
|
||||
AssemblyDefinition.ReadAssembly(Path.Combine(assembliesDirectory, AssemblyName + ".dll"), rParams);
|
||||
mainAssembly.Name = new AssemblyNameDefinition(AssemblyRename, mainAssembly.Name.Version);
|
||||
mainAssembly.MainModule.Name = AssemblyRename + ".dll";
|
||||
|
||||
// Change the firstpass reference in this assembly
|
||||
mainAssembly.MainModule.AssemblyReferences
|
||||
.First(x => x.Name == AssemblyFirstpassName)
|
||||
.Name = AssemblyFirstpassRename;
|
||||
|
||||
// Strip some types from the assembly to prevent doubles in the editor
|
||||
foreach (var typename in StripAssemblyTypes)
|
||||
{
|
||||
var type = mainAssembly.MainModule.GetType(typename);
|
||||
if (type != null) mainAssembly.MainModule.Types.Remove(type);
|
||||
else Debug.LogWarning("Type " + typename + " was not found in assembly.");
|
||||
}
|
||||
|
||||
// Apply modifications
|
||||
foreach (var editor in editors) editor.ApplyModification(mainAssembly);
|
||||
|
||||
// Publicize assembly
|
||||
AssemblyStripper.MakePublic(mainAssembly, new string[0], false, false);
|
||||
|
||||
// Write the main assembly out into the destination folder and dispose it
|
||||
mainAssembly.Write(Path.Combine(destinationDirectory, AssemblyRename + ".dll"));
|
||||
}
|
||||
|
||||
// Then lastly copy the other assemblies to the destination folder
|
||||
foreach (var file in ExtraAssemblies)
|
||||
{
|
||||
var path = Path.Combine(assembliesDirectory, file);
|
||||
if (File.Exists(path))
|
||||
ImportSingleAssembly(path, destinationDirectory);
|
||||
}
|
||||
|
||||
// Check if anything didn't apply
|
||||
foreach (var editor in editors)
|
||||
if (!editor.Applied)
|
||||
Debug.LogWarning(editor.name + " was not applied while importing.", editor);
|
||||
|
||||
// When we're done importing assemblies, let Unity refresh the asset database
|
||||
PlayerSettings.SetScriptingDefineSymbolsForGroup(BuildTargetGroup.Standalone, "H3VR_IMPORTED");
|
||||
NormalizeMetaFileGUIDs();
|
||||
}
|
||||
|
||||
private static void ImportSingleAssembly(string assemblyPath, string destinationDirectory)
|
||||
{
|
||||
var rParams = new ReaderParameters
|
||||
{
|
||||
AssemblyResolver =
|
||||
new RedirectedAssemblyResolver(Path.GetDirectoryName(assemblyPath), destinationDirectory)
|
||||
};
|
||||
|
||||
// We just want to rename the references to the game's assemblies here
|
||||
var asm = AssemblyDefinition.ReadAssembly(assemblyPath, rParams);
|
||||
foreach (var reference in asm.MainModule.AssemblyReferences)
|
||||
switch (reference.Name)
|
||||
{
|
||||
case AssemblyName:
|
||||
reference.Name = AssemblyRename;
|
||||
break;
|
||||
case AssemblyFirstpassName:
|
||||
reference.Name = AssemblyFirstpassRename;
|
||||
break;
|
||||
}
|
||||
|
||||
asm.Write(Path.Combine(destinationDirectory, Path.GetFileName(assemblyPath)));
|
||||
NormalizeMetaFileGUIDs();
|
||||
}
|
||||
|
||||
private static void NormalizeMetaFileGUIDs()
|
||||
{
|
||||
// This is a really important step. We need to make sure that the meta files for the assemblies are generated
|
||||
// WITH THE SAME GUIDs each time. Otherwise, if you lose one and didn't have a backup, all your scripts will be missing
|
||||
// and that is of course no bueno. Unity expects 32 hexadecimal digits for the guid so we'll use md5.
|
||||
|
||||
// We need every meta file to exist already.
|
||||
AssetDatabase.Refresh();
|
||||
|
||||
var hashFunction = MD5.Create();
|
||||
var replaceWith = new Regex(@"^guid: [0-9a-f]{32}$", RegexOptions.Multiline);
|
||||
|
||||
foreach (var metaFile in Directory.GetFiles(ManagedDirectory, "*.meta"))
|
||||
{
|
||||
// First we get the hash
|
||||
var assemblyName = Path.GetFileName(metaFile.Substring(0, metaFile.Length - 5));
|
||||
var hash = hashFunction.ComputeHash(Encoding.UTF8.GetBytes(assemblyName));
|
||||
var hexHash = Extensions.ByteArrayToString(hash).ToLower();
|
||||
|
||||
// Then we need to replace the hash in the meta file with it.
|
||||
var metaText = File.ReadAllText(metaFile);
|
||||
metaText = replaceWith.Replace(metaText, "guid: " + hexHash);
|
||||
File.WriteAllText(metaFile, metaText);
|
||||
}
|
||||
|
||||
// If anything was changed we need Unity to apply it immediately.
|
||||
AssetDatabase.Refresh();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assembly resolver that redirects references to another path if not found.
|
||||
/// </summary>
|
||||
private class RedirectedAssemblyResolver : BaseAssemblyResolver
|
||||
{
|
||||
private readonly DefaultAssemblyResolver _defaultResolver = new DefaultAssemblyResolver();
|
||||
private readonly string[] _redirectPaths;
|
||||
|
||||
public RedirectedAssemblyResolver(params string[] redirectPath)
|
||||
{
|
||||
_redirectPaths = redirectPath;
|
||||
}
|
||||
|
||||
public override AssemblyDefinition Resolve(AssemblyNameReference name)
|
||||
{
|
||||
AssemblyDefinition asm = null;
|
||||
try
|
||||
{
|
||||
asm = _defaultResolver.Resolve(name);
|
||||
}
|
||||
catch (AssemblyResolutionException)
|
||||
{
|
||||
foreach (var path in _redirectPaths)
|
||||
try
|
||||
{
|
||||
var asmPath = Path.Combine(path, name.Name + ".dll");
|
||||
Debug.Log("Assembly path: " + asmPath);
|
||||
if (File.Exists(asmPath))
|
||||
asm = AssemblyDefinition.ReadAssembly(asmPath,
|
||||
new ReaderParameters {AssemblyResolver = this});
|
||||
}
|
||||
catch (AssemblyResolutionException)
|
||||
{
|
||||
// Ignored
|
||||
}
|
||||
}
|
||||
|
||||
if (asm != null) return asm;
|
||||
throw new AssemblyResolutionException(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4d2f661647864f0689e2c38c7c687c99
|
||||
timeCreated: 1617325184
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 49df45b608024e5eb33594ddd1200d89
|
||||
timeCreated: 1628213725
|
||||
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using Mono.Cecil;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MeatKit
|
||||
{
|
||||
public abstract class AssemblyModifier : ScriptableObject
|
||||
{
|
||||
[NonSerialized]
|
||||
public bool Applied = false;
|
||||
|
||||
public abstract void ApplyModification(AssemblyDefinition assembly);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c7e21420a1d84ca5947b2fd9eb9db6d8
|
||||
timeCreated: 1628213934
|
||||
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using Mono.Cecil;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MeatKit
|
||||
{
|
||||
[CreateAssetMenu(menuName = "MeatKit/Assembly Editors/Enum", fileName = "New Enum Editor")]
|
||||
public class EnumModifier : AssemblyModifier
|
||||
{
|
||||
private const FieldAttributes Attributes =
|
||||
FieldAttributes.Static | FieldAttributes.Literal | FieldAttributes.Public | FieldAttributes.HasDefault;
|
||||
|
||||
[Tooltip("Specify the FULL NAME of the enum you want to change. e.g. Sub.Namespace.Type")]
|
||||
public string EnumName = "FistVR.FireArmRoundType";
|
||||
|
||||
[Tooltip("The new values you want to add to this enum")]
|
||||
public EnumValue[] AddedValues = new EnumValue[0];
|
||||
|
||||
public override void ApplyModification(AssemblyDefinition assembly)
|
||||
{
|
||||
// Try to get this type from the assembly. If it doesn't exist, we can just skip.
|
||||
TypeReference type = assembly.MainModule.GetType(EnumName);
|
||||
if (type == null) return;
|
||||
|
||||
var definition = type.Resolve();
|
||||
if (!definition.IsEnum)
|
||||
{
|
||||
Debug.LogError(EnumName + " is not an enum type!", this);
|
||||
Applied = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Add the new enum value to the type
|
||||
foreach (var value in AddedValues)
|
||||
definition.Fields.Add(new FieldDefinition(value.Name, Attributes, definition) {Constant = value.Value});
|
||||
|
||||
Applied = true;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public struct EnumValue
|
||||
{
|
||||
[Tooltip("The name of the new enum value")]
|
||||
public string Name;
|
||||
|
||||
[Tooltip("The new enum value")]
|
||||
public int Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 486e27304e8449efb6c6136482952c4d
|
||||
timeCreated: 1628213734
|
||||
@@ -0,0 +1,62 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using UnityEditor;
|
||||
|
||||
namespace MeatKit
|
||||
{
|
||||
[CustomEditor(typeof(EnumModifier))]
|
||||
public class EnumModifierEditor : Editor
|
||||
{
|
||||
private static readonly string[] CommonTypes =
|
||||
{
|
||||
"FistVR.FireArmRoundType",
|
||||
"FistVR.FireArmRoundClass",
|
||||
"FistVR.FireArmMagazineType",
|
||||
"FistVR.ItemSpawnerObjectDefinition.ItemSpawnerCategory",
|
||||
"FistVR.SosigEnemyID"
|
||||
};
|
||||
|
||||
private SerializedProperty _addedValues;
|
||||
private EnumModifier _enumModifier;
|
||||
private bool _isCustomType;
|
||||
private int _selectedType;
|
||||
|
||||
// Called when an object of this type is selected
|
||||
private void OnEnable()
|
||||
{
|
||||
// Get our properties and check if this is a common type
|
||||
_addedValues = serializedObject.FindProperty("AddedValues");
|
||||
_enumModifier = (EnumModifier) target;
|
||||
_selectedType = Array.IndexOf(CommonTypes, _enumModifier.EnumName);
|
||||
_isCustomType = _selectedType == -1 || string.IsNullOrEmpty(_enumModifier.EnumName);
|
||||
}
|
||||
|
||||
// Called to draw the inspector GUI
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
// I'll be real I have no idea what this does but the Unity docs had it so I'm not gonna mess with it
|
||||
serializedObject.Update();
|
||||
|
||||
// Use a toggle (checkbox) to determine if we're using a custom type or a commonly used one from the array
|
||||
_isCustomType = EditorGUILayout.Toggle("Custom type", _isCustomType);
|
||||
if (_isCustomType)
|
||||
_enumModifier.EnumName = EditorGUILayout.TextField("Enum name", _enumModifier.EnumName);
|
||||
else
|
||||
{
|
||||
if (_selectedType < 0 || _selectedType >= CommonTypes.Length) _selectedType = 0;
|
||||
_selectedType = EditorGUILayout.Popup("Type", _selectedType, CommonTypes);
|
||||
_enumModifier.EnumName = CommonTypes[_selectedType];
|
||||
}
|
||||
|
||||
// Draw the values field and then save the object
|
||||
EditorGUILayout.PropertyField(_addedValues, true);
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
|
||||
// Suggest to the user that all added enums should be negative
|
||||
if (_enumModifier.AddedValues.Any(x => x.Value >= 0))
|
||||
EditorGUILayout.HelpBox(
|
||||
"Your added enum values should be negative to avoid conflicts with vanilla items.",
|
||||
MessageType.Warning);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4980597d341c4eed913ae30680f3f9f3
|
||||
timeCreated: 1628218472
|
||||
@@ -0,0 +1,138 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Ionic.Zip;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using Debug = UnityEngine.Debug;
|
||||
|
||||
namespace MeatKit
|
||||
{
|
||||
public partial class MeatKit
|
||||
{
|
||||
public static void DoBuild()
|
||||
{
|
||||
try
|
||||
{
|
||||
DoBuildInternal();
|
||||
}
|
||||
catch (MeatKitBuildException e)
|
||||
{
|
||||
string message = e.Message;
|
||||
if (e.InnerException != null) message += "\n\n" + e.InnerException.Message;
|
||||
EditorUtility.DisplayDialog("Build failed", message, "Ok.");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
EditorUtility.DisplayDialog("Build failed with unknown error",
|
||||
"Error message: " + e.Message + "\n\nCheck console for full exception text.", "Ok.");
|
||||
Debug.LogException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void DoBuildInternal()
|
||||
{
|
||||
// Make sure the scripts are imported.
|
||||
if (ShowErrorIfH3VRNotImported()) return;
|
||||
|
||||
// Get our profile and make sure it isn't null
|
||||
BuildProfile profile = BuildWindow.SelectedProfile;
|
||||
if (!profile) return;
|
||||
|
||||
// Start a stopwatch to time the build
|
||||
Stopwatch sw = Stopwatch.StartNew();
|
||||
|
||||
// If there's anything invalid in the settings don't continue
|
||||
if (!profile.EnsureValidForEditor()) return;
|
||||
|
||||
// Clean the output folder
|
||||
CleanBuild();
|
||||
|
||||
// And export the assembly to the folder
|
||||
ExportEditorAssembly(BundleOutputPath);
|
||||
|
||||
// Then get their asset bundle configurations
|
||||
var bundles = profile.BuildItems.SelectMany(x => x.ConfigureBuild()).ToArray();
|
||||
|
||||
BuildPipeline.BuildAssetBundles(BundleOutputPath, bundles, BuildAssetBundleOptions.None,
|
||||
BuildTarget.StandaloneWindows64);
|
||||
|
||||
// Cleanup the unused files created with building the bundles
|
||||
foreach (var file in Directory.GetFiles(BundleOutputPath, "*.manifest"))
|
||||
File.Delete(file);
|
||||
File.Delete(Path.Combine(BundleOutputPath, "AssetBundles"));
|
||||
|
||||
// With the bundles done building we can process them
|
||||
var replaceMap = new Dictionary<string, string>
|
||||
{
|
||||
{"Assembly-CSharp.dll", profile.PackageName + ".dll"},
|
||||
{"Assembly-CSharp-firstpass.dll", profile.PackageName + "-firstpass.dll"},
|
||||
{"H3VRCode-CSharp.dll", "Assembly-CSharp.dll"},
|
||||
{"H3VRCode-CSharp-firstpass.dll", "Assembly-CSharp-firstpass.dll"}
|
||||
};
|
||||
|
||||
foreach (var bundle in bundles)
|
||||
{
|
||||
var path = Path.Combine(BundleOutputPath, bundle.assetBundleName);
|
||||
ProcessBundle(path, path, replaceMap, profile.BundleCompressionType);
|
||||
}
|
||||
|
||||
// Now we can write the Thunderstore stuff to the folder
|
||||
profile.WriteThunderstoreManifest(BundleOutputPath + "manifest.json");
|
||||
|
||||
// Check if the icon is already 256x256
|
||||
Texture2D icon = profile.Icon;
|
||||
|
||||
// Make sure our icon is marked as readable
|
||||
var importSettings = (TextureImporter) AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(profile.Icon));
|
||||
if (!importSettings.isReadable ||
|
||||
importSettings.textureCompression != TextureImporterCompression.Uncompressed)
|
||||
{
|
||||
importSettings.isReadable = true;
|
||||
importSettings.textureCompression = TextureImporterCompression.Uncompressed;
|
||||
importSettings.SaveAndReimport();
|
||||
}
|
||||
|
||||
if (profile.Icon.width != 256 || profile.Icon.height != 256)
|
||||
{
|
||||
// Resize it for the build
|
||||
icon = icon.ScaleTexture(256, 256);
|
||||
}
|
||||
|
||||
// Write the texture to file
|
||||
File.WriteAllBytes(BundleOutputPath + "icon.png", icon.EncodeToPNG());
|
||||
|
||||
// Copy the readme
|
||||
File.Copy(AssetDatabase.GetAssetPath(profile.ReadMe), BundleOutputPath + "README.md");
|
||||
|
||||
string packageName = profile.Author + "-" + profile.PackageName;
|
||||
if (profile.BuildAction == BuildAction.CopyToProfile)
|
||||
{
|
||||
string pluginFolder = Path.Combine(profile.OutputProfile, "BepInEx/plugins/" + packageName);
|
||||
if (Directory.Exists(pluginFolder)) Directory.Delete(pluginFolder, true);
|
||||
Directory.CreateDirectory(pluginFolder);
|
||||
Extensions.CopyFilesRecursively(BundleOutputPath, pluginFolder);
|
||||
}
|
||||
else if (profile.BuildAction == BuildAction.CreateThunderstorePackage)
|
||||
{
|
||||
using (var zip = new ZipFile())
|
||||
{
|
||||
zip.AddDirectory(BundleOutputPath, "");
|
||||
zip.Save(Path.Combine(BundleOutputPath, packageName + ".zip"));
|
||||
}
|
||||
}
|
||||
|
||||
// End the stopwatch and save the time
|
||||
MeatKitCache.LastBuildDuration = sw.Elapsed;
|
||||
MeatKitCache.LastBuildTime = DateTime.Now;
|
||||
}
|
||||
|
||||
public static void CleanBuild()
|
||||
{
|
||||
if (Directory.Exists(BundleOutputPath)) Directory.Delete(BundleOutputPath, true);
|
||||
Directory.CreateDirectory(BundleOutputPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 918df2720ddc4655b198835c566d115a
|
||||
timeCreated: 1639093595
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 70638cf8eda1487ba7a232bb4377a23c
|
||||
folderAsset: yes
|
||||
timeCreated: 1635003658
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,51 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using BepInEx;
|
||||
using Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MeatKit
|
||||
{
|
||||
public abstract class BuildItem : ScriptableObject, IValidatable
|
||||
{
|
||||
public abstract IEnumerable<string> RequiredDependencies { get; }
|
||||
|
||||
public virtual Dictionary<string, BuildMessage> Validate()
|
||||
{
|
||||
return new Dictionary<string, BuildMessage>();
|
||||
}
|
||||
|
||||
public virtual List<AssetBundleBuild> ConfigureBuild()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public virtual void GenerateLoadAssets(TypeDefinition plugin, ILProcessor il)
|
||||
{
|
||||
}
|
||||
|
||||
protected void EnsurePluginDependsOn(TypeDefinition plugin, string pluginGuid, string pluginVersion)
|
||||
{
|
||||
// Check if the plugin already has this dependency
|
||||
var alreadyDependsOn = plugin.CustomAttributes
|
||||
.Where(a => a.AttributeType.Name == "BepInDependency")
|
||||
.Any(attribute => (string) attribute.ConstructorArguments[0].Value == pluginGuid);
|
||||
|
||||
// If it doesn't we need to add it.
|
||||
if (!alreadyDependsOn)
|
||||
{
|
||||
MethodBase constructor =
|
||||
typeof(BepInDependency).GetConstructor(new[] {typeof(string), typeof(string)});
|
||||
var attribute = new CustomAttribute(plugin.Module.ImportReference(constructor));
|
||||
plugin.CustomAttributes.Add(attribute);
|
||||
|
||||
var str = plugin.Module.TypeSystem.String;
|
||||
attribute.ConstructorArguments.Add(new CustomAttributeArgument(str, pluginGuid));
|
||||
attribute.ConstructorArguments.Add(new CustomAttributeArgument(str, pluginVersion));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 32dfb407962b465197e145b9cf760fcf
|
||||
timeCreated: 1635010462
|
||||
@@ -0,0 +1,70 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MeatKit
|
||||
{
|
||||
[CustomEditor(typeof(BuildItem), true)]
|
||||
public class BuildItemEditor : Editor
|
||||
{
|
||||
protected Dictionary<string, BuildMessage> ValidationMessages;
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
// Apply any changes and validate them
|
||||
ValidationMessages = ((IValidatable) target).Validate();
|
||||
|
||||
// Draw the property fields and their message boxes, if any.
|
||||
var property = serializedObject.GetIterator();
|
||||
if (!property.NextVisible(true)) return;
|
||||
do DrawProperty(property);
|
||||
while (property.NextVisible(false));
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
|
||||
protected virtual void DrawProperty(SerializedProperty property)
|
||||
{
|
||||
// Don't draw the script name window.
|
||||
if (property.name == "m_Script") return;
|
||||
|
||||
EditorGUILayout.PropertyField(property, true);
|
||||
DrawMessageIfExists(property.name);
|
||||
}
|
||||
|
||||
protected void DrawMessageIfExists(string propertyName)
|
||||
{
|
||||
BuildMessage message;
|
||||
if (ValidationMessages.TryGetValue(propertyName, out message))
|
||||
EditorGUILayout.HelpBox(message.Message, message.Type);
|
||||
}
|
||||
|
||||
protected void DrawListWithRequiredElements(string[] required, SerializedProperty additional)
|
||||
{
|
||||
EditorGUI.indentLevel++;
|
||||
|
||||
// Draw the size field
|
||||
var size = EditorGUILayout.DelayedIntField("Size", required.Length + additional.arraySize);
|
||||
size = Mathf.Max(required.Length, size);
|
||||
|
||||
// Resize the array if necessary
|
||||
var newSize = size - required.Length;
|
||||
|
||||
if (newSize != additional.arraySize) additional.arraySize = newSize;
|
||||
|
||||
// Draw the required dependencies. These are disabled.
|
||||
EditorGUI.BeginDisabledGroup(true);
|
||||
foreach (var dep in required)
|
||||
EditorGUILayout.TextField(dep);
|
||||
EditorGUI.EndDisabledGroup();
|
||||
|
||||
// Draw the additional dependencies
|
||||
for (var i = 0; i < additional.arraySize; i++)
|
||||
{
|
||||
var value = additional.GetArrayElementAtIndex(i);
|
||||
value.stringValue = EditorGUILayout.TextField(value.stringValue);
|
||||
}
|
||||
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ebaaff1430114c54b502477e745a02c2
|
||||
timeCreated: 1635011555
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 131929050e124316a330bb2af8cf8f94
|
||||
timeCreated: 1637507648
|
||||
@@ -0,0 +1,56 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
using Sodalite;
|
||||
using Sodalite.Api;
|
||||
|
||||
namespace MeatKit
|
||||
{
|
||||
[CreateAssetMenu(menuName = "MeatKit/Build Items/Preload Assets", fileName = "New build item")]
|
||||
public class PreloadAssetsBuildItem : BuildItem
|
||||
{
|
||||
public Object[] Items;
|
||||
|
||||
public override IEnumerable<string> RequiredDependencies
|
||||
{
|
||||
get { return new[] {"nrgill28-Sodalite-1.2.0"}; }
|
||||
}
|
||||
|
||||
public override List<AssetBundleBuild> ConfigureBuild()
|
||||
{
|
||||
List<AssetBundleBuild> bundles = new List<AssetBundleBuild>();
|
||||
|
||||
bundles.Add(new AssetBundleBuild
|
||||
{
|
||||
assetBundleName = BuildWindow.SelectedProfile.PackageName.ToLower() + "_preload",
|
||||
assetNames = Items.Select(AssetDatabase.GetAssetPath).ToArray()
|
||||
});
|
||||
|
||||
return bundles;
|
||||
}
|
||||
|
||||
public override void GenerateLoadAssets(TypeDefinition plugin, ILProcessor il)
|
||||
{
|
||||
#if H3VR_IMPORTED
|
||||
EnsurePluginDependsOn(plugin, SodaliteConstants.Guid, SodaliteConstants.Version);
|
||||
|
||||
// Get some references
|
||||
const BindingFlags publicStatic = BindingFlags.Public | BindingFlags.Static;
|
||||
FieldReference basePath = plugin.Fields.First(f => f.Name == "BasePath");
|
||||
MethodInfo pathCombine = typeof(Path).GetMethod("Combine", publicStatic);
|
||||
MethodInfo sodalitePreloadAllAssets = typeof(GameAPI).GetMethod("PreloadAllAssets", publicStatic);
|
||||
|
||||
// Emit our opcodes
|
||||
il.Emit(OpCodes.Ldsfld, basePath);
|
||||
il.Emit(OpCodes.Ldstr, BuildWindow.SelectedProfile.PackageName.ToLower() + "_preload");
|
||||
il.Emit(OpCodes.Call, plugin.Module.ImportReference(pathCombine));
|
||||
il.Emit(OpCodes.Call, plugin.Module.ImportReference(sodalitePreloadAllAssets));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d18106ea7c52411db45cabd362f21f16
|
||||
timeCreated: 1640061580
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: 151c1f5398ee70041a49c417b23f1846, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,42 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MeatKit
|
||||
{
|
||||
[CreateAssetMenu(menuName = "MeatKit/Build Items/Store Files", fileName = "New build item")]
|
||||
public class StoreFilesBuildItem : BuildItem
|
||||
{
|
||||
public string BundleName;
|
||||
public Object[] Items;
|
||||
|
||||
public override IEnumerable<string> RequiredDependencies
|
||||
{
|
||||
get { return new string[0]; }
|
||||
}
|
||||
|
||||
public override Dictionary<string, BuildMessage> Validate()
|
||||
{
|
||||
var messages = base.Validate();
|
||||
|
||||
if (BundleName != Extensions.MakeValidFileName(BundleName))
|
||||
messages["BundleName"] = BuildMessage.Error("Bundle name contains invalid characters.");
|
||||
|
||||
return messages;
|
||||
}
|
||||
|
||||
public override List<AssetBundleBuild> ConfigureBuild()
|
||||
{
|
||||
List<AssetBundleBuild> bundles = new List<AssetBundleBuild>();
|
||||
|
||||
bundles.Add(new AssetBundleBuild
|
||||
{
|
||||
assetBundleName = BundleName,
|
||||
assetNames = Items.Select(AssetDatabase.GetAssetPath).ToArray()
|
||||
});
|
||||
|
||||
return bundles;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f6d6db4144b34551885c304840edfb62
|
||||
timeCreated: 1640061353
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: 6fae6dfd178cd184180013acef55632c, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,31 @@
|
||||
using UnityEditor;
|
||||
|
||||
namespace MeatKit
|
||||
{
|
||||
public class BuildMessage
|
||||
{
|
||||
private BuildMessage(MessageType type, string message)
|
||||
{
|
||||
Type = type;
|
||||
Message = message;
|
||||
}
|
||||
|
||||
public MessageType Type { get; private set; }
|
||||
public string Message { get; private set; }
|
||||
|
||||
public static BuildMessage Info(string message)
|
||||
{
|
||||
return new BuildMessage(MessageType.Info, message);
|
||||
}
|
||||
|
||||
public static BuildMessage Warning(string message)
|
||||
{
|
||||
return new BuildMessage(MessageType.Warning, message);
|
||||
}
|
||||
|
||||
public static BuildMessage Error(string message)
|
||||
{
|
||||
return new BuildMessage(MessageType.Error, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4bad4598a9f145a3b5163239bb6c7536
|
||||
timeCreated: 1635009317
|
||||
@@ -0,0 +1,201 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using AssetsTools.NET;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using Object = UnityEngine.Object;
|
||||
#if H3VR_IMPORTED
|
||||
using Valve.Newtonsoft.Json;
|
||||
using Valve.Newtonsoft.Json.Linq;
|
||||
#endif
|
||||
|
||||
|
||||
namespace MeatKit
|
||||
{
|
||||
[CreateAssetMenu(menuName = "MeatKit/Build Profile")]
|
||||
public class BuildProfile : ScriptableObject, IValidatable
|
||||
{
|
||||
[Header("Thunderstore Metadata")]
|
||||
public string PackageName = "";
|
||||
public string Author = "";
|
||||
public string Version = "";
|
||||
public Texture2D Icon;
|
||||
public Object ReadMe;
|
||||
public string WebsiteURL = "";
|
||||
public string Description = "";
|
||||
public string[] AdditionalDependencies = new string[0];
|
||||
|
||||
[Header("Script Options")]
|
||||
public bool StripNamespaces = true;
|
||||
public string[] AdditionalNamespaces = new string[0];
|
||||
|
||||
[Header("Export Options")]
|
||||
public BuildItem[] BuildItems = new BuildItem[0];
|
||||
public AssetBundleCompressionType BundleCompressionType = AssetBundleCompressionType.LZ4;
|
||||
public BuildAction BuildAction = BuildAction.JustBuildFiles;
|
||||
|
||||
[HideInInspector]
|
||||
public string OutputProfile = "";
|
||||
|
||||
public Dictionary<string, BuildMessage> Validate()
|
||||
{
|
||||
var messages = new Dictionary<string, BuildMessage>();
|
||||
|
||||
// Package name needs to match regex
|
||||
if (!Regex.IsMatch(PackageName, @"^[a-zA-Z_0-9]+$"))
|
||||
messages["PackageName"] =
|
||||
BuildMessage.Error("Package name can only contain letters, numbers, and underscores.");
|
||||
|
||||
// Make sure the version number is a valid x.x.x
|
||||
if (!Regex.IsMatch(Version, @"^\d+\.\d+\.\d+$"))
|
||||
messages["Version"] = BuildMessage.Error("Version number must be in format 'x.x.x'.");
|
||||
|
||||
// Description must be no longer than 250 chars
|
||||
if (Description.Length > 250)
|
||||
messages["Description"] = BuildMessage.Error("Description cannot be longer than 250 characters.");
|
||||
|
||||
// Icon must exist and be 256 x 256
|
||||
if (!Icon)
|
||||
messages["Icon"] = BuildMessage.Error("Missing icon.");
|
||||
else if (Icon.width != 256 || Icon.height != 256)
|
||||
messages["Icon"] = BuildMessage.Info("Icon will be resized to 256x256.");
|
||||
|
||||
if (!ReadMe)
|
||||
messages["ReadMe"] = BuildMessage.Error("Missing readme.");
|
||||
|
||||
switch (BundleCompressionType)
|
||||
{
|
||||
case AssetBundleCompressionType.NONE:
|
||||
messages["BundleCompressionType"] = BuildMessage.Warning(
|
||||
"Uncompressed bundles are not recommended for publication. They can and will be very large.");
|
||||
break;
|
||||
case AssetBundleCompressionType.LZMA:
|
||||
messages["BundleCompressionType"] = BuildMessage.Info(
|
||||
"LZMA can take longer to compress than LZ4, however it will result in smaller file sizes usually.");
|
||||
break;
|
||||
}
|
||||
|
||||
switch (BuildAction)
|
||||
{
|
||||
case BuildAction.JustBuildFiles:
|
||||
messages["BuildAction"] =
|
||||
BuildMessage.Info(
|
||||
"This will just create the files for a Thunderstore package in your AssetBundles folder.");
|
||||
break;
|
||||
case BuildAction.CopyToProfile:
|
||||
messages["BuildAction"] =
|
||||
BuildMessage.Info(
|
||||
"This will copy the built files into the plugins folder of the selected profile.");
|
||||
if (string.IsNullOrEmpty(OutputProfile))
|
||||
messages["OutputProfile"] = BuildMessage.Error("Please set the output profile.");
|
||||
else if (!Directory.Exists(OutputProfile))
|
||||
messages["OutputProfile"] = BuildMessage.Error("Selected profile no longer exists.");
|
||||
else if (!File.Exists(Path.Combine(OutputProfile, "mods.yml")))
|
||||
messages["OutputProfile"] = BuildMessage.Error(
|
||||
"Selected folder is not a r2mm profile. Please select the folder which contains the plugins folder.");
|
||||
break;
|
||||
case BuildAction.CreateThunderstorePackage:
|
||||
messages["BuildAction"] =
|
||||
BuildMessage.Info(
|
||||
"This will zip up the built files as a final build for importing into r2mm / uploading to Thunderstore.");
|
||||
break;
|
||||
}
|
||||
|
||||
return messages;
|
||||
}
|
||||
|
||||
public bool EnsureValidForEditor()
|
||||
{
|
||||
// Go over each build item
|
||||
bool hasErrors = false, hasWarnings = false;
|
||||
foreach (var item in BuildItems)
|
||||
// Check if it has any validation messages
|
||||
foreach (var message in item.Validate().Values)
|
||||
// Log them
|
||||
switch (message.Type)
|
||||
{
|
||||
case MessageType.Error:
|
||||
Debug.LogError(AssetDatabase.GetAssetPath(item) + ": " + message.Message);
|
||||
hasErrors = true;
|
||||
break;
|
||||
case MessageType.Warning:
|
||||
Debug.LogWarning(AssetDatabase.GetAssetPath(item) + ": " + message.Message);
|
||||
hasWarnings = true;
|
||||
break;
|
||||
case MessageType.Info:
|
||||
Debug.Log(AssetDatabase.GetAssetPath(item) + ": " + message.Message);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
// If there's errors don't let anything continue
|
||||
if (hasErrors)
|
||||
{
|
||||
EditorUtility.DisplayDialog("Build errors",
|
||||
"There were errors validating your build items. Please check the console for more info.", "Ok.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// If there's only warnings, let the user decide if they want to continue
|
||||
if (hasWarnings)
|
||||
return EditorUtility.DisplayDialog("Build warnings",
|
||||
"Some build items validated with warnings. Continue with build anyway?", "Yes", "No");
|
||||
|
||||
// Otherwise continue
|
||||
return true;
|
||||
}
|
||||
|
||||
public string[] GetRequiredDependencies()
|
||||
{
|
||||
return BuildItems
|
||||
.Where(x => x != null)
|
||||
.SelectMany(x => x.RequiredDependencies).ToArray();
|
||||
}
|
||||
|
||||
public string MainNamespace
|
||||
{
|
||||
get
|
||||
{
|
||||
return Author + "." + PackageName;
|
||||
}
|
||||
}
|
||||
|
||||
public string[] GetRequiredNamespaces()
|
||||
{
|
||||
return new[] {MainNamespace};
|
||||
}
|
||||
|
||||
public string[] GetAllAllowedNamespaces()
|
||||
{
|
||||
return GetRequiredNamespaces().Concat(AdditionalNamespaces).ToArray();
|
||||
}
|
||||
|
||||
public void WriteThunderstoreManifest(string location)
|
||||
{
|
||||
#if H3VR_IMPORTED
|
||||
var obj = new JObject();
|
||||
obj["name"] = PackageName;
|
||||
obj["author"] = Author;
|
||||
obj["version_number"] = Version;
|
||||
obj["description"] = Description;
|
||||
obj["website_url"] = string.IsNullOrEmpty(WebsiteURL) ? "" : WebsiteURL;
|
||||
|
||||
// ReSharper disable once CoVariantArrayConversion
|
||||
obj["dependencies"] = new JArray(GetRequiredDependencies().Concat(AdditionalDependencies).ToArray());
|
||||
|
||||
File.WriteAllText(location, JsonConvert.SerializeObject(obj));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
public enum BuildAction
|
||||
{
|
||||
JustBuildFiles,
|
||||
CopyToProfile,
|
||||
CreateThunderstorePackage
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: afed54a239594b8abd631a8047a54987
|
||||
timeCreated: 1635004125
|
||||
@@ -0,0 +1,63 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MeatKit
|
||||
{
|
||||
[CustomEditor(typeof(BuildProfile))]
|
||||
public class BuildProfileEditor : BuildItemEditor
|
||||
{
|
||||
private bool _folded1, _folded2;
|
||||
private BuildProfile _profile;
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
_profile = (BuildProfile) target;
|
||||
}
|
||||
|
||||
protected override void DrawProperty(SerializedProperty property)
|
||||
{
|
||||
if (property.name == "AdditionalDependencies")
|
||||
{
|
||||
_folded1 = EditorGUILayout.Foldout(_folded1, "Dependencies");
|
||||
if (_folded1) DrawListWithRequiredElements(_profile.GetRequiredDependencies(), property);
|
||||
}
|
||||
else if (property.name == "AdditionalNamespaces")
|
||||
{
|
||||
if (!_profile.StripNamespaces) return;
|
||||
|
||||
_folded2 = EditorGUILayout.Foldout(_folded2, "Allowed Namespaces");
|
||||
if (_folded2)
|
||||
{
|
||||
DrawListWithRequiredElements(_profile.GetRequiredNamespaces(), property);
|
||||
EditorGUI.indentLevel++;
|
||||
EditorGUILayout.HelpBox("Namespaces relate to custom scripts in your project. Any code in a namespace not included here will not be included in your final build. Note this only applies to .cs files directly in your project. Scripts coming from .dll files are not affected by this setting.", MessageType.Info);
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
}
|
||||
else if (property.name == "BuildAction")
|
||||
{
|
||||
base.DrawProperty(property);
|
||||
|
||||
// Draw the build action stuff
|
||||
if (_profile.BuildAction == BuildAction.CopyToProfile)
|
||||
{
|
||||
// Tell the user which profile it will be output to
|
||||
string profileName = Path.GetFileName(_profile.OutputProfile);
|
||||
GUILayout.Label("Selected profile: " + (string.IsNullOrEmpty(profileName) ? "None" : profileName));
|
||||
|
||||
// Give a button to change the output folder
|
||||
if (GUILayout.Button("Select profile folder"))
|
||||
_profile.OutputProfile = EditorUtility.OpenFolderPanel("Select your r2mm profile folder", @"%APPDATA%\Roaming\r2modmanPlus-local\H3VR\profiles", "");
|
||||
|
||||
// Draw any errors that come from the BuildAction property, as those won't get displayed otherwise.
|
||||
DrawMessageIfExists("OutputProfile");
|
||||
}
|
||||
}
|
||||
else base.DrawProperty(property);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b94a892488a449798f8fd227d82d86fc
|
||||
timeCreated: 1635006063
|
||||
@@ -0,0 +1,76 @@
|
||||
using System;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MeatKit
|
||||
{
|
||||
public class BuildWindow : EditorWindow
|
||||
{
|
||||
private static BuildProfile _selectedProfileInternal;
|
||||
private static BuildProfileEditor _editor;
|
||||
|
||||
public static BuildProfile SelectedProfile
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!_selectedProfileInternal)
|
||||
_selectedProfileInternal = MeatKitCache.LastSelectedProfile;
|
||||
return _selectedProfileInternal;
|
||||
}
|
||||
private set
|
||||
{
|
||||
if (_selectedProfileInternal != value)
|
||||
_editor = null;
|
||||
_selectedProfileInternal = value;
|
||||
}
|
||||
}
|
||||
|
||||
private static BuildProfileEditor EditorInstance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!_editor && SelectedProfile)
|
||||
_editor = (BuildProfileEditor) Editor.CreateEditor(SelectedProfile, typeof(BuildProfileEditor));
|
||||
return _editor;
|
||||
}
|
||||
}
|
||||
|
||||
[MenuItem("MeatKit/Build Window")]
|
||||
public static void Open()
|
||||
{
|
||||
GetWindow<BuildWindow>("MeatKit Build").Show();
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
EditorGUILayout.LabelField("Selected Build Profile", EditorStyles.boldLabel);
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
SelectedProfile = EditorGUILayout.ObjectField(SelectedProfile, typeof(BuildProfile), false) as BuildProfile;
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
MeatKitCache.LastSelectedProfile = SelectedProfile;
|
||||
}
|
||||
|
||||
if (!SelectedProfile)
|
||||
{
|
||||
GUILayout.Label("Please select a profile");
|
||||
return;
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorInstance.OnInspectorGUI();
|
||||
|
||||
GUILayout.FlexibleSpace();
|
||||
|
||||
if (GUILayout.Button("Build!", GUILayout.Height(50)))
|
||||
MeatKit.DoBuild();
|
||||
|
||||
if (MeatKitCache.LastBuildTime != default(DateTime))
|
||||
GUILayout.Label("Last build: " + MeatKitCache.LastBuildTime + " (" +
|
||||
MeatKitCache.LastBuildDuration.GetReadableTimespan() + ")");
|
||||
else GUILayout.Label("Last build: Never");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0a4b91e470df48f8be84b62e7c91d4a7
|
||||
timeCreated: 1639092690
|
||||
@@ -0,0 +1,9 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MeatKit
|
||||
{
|
||||
public interface IValidatable
|
||||
{
|
||||
Dictionary<string, BuildMessage> Validate();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f1c893eea8264e16a0b37abe9c2d6861
|
||||
timeCreated: 1636270330
|
||||
@@ -0,0 +1,12 @@
|
||||
using System;
|
||||
|
||||
namespace MeatKit
|
||||
{
|
||||
public class MeatKitBuildException : Exception
|
||||
{
|
||||
public MeatKitBuildException(string message, Exception innerException) : base(message, innerException)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9dd595cdd27a46049e3d8005658214de
|
||||
timeCreated: 1641928037
|
||||
@@ -0,0 +1,85 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using AssetsTools.NET;
|
||||
using AssetsTools.NET.Extra;
|
||||
using UnityEditor;
|
||||
|
||||
namespace MeatKit
|
||||
{
|
||||
public static partial class MeatKit
|
||||
{
|
||||
private static void ProcessBundle(string source, string destination, IDictionary<string, string> replaceMap,
|
||||
AssetBundleCompressionType recompressAs)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Step 1: Load the asset bundle.
|
||||
EditorUtility.DisplayProgressBar("Processing bundle", "Loading asset bundle...", 0f);
|
||||
var am = new AssetsManager();
|
||||
var bundle = am.LoadBundleFile(source);
|
||||
var assets = am.LoadAssetsFileFromBundle(bundle, 0);
|
||||
|
||||
// Step 2: For each MonoScript asset, alter it's assembly name
|
||||
EditorUtility.DisplayProgressBar("Processing bundle", "Modifying assets...", 0.33f);
|
||||
var modifications = new List<AssetsReplacer>();
|
||||
foreach (AssetFileInfoEx assetInfo in assets.table.assetFileInfo)
|
||||
{
|
||||
// We only want MonoScripts (type 115)
|
||||
if (assetInfo.curFileType != 115) continue;
|
||||
|
||||
// Get the field for this asset
|
||||
var field = am.GetTypeInstance(assets, assetInfo).GetBaseField();
|
||||
var assemblyNameValue = field["m_AssemblyName"].GetValue();
|
||||
|
||||
// Check if we want to replace this name
|
||||
var asmName = assemblyNameValue.AsString();
|
||||
if (replaceMap.ContainsKey(asmName))
|
||||
{
|
||||
// Modify it's assembly name
|
||||
assemblyNameValue.Set(replaceMap[asmName]);
|
||||
|
||||
// Write the modifications to the list
|
||||
var newBytes = field.WriteToByteArray();
|
||||
modifications.Add(new AssetsReplacerFromMemory(0, assetInfo.index, (int) assetInfo.curFileType,
|
||||
0xFFFF, newBytes));
|
||||
}
|
||||
}
|
||||
|
||||
// Step 3: Write the modified assets back into an uncompressed bundle
|
||||
EditorUtility.DisplayProgressBar("Processing bundle", "Saving changes...", 0.66f);
|
||||
using (var fileStream = new FileStream(destination + ".uncompressed", FileMode.Create))
|
||||
{
|
||||
var bunRepl = new BundleReplacerFromAssets(assets.name, assets.name, assets.file, modifications);
|
||||
var bunWriter = new AssetsFileWriter(fileStream);
|
||||
bundle.file.Write(bunWriter, new List<BundleReplacer> {bunRepl});
|
||||
}
|
||||
|
||||
// Unload the existing bundle
|
||||
am.UnloadAll();
|
||||
|
||||
// Step 4: Re-compress the bundle if requested
|
||||
EditorUtility.DisplayProgressBar("Processing bundle", "Recompressing bundle...", 1f);
|
||||
if (recompressAs != AssetBundleCompressionType.NONE)
|
||||
{
|
||||
var compressedBundle = am.LoadBundleFile(destination + ".uncompressed");
|
||||
using (var fs = File.OpenWrite(destination))
|
||||
using (var writer = new AssetsFileWriter(fs))
|
||||
{
|
||||
compressedBundle.file.Pack(compressedBundle.file.reader, writer, recompressAs);
|
||||
}
|
||||
|
||||
am.UnloadAll();
|
||||
File.Delete(destination + ".uncompressed");
|
||||
}
|
||||
else File.Move(destination + ".uncompressed", destination);
|
||||
|
||||
}
|
||||
finally
|
||||
{
|
||||
EditorUtility.ClearProgressBar();
|
||||
GC.Collect();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 69efae90f6bb4c3eadcd9e7c4af6ec67
|
||||
timeCreated: 1617337071
|
||||
@@ -0,0 +1,192 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace MeatKit
|
||||
{
|
||||
public static class Extensions
|
||||
{
|
||||
public static Type[] GetTypesSafe(this Assembly assembly)
|
||||
{
|
||||
try
|
||||
{
|
||||
return assembly.GetTypes();
|
||||
}
|
||||
catch (ReflectionTypeLoadException e)
|
||||
{
|
||||
return e.Types.Where(t => t != null).ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
public static string ByteArrayToString(byte[] ba)
|
||||
{
|
||||
var hex = new StringBuilder(ba.Length * 2);
|
||||
foreach (var b in ba)
|
||||
hex.AppendFormat("{0:x2}", b);
|
||||
return hex.ToString();
|
||||
}
|
||||
|
||||
// Modified version of http://answers.unity.com/answers/1425776/view.html
|
||||
public static T[] GetAllInstances<T>() where T : ScriptableObject
|
||||
{
|
||||
return AssetDatabase.FindAssets("t:" + typeof(T).FullName)
|
||||
.Select(AssetDatabase.GUIDToAssetPath)
|
||||
.Select(AssetDatabase.LoadAssetAtPath<T>)
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
public static Object[] GetAllInstances(Type t)
|
||||
{
|
||||
return AssetDatabase.FindAssets("t:" + t.FullName)
|
||||
.Select(AssetDatabase.GUIDToAssetPath)
|
||||
.Select(p => AssetDatabase.LoadAssetAtPath(p, t))
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/a/25223884
|
||||
public static string MakeValidFileName(string text, char? replacement = '_')
|
||||
{
|
||||
if (string.IsNullOrEmpty(text)) return "";
|
||||
|
||||
var invalids = Path.GetInvalidFileNameChars();
|
||||
var sb = new StringBuilder(text.Length);
|
||||
var changed = false;
|
||||
for (var i = 0; i < text.Length; i++)
|
||||
{
|
||||
var c = text[i];
|
||||
if (invalids.Contains(c))
|
||||
{
|
||||
changed = true;
|
||||
var repl = replacement ?? '\0';
|
||||
if (repl != '\0')
|
||||
sb.Append(repl);
|
||||
}
|
||||
else
|
||||
sb.Append(c);
|
||||
}
|
||||
|
||||
if (sb.Length == 0)
|
||||
return "_";
|
||||
return changed ? sb.ToString() : text;
|
||||
}
|
||||
|
||||
// https://answers.unity.com/questions/150942/texture-scale.html
|
||||
public static Texture2D ScaleTexture(this Texture2D source, int targetWidth, int targetHeight)
|
||||
{
|
||||
Texture2D result = new Texture2D(targetWidth, targetHeight, TextureFormat.ARGB32, false);
|
||||
for (int i = 0; i < result.height; ++i)
|
||||
{
|
||||
for (int j = 0; j < result.width; ++j)
|
||||
{
|
||||
Color newColor = source.GetPixelBilinear(j / (float) result.width, i / (float) result.height);
|
||||
result.SetPixel(j, i, newColor);
|
||||
}
|
||||
}
|
||||
|
||||
result.Apply();
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void CopyFilesRecursively(DirectoryInfo source, DirectoryInfo target) {
|
||||
foreach (DirectoryInfo dir in source.GetDirectories())
|
||||
CopyFilesRecursively(dir, target.CreateSubdirectory(dir.Name));
|
||||
foreach (FileInfo file in source.GetFiles())
|
||||
file.CopyTo(Path.Combine(target.FullName, file.Name));
|
||||
}
|
||||
|
||||
public static void CopyFilesRecursively(string source, string target)
|
||||
{
|
||||
CopyFilesRecursively(new DirectoryInfo(source), new DirectoryInfo(target));
|
||||
}
|
||||
|
||||
#region TimeSpan formatting https://stackoverflow.com/a/21649465/8809017
|
||||
|
||||
public static string GetReadableTimespan(this TimeSpan ts)
|
||||
{
|
||||
// formats and its cutoffs based on totalseconds
|
||||
var cutoff = new SortedList<long, string>
|
||||
{
|
||||
{59, "{3:S}"},
|
||||
{60, "{2:M}"},
|
||||
{60 * 60 - 1, "{2:M}, {3:S}"},
|
||||
{60 * 60, "{1:H}"},
|
||||
{24 * 60 * 60 - 1, "{1:H}, {2:M}"},
|
||||
{24 * 60 * 60, "{0:D}"},
|
||||
{long.MaxValue, "{0:D}, {1:H}"}
|
||||
};
|
||||
|
||||
// find nearest best match
|
||||
var find = cutoff.Keys.ToList()
|
||||
.BinarySearch((long) ts.TotalSeconds);
|
||||
// negative values indicate a nearest match
|
||||
var near = find < 0 ? Math.Abs(find) - 1 : find;
|
||||
// use custom formatter to get the string
|
||||
return String.Format(
|
||||
new HMSFormatter(),
|
||||
cutoff[cutoff.Keys[near]],
|
||||
ts.Days,
|
||||
ts.Hours,
|
||||
ts.Minutes,
|
||||
ts.Seconds);
|
||||
}
|
||||
|
||||
// formatter for forms of
|
||||
// seconds/hours/day
|
||||
private class HMSFormatter : ICustomFormatter, IFormatProvider
|
||||
{
|
||||
// list of Formats, with a P customformat for pluralization
|
||||
static Dictionary<string, string> timeformats = new Dictionary<string, string>
|
||||
{
|
||||
{"S", "{0:P:s:s}"},
|
||||
{"M", "{0:P:m:m}"},
|
||||
{"H", "{0:P:h:h}"},
|
||||
{"D", "{0:P:d:d}"}
|
||||
};
|
||||
|
||||
public string Format(string format, object arg, IFormatProvider formatProvider)
|
||||
{
|
||||
return String.Format(new PluralFormatter(), timeformats[format], arg);
|
||||
}
|
||||
|
||||
public object GetFormat(Type formatType)
|
||||
{
|
||||
return formatType == typeof(ICustomFormatter) ? this : null;
|
||||
}
|
||||
}
|
||||
|
||||
// formats a numeric value based on a format P:Plural:Singular
|
||||
private class PluralFormatter : ICustomFormatter, IFormatProvider
|
||||
{
|
||||
public string Format(string format, object arg, IFormatProvider formatProvider)
|
||||
{
|
||||
if (arg != null)
|
||||
{
|
||||
var parts = format.Split(':'); // ["P", "Plural", "Singular"]
|
||||
|
||||
if (parts[0] == "P") // correct format?
|
||||
{
|
||||
// which index postion to use
|
||||
int partIndex = (arg.ToString() == "1") ? 2 : 1;
|
||||
// pick string (safe guard for array bounds) and format
|
||||
return String.Format("{0}{1}", arg, (parts.Length > partIndex ? parts[partIndex] : ""));
|
||||
}
|
||||
}
|
||||
|
||||
return String.Format(format, arg);
|
||||
}
|
||||
|
||||
public object GetFormat(Type formatType)
|
||||
{
|
||||
return formatType == typeof(ICustomFormatter) ? this : null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d928d818e3c24bce8c1c938e971da916
|
||||
timeCreated: 1628577239
|
||||
@@ -0,0 +1,113 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using AssetsTools.NET;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using Debug = UnityEngine.Debug;
|
||||
|
||||
namespace MeatKit
|
||||
{
|
||||
public static partial class MeatKit
|
||||
{
|
||||
private static readonly string ManagedDirectory = Path.Combine(Application.dataPath, "MeatKit/Managed/");
|
||||
|
||||
private static bool ShowErrorIfH3VRNotImported()
|
||||
{
|
||||
#if (H3VR_IMPORTED == false)
|
||||
EditorUtility.DisplayDialog("Cannot continue.", "You don't have the H3 scripts imported. Please do that before trying to export anything.", "Ok");
|
||||
return true;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
[MenuItem("MeatKit/Scripts/Import Game", priority = 0)]
|
||||
public static void ImportAssemblies()
|
||||
{
|
||||
// If the path has never been set, or no longer exists, prompt the user to find it again
|
||||
var gameManagedLocation = MeatKitCache.GameManagedLocation;
|
||||
if (string.IsNullOrEmpty(gameManagedLocation) || !Directory.Exists(gameManagedLocation))
|
||||
{
|
||||
gameManagedLocation = EditorUtility.OpenFolderPanel("Select H3VR Managed directory", string.Empty, "Managed");
|
||||
MeatKitCache.GameManagedLocation = gameManagedLocation;
|
||||
}
|
||||
|
||||
// If it's _still_ empty, the user must have cancelled.
|
||||
if (string.IsNullOrEmpty(gameManagedLocation)) return;
|
||||
ImportAssemblies(gameManagedLocation, ManagedDirectory);
|
||||
}
|
||||
|
||||
[MenuItem("MeatKit/Scripts/Import Single", priority = 0)]
|
||||
public static void ImportSingleAssembly()
|
||||
{
|
||||
var assemblyLocation =
|
||||
EditorUtility.OpenFilePanel("Select assembly", null, "dll");
|
||||
if (string.IsNullOrEmpty(assemblyLocation)) return;
|
||||
MeatKitCache.LastImportedAssembly = assemblyLocation;
|
||||
ImportSingleAssembly(assemblyLocation, ManagedDirectory);
|
||||
Debug.Log("Finished importing " + assemblyLocation);
|
||||
}
|
||||
|
||||
[MenuItem("MeatKit/Scripts/Re-Import Last", priority = 0)]
|
||||
public static void ReimportLast()
|
||||
{
|
||||
if (string.IsNullOrEmpty(MeatKitCache.LastImportedAssembly))
|
||||
{
|
||||
Debug.Log("Nothing to re-import.");
|
||||
return;
|
||||
}
|
||||
|
||||
ImportSingleAssembly(MeatKitCache.LastImportedAssembly, ManagedDirectory);
|
||||
Debug.Log("Re-imported " + MeatKitCache.LastImportedAssembly);
|
||||
}
|
||||
|
||||
[MenuItem("MeatKit/Scripts/Export", priority = 0)]
|
||||
public static void ExportEditorScripts()
|
||||
{
|
||||
// Make sure the scripts are imported and there are no errors before exporting
|
||||
if (ShowErrorIfH3VRNotImported()) return;
|
||||
if (!BuildWindow.SelectedProfile.EnsureValidForEditor()) return;
|
||||
ExportEditorAssembly(BundleOutputPath);
|
||||
}
|
||||
|
||||
|
||||
[MenuItem("MeatKit/Asset Bundle/Export", priority = 1)]
|
||||
public static void ExportBundle()
|
||||
{
|
||||
var assetBundlePath = EditorUtility.OpenFilePanel("Select asset bundle", Application.dataPath, "");
|
||||
var settings = BuildWindow.SelectedProfile;
|
||||
var replaceMap = new Dictionary<string, string>
|
||||
{
|
||||
{"Assembly-CSharp.dll", settings.PackageName + ".dll"},
|
||||
{"Assembly-CSharp-firstpass.dll", settings.PackageName + "-firstpass.dll"},
|
||||
{"H3VRCode-CSharp.dll", "Assembly-CSharp.dll"},
|
||||
{"H3VRCode-CSharp-firstpass.dll", "Assembly-CSharp-firstpass.dll"}
|
||||
};
|
||||
|
||||
ProcessBundle(assetBundlePath, assetBundlePath, replaceMap, AssetBundleCompressionType.LZ4);
|
||||
}
|
||||
|
||||
[MenuItem("MeatKit/Asset Bundle/Import", priority = 1)]
|
||||
public static void ImportBundle()
|
||||
{
|
||||
var assetBundlePath = EditorUtility.OpenFilePanel("Select asset bundle", Application.dataPath, "");
|
||||
var replaceMap = new Dictionary<string, string>
|
||||
{
|
||||
{"Assembly-CSharp.dll", "H3VRCode-CSharp.dll"},
|
||||
{"Assembly-CSharp-firstpass.dll", "H3VRCode-CSharp-firstpass.dll"}
|
||||
};
|
||||
|
||||
ProcessBundle(assetBundlePath, assetBundlePath + "-imported", replaceMap, AssetBundleCompressionType.NONE);
|
||||
}
|
||||
|
||||
public static void ClearCache()
|
||||
{
|
||||
AssetDatabase.SaveAssets();
|
||||
|
||||
if (Directory.Exists(ManagedDirectory))
|
||||
Directory.Delete(ManagedDirectory, true);
|
||||
PlayerSettings.SetScriptingDefineSymbolsForGroup(BuildTargetGroup.Standalone, "");
|
||||
AssetDatabase.Refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dc821cd525214a745a8b5ee5624f98c6
|
||||
timeCreated: 1617325095
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,112 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MeatKit
|
||||
{
|
||||
[Serializable]
|
||||
public class MeatKitCache
|
||||
{
|
||||
private const string CacheFileName = "meatkit.json";
|
||||
|
||||
[SerializeField]
|
||||
private string _lastBuildTime = default(DateTime).ToString(CultureInfo.InvariantCulture);
|
||||
|
||||
[SerializeField]
|
||||
private string _lastBuildDuration = default(TimeSpan).ToString();
|
||||
|
||||
[SerializeField]
|
||||
private string _gameManagedLocation;
|
||||
|
||||
[SerializeField]
|
||||
private string _lastImportedAssembly;
|
||||
|
||||
[SerializeField]
|
||||
private string _lastSelectedProfileGuid;
|
||||
|
||||
private static string CacheFilePath
|
||||
{
|
||||
get { return Path.Combine(Path.GetDirectoryName(Application.dataPath), CacheFileName); }
|
||||
}
|
||||
|
||||
private static MeatKitCache _instance;
|
||||
private static MeatKitCache Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_instance != null) return _instance;
|
||||
|
||||
if (!File.Exists(CacheFilePath))
|
||||
{
|
||||
_instance = new MeatKitCache();
|
||||
WriteCache();
|
||||
}
|
||||
else _instance = JsonUtility.FromJson<MeatKitCache>(File.ReadAllText(CacheFileName));
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
|
||||
private static void WriteCache()
|
||||
{
|
||||
File.WriteAllText(CacheFilePath, JsonUtility.ToJson(_instance));
|
||||
}
|
||||
|
||||
public static DateTime LastBuildTime
|
||||
{
|
||||
get { return DateTime.Parse(Instance._lastBuildTime); }
|
||||
set
|
||||
{
|
||||
Instance._lastBuildTime = value.ToString(CultureInfo.InvariantCulture);
|
||||
WriteCache();
|
||||
}
|
||||
}
|
||||
|
||||
public static TimeSpan LastBuildDuration
|
||||
{
|
||||
get { return TimeSpan.Parse(Instance._lastBuildDuration); }
|
||||
set
|
||||
{
|
||||
Instance._lastBuildDuration = value.ToString();
|
||||
WriteCache();
|
||||
}
|
||||
}
|
||||
|
||||
public static string GameManagedLocation
|
||||
{
|
||||
get { return Instance._gameManagedLocation; }
|
||||
set
|
||||
{
|
||||
Instance._gameManagedLocation = value;
|
||||
WriteCache();
|
||||
}
|
||||
}
|
||||
|
||||
public static string LastImportedAssembly
|
||||
{
|
||||
get { return Instance._lastImportedAssembly; }
|
||||
set
|
||||
{
|
||||
Instance._lastImportedAssembly = value;
|
||||
WriteCache();
|
||||
}
|
||||
}
|
||||
|
||||
public static BuildProfile LastSelectedProfile
|
||||
{
|
||||
get
|
||||
{
|
||||
if (string.IsNullOrEmpty(Instance._lastSelectedProfileGuid)) return null;
|
||||
var path = AssetDatabase.GUIDToAssetPath(Instance._lastSelectedProfileGuid);
|
||||
return string.IsNullOrEmpty(path) ? null : AssetDatabase.LoadAssetAtPath<BuildProfile>(path);
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value == null) Instance._lastSelectedProfileGuid = "";
|
||||
Instance._lastSelectedProfileGuid = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(value));
|
||||
WriteCache();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0dd6818cfc454c2a881d31442b37269e
|
||||
timeCreated: 1638838073
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c302b95892ac1244bbc2d7eb2a4584a2
|
||||
folderAsset: yes
|
||||
timeCreated: 1640060210
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 6.1 KiB |
@@ -0,0 +1,76 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f999bddab1821b94691466e97cc6891f
|
||||
timeCreated: 1642657287
|
||||
licenseType: Free
|
||||
TextureImporter:
|
||||
fileIDToRecycleName: {}
|
||||
serializedVersion: 4
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 1
|
||||
sRGBTexture: 0
|
||||
linearTexture: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
mipMapFadeDistanceStart: 1
|
||||
mipMapFadeDistanceEnd: 3
|
||||
bumpmap:
|
||||
convertToNormalMap: 0
|
||||
externalNormalMap: 0
|
||||
heightScale: 0.25
|
||||
normalMapFilter: 0
|
||||
isReadable: 1
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
seamlessCubemap: 0
|
||||
textureFormat: 1
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
filterMode: 2
|
||||
aniso: -1
|
||||
mipBias: -1
|
||||
wrapMode: -1
|
||||
nPOTScale: 1
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 0
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: 0.5, y: 0.5}
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spritePixelsToUnits: 100
|
||||
alphaUsage: 1
|
||||
alphaIsTransparency: 1
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 0
|
||||
textureShape: 1
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
platformSettings:
|
||||
- buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 2048
|
||||
textureFormat: -1
|
||||
textureCompression: 0
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
- buildTarget: Standalone
|
||||
maxTextureSize: 2048
|
||||
textureFormat: -1
|
||||
textureCompression: 0
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
spritePackingTag:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.5 KiB |
@@ -0,0 +1,76 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 151c1f5398ee70041a49c417b23f1846
|
||||
timeCreated: 1640061561
|
||||
licenseType: Pro
|
||||
TextureImporter:
|
||||
fileIDToRecycleName: {}
|
||||
serializedVersion: 4
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 1
|
||||
sRGBTexture: 0
|
||||
linearTexture: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
mipMapFadeDistanceStart: 1
|
||||
mipMapFadeDistanceEnd: 3
|
||||
bumpmap:
|
||||
convertToNormalMap: 0
|
||||
externalNormalMap: 0
|
||||
heightScale: 0.25
|
||||
normalMapFilter: 0
|
||||
isReadable: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
seamlessCubemap: 0
|
||||
textureFormat: 1
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
filterMode: 2
|
||||
aniso: -1
|
||||
mipBias: -1
|
||||
wrapMode: -1
|
||||
nPOTScale: 1
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 0
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: 0.5, y: 0.5}
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spritePixelsToUnits: 100
|
||||
alphaUsage: 1
|
||||
alphaIsTransparency: 1
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 0
|
||||
textureShape: 1
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
platformSettings:
|
||||
- buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 2048
|
||||
textureFormat: -1
|
||||
textureCompression: 0
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
- buildTarget: Standalone
|
||||
maxTextureSize: 2048
|
||||
textureFormat: -1
|
||||
textureCompression: 0
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
spritePackingTag:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 2.9 KiB |
@@ -0,0 +1,76 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6fae6dfd178cd184180013acef55632c
|
||||
timeCreated: 1640061322
|
||||
licenseType: Pro
|
||||
TextureImporter:
|
||||
fileIDToRecycleName: {}
|
||||
serializedVersion: 4
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 1
|
||||
sRGBTexture: 0
|
||||
linearTexture: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
mipMapFadeDistanceStart: 1
|
||||
mipMapFadeDistanceEnd: 3
|
||||
bumpmap:
|
||||
convertToNormalMap: 0
|
||||
externalNormalMap: 0
|
||||
heightScale: 0.25
|
||||
normalMapFilter: 0
|
||||
isReadable: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
seamlessCubemap: 0
|
||||
textureFormat: 1
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
filterMode: 2
|
||||
aniso: -1
|
||||
mipBias: -1
|
||||
wrapMode: -1
|
||||
nPOTScale: 1
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 0
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: 0.5, y: 0.5}
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spritePixelsToUnits: 100
|
||||
alphaUsage: 1
|
||||
alphaIsTransparency: 1
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 0
|
||||
textureShape: 1
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
platformSettings:
|
||||
- buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 2048
|
||||
textureFormat: -1
|
||||
textureCompression: 0
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
- buildTarget: Standalone
|
||||
maxTextureSize: 2048
|
||||
textureFormat: -1
|
||||
textureCompression: 0
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
spritePackingTag:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c7a0d66f64ea9324e9fcfe43a9f69c4e
|
||||
folderAsset: yes
|
||||
timeCreated: 1638903534
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,34 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cfc6c4032c0e78f7c0480d3c1c93001e
|
||||
timeCreated: 1637991225
|
||||
licenseType: Pro
|
||||
PluginImporter:
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
isPreloaded: 0
|
||||
isOverridable: 0
|
||||
platformData:
|
||||
data:
|
||||
first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
data:
|
||||
first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
data:
|
||||
first:
|
||||
Windows Store Apps: WindowsStoreApps
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,34 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f0ebcb063f9204b9162f28fbb60fc29c
|
||||
timeCreated: 1634082208
|
||||
licenseType: Pro
|
||||
PluginImporter:
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
isPreloaded: 0
|
||||
isOverridable: 0
|
||||
platformData:
|
||||
data:
|
||||
first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
data:
|
||||
first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
data:
|
||||
first:
|
||||
Windows Store Apps: WindowsStoreApps
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,34 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 435f5990a4548dbc0126c23e6081aa8d
|
||||
timeCreated: 1638843984
|
||||
licenseType: Pro
|
||||
PluginImporter:
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
isPreloaded: 0
|
||||
isOverridable: 0
|
||||
platformData:
|
||||
data:
|
||||
first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
data:
|
||||
first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
data:
|
||||
first:
|
||||
Windows Store Apps: WindowsStoreApps
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,34 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7399badb939ce1137fff9ef0738fd1aa
|
||||
timeCreated: 1637991225
|
||||
licenseType: Pro
|
||||
PluginImporter:
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
isPreloaded: 0
|
||||
isOverridable: 0
|
||||
platformData:
|
||||
data:
|
||||
first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
data:
|
||||
first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
data:
|
||||
first:
|
||||
Windows Store Apps: WindowsStoreApps
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,34 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 35077c065319ab23c8ea7477fa04223d
|
||||
timeCreated: 1635021478
|
||||
licenseType: Pro
|
||||
PluginImporter:
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
isPreloaded: 0
|
||||
isOverridable: 0
|
||||
platformData:
|
||||
data:
|
||||
first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
data:
|
||||
first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
data:
|
||||
first:
|
||||
Windows Store Apps: WindowsStoreApps
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,34 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 99b8fb0fa0c1bbbbacd9654ef1e25df4
|
||||
timeCreated: 1636265531
|
||||
licenseType: Pro
|
||||
PluginImporter:
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
isPreloaded: 0
|
||||
isOverridable: 0
|
||||
platformData:
|
||||
data:
|
||||
first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
data:
|
||||
first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
data:
|
||||
first:
|
||||
Windows Store Apps: WindowsStoreApps
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,89 @@
|
||||
#if H3VR_IMPORTED
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using BepInEx;
|
||||
using BepInEx.Configuration;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
using TNHQoLImprovements;
|
||||
|
||||
/*
|
||||
* SUPER LARGE WARNING ABOUT THIS CLASS
|
||||
* This class can be used to add custom behaviour to your generated BepInEx plugin.
|
||||
* Please note, however, that all of the things in here already are REQUIRED and CANNOT BE CHANGED.
|
||||
* There are LARGE TEXT WARNINGS above such items so you don't forget.
|
||||
* You may add to this class so long as you do not modify anything with those notices (lest you want build errors)
|
||||
*
|
||||
* The class name and BepInPlugin attribute are modified at build-time to reflect your build settings.
|
||||
* BepInDependency attributes will automatically be generated if they're required by a build item, otherwise
|
||||
* may add it yourself here.
|
||||
*/
|
||||
|
||||
// DO NOT REMOVE OR CHANGE ANY OF THESE ATTRIBUTES
|
||||
[BepInPlugin("MeatKit", "MeatKit Plugin", "1.0.0")]
|
||||
[BepInProcess("h3vr.exe")]
|
||||
|
||||
// DO NOT CHANGE THE NAME OF THIS CLASS.
|
||||
public class MeatKitPlugin : BaseUnityPlugin
|
||||
{
|
||||
// DO NOT CHANGE OR REMOVE THIS FIELD.
|
||||
#pragma warning disable 414
|
||||
private static readonly string BasePath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
|
||||
#pragma warning restore 414
|
||||
|
||||
public static AssetBundle bundle;
|
||||
|
||||
public static ConfigEntry<bool> showHPBackground;
|
||||
public static ConfigEntry<float> hpBackgroundOpacity;
|
||||
public static ConfigEntry<bool> showTokens;
|
||||
public static ConfigEntry<bool> showHolds;
|
||||
|
||||
private static InPlay instance;
|
||||
private LeaderboardPlayerCountPatch lpc;
|
||||
|
||||
private void SceneChanged(Scene from, Scene to)
|
||||
{
|
||||
//Logger.LogInfo(string.Format("scene chg: {0} --> {1}", from.name, to.name));
|
||||
if(GameObject.Find("_NewTAHReticle") != null)
|
||||
{
|
||||
instance = new GameObject().AddComponent<InPlay>();
|
||||
}
|
||||
else
|
||||
{
|
||||
Destroy(instance);
|
||||
}
|
||||
}
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
// load asset bundle
|
||||
bundle = AssetBundle.LoadFromFile(Path.Combine(BasePath, "tnh_qol_improvements"));
|
||||
SceneManager.activeSceneChanged += SceneChanged;
|
||||
LoadAssets();
|
||||
|
||||
// setup configuration
|
||||
showHPBackground = Config.Bind("Health Counter",
|
||||
"Background enabled",
|
||||
true,
|
||||
"Apply a background to the health text.");
|
||||
hpBackgroundOpacity = Config.Bind("Health Counter",
|
||||
"Background opacity",
|
||||
0.74f,
|
||||
"Set opacity of health text's background (if enabled).");
|
||||
showTokens = Config.Bind("Game Info",
|
||||
"Show Tokens",
|
||||
true,
|
||||
"Shows how many tokens the player has by their radar hand.");
|
||||
showHolds = Config.Bind("Game Info",
|
||||
"Show Holds",
|
||||
true,
|
||||
"Shows how many holds the player has completed by their radar hand.");
|
||||
|
||||
// patch the leaderboard
|
||||
lpc = new LeaderboardPlayerCountPatch();
|
||||
}
|
||||
// DO NOT EDIT.
|
||||
private void LoadAssets() {}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 22911b70b8624c13bc815f600dc9d5b4
|
||||
timeCreated: 1629440186
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f69811f758fe69d4a8db916798dae1d0
|
||||
timeCreated: 1641677517
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7a617135c1b441e09689a962f3a634c8
|
||||
folderAsset: yes
|
||||
timeCreated: 1617388092
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 909a2b7b1c734764287b9acd96752fc7
|
||||
folderAsset: yes
|
||||
timeCreated: 1640643655
|
||||
licenseType: Free
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,181 @@
|
||||
// Adapted from https://gist.github.com/kalineh/ad5135946f2009c36f755eea0a880998
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
#if H3VR_IMPORTED
|
||||
using FistVR;
|
||||
#endif
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
public class EnumPickerWindow : EditorWindow
|
||||
{
|
||||
private static GUIStyle _regularStyle;
|
||||
private static GUIStyle _selectedStyle;
|
||||
|
||||
private string _enumName;
|
||||
private string _filter;
|
||||
|
||||
private Action<string> _onSelectCallback;
|
||||
|
||||
private EditorWindow _parent;
|
||||
private Vector2 _scroll;
|
||||
private List<string> _valuesFiltered;
|
||||
private List<string> _valuesRaw;
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
GUILayout.Label(string.Format("Enum Type: {0}", _enumName));
|
||||
|
||||
GUI.SetNextControlName("filter");
|
||||
var filterUpdate = GUILayout.TextField(_filter);
|
||||
if (filterUpdate != _filter)
|
||||
FilterValues(filterUpdate);
|
||||
|
||||
// always focused
|
||||
GUI.FocusControl("filter");
|
||||
|
||||
_scroll = GUILayout.BeginScrollView(_scroll);
|
||||
|
||||
for (var i = 0; i < _valuesFiltered.Count; ++i)
|
||||
{
|
||||
var value = _valuesFiltered[i];
|
||||
var style = i == 0 ? _selectedStyle : _regularStyle;
|
||||
var rect = GUILayoutUtility.GetRect(new GUIContent(value), style);
|
||||
|
||||
var clicked = GUI.Button(rect, value);
|
||||
if (clicked)
|
||||
{
|
||||
GUILayout.EndScrollView();
|
||||
|
||||
_onSelectCallback(value);
|
||||
Close();
|
||||
_parent.Repaint();
|
||||
_parent.Focus();
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
GUILayout.EndScrollView();
|
||||
|
||||
if (Event.current.type == EventType.KeyDown && Event.current.keyCode == KeyCode.Return)
|
||||
{
|
||||
if (_valuesFiltered.Count > 0)
|
||||
_onSelectCallback(_valuesFiltered[0]);
|
||||
|
||||
Close();
|
||||
_parent.Repaint();
|
||||
_parent.Focus();
|
||||
}
|
||||
|
||||
if (Event.current.type == EventType.KeyDown && Event.current.keyCode == KeyCode.Escape)
|
||||
{
|
||||
Close();
|
||||
_parent.Repaint();
|
||||
_parent.Focus();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnLostFocus()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
public void ShowCustom(string enumName, List<string> values, Rect rect, Action<string> onSelect)
|
||||
{
|
||||
_regularStyle = new GUIStyle(EditorStyles.label);
|
||||
_regularStyle.active = _regularStyle.normal;
|
||||
|
||||
_selectedStyle = new GUIStyle(EditorStyles.label);
|
||||
_selectedStyle.normal = _selectedStyle.focused;
|
||||
_selectedStyle.active = _selectedStyle.focused;
|
||||
|
||||
_enumName = enumName;
|
||||
_valuesRaw = new List<string>(values);
|
||||
_valuesFiltered = new List<string>(values);
|
||||
_filter = "";
|
||||
_onSelectCallback = onSelect;
|
||||
|
||||
_parent = focusedWindow;
|
||||
|
||||
var screenRect = rect;
|
||||
var screenSize = new Vector2(400, 400);
|
||||
|
||||
screenRect.position = GUIUtility.GUIToScreenPoint(screenRect.position);
|
||||
|
||||
ShowAsDropDown(screenRect, screenSize);
|
||||
Focus();
|
||||
|
||||
GUI.FocusControl("filter");
|
||||
}
|
||||
|
||||
private void FilterValues(string filterUpdate)
|
||||
{
|
||||
_filter = filterUpdate;
|
||||
var filterLower = _filter.ToLower();
|
||||
_valuesFiltered.Clear();
|
||||
foreach (var value in from value in _valuesRaw
|
||||
let lower = value.ToLower()
|
||||
where lower.Contains(filterLower)
|
||||
select value)
|
||||
_valuesFiltered.Add(value);
|
||||
}
|
||||
}
|
||||
|
||||
public class EnumPicker : PropertyDrawer
|
||||
{
|
||||
private EnumPickerWindow _window;
|
||||
|
||||
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
|
||||
{
|
||||
Array valuesRaw = null;
|
||||
Type enumType = fieldInfo.FieldType;
|
||||
if (enumType.IsArray) enumType = enumType.GetElementType(); //this turns the enum array into just the enum to prevent issues with arrays of enums
|
||||
|
||||
valuesRaw = Enum.GetValues(enumType);
|
||||
if (valuesRaw.Length <= 0)
|
||||
return;
|
||||
var valuesStr = new List<string>();
|
||||
for (var i = 0; i < valuesRaw.Length; ++i)
|
||||
{
|
||||
object raw = valuesRaw.GetValue(i);
|
||||
var str = raw.ToString();
|
||||
valuesStr.Add(str);
|
||||
}
|
||||
string enumName = enumType.Name;
|
||||
string currentName = Enum.GetName(enumType, property.intValue);
|
||||
EditorGUI.PrefixLabel(position, label);
|
||||
GUI.SetNextControlName(property.propertyPath);
|
||||
var fieldRect = new Rect(position.x + EditorGUIUtility.labelWidth, position.y,
|
||||
position.width - EditorGUIUtility.labelWidth, position.height);
|
||||
if (GUI.Button(fieldRect, currentName, EditorStyles.popup))
|
||||
{
|
||||
_window = EditorWindow.GetWindow<EnumPickerWindow>();
|
||||
Action<string> callback = str =>
|
||||
{
|
||||
var index = (int) Convert.ChangeType(Enum.Parse(enumType, str), enumType);
|
||||
property.serializedObject.Update();
|
||||
property.intValue = index;
|
||||
property.serializedObject.ApplyModifiedProperties();
|
||||
};
|
||||
_window.ShowCustom(enumName, valuesStr, fieldRect, callback);
|
||||
_window.Focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if H3VR_IMPORTED
|
||||
[CustomPropertyDrawer(typeof(FireArmMagazineType))]
|
||||
[CustomPropertyDrawer(typeof(FireArmClipType))]
|
||||
[CustomPropertyDrawer(typeof(FireArmRoundClass))]
|
||||
[CustomPropertyDrawer(typeof(ItemSpawnerObjectDefinition.ItemSpawnerCategory))]
|
||||
[CustomPropertyDrawer(typeof(ItemSpawnerID.EItemCategory))]
|
||||
[CustomPropertyDrawer(typeof(ItemSpawnerID.ESubCategory))]
|
||||
[CustomPropertyDrawer(typeof(FireArmRoundType))]
|
||||
[CustomPropertyDrawer(typeof(SosigEnemyID))]
|
||||
public class EnumDrawers : EnumPicker
|
||||
{
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 27e4b73d795c232439cf3aee1d6ed3ba
|
||||
timeCreated: 1628222780
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,70 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
[CustomEditor(typeof(IconCamera))]
|
||||
[CanEditMultipleObjects]
|
||||
public class IconCameraEditor : Editor {
|
||||
|
||||
|
||||
Texture2D previewTexture = null;
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
serializedObject.Update();
|
||||
|
||||
var property = serializedObject.GetIterator();
|
||||
if (!property.NextVisible(true)) return;
|
||||
do EditorGUILayout.PropertyField(property, true);
|
||||
while (property.NextVisible(false));
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
|
||||
IconCamera iconCamera = serializedObject.targetObject as IconCamera;
|
||||
iconCamera.thisCamera.depthTextureMode = iconCamera.CameraDepthMode;
|
||||
|
||||
if (previewTexture != null)
|
||||
{
|
||||
GUILayout.BeginVertical("Box");
|
||||
|
||||
GUIStyle style = new GUIStyle();
|
||||
style.alignment = TextAnchor.UpperCenter;
|
||||
style.fixedWidth = Screen.width - 50;
|
||||
style.fixedHeight = Screen.width - 50;
|
||||
GUILayout.Label(previewTexture, style);
|
||||
|
||||
GUILayout.EndVertical();
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (GUILayout.Button("Update Preview"))
|
||||
{
|
||||
if (iconCamera.renderTexture != null)
|
||||
{
|
||||
RenderTexture temp = RenderTexture.active;
|
||||
RenderTexture.active = iconCamera.renderTexture;
|
||||
Texture2D texture = new Texture2D(iconCamera.renderTexture.width, iconCamera.renderTexture.height);
|
||||
texture.ReadPixels(new Rect(0, 0, iconCamera.renderTexture.width, iconCamera.renderTexture.height), 0, 0);
|
||||
texture.Apply();
|
||||
RenderTexture.active = temp;
|
||||
|
||||
texture = iconCamera.FlipTexture(texture);
|
||||
|
||||
if (iconCamera.background != null)
|
||||
{
|
||||
texture = iconCamera.AddBackground(texture, iconCamera.background);
|
||||
}
|
||||
|
||||
previewTexture = texture;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (GUILayout.Button("Take Picture"))
|
||||
{
|
||||
Selection.activeGameObject.GetComponent<IconCamera>().Capture();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b4607bcf07fdb67428d41b6bd2f9b2fb
|
||||
timeCreated: 1640645560
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,37 @@
|
||||
using System;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
public class PrefabLoader : EditorWindow
|
||||
{
|
||||
private AssetBundle _bundle;
|
||||
private string[] _assets = new string[0];
|
||||
private int _selectedAsset = 0;
|
||||
|
||||
[MenuItem("MeatKit/Asset Bundle/Prefab Loader")]
|
||||
private static void Init()
|
||||
{
|
||||
GetWindow<PrefabLoader>().Show();
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
if (GUILayout.Button("Select Asset Bundle"))
|
||||
{
|
||||
// If there's already a bundle loaded, unload it.
|
||||
if (_bundle) _bundle.Unload(false);
|
||||
|
||||
// Ask for the new bundle, load it, and get its assets
|
||||
string assetBundlePath = EditorUtility.OpenFilePanel("Select Asset Bundle", string.Empty, string.Empty);
|
||||
_bundle = AssetBundle.LoadFromFile(assetBundlePath);
|
||||
_assets = _bundle.GetAllAssetNames();
|
||||
_selectedAsset = 0;
|
||||
}
|
||||
|
||||
if (_assets.Length > 0)
|
||||
{
|
||||
_selectedAsset = EditorGUILayout.Popup(_selectedAsset, _assets);
|
||||
if (GUILayout.Button("Spawn")) Instantiate(_bundle.LoadAsset(_assets[_selectedAsset]));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4d4b78186f45453aa4abc82dc1364363
|
||||
timeCreated: 1637988640
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bf9dd0c01456ee745baa552ed8b66182
|
||||
folderAsset: yes
|
||||
timeCreated: 1641758894
|
||||
licenseType: Free
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b2cc9af527c35194481307c551f26f04
|
||||
folderAsset: yes
|
||||
timeCreated: 1641758894
|
||||
licenseType: Free
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,230 @@
|
||||
#if H3VR_IMPORTED
|
||||
using FistVR;
|
||||
#endif
|
||||
using MeatKit;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
public class GunRipperWindow : EditorWindow
|
||||
{
|
||||
|
||||
public GameObject SelectedGameObject;
|
||||
public string ExportPath = "Meatkit/Tools/GunRipper/Export";
|
||||
|
||||
|
||||
[MenuItem("Tools/Gun Ripper")]
|
||||
public static void Open()
|
||||
{
|
||||
GetWindow<GunRipperWindow>("Gun Ripper").Show();
|
||||
}
|
||||
|
||||
#if H3VR_IMPORTED
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
EditorGUILayout.LabelField("Selected GameObject", EditorStyles.boldLabel);
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
SelectedGameObject = EditorGUILayout.ObjectField(SelectedGameObject, typeof(GameObject), true) as GameObject;
|
||||
|
||||
if (string.IsNullOrEmpty(ExportPath)) ExportPath = "Meatkit/Tools/GunRipper/Export";
|
||||
ExportPath = EditorGUILayout.TextField(ExportPath);
|
||||
|
||||
if (!SelectedGameObject)
|
||||
{
|
||||
GUILayout.Label("Please select a game object");
|
||||
return;
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
FVRFireArm firearmComp = SelectedGameObject.GetComponent<FVRFireArm>();
|
||||
FVRFireArmMagazine magazineComp = SelectedGameObject.GetComponent<FVRFireArmMagazine>();
|
||||
FVRFireArmAttachment attachment = SelectedGameObject.GetComponent<FVRFireArmAttachment>();
|
||||
|
||||
if (firearmComp != null && firearmComp.AudioClipSet != null && GUILayout.Button("Rip Firearm Audio"))
|
||||
{
|
||||
Debug.Log("Ripping Audio!");
|
||||
RipAudio(firearmComp.AudioClipSet, "AudioSet");
|
||||
}
|
||||
|
||||
if (magazineComp != null && magazineComp.Profile != null && GUILayout.Button("Rip Magazine Audio"))
|
||||
{
|
||||
Debug.Log("Ripping Audio!");
|
||||
RipAudio(magazineComp.Profile, "AudioSet");
|
||||
}
|
||||
|
||||
if(attachment != null && (attachment.AudClipAttach != null || attachment.AudClipDettach != null) && GUILayout.Button("Rip Attachment Audio"))
|
||||
{
|
||||
if(attachment.AudClipAttach != null)
|
||||
{
|
||||
Debug.Log("Ripping Audio!");
|
||||
RipAudio(attachment.AudClipAttach, "AudioAttach");
|
||||
}
|
||||
|
||||
if (attachment.AudClipDettach != null)
|
||||
{
|
||||
Debug.Log("Ripping Audio!");
|
||||
RipAudio(attachment.AudClipDettach, "AudioDettach");
|
||||
}
|
||||
}
|
||||
|
||||
if (firearmComp != null && (firearmComp.RecoilProfile != null || firearmComp.RecoilProfileStocked != null) && GUILayout.Button("Rip Firearm Recoil"))
|
||||
{
|
||||
if(firearmComp.RecoilProfile != null)
|
||||
{
|
||||
Debug.Log("Ripping Stockless Recoil!");
|
||||
RipRecoil(firearmComp.RecoilProfile, "Recoil");
|
||||
}
|
||||
|
||||
if(firearmComp.RecoilProfileStocked != null)
|
||||
{
|
||||
Debug.Log("Ripping Stocked Recoil!");
|
||||
RipRecoil(firearmComp.RecoilProfileStocked, "RecoilStocked");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void RipAudio(AudioEvent audioEvent, string suffix)
|
||||
{
|
||||
string exportFolderPath = "Assets/" + ExportPath.Trim('/');
|
||||
string destinationFolderName = SelectedGameObject.name + "_Rip";
|
||||
string destinationFolderPath = exportFolderPath + "/" + destinationFolderName;
|
||||
|
||||
if (!AssetDatabase.IsValidFolder(destinationFolderPath))
|
||||
{
|
||||
AssetDatabase.CreateFolder(exportFolderPath, destinationFolderName);
|
||||
}
|
||||
|
||||
RipAudioClips(audioEvent, destinationFolderPath);
|
||||
}
|
||||
|
||||
|
||||
private void RipAudio(FVRFirearmAudioSet audioSet, string suffix)
|
||||
{
|
||||
string exportFolderPath = "Assets/" + ExportPath.Trim('/');
|
||||
string destinationFolderName = SelectedGameObject.name + "_Rip";
|
||||
string destinationFolderPath = exportFolderPath + "/" + destinationFolderName;
|
||||
string audioPath = destinationFolderPath + "/" + SelectedGameObject.name + "_" + suffix + ".asset";
|
||||
|
||||
if (!AssetDatabase.IsValidFolder(destinationFolderPath))
|
||||
{
|
||||
AssetDatabase.CreateFolder(exportFolderPath, destinationFolderName);
|
||||
}
|
||||
|
||||
FVRFirearmAudioSet audioCopy = CreateInstance<FVRFirearmAudioSet>();
|
||||
CopyFields(audioCopy, audioSet);
|
||||
RipAudioClips(audioCopy, destinationFolderPath);
|
||||
|
||||
AssetDatabase.DeleteAsset(audioPath);
|
||||
AssetDatabase.CreateAsset(audioCopy, audioPath);
|
||||
AssetDatabase.SaveAssets();
|
||||
}
|
||||
|
||||
private void RipRecoil(FVRFireArmRecoilProfile recoil, string suffix)
|
||||
{
|
||||
string exportFolderPath = "Assets/" + ExportPath.Trim('/');
|
||||
string destinationFolderName = SelectedGameObject.name + "_Rip";
|
||||
string destinationFolderPath = exportFolderPath + "/" + destinationFolderName;
|
||||
string recoilPath = destinationFolderPath + "/" + SelectedGameObject.name + "_" + suffix + ".asset";
|
||||
|
||||
if (!AssetDatabase.IsValidFolder(destinationFolderPath))
|
||||
{
|
||||
AssetDatabase.CreateFolder(exportFolderPath, destinationFolderName);
|
||||
}
|
||||
|
||||
FVRFireArmRecoilProfile recoilCopy = CreateInstance<FVRFireArmRecoilProfile>();
|
||||
CopyFields(recoilCopy, recoil);
|
||||
|
||||
AssetDatabase.DeleteAsset(recoilPath);
|
||||
AssetDatabase.CreateAsset(recoilCopy, recoilPath);
|
||||
AssetDatabase.SaveAssets();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private void CopyFields(UnityEngine.Object copyAsset, UnityEngine.Object origAsset, bool allowMismatch = false)
|
||||
{
|
||||
Type type = origAsset.GetType();
|
||||
if (!allowMismatch && type != copyAsset.GetType())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Default;
|
||||
PropertyInfo[] pinfos = type.GetProperties(flags);
|
||||
foreach (var pinfo in pinfos)
|
||||
{
|
||||
|
||||
if (pinfo.CanWrite)
|
||||
{
|
||||
try
|
||||
{
|
||||
pinfo.SetValue(copyAsset, pinfo.GetValue(origAsset, null), null);
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
FieldInfo[] finfos = type.GetFields(flags);
|
||||
foreach (var finfo in finfos)
|
||||
{
|
||||
finfo.SetValue(copyAsset, finfo.GetValue(origAsset));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void RipAudioClips(System.Object asset, string exportPath)
|
||||
{
|
||||
Type type = asset.GetType();
|
||||
BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Default;
|
||||
|
||||
FieldInfo[] finfos = type.GetFields(flags);
|
||||
foreach (var finfo in finfos)
|
||||
{
|
||||
Debug.Log("Field: " + finfo.Name + ", Type: " + finfo.FieldType);
|
||||
|
||||
if(finfo.GetValue(asset) is List<AudioClip>)
|
||||
{
|
||||
Debug.Log("List!");
|
||||
|
||||
List<AudioClip> audioList = finfo.GetValue(asset) as List<AudioClip>;
|
||||
|
||||
for (int i = 0; i < audioList.Count; i++)
|
||||
{
|
||||
AudioClip clip = audioList[i];
|
||||
if(clip != null)
|
||||
{
|
||||
Debug.Log("Audio Clip! " + clip.name);
|
||||
audioList[i] = SavWav.Save(exportPath, clip);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(finfo.FieldType == typeof(AudioEvent))
|
||||
{
|
||||
Debug.Log("Audio Event!");
|
||||
RipAudioClips(finfo.GetValue(asset), exportPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 80429e2d9786fda44b01ab04cadd4b75
|
||||
timeCreated: 1641758895
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,190 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// Allows for saving audio clips to wav file.
|
||||
/// Code found here: https://gist.github.com/darktable/2317063
|
||||
/// </summary>
|
||||
public static class SavWav
|
||||
{
|
||||
|
||||
const int HEADER_SIZE = 44;
|
||||
struct ClipData
|
||||
{
|
||||
|
||||
public int samples;
|
||||
public int channels;
|
||||
public float[] samplesData;
|
||||
|
||||
}
|
||||
|
||||
public static AudioClip Save(string path, AudioClip clip)
|
||||
{
|
||||
var assetPath = path.Trim('/') + "/" + clip.name + ".wav";
|
||||
var filepath = Directory.GetParent(Application.dataPath) + "/" + path.Trim('/') + "/" + clip.name + ".wav";
|
||||
|
||||
Debug.Log(filepath);
|
||||
Debug.Log(assetPath);
|
||||
|
||||
AssetDatabase.DeleteAsset(assetPath);
|
||||
|
||||
// Make sure directory exists if user is saving to sub dir.
|
||||
ClipData clipdata = new ClipData();
|
||||
clipdata.samples = clip.samples;
|
||||
clipdata.channels = clip.channels;
|
||||
float[] dataFloat = new float[clip.samples * clip.channels];
|
||||
clip.GetData(dataFloat, 0);
|
||||
clipdata.samplesData = dataFloat;
|
||||
|
||||
using (var fileStream = CreateEmpty(filepath))
|
||||
{
|
||||
MemoryStream memstrm = new MemoryStream();
|
||||
ConvertAndWrite(memstrm, clipdata);
|
||||
memstrm.WriteTo(fileStream);
|
||||
WriteHeader(fileStream, clip);
|
||||
}
|
||||
|
||||
AssetDatabase.ImportAsset(assetPath, ImportAssetOptions.ForceUpdate);
|
||||
AssetDatabase.Refresh();
|
||||
|
||||
return (AudioClip)AssetDatabase.LoadAssetAtPath(assetPath, typeof(AudioClip));
|
||||
}
|
||||
|
||||
public static AudioClip TrimSilence(AudioClip clip, float min)
|
||||
{
|
||||
var samples = new float[clip.samples];
|
||||
|
||||
clip.GetData(samples, 0);
|
||||
|
||||
return TrimSilence(new List<float>(samples), min, clip.channels, clip.frequency);
|
||||
}
|
||||
|
||||
public static AudioClip TrimSilence(List<float> samples, float min, int channels, int hz)
|
||||
{
|
||||
return TrimSilence(samples, min, channels, hz, false, false);
|
||||
}
|
||||
|
||||
public static AudioClip TrimSilence(List<float> samples, float min, int channels, int hz, bool _3D, bool stream)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < samples.Count; i++)
|
||||
{
|
||||
if (Mathf.Abs(samples[i]) > min)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
samples.RemoveRange(0, i);
|
||||
|
||||
for (i = samples.Count - 1; i > 0; i--)
|
||||
{
|
||||
if (Mathf.Abs(samples[i]) > min)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
samples.RemoveRange(i, samples.Count - i);
|
||||
|
||||
var clip = AudioClip.Create("TempClip", samples.Count, channels, hz, _3D, stream);
|
||||
|
||||
clip.SetData(samples.ToArray(), 0);
|
||||
|
||||
return clip;
|
||||
}
|
||||
|
||||
static FileStream CreateEmpty(string filepath)
|
||||
{
|
||||
var fileStream = new FileStream(filepath, FileMode.Create);
|
||||
byte emptyByte = new byte();
|
||||
|
||||
for (int i = 0; i < HEADER_SIZE; i++) //preparing the header
|
||||
{
|
||||
fileStream.WriteByte(emptyByte);
|
||||
}
|
||||
|
||||
return fileStream;
|
||||
}
|
||||
|
||||
static void ConvertAndWrite(MemoryStream memStream, ClipData clipData)
|
||||
{
|
||||
float[] samples = new float[clipData.samples * clipData.channels];
|
||||
|
||||
samples = clipData.samplesData;
|
||||
|
||||
Int16[] intData = new Int16[samples.Length];
|
||||
|
||||
Byte[] bytesData = new Byte[samples.Length * 2];
|
||||
|
||||
const float rescaleFactor = 32767; //to convert float to Int16
|
||||
|
||||
for (int i = 0; i < samples.Length; i++)
|
||||
{
|
||||
intData[i] = (short)(samples[i] * rescaleFactor);
|
||||
//Debug.Log (samples [i]);
|
||||
}
|
||||
Buffer.BlockCopy(intData, 0, bytesData, 0, bytesData.Length);
|
||||
memStream.Write(bytesData, 0, bytesData.Length);
|
||||
}
|
||||
|
||||
static void WriteHeader(FileStream fileStream, AudioClip clip)
|
||||
{
|
||||
|
||||
var hz = clip.frequency;
|
||||
var channels = clip.channels;
|
||||
var samples = clip.samples;
|
||||
|
||||
fileStream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
Byte[] riff = System.Text.Encoding.UTF8.GetBytes("RIFF");
|
||||
fileStream.Write(riff, 0, 4);
|
||||
|
||||
Byte[] chunkSize = BitConverter.GetBytes(fileStream.Length - 8);
|
||||
fileStream.Write(chunkSize, 0, 4);
|
||||
|
||||
Byte[] wave = System.Text.Encoding.UTF8.GetBytes("WAVE");
|
||||
fileStream.Write(wave, 0, 4);
|
||||
|
||||
Byte[] fmt = System.Text.Encoding.UTF8.GetBytes("fmt ");
|
||||
fileStream.Write(fmt, 0, 4);
|
||||
|
||||
Byte[] subChunk1 = BitConverter.GetBytes(16);
|
||||
fileStream.Write(subChunk1, 0, 4);
|
||||
|
||||
UInt16 two = 2;
|
||||
UInt16 one = 1;
|
||||
|
||||
Byte[] audioFormat = BitConverter.GetBytes(one);
|
||||
fileStream.Write(audioFormat, 0, 2);
|
||||
|
||||
Byte[] numChannels = BitConverter.GetBytes(channels);
|
||||
fileStream.Write(numChannels, 0, 2);
|
||||
|
||||
Byte[] sampleRate = BitConverter.GetBytes(hz);
|
||||
fileStream.Write(sampleRate, 0, 4);
|
||||
|
||||
Byte[] byteRate = BitConverter.GetBytes(hz * channels * 2); // sampleRate * bytesPerSample*number of channels, here 44100*2*2
|
||||
fileStream.Write(byteRate, 0, 4);
|
||||
|
||||
UInt16 blockAlign = (ushort)(channels * 2);
|
||||
fileStream.Write(BitConverter.GetBytes(blockAlign), 0, 2);
|
||||
|
||||
UInt16 bps = 16;
|
||||
Byte[] bitsPerSample = BitConverter.GetBytes(bps);
|
||||
fileStream.Write(bitsPerSample, 0, 2);
|
||||
|
||||
Byte[] datastring = System.Text.Encoding.UTF8.GetBytes("data");
|
||||
fileStream.Write(datastring, 0, 4);
|
||||
|
||||
Byte[] subChunk2 = BitConverter.GetBytes(samples * channels * 2);
|
||||
fileStream.Write(subChunk2, 0, 4);
|
||||
|
||||
// fileStream.Close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 16f4f028d3b75704fbc40b3122b0c568
|
||||
timeCreated: 1641758895
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 67b0d6a5c1b85824bbcb13aefd6b9879
|
||||
folderAsset: yes
|
||||
timeCreated: 1640644671
|
||||
licenseType: Free
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7390fd4499410bb4da9b31d5f5773db7
|
||||
folderAsset: yes
|
||||
timeCreated: 1640720511
|
||||
licenseType: Free
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 6.5 KiB |
@@ -0,0 +1,76 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 95d557f2518a98942b8b75fb4c572df2
|
||||
timeCreated: 1640801294
|
||||
licenseType: Free
|
||||
TextureImporter:
|
||||
fileIDToRecycleName: {}
|
||||
serializedVersion: 4
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 0
|
||||
sRGBTexture: 1
|
||||
linearTexture: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
mipMapFadeDistanceStart: 1
|
||||
mipMapFadeDistanceEnd: 3
|
||||
bumpmap:
|
||||
convertToNormalMap: 0
|
||||
externalNormalMap: 0
|
||||
heightScale: 0.25
|
||||
normalMapFilter: 0
|
||||
isReadable: 1
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
seamlessCubemap: 0
|
||||
textureFormat: 1
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
filterMode: -1
|
||||
aniso: -1
|
||||
mipBias: -1
|
||||
wrapMode: 1
|
||||
nPOTScale: 0
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 1
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: 0.5, y: 0.5}
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spritePixelsToUnits: 100
|
||||
alphaUsage: 1
|
||||
alphaIsTransparency: 1
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 8
|
||||
textureShape: 1
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
platformSettings:
|
||||
- buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 2048
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
- buildTarget: Standalone
|
||||
maxTextureSize: 2048
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
spritePackingTag:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e2c6465d11669fe48a03a5e0a4858961
|
||||
folderAsset: yes
|
||||
timeCreated: 1640644677
|
||||
licenseType: Free
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,86 @@
|
||||
%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: BlackWhiteEffect
|
||||
m_Shader: {fileID: 4800000, guid: ca1ad64f6fe2663418174309f73c9ec5, type: 3}
|
||||
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
|
||||
- _ClipDistance: 10
|
||||
- _ColorBands: 2
|
||||
- _Cutoff: 0.5
|
||||
- _DetailNormalMapScale: 1
|
||||
- _DstBlend: 0
|
||||
- _GlossMapScale: 1
|
||||
- _Glossiness: 0.5
|
||||
- _GlossyReflections: 1
|
||||
- _LightToNormalFactor: 0.366
|
||||
- _MaxBrightness: 9.34
|
||||
- _Metallic: 0
|
||||
- _MinBrightness: -1.57
|
||||
- _Mode: 0
|
||||
- _NormalCutoff: 0.548
|
||||
- _NormalMultiplier: 11.1
|
||||
- _NormalPower: 4.08
|
||||
- _OcclusionStrength: 1
|
||||
- _Parallax: 0.02
|
||||
- _SmoothnessTextureChannel: 0
|
||||
- _SpecularHighlights: 1
|
||||
- _SrcBlend: 1
|
||||
- _UVSec: 0
|
||||
- _ZWrite: 1
|
||||
m_Colors:
|
||||
- _BrightColor: {r: 1, g: 1, b: 1, a: 1}
|
||||
- _Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
- _DarkColor: {r: 0, g: 0, b: 0, a: 1}
|
||||
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 95bd255dc1046994d88a5333bd4df105
|
||||
timeCreated: 1640641178
|
||||
licenseType: Free
|
||||
NativeFormatImporter:
|
||||
mainObjectFileID: 2100000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3d764943143e36d439f718a1ba9ad47c
|
||||
folderAsset: yes
|
||||
timeCreated: 1640644682
|
||||
licenseType: Free
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,83 @@
|
||||
Shader "Image/BlackWhiteEffect"
|
||||
{
|
||||
Properties
|
||||
{
|
||||
_MainTex ("Texture", 2D) = "white" {}
|
||||
_MinBrightness("Min Brightness", Float) = 0
|
||||
_MaxBrightness("Max Brightness", Float) = 1
|
||||
_ColorBands("Color Bands", Float) = 1000
|
||||
_DarkColor("Dark Color", Color) = (0, 0, 0, 1)
|
||||
_BrightColor("Bright Color", Color) = (1, 1, 1, 1)
|
||||
_LightToNormalFactor("Light To Normal Factor", Range(0, 1)) = 1
|
||||
}
|
||||
SubShader
|
||||
{
|
||||
// No culling or depth
|
||||
Cull Off ZWrite Off ZTest Always
|
||||
|
||||
Pass
|
||||
{
|
||||
CGPROGRAM
|
||||
#pragma vertex vert
|
||||
#pragma fragment frag
|
||||
|
||||
#include "UnityCG.cginc"
|
||||
|
||||
struct appdata
|
||||
{
|
||||
float4 vertex : POSITION;
|
||||
float2 uv : TEXCOORD0;
|
||||
};
|
||||
|
||||
struct v2f
|
||||
{
|
||||
float2 uv : TEXCOORD0;
|
||||
float4 vertex : SV_POSITION;
|
||||
};
|
||||
|
||||
float remap(float value, float from1, float to1, float from2, float to2) {
|
||||
return (value - from1) / (to1 - from1) * (to2 - from2) + from2;
|
||||
}
|
||||
|
||||
|
||||
v2f vert (appdata v)
|
||||
{
|
||||
v2f o;
|
||||
o.vertex = UnityObjectToClipPos(v.vertex);
|
||||
o.uv = v.uv;
|
||||
return o;
|
||||
}
|
||||
|
||||
sampler2D _MainTex, _CameraDepthNormalsTexture;
|
||||
float _MinBrightness, _MaxBrightness, _ColorBands, _LightToNormalFactor;
|
||||
fixed4 _DarkColor, _BrightColor;
|
||||
|
||||
fixed4 frag (v2f i) : SV_Target
|
||||
{
|
||||
float4 depthnormal = tex2D(_CameraDepthNormalsTexture, i.uv);
|
||||
fixed4 col = tex2D(_MainTex, i.uv);
|
||||
|
||||
//decode depthnormal
|
||||
float3 normal;
|
||||
float depth;
|
||||
DecodeDepthNormal(depthnormal, depth, normal);
|
||||
|
||||
//Get the luminance and process it
|
||||
fixed normalLuminance = Luminance(fixed3(normal.b, normal.b, normal.b));
|
||||
fixed lightLuminance = Luminance(fixed3(col.r, col.g, col.b));
|
||||
|
||||
fixed luminance = lerp(normalLuminance, lightLuminance, _LightToNormalFactor);
|
||||
|
||||
luminance = remap(luminance, 0, 1, _MinBrightness, _MaxBrightness);
|
||||
luminance = floor(luminance * _ColorBands) / _ColorBands;
|
||||
|
||||
float depthFactor = 1 - step(1, depth);
|
||||
fixed4 output = lerp(_DarkColor, _BrightColor, luminance);
|
||||
output = fixed4(output.r, output.g, output.b, depthFactor);
|
||||
|
||||
return output;
|
||||
}
|
||||
ENDCG
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ca1ad64f6fe2663418174309f73c9ec5
|
||||
timeCreated: 1640641164
|
||||
licenseType: Free
|
||||
ShaderImporter:
|
||||
defaultTextures: []
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,204 @@
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using System.IO;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
[ExecuteInEditMode]
|
||||
public class IconCamera : MonoBehaviour
|
||||
{
|
||||
[Header("Hover over variables to show additional tooltips.")]
|
||||
[Tooltip("Use this to trigger the capture of an icon IN PLAYMODE ONLY!")]
|
||||
public KeyCode iconCaptureKey;
|
||||
|
||||
[Tooltip("Use this checkbox like a button to trigger the capture of an icon (Editor or Playmode).")]
|
||||
public bool iconCaptureButton;
|
||||
|
||||
[Tooltip("Path to icons folder (without \"Assets\" in the beginning).")]
|
||||
public string path = "Icons";
|
||||
|
||||
[Tooltip("Name of the generated Icon without a file extension (no \".png\" required).")]
|
||||
public string iconName = "ExampleIcon";
|
||||
|
||||
[Tooltip("The depth texture mode of the camera. Some effects will require either DepthNormals or Depth to work correctly")]
|
||||
public DepthTextureMode CameraDepthMode = DepthTextureMode.DepthNormals;
|
||||
|
||||
[Tooltip("Material that determines the post effect of the image")]
|
||||
public Material effectMaterial;
|
||||
|
||||
[Tooltip("The background image that will be applied in areas with full transparency")]
|
||||
public Texture2D background;
|
||||
|
||||
[HideInInspector]
|
||||
public RenderTexture renderTexture;
|
||||
|
||||
|
||||
public Camera thisCamera
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!_camera)
|
||||
{
|
||||
_camera = this.gameObject.GetComponent<Camera>();
|
||||
}
|
||||
|
||||
return _camera;
|
||||
}
|
||||
}
|
||||
private Camera _camera;
|
||||
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (Input.GetKeyDown(iconCaptureKey))
|
||||
{
|
||||
Capture();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void OnRenderImage(RenderTexture source, RenderTexture destination)
|
||||
{
|
||||
if(renderTexture != null)
|
||||
{
|
||||
RenderTexture.ReleaseTemporary(renderTexture);
|
||||
}
|
||||
|
||||
if (effectMaterial != null)
|
||||
{
|
||||
Graphics.Blit(source, destination, effectMaterial);
|
||||
}
|
||||
else
|
||||
{
|
||||
Graphics.Blit(source, destination);
|
||||
}
|
||||
|
||||
renderTexture = RenderTexture.GetTemporary(source.width, source.height);
|
||||
Graphics.Blit(destination, renderTexture);
|
||||
}
|
||||
|
||||
public void Capture()
|
||||
{
|
||||
Debug.Log("Say Cheese!");
|
||||
|
||||
//Manually calling render on the camera caused editor lockup with larger resolutions
|
||||
//thisCamera.Render();
|
||||
|
||||
RenderTexture.active = renderTexture;
|
||||
Texture2D texture = new Texture2D(renderTexture.width, renderTexture.height);
|
||||
texture.ReadPixels(new Rect(0, 0, renderTexture.width, renderTexture.height), 0, 0);
|
||||
texture.Apply();
|
||||
|
||||
texture = FlipTexture(texture);
|
||||
|
||||
if(background != null)
|
||||
{
|
||||
texture = AddBackground(texture, background);
|
||||
}
|
||||
|
||||
|
||||
byte[] bytes = texture.EncodeToPNG();
|
||||
|
||||
string imagePath = Application.dataPath + "/" + path + "/" + iconName + ".png";
|
||||
string assetPath = "Assets" + "/" + path + "/" + iconName + ".png";
|
||||
File.WriteAllBytes(imagePath, bytes);
|
||||
|
||||
AssetDatabase.ImportAsset(assetPath, ImportAssetOptions.ForceUpdate);
|
||||
|
||||
|
||||
TextureImporter importer = (TextureImporter)TextureImporter.GetAtPath(assetPath);
|
||||
|
||||
importer.isReadable = true;
|
||||
importer.textureType = TextureImporterType.Sprite;
|
||||
importer.spriteImportMode = SpriteImportMode.Single;
|
||||
importer.alphaSource = TextureImporterAlphaSource.FromInput;
|
||||
importer.alphaIsTransparency = true;
|
||||
importer.mipmapEnabled = false;
|
||||
importer.wrapMode = TextureWrapMode.Clamp;
|
||||
|
||||
EditorUtility.SetDirty(importer);
|
||||
importer.SaveAndReimport();
|
||||
|
||||
AssetDatabase.Refresh();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns a flipped copy of the given texture
|
||||
/// Taken from this forum: https://forum.unity.com/threads/flipping-texture2d-image-within-unity.35974/
|
||||
/// </summary>
|
||||
/// <param name="original"></param>
|
||||
/// <returns></returns>
|
||||
public Texture2D FlipTexture(Texture2D original)
|
||||
{
|
||||
Texture2D flipped = new Texture2D(original.width, original.height);
|
||||
|
||||
int origWidth = original.width;
|
||||
int origHeight = original.height;
|
||||
|
||||
for (int x = 0; x < origWidth; x++)
|
||||
{
|
||||
for (int y = 0; y < origHeight; y++)
|
||||
{
|
||||
flipped.SetPixel(x, origHeight - y - 1, original.GetPixel(x, y));
|
||||
}
|
||||
}
|
||||
flipped.Apply();
|
||||
|
||||
return flipped;
|
||||
}
|
||||
|
||||
|
||||
public Texture2D AddBackground(Texture2D original, Texture2D background)
|
||||
{
|
||||
Texture2D result = new Texture2D(original.width, original.height);
|
||||
|
||||
int origWidth = original.width;
|
||||
int origHeight = original.height;
|
||||
int backWidth = background.width;
|
||||
int backHeight = background.height;
|
||||
|
||||
for (int x = 0; x < origWidth; x++)
|
||||
{
|
||||
for (int y = 0; y < origHeight; y++)
|
||||
{
|
||||
Color originalPixel = original.GetPixel(x, y);
|
||||
|
||||
if(originalPixel.a == 0)
|
||||
{
|
||||
int backX = (int)Remap(x, 0, origWidth, 0, backWidth);
|
||||
int backY = (int)Remap(y, 0, origHeight, 0, backHeight);
|
||||
result.SetPixel(x, y, background.GetPixel(backX, backY));
|
||||
}
|
||||
else
|
||||
{
|
||||
result.SetPixel(x, y, originalPixel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result.Apply();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maps a given value from one range to another
|
||||
/// Taken from this forum: https://forum.unity.com/threads/re-map-a-number-from-one-range-to-another.119437/
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <param name="from1"></param>
|
||||
/// <param name="to1"></param>
|
||||
/// <param name="from2"></param>
|
||||
/// <param name="to2"></param>
|
||||
/// <returns></returns>
|
||||
public float Remap(float value, float from1, float to1, float from2, float to2)
|
||||
{
|
||||
return (value - from1) / (to1 - from1) * (to2 - from2) + from2;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dcb72b0d9a9ea3b43a48a22b952f43c5
|
||||
timeCreated: 1637536229
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user