squash commits

This commit is contained in:
2025-01-07 18:54:46 +02:00
parent 855639487b
commit 62c0a21987
3632 changed files with 708443 additions and 999 deletions
@@ -0,0 +1,841 @@
using System;
using System.Collections.Generic;
using UMA.CharacterSystem;
using Unity.Collections;
using UnityEditor;
using UnityEngine;
using UnityEngine.SceneManagement;
/// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
/// Are we missing // SkinnedMeshAligner.AlignBindPose(prefabMesh, resultingSkinnedMesh);
/// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
namespace UMA
{
public class DecalEditor : IEditorScene
{
static int instance = 0;
int localInstance;
Scene scene;
InteractiveUMAWindow sceneView;
private Rect infoRect = new Rect(10, 30, 400, 30);
private GUIStyle whiteLabels;
private GUIStyle blackLabels;
private GameObject Character;
private DynamicCharacterAvatar avatar;
private GameObject sceneRoot;
// UMA transform bones
private Transform Root;
private Transform Global;
private Transform Position;
public Material ScreenBlocker;
public Texture blockerTex;
private bool showHelp;
private bool showRadius = true;
private int savedLockedLayers;
private DecalIndicator decalIndicator;
private DecalManager decalManager;
float currentRotation = 0;
float distance = 0.006f;
public Material decalMaterial;
public List<string> RaceNames = new List<string>();
int raceNumber = 0;
private GameObject VertexMarker;
private GameObject PlanesMarker;
bool ShowSlotDialog = false;
DecalDefinition SlotDecal;
private string SaveFolder;
private string decalSlotName;
int decalNum;
public int refVertexNumber;
public Vector3 refVertexPosition;
NativeArray<byte> basebonesPerVertex;
NativeArray<BoneWeight1> baseboneWeights;
List<string> boneNames = new List<string>();
public DecalEditor()
{
localInstance = instance;
instance++;
}
[MenuItem("UMA/Interactive Decals (EXPERIMENTAL)")]
public static void Init()
{
DecalEditor de = new DecalEditor();
InteractiveUMAWindow.Init("UMA Decals - EXPERIMENTAL", de);
}
private void ResetLabelStart()
{
infoRect = new Rect(10, 20, 400, 30);
}
private void MoveToNextMessage(float xoffset, float yoffset)
{
infoRect.x += xoffset;
infoRect.y += yoffset;
}
private void DrawNextLabel(string lbl)
{
// Frame the text so it's visible everywhere
MoveToNextMessage(-1, -1);
GUI.Label(infoRect, lbl, blackLabels);
MoveToNextMessage(2, 0);
GUI.Label(infoRect, lbl, blackLabels);
MoveToNextMessage(0, 2);
GUI.Label(infoRect, lbl, blackLabels);
MoveToNextMessage(-2, 0);
GUI.Label(infoRect, lbl, blackLabels);
MoveToNextMessage(1, -1);
GUI.Label(infoRect, lbl, whiteLabels);
MoveToNextMessage(0, 20);
}
public void Initialize(InteractiveUMAWindow sceneView, Scene scene)
{
this.sceneView = sceneView;
this.scene = scene;
ResetLabelStart();
whiteLabels = new GUIStyle(EditorStyles.boldLabel);
blackLabels = new GUIStyle(EditorStyles.boldLabel);
whiteLabels.normal.textColor = Color.white;
blackLabels.normal.textColor = Color.black;
}
void HelpWindow(int WindowID)
{
GUILayout.Label("Left click and drag to area select");
GUILayout.Label("Hold SHIFT while dragging to ADD polygons");
GUILayout.Label("Hold CTRL while dragging to REMOVE polygons");
GUILayout.Label("Hold ALT while dragging to orbit");
}
Plane GetPlaneInWorldSpace(GameObject planeObject)
{
// get the corners of the plane in worldspace
// return new plane for those corners.
// 0, 10, 110
MeshFilter m = planeObject.GetComponent<MeshFilter>();
Transform t = planeObject.transform;
Vector3 v0 = t.TransformPoint(m.sharedMesh.vertices[0]);
Vector3 v2 = t.TransformPoint(m.sharedMesh.vertices[10]);
Vector3 v1 = t.TransformPoint(m.sharedMesh.vertices[110]);
#if SHOW_PLANES
GameObject g1 = GameObject.Instantiate(PlanesMarker, v0, Quaternion.identity, Root);
GameObject g2 = GameObject.Instantiate(PlanesMarker, v1, Quaternion.identity, Root);
GameObject g3 = GameObject.Instantiate(PlanesMarker, v2, Quaternion.identity, Root);
SceneManager.MoveGameObjectToScene(g1, scene);
SceneManager.MoveGameObjectToScene(g2, scene);
SceneManager.MoveGameObjectToScene(g3, scene);
#endif
return new Plane(v0, v1, v2);
}
Plane[] GetPlanesInWorldspace()
{
Plane[] WorldspacePlanes = new Plane[6];
WorldspacePlanes[0] = GetPlaneInWorldSpace(decalIndicator.U1);
WorldspacePlanes[1] = GetPlaneInWorldSpace(decalIndicator.U2);
WorldspacePlanes[2] = GetPlaneInWorldSpace(decalIndicator.V1);
WorldspacePlanes[3] = GetPlaneInWorldSpace(decalIndicator.V2);
WorldspacePlanes[4] = GetPlaneInWorldSpace(decalIndicator.Front);
WorldspacePlanes[5] = GetPlaneInWorldSpace(decalIndicator.Back);
return WorldspacePlanes;
}
private void SlotDialog(int id)
{
GUILayout.BeginHorizontal(EditorStyles.toolbarButton);
EditorGUILayout.LabelField("Save Slot",EditorStyles.boldLabel);
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
if (GUILayout.Button("Select Folder", GUILayout.Width(100)))
{
SaveFolder = EditorUtility.OpenFolderPanel("Select destination folder", SaveFolder, "Decal_" + decalNum);
PlayerPrefs.SetString("UMADecalFolder", SaveFolder);
}
GUILayout.Space(16);
GUILayout.Label(SaveFolder);
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
GUILayout.Label("Enter Name", GUILayout.Width(100));
SlotDecal.Name = GUILayout.TextField(SlotDecal.Name);
GUILayout.EndHorizontal();
GUILayout.Space(10);
if (String.IsNullOrEmpty(SaveFolder))
{
GUILayout.Label("Select a folder to save the prefab");
}
else
{
GUILayout.Label(" ");
}
GUILayout.Space(60);
GUILayout.BeginHorizontal();
GUILayout.Label("", GUILayout.ExpandWidth(true));
if (GUILayout.Button("Save",GUILayout.Width(60)))
{
if (string.IsNullOrEmpty(SaveFolder))
{
EditorUtility.DisplayDialog("error", "Please select a folder before saving the slot!", "OK");
}
else
{
SaveSlot(SlotDecal);
ShowSlotDialog = false;
}
}
if (GUILayout.Button("Exit",GUILayout.Width(60)))
{
ShowSlotDialog = false;
}
GUILayout.EndHorizontal();
}
void SaveSlot(DecalDefinition d)
{
SimpleDecal sd = d.DecalMeshObject.GetComponent<SimpleDecal>();
sd.SaveDecal(d.Name, SaveFolder, true, Character.GetComponentInChildren<SkinnedMeshRenderer>(),d,VertexMarker,scene,sceneRoot);
}
void DecalList(int WindowID)
{
DecalDefinition deleteMe = null;
EditorGUILayout.BeginHorizontal();
raceNumber = EditorGUILayout.Popup(raceNumber, RaceNames.ToArray(),GUILayout.ExpandWidth(true));
if (GUILayout.Button("Change Race",GUILayout.ExpandWidth(false)))
{
if (raceNumber >= 0)
{
Debug.Log($"Changing race to {raceNumber}: {RaceNames[raceNumber]} ");
string theRace = RaceNames[raceNumber];
avatar.ForceRaceChange(theRace);
avatar.GenerateSingleUMA();
}
foreach (DecalDefinition d in decalManager.Decals)
{
if (d.DecalMeshObject != null)
{
GameObject.DestroyImmediate(d.DecalMeshObject);
}
}
decalManager.Decals.Clear();
}
if (GUILayout.Button("Insp Char", GUILayout.ExpandWidth(false)))
{
InspectorUtlity.InspectTarget(Character);
}
EditorGUILayout.EndHorizontal();
GUILayout.Space(10);
decalNum = 1;
foreach(DecalDefinition d in decalManager.Decals)
{
EditorGUILayout.BeginHorizontal();
if (GUILayout.Button(d.Name, GUILayout.ExpandWidth(true)))
{
Selection.activeGameObject = d.DecalMeshObject;
sceneView.FrameSelected();
}
if (GUILayout.Button("Insp",GUILayout.Width(42)))
{
InspectorUtlity.InspectTarget(d.DecalMeshObject);
}
if (GUILayout.Button("Save", GUILayout.Width(42)))
{
SlotDecal = d;
SaveFolder = PlayerPrefs.GetString("UMADecalFolder", "");
ShowSlotDialog = true;
}
if (GUILayout.Button("x",GUILayout.Width(20)))
{
deleteMe = d;
}
EditorGUILayout.EndHorizontal();
decalNum++;
}
if (deleteMe != null)
{
decalManager.Decals.Remove(deleteMe);
if (deleteMe.DecalMeshObject != null)
{
GameObject.DestroyImmediate(deleteMe.DecalMeshObject);
}
}
}
void SceneWindow(int WindowID)
{
if (decalIndicator == null)
{
return;
}
float val = decalIndicator.transform.localScale.x;
float newval = EditorGUILayout.Slider("Decal Size", val, 0.0f, 1.0f);
if (val != newval)
{
decalIndicator.transform.localScale = new Vector3(newval, newval, newval);
EditorPrefs.SetFloat("DecalIndicatorScale", newval);
}
float newRotation = EditorGUILayout.Slider("Decal Rotation", currentRotation, 0.0f, 360.0f);
if (newRotation != currentRotation)
{
Vector3 localEuler = decalIndicator.LocalEuler;
localEuler.z = newRotation;
decalIndicator.transform.localEulerAngles = localEuler;
currentRotation = newRotation;
}
distance = EditorGUILayout.Slider("Decal Offset",distance, 0.001f, 0.02f);
showRadius = GUILayout.Toggle(showRadius, "Show indicator");
if (GUI.changed)
{
decalIndicator.gameObject.SetActive(showRadius);
}
var newMaterial = EditorGUILayout.ObjectField(decalMaterial, typeof(Material),false) as Material;
if (newMaterial != decalMaterial)
{
decalIndicator.visualPlane.GetComponent<MeshRenderer>().material = newMaterial;
decalMaterial = newMaterial;
}
if (GUILayout.Button("Capture current decal"))
{
// Todo: get all meshes
Mesh bakedMesh = FreezeCurrentMesh(sceneView);
GameObject newDecal = GameObject.Instantiate(decalIndicator.visualPlane,Character.transform.position,Character.transform.rotation);
newDecal.transform.localScale= new Vector3(1f, 1f, 1f);
MeshFilter DecalMeshFilter = newDecal.GetComponent<MeshFilter>();
Mesh decalMesh = Mesh.Instantiate(DecalMeshFilter.sharedMesh);
Vector3[] verts = DecalMeshFilter.sharedMesh.vertices;
PhysicsScene physcene = PhysicsSceneExtensions.GetPhysicsScene(scene);
Transform t = decalIndicator.visualPlane.transform;
Mesh characterMesh = avatar.umaData.GetRenderer(0).sharedMesh;
NativeArray<byte> basebonesPerVertex = characterMesh.GetBonesPerVertex();
NativeArray<BoneWeight1> baseboneWeights = characterMesh.GetAllBoneWeights();
Dictionary<int, int> VertexBoneWeightOffset = CalculateVertxBoneWeightOffsets(basebonesPerVertex,baseboneWeights);
List<BoneWeight1> newBoneWeights = new List<BoneWeight1>();
List<byte> newBonesPerVertex = new List<byte>();
// logbones = true;
FoundBones.Clear();
for (int i=0;i<verts.Length;i++)
{
Vector3 src =t.TransformPoint(verts[i]);
verts[i] = LocalRayHit(physcene, src , distance, bakedMesh,VertexBoneWeightOffset,basebonesPerVertex,baseboneWeights);
if (lastboneWeights != null)
{
newBoneWeights.AddRange(lastboneWeights);
newBonesPerVertex.Add((byte)lastboneWeights.Length);
}
else
{
BoneWeight1 nullBW = new BoneWeight1();
nullBW.boneIndex = 0;
nullBW.weight = 1.0f;
newBoneWeights.Add(nullBW);
newBonesPerVertex.Add(1);
}
}
if (FoundBones.Count > 0)
{
foreach(var kp in FoundBones)
{
Debug.Log($"FoundBone: {kp.Key}, {kp.Value} Hash {UMAUtils.StringToHash(kp.Value)}");
}
}
decalMesh.SetVertices(verts);
DecalMeshFilter.mesh = decalMesh;
decalMesh.RecalculateNormals();
decalMesh.RecalculateTangents();
decalMesh.RecalculateBounds();
var smr = newDecal.AddComponent<SkinnedMeshRenderer>();
smr.sharedMesh = decalMesh;
smr.updateWhenOffscreen = true;
SimpleDecal sd = newDecal.AddComponent<SimpleDecal>();
SkinnedMeshRenderer characterRenderer = Character.GetComponentInChildren<SkinnedMeshRenderer>();
UMAData cData = avatar.umaData;
var boneHashNames = cData.skeleton.GetBoneHashNames();
int[] hashes = new int[boneHashNames.Count];
string[] names = new string[boneHashNames.Count];
for(int i=0;i<boneHashNames.Count;i++)
{
hashes[i] = boneHashNames[i].Key;
names[i] = boneHashNames[i].Value;
}
sd.Configure(names,hashes, newBonesPerVertex.ToArray(), newBoneWeights.ToArray());
SceneManager.MoveGameObjectToScene(newDecal, scene);
DecalManager dm = Character.GetComponentInChildren<DecalManager>();
DecalDefinition dd = new DecalDefinition();
dd.bakedMesh = bakedMesh;
dd.planesInWorldSpace = GetPlanesInWorldspace();
dd.WorldImpactPoint = decalIndicator.gameObject.transform.position;
dd.LocalImpactPoint = avatar.gameObject.transform.InverseTransformPoint(dd.WorldImpactPoint);
int MaxIndex = 0;
foreach(DecalDefinition cd in dm.Decals)
{
if (cd.material.name == decalMaterial.name)
{
if (cd.InitialIndex > MaxIndex)
{
MaxIndex = cd.InitialIndex;
}
}
}
dd.InitialIndex = MaxIndex + 1;
dd.Name = decalMaterial.name + "_" + dd.InitialIndex;
dd.material = decalMaterial;
dd.DecalMeshObject = newDecal;
dm.Decals.Add(dd);
// DecalMeshFilter.sharedMesh.SetVertices(verts);
// get all vertexes in the circle,
// get all triangles that face the camera.
// build new triangle list for the submesh
/*
List<Vector3> verts = new List<Vector3>();
List<int> decalVerts = new List<int>();
m.GetVertices(verts);
for(int i=0;i<verts.Count;i++)
{
Vector3 meshvert = verts[i];
Vector3 Delta = meshvert - decalIndicator.transform.localPosition;
if (Delta.magnitude < decalIndicator.transform.localScale.x)
{
decalVerts.Add(i);
}
}
if (decalVerts.Count > 0)
{
// find all the triangles for the new decal.
//
}
// Save decal information needed for submesh.
// Add the submesh.
*/
}
}
private Dictionary<int, int> CalculateVertxBoneWeightOffsets(NativeArray<byte> basebonesPerVertex, NativeArray<BoneWeight1> baseboneWeights)
{
Dictionary<int, int> offsets = new Dictionary<int, int>();
int offset = 0;
for(int i=0;i<basebonesPerVertex.Length;i++)
{
offsets.Add(i, offset);
offset += basebonesPerVertex[i];
}
return offsets;
}
public void Cleanup(InteractiveUMAWindow scene)
{
}
public void OnSceneGUI(InteractiveUMAWindow sceneView)
{
ProcessEvents(sceneView);
const float WindowHeight = 140;
const float WindowWidth = 380;
const float Margin = 20;
Handles.BeginGUI();
if (ShowSlotDialog)
{
Rect ScrBox = new Rect(0, 0, sceneView.position.width, sceneView.position.height);
// EditorGUI.DrawPreviewTexture(ScrBox,blockerTex, ScreenBlocker,ScaleMode.StretchToFill);
GUI.DrawTexture(ScrBox, blockerTex, ScaleMode.StretchToFill, true, 0, new Color(0, 0, 0, 0.8f), 0, 0);
//GUI.Box(ScrBox, blockerTex);
GUI.Window(3, new Rect((sceneView.position.width/2) - 250, (sceneView.position.height/2) - 150, 500, 200), SlotDialog, "");
}
else
{
if (showHelp)
{
// GUI.Window(2, new Rect(10, 20, 280, 104), HelpWindow, "");
GUI.Window(2, new Rect(10, 20, 300, 300), DecalList, "");
}
GUI.Window(1, new Rect(sceneView.position.width - (WindowWidth + Margin), sceneView.position.height - (WindowHeight + Margin), WindowWidth, WindowHeight), SceneWindow, "Interactive Decals");
}
Handles.EndGUI();
}
BoneWeight1[] lastboneWeights = null;
private bool logbones = false;
private Dictionary<int, string> FoundBones = new Dictionary<int, string>();
private Vector3 LocalRayHit(PhysicsScene physcene, Vector3 vert, float offset, Mesh theMesh,
Dictionary<int, int> VertexBoneWeightOffset,
NativeArray<byte> basebonesPerVertex,
NativeArray<BoneWeight1> baseboneWeights)
{
lastboneWeights = null;
// get world position of hit.
// translate to local coordinates of character.
//
Ray ray = new Ray(vert, decalIndicator.Ray.direction);
RaycastHit hit;
Debug.DrawRay(vert, decalIndicator.Ray.direction, Color.green,30.0f);
// Rays.Add(ray);
if (!physcene.Raycast(ray.origin, ray.direction, out hit, 512.0f, LayerMask.GetMask("Player")))
{
Debug.Log("Ray did not hit");
return vert;
}
if (hit.triangleIndex < 0)
{
return vert;
}
Vector3 hitpoint = hit.point + ((decalIndicator.Ray.direction.normalized * -1) * offset);
Vector3 LocalHitpoint = hit.transform.InverseTransformPoint(hitpoint);
// Transform umaTransform = GetUMATransform(hit.transform);
for (int i = 0; i < theMesh.subMeshCount; i++)
{
var smd = theMesh.GetSubMesh(i);
if (hit.triangleIndex < smd.indexStart)
{
continue;
}
if (hit.triangleIndex >= (smd.indexStart + (smd.indexCount/3)))
{
continue;
}
// should fall through for only ONE submesh.
int[] tris = theMesh.GetTriangles(i);
int submishtricount = smd.indexCount / 3;
int baseIndexStart = hit.triangleIndex - smd.indexStart;
int tribase = 3 * (hit.triangleIndex - smd.indexStart);
try
{
int v1x = tris[tribase];
int v2x = tris[tribase + 1];
int v3x = tris[tribase + 2];
Vector3 v1 = theMesh.vertices[v1x];
Vector3 v2 = theMesh.vertices[v2x];
Vector3 v3 = theMesh.vertices[v3x];
#if LOCAL_SPACE
// lastboneWeights = GetBoneWeights(LocalHitpoint, v1, v2, v3, v1x, v2x, v3x, VertexBoneWeightOffset, basebonesPerVertex, baseboneWeights);
#else
Matrix4x4 theMat = Character.transform.localToWorldMatrix;
v1 = theMat * v1;
v2 = theMat * v2;
v3 = theMat * v3;
lastboneWeights = GetBoneWeights(hitpoint, v1, v2, v3, v1x, v2x, v3x, VertexBoneWeightOffset, basebonesPerVertex, baseboneWeights);
#endif
}
catch (Exception ex)
{
Debug.LogException(ex);
}
}
return LocalHitpoint;
}
/// <summary>
///
/// </summary>
/// <param name="Hit"></param>
/// <param name="v1"></param>
/// <param name="v2"></param>
/// <param name="v3"></param>
/// <param name="v1x"></param>
/// <param name="v2x"></param>
/// <param name="v3x"></param>
/// <param name="VertexBoneWeightOffset"> this is a dictionary calculated from the following two. It translates a vertex to the starting index by adding up all the BonesPerVertex of preceding verts</param>
/// <param name="basebonesPerVertex"></param>
/// <param name="baseboneWeights"></param>
/// <returns></returns>
private BoneWeight1[] GetBoneWeights(Vector3 Hit, Vector3 v1, Vector3 v2, Vector3 v3,
int v1x, int v2x, int v3x,
Dictionary<int,int> VertexBoneWeightOffset,
NativeArray<byte> basebonesPerVertex,
NativeArray<BoneWeight1> baseboneWeights)
{
Dictionary<int, float> boneDictionary = new Dictionary<int, float>();
List<BoneWeight1> boneWeights = new List<BoneWeight1>();
float v1len = Mathf.Abs((Hit - v1).magnitude);
float v2len = Mathf.Abs((Hit - v2).magnitude);
float v3len = Mathf.Abs((Hit - v3).magnitude);
float totalDistance = v1len + v2len + v3len;
float v1Perc = v1len / totalDistance;
float v2Perc = v2len / totalDistance;
float v3Perc = v3len / totalDistance;
AddBoneWeights(boneDictionary, v1Perc, v1x, VertexBoneWeightOffset, basebonesPerVertex, baseboneWeights);
AddBoneWeights(boneDictionary, v2Perc, v2x, VertexBoneWeightOffset, basebonesPerVertex, baseboneWeights);
AddBoneWeights(boneDictionary, v3Perc, v3x, VertexBoneWeightOffset, basebonesPerVertex, baseboneWeights);
foreach(var kp in boneDictionary)
{
BoneWeight1 b = new BoneWeight1();
b.boneIndex = kp.Key;
b.weight = kp.Value;
boneWeights.Add(b);
}
boneWeights.Sort((a, b) => (0-(a.weight.CompareTo(b.weight))));
return boneWeights.ToArray();
}
private void AddBoneWeights(Dictionary<int, float> boneDictionary,float percentage, int vertexnumber,
Dictionary<int, int> VertexBoneWeightOffset,
NativeArray<byte> basebonesPerVertex,
NativeArray<BoneWeight1> baseboneWeights)
{
int boneStart = VertexBoneWeightOffset[vertexnumber];
int numBones = basebonesPerVertex[vertexnumber];
for(int i = boneStart; i < (boneStart + numBones); i++)
{
BoneWeight1 b = baseboneWeights[i];
#if _INDEX
if (!boneDictionary.ContainsKey(b.boneIndex)) boneDictionary.Add(b.boneIndex, 0.0f);
boneDictionary[b.boneIndex] += b.weight * percentage;
#else
int boneNum = b.boneIndex;
int boneHash = UMAUtils.StringToHash(boneNames[boneNum]);
if (!boneDictionary.ContainsKey(boneHash))
{
boneDictionary.Add(boneHash, 0.0f);
}
boneDictionary[boneHash] += b.weight * percentage;
#endif
if (logbones)
{
if (FoundBones.ContainsKey(b.boneIndex) == false)
{
FoundBones.Add(b.boneIndex, boneNames[b.boneIndex]);
}
}
}
//logbones = false;
}
private void ProcessEvents(InteractiveUMAWindow sceneView)
{
if (Event.current != null && Event.current.type == EventType.MouseDown && Event.current.button == 0)
{
FreezeCurrentMesh(sceneView);
Ray ray = sceneView.GUIPointToWorldRay(Event.current.mousePosition);
RaycastHit hit;
PhysicsScene physcene = PhysicsSceneExtensions.GetPhysicsScene(sceneView.CurrentScene);
if (!physcene.Raycast(ray.origin, ray.direction, out hit, 512.0f, LayerMask.GetMask("Player")))
{
return;
}
if (decalIndicator != null)
{
if ((hit.transform.gameObject == sceneView.avatarGo) || (hit.transform.parent == sceneView.avatarGo.transform))
{
// Event.current.Use();
// Indicator.transform.position = hit.point;
decalIndicator.gameObject.transform.position = hit.point;
decalIndicator.Ray = ray;
decalIndicator.gameObject.transform.forward = ray.direction;
decalIndicator.LocalEuler = decalIndicator.gameObject.transform.localEulerAngles;
}
}
}
}
private Mesh FreezeCurrentMesh(InteractiveUMAWindow sceneView)
{
SkinnedMeshRenderer smr = sceneView.avatarGo.GetComponentInChildren<SkinnedMeshRenderer>();
boneNames.Clear();
var t = smr.bones;
for (int i=0;i<t.Length;i++)
{
boneNames.Add(t[i].name);
}
MeshCollider mc = sceneView.avatarGo.GetComponentInChildren<MeshCollider>();
if (smr != null)
{
Mesh mesh = new Mesh();
smr.BakeMesh(mesh);
Physics.BakeMesh(mesh.GetInstanceID(), false);
mc.sharedMesh = mesh;
Physics.SyncTransforms();
return mesh;
}
return null;
}
public void ShowHelp(bool isShown)
{
showHelp = isShown;
}
public Material FindBlockerMaterial(GameObject root)
{
MeshRenderer[] mrs = root.GetComponentsInChildren<MeshRenderer>();
foreach (MeshRenderer r in mrs)
{
if (r.gameObject.name == "screenBlocker")
{
return r.sharedMaterial;
}
}
return null;
}
public void InitializationComplete(GameObject root)
{
sceneRoot = root;
float scale = EditorPrefs.GetFloat("DecalIndicatorScale", 1.0f);
decalIndicator = root.GetComponentInChildren<DecalIndicator>();
this.ScreenBlocker = FindBlockerMaterial(root);
string[] texs = ScreenBlocker.GetTexturePropertyNames();
foreach(string s in texs)
{
Texture tex = ScreenBlocker.GetTexture(s);
if (tex != null)
{
blockerTex = tex;
}
}
foreach (Transform t in root.transform)
{
if (t.name == "Sphere")
{
PlanesMarker = t.gameObject;
}
if (t.name == "VertexMarker")
{
VertexMarker = t.gameObject;
}
}
if (decalIndicator != null)
{
decalIndicator.transform.localScale = new Vector3(scale, scale, scale);
}
avatar = root.GetComponentInChildren<DynamicCharacterAvatar>();
Character = avatar.gameObject;
decalManager = root.GetComponentInChildren<DecalManager>();
var races = UMAAssetIndexer.Instance.GetAllAssets<RaceData>();
foreach(RaceData r in races)
{
if (r.raceName == avatar.activeRace.name)
{
raceNumber = RaceNames.Count;
}
RaceNames.Add(r.raceName);
}
var mr = decalIndicator.visualPlane.GetComponent<MeshRenderer>();
if (mr != null)
{
decalMaterial = mr.sharedMaterial;
}
}
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 7aca515d4e40c624c8db3b66e9a6c63e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 35611
packageName: UMA 2
packageVersion: 2.13
assetPath: Assets/UMA/Core/Editor/Decals/DecalEditor.cs
uploadId: 679826
@@ -0,0 +1,16 @@
using UnityEngine;
public class GameObjectTreeViewItem : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 7b60b7fae88d7a54bb2c4fd9f5593461
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 35611
packageName: UMA 2
packageVersion: 2.13
assetPath: Assets/UMA/Core/Editor/Decals/GameObjectTreeViewItem.cs
uploadId: 679826
@@ -0,0 +1,14 @@
using UnityEngine;
using UnityEngine.SceneManagement;
namespace UMA
{
public interface IEditorScene
{
void OnSceneGUI(InteractiveUMAWindow scene);
void Initialize(InteractiveUMAWindow sceneView, Scene scene);
void InitializationComplete(GameObject root);
void Cleanup(InteractiveUMAWindow scene);
void ShowHelp(bool isShown);
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 0f14d5c18226f034bbe531ed3b5b3d23
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 35611
packageName: UMA 2
packageVersion: 2.13
assetPath: Assets/UMA/Core/Editor/Decals/IEditorScene.cs
uploadId: 679826
@@ -0,0 +1,334 @@
using System;
using System.Collections.Generic;
using UMA.CharacterSystem;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.SceneManagement;
using UMA;
using UnityEditor.Compilation;
#if UNITY_2020_1_OR_NEWER
// Get rid of the annoying icon not found error.
[EditorWindowTitle(icon = "", title = "InteractiveWindow", useTypeNameAsIconName = false)]
#endif
public class InteractiveUMAWindow : SceneView
{
bool isInitialized;
string UMAPrefab = "";
GameObject EnvironmentPrefab;
public GameObject libGo;
public GameObject avatarGo;
public DynamicCharacterAvatar avatar;
public IEditorScene editor;
public Scene CurrentScene;
private static GUIStyle bsNormal = null;
private static GUIStyle bsToggled = null;
private string[] cameraModeNames = { "Textured", "Textured Wireframe", "Wireframe" };
private DrawCameraMode[] cameraModes = { DrawCameraMode.Textured, DrawCameraMode.TexturedWire, DrawCameraMode.Wireframe };
private int camMode = 0;
private int savedLockedLayers;
private static List<InteractiveUMAWindow> Windows = new List<InteractiveUMAWindow>();
public GameObject Indicator;
//private GameObject capsule;
public static string WindowName;
private bool showHelp = true;
private static Dictionary<string, InteractiveUMAWindow> windows = new Dictionary<string, InteractiveUMAWindow>();
public static void Init(string windowName, IEditorScene editor)
{
if (Application.isPlaying)
{
if (EditorUtility.DisplayDialog("Error", "The UMA Interactive window does not work in playmode", "OK"))
{
return;
}
}
if (windows.ContainsKey(windowName))
{
InteractiveUMAWindow window = windows[windowName];
window.Close();
}
bsNormal = "Button";
bsToggled = new GUIStyle(bsNormal);
bsToggled.normal.background = bsToggled.active.background;
WindowName = windowName;
var w = GetWindow<InteractiveUMAWindow>(WindowName, true, typeof(SceneView));
if (w == null)
{
EditorUtility.DisplayDialog("Error", "Unable to create or get Interactive UMA window!", "OK");
return;
}
w.editor = editor;
//w.InitializeIfNeeded();
w.showGrid = false;
w.drawGizmos = false;
w.sceneLighting = true;
w.SetCameraMode(w.camMode);
w.Focus();
AppDomain.CurrentDomain.DomainUnload += CurrentDomain_DomainUnload;
}
public bool ShowToggleButton(string txt, bool initialValue, params GUILayoutOption[] Options)
{
if (GUILayout.Button(txt, initialValue ? bsToggled : bsNormal, Options))
{
initialValue = !initialValue;
}
return initialValue;
}
public bool ShowToggleButton(GUIContent content, bool initialValue, params GUILayoutOption[] Options)
{
if (GUILayout.Button(content, initialValue ? bsToggled : bsNormal, Options))
{
initialValue = !initialValue;
}
return initialValue;
}
public void SetCameraMode(int mode)
{
DrawCameraMode newMode = cameraModes[mode];
CameraMode currentMode = this.cameraMode;
if (currentMode.drawMode == newMode)
{
return;
}
cameraMode = SceneView.GetBuiltinCameraMode(newMode);
}
protected override void OnSceneGUI()
{
InitializeIfNeeded();
if (bsToggled == null)
{
// lost it all...
Close();
return;
}
using (new GUILayout.HorizontalScope())
{
drawGizmos = ShowToggleButton("Gizmos:" + drawGizmos, drawGizmos, GUILayout.ExpandWidth(false));
sceneLighting = ShowToggleButton("Lighting:" + sceneLighting, sceneLighting, GUILayout.ExpandWidth(false));
showHelp = ShowToggleButton("Help:" + showHelp, showHelp, GUILayout.ExpandWidth(false));
camMode = EditorGUILayout.Popup(camMode, cameraModeNames, GUILayout.ExpandWidth(false));
editor.ShowHelp(showHelp);
SetCameraMode(camMode);
GUILayout.Button("", GUILayout.ExpandWidth(true));
if (GUILayout.Button("Focus Avatar", GUILayout.ExpandWidth(false)))
{
Selection.activeGameObject = avatar.gameObject;
avatar.gameObject.SetActive(true);
FrameSelected();
}
}
base.OnSceneGUI();
}
private void InitializeIfNeeded()
{
if (isInitialized)
{
return;
}
if (UMAPrefab == "")
{
string[] assets = AssetDatabase.FindAssets("UMADefaultUtilityEnvironment");
if (assets == null || assets.Length < 1)
{
EditorUtility.DisplayDialog("Error", "Unable to find UMADefaultUtilityEnvironment Prefab!", "OK");
Close();
return;
}
UMAPrefab = AssetDatabase.GUIDToAssetPath(assets[0]);
}
customScene = EditorSceneManager.NewPreviewScene();
CurrentScene = customScene;
sceneLighting = true;
drawGizmos = true;
editor.Initialize(this, customScene);
EnvironmentPrefab = AssetDatabase.LoadAssetAtPath<GameObject>(UMAPrefab);
libGo = GameObject.Instantiate(EnvironmentPrefab);
SceneManager.MoveGameObjectToScene(libGo, customScene);
avatar = libGo.GetComponentInChildren<DynamicCharacterAvatar>();
avatarGo = avatar.gameObject;
// cube = libGo.GetComponentInChildren<BoxCollider>().gameObject;
// sphere = libGo.GetComponentInChildren<SphereCollider>().gameObject;
//capsule = libGo.GetComponentInChildren<CapsuleCollider>().gameObject;
Selection.activeObject = avatarGo;
// Zoom the scene view into the new object
bool val = FrameSelected(true, true);
Repaint();
avatar.InitialStartup();
isInitialized = true;
editor.InitializationComplete(libGo);
}
public void OnFocus()
{
if (avatarGo != null)
{
Selection.activeTransform = avatarGo.transform;
FrameSelected();
}
savedLockedLayers = Tools.lockedLayers;
Tools.lockedLayers = ~LayerMask.GetMask("Player");
}
public void OnLostFocus()
{
Debug.Log("lost focus");
Tools.lockedLayers = savedLockedLayers;
}
private void EditorChanged(PlayModeStateChange state)
{
// stop the gazillion errors trying to get the controls
CloseCleanup();
}
private static void CurrentDomain_DomainUnload(object sender, EventArgs e)
{
}
public new void OnDestroy()
{
Cleanup();
base.OnDestroy();
}
public override void OnEnable()
{
try
{
//InitializeIfNeeded(); Don't ever do this
base.OnEnable();
titleContent = new GUIContent(WindowName);
EditorApplication.playModeStateChanged += EditorChanged;
CompilationPipeline.compilationStarted += CompilationPipeline_assemblyCompilationStarted;
duringSceneGui += InteractiveUMAWindow_duringSceneGui;
}
catch (Exception) { };
}
private void InteractiveUMAWindow_duringSceneGui(SceneView obj)
{
if (this != null)
{
//Rect rect = new Rect(0, 0, position.width, position.height);
//using (new GUILayout.AreaScope(rect))
//{
if (obj == this)
{
editor.OnSceneGUI(this);
}
//}
}
}
public Vector2 GUIPointToScreenPixelCoordinate(Vector2 guiPoint)
{
return HandleUtility.GUIPointToScreenPixelCoordinate(guiPoint);
}
public Ray GUIPointToWorldRay(Vector2 position, float startZ = float.NegativeInfinity)
{
if (float.IsNegativeInfinity(startZ))
{
startZ = camera.nearClipPlane;
}
Vector2 screenPixelPos = GUIPointToScreenPixelCoordinate(position);
Rect viewport = camera.pixelRect;
Matrix4x4 camToWorld = camera.cameraToWorldMatrix;
Matrix4x4 camToClip = camera.projectionMatrix;
Matrix4x4 clipToCam = camToClip.inverse;
// calculate ray origin and direction in world space
Vector3 rayOriginWorldSpace;
Vector3 rayDirectionWorldSpace;
// first construct an arbitrary point that is on the ray through this screen pixel (remap screen pixel point to clip space [-1, 1])
Vector3 rayPointClipSpace = new Vector3(
(screenPixelPos.x - viewport.x) * 2.0f / viewport.width - 1.0f,
(screenPixelPos.y - viewport.y) * 2.0f / viewport.height - 1.0f,
0.95f
);
// and convert that point to camera space
Vector3 rayPointCameraSpace = clipToCam.MultiplyPoint(rayPointClipSpace);
// in projective mode, the ray passes through the origin in camera space
// so the ray direction is just (ray point - origin) == (ray point)
Vector3 rayDirectionCameraSpace = rayPointCameraSpace;
rayDirectionCameraSpace.Normalize();
rayDirectionWorldSpace = camToWorld.MultiplyVector(rayDirectionCameraSpace);
// calculate the correct startZ offset from the camera by moving a distance along the ray direction
// this assumes camToWorld is a pure rotation/offset, with no scale, so we can use rayDirection.z to calculate how far we need to move
Vector3 cameraPositionWorldSpace = camToWorld.MultiplyPoint(Vector3.zero);
// The camera/projection matrices follow OpenGL convention: positive Z is towards the viewer.
// So negate it to get into Unity convention.
Vector3 originOffsetWorldSpace = rayDirectionWorldSpace * -startZ / rayDirectionCameraSpace.z;
rayOriginWorldSpace = cameraPositionWorldSpace + originOffsetWorldSpace;
return new Ray(rayOriginWorldSpace, rayDirectionWorldSpace);
}
private void CompilationPipeline_assemblyCompilationStarted(object obj)
{
CloseCleanup();
}
public void CloseCleanup()
{
Cleanup();
Close();
}
public override void OnDisable()
{
Debug.Log("On Disable");
base.OnDisable();
Cleanup();
}
private void Cleanup()
{
if (isInitialized)
{
isInitialized = false;
editor.Cleanup(this);
if (customScene != null)
{
Scene s = customScene;
EditorSceneManager.ClosePreviewScene(s);
}
EditorApplication.playModeStateChanged -= EditorChanged;
CompilationPipeline.compilationStarted -= CompilationPipeline_assemblyCompilationStarted;
duringSceneGui -= InteractiveUMAWindow_duringSceneGui;
}
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: f91282e70d6b76b4b80ea9341c8fcbff
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 35611
packageName: UMA 2
packageVersion: 2.13
assetPath: Assets/UMA/Core/Editor/Decals/InteractiveWindow.cs
uploadId: 679826