147 lines
7.0 KiB
C#
147 lines
7.0 KiB
C#
using System;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using UnityEditor;
|
|
using UnityEngine;
|
|
using VF.Builder.Exceptions;
|
|
using Object = UnityEngine.Object;
|
|
|
|
namespace VF.Builder.Ogb {
|
|
public static class TpsConfigurer {
|
|
private static readonly int TpsPenetratorEnabled = Shader.PropertyToID("_TPSPenetratorEnabled");
|
|
private static readonly int TpsPenetratorLength = Shader.PropertyToID("_TPS_PenetratorLength");
|
|
private static readonly int TpsPenetratorScale = Shader.PropertyToID("_TPS_PenetratorScale");
|
|
private static readonly int TpsPenetratorRight = Shader.PropertyToID("_TPS_PenetratorRight");
|
|
private static readonly int TpsPenetratorUp = Shader.PropertyToID("_TPS_PenetratorUp");
|
|
private static readonly int TpsPenetratorForward = Shader.PropertyToID("_TPS_PenetratorForward");
|
|
private static readonly int TpsIsSkinnedMeshRenderer = Shader.PropertyToID("_TPS_IsSkinnedMeshRenderer");
|
|
private static readonly string TpsIsSkinnedMeshKeyword = "TPS_IsSkinnedMesh";
|
|
private static readonly int TpsBakedMesh = Shader.PropertyToID("_TPS_BakedMesh");
|
|
|
|
public static SkinnedMeshRenderer ConfigureRenderer(
|
|
Renderer renderer,
|
|
Transform rootTransform,
|
|
string tmpDir,
|
|
float worldLength
|
|
) {
|
|
if (!renderer.sharedMaterials.Any(m => IsTps(m))) {
|
|
return null;
|
|
}
|
|
|
|
// Convert MeshRenderer to SkinnedMeshRenderer
|
|
if (renderer is MeshRenderer) {
|
|
var newSkin = renderer.gameObject.AddComponent<SkinnedMeshRenderer>();
|
|
var meshFilter = renderer.gameObject.GetComponent<MeshFilter>();
|
|
newSkin.sharedMesh = meshFilter.sharedMesh;
|
|
newSkin.sharedMaterials = renderer.sharedMaterials;
|
|
newSkin.probeAnchor = renderer.probeAnchor;
|
|
Object.DestroyImmediate(renderer);
|
|
Object.DestroyImmediate(meshFilter);
|
|
renderer = newSkin;
|
|
}
|
|
|
|
var skin = renderer as SkinnedMeshRenderer;
|
|
if (!skin) {
|
|
throw new VRCFBuilderException("TPS material found on non-mesh renderer");
|
|
}
|
|
|
|
// Convert unweighted (static) meshes, to true skinned, rigged meshes
|
|
if (skin.sharedMesh.boneWeights.Length == 0) {
|
|
var mainBone = new GameObject("MainBone");
|
|
mainBone.transform.SetParent(rootTransform, false);
|
|
var meshCopy = Object.Instantiate(skin.sharedMesh);
|
|
VRCFuryAssetDatabase.SaveAsset(meshCopy, tmpDir, "withbones_" + meshCopy.name);
|
|
meshCopy.boneWeights = meshCopy.vertices.Select(v => new BoneWeight { weight0 = 1 }).ToArray();
|
|
meshCopy.bindposes = new[] {
|
|
Matrix4x4.identity,
|
|
};
|
|
EditorUtility.SetDirty(meshCopy);
|
|
skin.bones = new[] { mainBone.transform };
|
|
skin.sharedMesh = meshCopy;
|
|
EditorUtility.SetDirty(skin);
|
|
}
|
|
|
|
foreach (var matSlot in Enumerable.Range(0, skin.sharedMaterials.Length)) {
|
|
ConfigureMaterial(skin, matSlot, rootTransform, tmpDir, worldLength);
|
|
}
|
|
|
|
skin.rootBone = rootTransform;
|
|
EditorUtility.SetDirty(skin);
|
|
|
|
return skin;
|
|
}
|
|
|
|
public static void ConfigureMaterial(
|
|
SkinnedMeshRenderer skin,
|
|
int matSlot,
|
|
Transform rootTransform,
|
|
string tmpDir,
|
|
float worldLength
|
|
) {
|
|
var shaderRotation = Quaternion.identity;
|
|
var mat = skin.sharedMaterials[matSlot];
|
|
if (!IsTps(mat)) return;
|
|
mat = Object.Instantiate(mat);
|
|
VRCFuryAssetDatabase.SaveAsset(mat, tmpDir, "ogb_" + mat.name);
|
|
{
|
|
var mats = skin.sharedMaterials;
|
|
mats[matSlot] = mat;
|
|
skin.sharedMaterials = mats;
|
|
EditorUtility.SetDirty(skin);
|
|
}
|
|
|
|
var shaderOptimizer = ReflectionUtils.GetTypeFromAnyAssembly("Thry.ShaderOptimizer");
|
|
if (shaderOptimizer == null) {
|
|
throw new VRCFBuilderException(
|
|
"OGB Penetrator has 'auto-configure TPS' checked, but Poiyomi Pro TPS does not seem to be imported in project.");
|
|
}
|
|
var unlockMethod = shaderOptimizer.GetMethod("Unlock", BindingFlags.NonPublic | BindingFlags.Static);
|
|
VRCFuryAssetDatabase.WithoutAssetEditing(() => {
|
|
ReflectionUtils.CallWithOptionalParams(unlockMethod, null, mat);
|
|
});
|
|
var localScale = rootTransform.lossyScale;
|
|
|
|
mat.SetFloat(TpsPenetratorLength, worldLength);
|
|
mat.SetVector(TpsPenetratorScale, ThreeToFour(localScale));
|
|
mat.SetVector(TpsPenetratorRight, ThreeToFour(shaderRotation * Vector3.right));
|
|
mat.SetVector(TpsPenetratorUp, ThreeToFour(shaderRotation * Vector3.up));
|
|
mat.SetVector(TpsPenetratorForward, ThreeToFour(shaderRotation * Vector3.forward));
|
|
mat.SetFloat(TpsIsSkinnedMeshRenderer, 1);
|
|
mat.EnableKeyword(TpsIsSkinnedMeshKeyword);
|
|
|
|
var bakeUtil = ReflectionUtils.GetTypeFromAnyAssembly("Thry.TPS.BakeToVertexColors");
|
|
var meshInfoType = bakeUtil.GetNestedType("MeshInfo");
|
|
var meshInfo = Activator.CreateInstance(meshInfoType);
|
|
var bakedMesh = MeshBaker.BakeMesh(skin, rootTransform);
|
|
if (bakedMesh == null)
|
|
throw new VRCFBuilderException("Failed to bake mesh for TPS configuration");
|
|
meshInfoType.GetField("bakedVertices").SetValue(meshInfo, bakedMesh.vertices);
|
|
meshInfoType.GetField("bakedNormals").SetValue(meshInfo, bakedMesh.normals);
|
|
meshInfoType.GetField("ownerRenderer").SetValue(meshInfo, skin);
|
|
meshInfoType.GetField("sharedMesh").SetValue(meshInfo, skin.sharedMesh);
|
|
var bakeMethod = bakeUtil.GetMethod(
|
|
"BakePositionsToTexture",
|
|
BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public,
|
|
null,
|
|
new[] { meshInfoType, typeof(Texture2D) },
|
|
null
|
|
);
|
|
Texture2D tex = null;
|
|
VRCFuryAssetDatabase.WithoutAssetEditing(() => {
|
|
tex = (Texture2D)ReflectionUtils.CallWithOptionalParams(bakeMethod, null, meshInfo, null);
|
|
});
|
|
if (string.IsNullOrWhiteSpace(AssetDatabase.GetAssetPath(tex))) {
|
|
throw new VRCFBuilderException("Failed to bake TPS texture");
|
|
}
|
|
mat.SetTexture(TpsBakedMesh, tex);
|
|
EditorUtility.SetDirty(mat);
|
|
}
|
|
|
|
private static Vector4 ThreeToFour(Vector3 a) => new Vector4(a.x, a.y, a.z);
|
|
|
|
public static bool IsTps(Material mat) {
|
|
return mat.HasProperty(TpsPenetratorEnabled) && mat.GetFloat(TpsPenetratorEnabled) > 0;
|
|
}
|
|
}
|
|
}
|