mirror of
https://github.com/muskit/H3VR-TNH-Quality-of-Life-Improvements.git
synced 2026-06-03 04:34:26 -07:00
Initial commit
This commit is contained in:
@@ -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:
|
||||
Reference in New Issue
Block a user