diff --git a/Editor/MeshPostProcessor.cs b/Editor/MeshPostProcessor.cs new file mode 100644 index 0000000..dcb4bf7 --- /dev/null +++ b/Editor/MeshPostProcessor.cs @@ -0,0 +1,219 @@ +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text.RegularExpressions; +using UnityEditor; +using UnityEngine; + +namespace KSPTools +{ + public class MeshPostProcessor : AssetPostprocessor + { + private static string[] components = new string[] {"x", "y", "z", "w"}; + private void OnPostprocessModel(GameObject g) + { + var deleteList = new List(); + Process(g.transform, deleteList); + foreach (var d in deleteList) + { + Debug.Log("Deleting object " + d.name); + Object.DestroyImmediate(d); + } + } + + private static void Process(Transform t, List deleteList) + { + var regex = new Regex(@"\.[0-9][0-9][0-9]"); + var name = t.name; + t.name = regex.Replace(name, ""); + + if (t.name.Contains("_DEL")) + { + deleteList.Add(t.gameObject); + return; + } + + if (t.name.Contains("_MERGE")) + { + // get all mesh filters + var filters = t.GetComponentsInChildren(); + + // get all meshes and transforms + var meshes = filters.Select(i => i.sharedMesh).ToArray(); + var transforms = filters.Select(i => i.transform).ToArray(); + if (meshes.Length != transforms.Length) + Debug.LogError("Mesh and transformation count mismatch when attempting to process " + t.name); + + // generate CombineInstance array + var combines = new CombineInstance[meshes.Length]; + + var mergedMesh = meshes[0]; + var newMesh = new Mesh(); + var baseMatrix = t.worldToLocalMatrix; + var material = filters[0].GetComponent().sharedMaterial; + + for (var i = 0; i < combines.Length; i++) + combines[i] = new CombineInstance + { + mesh = meshes[i], + transform = baseMatrix * transforms[i].localToWorldMatrix + }; + + // combine meshes into newMesh and copy data to mergedMesh + // we cant use newMesh because it will disappear once importing is done, + // also CombineMeshes doesnt include the base mesh in 2017.1 + newMesh.CombineMeshes(combines, true, true, false); + mergedMesh.vertices = newMesh.vertices; + mergedMesh.normals = newMesh.normals; + mergedMesh.triangles = newMesh.triangles; + mergedMesh.colors = newMesh.colors; + mergedMesh.boneWeights = newMesh.boneWeights; + mergedMesh.uv = newMesh.uv; + mergedMesh.RecalculateBounds(); + mergedMesh.RecalculateTangents(); + mergedMesh.name = t.name; + + // clear any existing MeshFilter or MeshRenderer and delete other meshes + Object.DestroyImmediate(t.GetComponent()); + Object.DestroyImmediate(t.GetComponent()); + + for (var i = 1; i < combines.Length; i++) Object.DestroyImmediate(meshes[i]); + + + // assign mesh and material to this gameobject + var filter = t.gameObject.AddComponent(); + var renderer = t.gameObject.AddComponent(); + + filter.sharedMesh = mergedMesh; + renderer.sharedMaterial = material; + + // add children to list for deletion at end of processing step + deleteList.AddRange(transforms.Select(i => i.gameObject)); + + return; + } + + if (t.name.Contains("_COLLIDER")) + { + var filter = t.GetComponent(); + var renderer = t.GetComponent(); + + if (filter == null) Debug.LogWarning("Error processing " + t.name + ": no mesh filter available"); + + var collider = t.gameObject.AddComponent(); + collider.sharedMesh = filter.sharedMesh; + collider.convex = true; + + Object.DestroyImmediate(renderer); + Object.DestroyImmediate(filter); + } + + if (t.name.Contains("_SPHERECOLLIDER")) + { + var filter = t.GetComponent(); + var renderer = t.GetComponent(); + + if (filter == null) Debug.LogWarning("Error processing " + t.name + ": no mesh filter available"); + + var aabb = filter.sharedMesh.bounds; + + var collider = t.gameObject.AddComponent(); + collider.center = aabb.center; + collider.radius = (aabb.size.x + aabb.size.y + aabb.size.z) / 6; + + Object.DestroyImmediate(renderer); + Object.DestroyImmediate(filter); + } + + if (t.name.Contains("_CAPSULECOLLIDER")) + { + var filter = t.GetComponent(); + var renderer = t.GetComponent(); + + if (filter == null) Debug.LogWarning("Error processing " + t.name + ": no mesh filter available"); + + var aabb = filter.sharedMesh.bounds; + var size = aabb.size; + + var collider = t.gameObject.AddComponent(); + collider.center = aabb.center; + + if (size.x > size.y && size.x > size.z) + { + collider.radius = (size.y + size.z) / 4; + collider.height = size.x; + collider.direction = 0; + } + else if (size.y > size.z && size.y > size.x) + { + collider.radius = (size.x + size.z) / 4; + collider.height = size.y; + collider.direction = 1; + } + else + { + collider.radius = (size.x + size.y) / 4; + collider.height = size.z; + collider.direction = 2; + } + + Object.DestroyImmediate(renderer); + Object.DestroyImmediate(filter); + } + + if (t.name.Contains("_BOXCOLLIDER")) + { + var filter = t.GetComponent(); + var renderer = t.GetComponent(); + + if (filter == null) Debug.LogWarning("Error processing " + t.name + ": no mesh filter available"); + + var aabb = filter.sharedMesh.bounds; + + var collider = t.gameObject.AddComponent(); + + collider.center = aabb.center; + collider.size = aabb.size; + + Object.DestroyImmediate(renderer); + Object.DestroyImmediate(filter); + } + + foreach (Transform child in t) Process(child, deleteList); + + var anim = t.GetComponent(); + if (anim != null) + { + foreach (var state in anim) + { + var clip = ((AnimationState) state).clip; + Debug.Log("Trimming animation " + clip.name); + TrimAnimation(clip, t, deleteList); + } + } + } + + private static void TrimAnimation(AnimationClip clip, Transform t, List deleteList) + { + var bindings = AnimationUtility.GetCurveBindings(clip); + + foreach (EditorCurveBinding b in bindings) + { + var curve = AnimationUtility.GetEditorCurve(clip, b); + + var target = (Transform) AnimationUtility.GetAnimatedObject(t.gameObject, b); + + if (curve.length > 2) continue; + if (curve.length == 2 && !deleteList.Contains(target.gameObject)) + { + continue; + //if (!Mathf.Approximately(curve[0].value, curve[1].value)) continue; + //if (!Mathf.Approximately(curve[0].value, 0.0f) && !Mathf.Approximately(curve[0].value, 1.0f)) continue; + + } + + AnimationUtility.SetEditorCurve(clip, b, null); + } + } + } +} \ No newline at end of file diff --git a/Editor/TexturePostProcessor.cs b/Editor/TexturePostProcessor.cs new file mode 100644 index 0000000..af48d48 --- /dev/null +++ b/Editor/TexturePostProcessor.cs @@ -0,0 +1,137 @@ +using System; +using UnityEditor; +using UnityEngine; +using System.IO; + +namespace KSPTools +{ + public class TexturePostProcessor : AssetPostprocessor + { + // what file type to allow + private const string EXTENSION = ".png"; + + + /* SPECULAR */ + // enable specular map copying + private const bool SPEC_ENABLE = true; + + // what the file name should end with for specular maps + private const string SPEC_TAG = "-spec"; + + + /* NORMALS */ + // enable normal map processing + private const bool NORMAL_ENABLE = true; + + // what the file name should end with for normal maps + private const string NORMAL_TAG = "-n"; + + // enable normal map generation from heightmaps + private const bool NORMAL_GENERATE = true; + + + + private Texture2D _otherTexture = null; + private bool _isSpecular = false; + private bool _hasSpecular = false; + + void OnPreprocessTexture() + { + var importer = (TextureImporter) assetImporter; + importer.textureCompression = TextureImporterCompression.Uncompressed; + importer.isReadable = true; + + var extension = Path.GetExtension(assetPath); + if (extension != EXTENSION) return; + + var basename = Path.GetFileNameWithoutExtension(assetPath); + var directory = Path.GetDirectoryName(assetPath); + + if (NORMAL_ENABLE) + { + if (basename.EndsWith(NORMAL_TAG)) + { + importer.textureType = TextureImporterType.NormalMap; + if (NORMAL_GENERATE) importer.convertToNormalmap = true; + } + } + + if (SPEC_ENABLE) { + if (basename.EndsWith(SPEC_TAG)) + { + var othername = basename.Replace(SPEC_TAG, ""); + var otherpath = directory + "/" + othername + extension; + _otherTexture = (Texture2D) AssetDatabase.LoadAssetAtPath(otherpath); + if (_otherTexture == null) + { + LogError("Diffuse file " + otherpath + " not found. Aborting"); + return; + } + + _isSpecular = true; + _hasSpecular = false; + } + else + { + var othername = basename + SPEC_TAG; + var otherpath = directory + "/" + othername + extension; + _otherTexture = (Texture2D) AssetDatabase.LoadAssetAtPath(otherpath); + if (_otherTexture == null) + { + return; + } + + _hasSpecular = true; + importer.alphaSource = TextureImporterAlphaSource.FromGrayScale; + } + } + } + + void OnPostprocessTexture(Texture2D texture) + { + + if (_isSpecular) + { + CopyAlphaChannel(texture, _otherTexture); + return; + } + + if (_hasSpecular) + { + CopyAlphaChannel(_otherTexture, texture); + return; + } + AssetDatabase.SaveAssets(); + } + + private void CopyAlphaChannel(Texture2D source, Texture2D target) + { + var width = source.width; + var height = source.height; + var depth = source.mipmapCount; + + if (width != target.width || height != target.height) + { + LogError("Specular and Diffuse texture sizes to not match in texture " + assetPath); + return; + } + + for (int level = 0; level < depth; level++) + { + var sourcePixels = source.GetPixels32(level); + var targetPixels = target.GetPixels32(level); + + var length = sourcePixels.Length; + + for (var i = 0; i < length; i++) + { + targetPixels[i].a = sourcePixels[i].r; + } + + target.SetPixels32(targetPixels, level); + } + + target.Apply(); + } + } +} \ No newline at end of file