You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

270 lines
10 KiB
C#

3 years ago
using UnityEngine;
using System.Collections.Generic;
using System.Linq;
using UnityEngine.Polybrush;
using UnityEditor.SettingsManagement;
using System;
using Math = UnityEngine.Polybrush.Math;
namespace UnityEditor.Polybrush
{
internal static class PolySceneUtility
{
#pragma warning disable 618
[UserSetting]
internal static Pref<int> s_GIWorkflowMode = new Pref<int>("GI.WorkflowMode", (int)Lightmapping.giWorkflowMode, SettingsScope.Project);
#pragma warning restore 618
internal static Ray InverseTransformRay(this Transform transform, Ray InWorldRay)
{
Vector3 o = InWorldRay.origin;
o -= transform.position;
o = transform.worldToLocalMatrix * o;
Vector3 d = transform.worldToLocalMatrix.MultiplyVector(InWorldRay.direction);
return new Ray(o, d);
}
/// <summary>
/// Find the nearest triangle intersected by InWorldRay on this mesh. InWorldRay is in world space.
/// @hit contains information about the hit point. @distance limits how far from @InWorldRay.origin the hit
/// point may be. @cullingMode determines what face orientations are tested (Culling.Front only tests front
/// faces, Culling.Back only tests back faces, and Culling.FrontBack tests both).
/// Ray origin and position values are in local space.
/// </summary>
/// <param name="InWorldRay"></param>
/// <param name="transform"></param>
/// <param name="vertices"></param>
/// <param name="triangles"></param>
/// <param name="hit"></param>
/// <param name="distance"></param>
/// <param name="cullingMode"></param>
/// <returns></returns>
internal static bool WorldRaycast(Ray InWorldRay, Transform transform, Vector3[] vertices, int[] triangles, out PolyRaycastHit hit, float distance = Mathf.Infinity, Culling cullingMode = Culling.Front)
{
//null checks, must have a transform, vertices and triangles
if(transform == null || vertices == null || triangles == null )
{
hit = null;
return false;
}
Ray ray = transform.InverseTransformRay(InWorldRay);
return MeshRaycast(ray, vertices, triangles, out hit, distance, cullingMode);
}
/// <summary>
/// Cast a ray (in model space) against a mesh.
/// </summary>
internal static bool MeshRaycast(Ray InRay, Vector3[] vertices, int[] triangles, out PolyRaycastHit hit, float distance = Mathf.Infinity, Culling cullingMode = Culling.Front)
{
Vector3 hitNormal, vert0, vert1, vert2;
Vector3 origin = InRay.origin, direction = InRay.direction;
hit = new PolyRaycastHit(Mathf.Infinity,
Vector3.zero,
Vector3.zero,
-1);
// Iterate faces, testing for nearest hit to ray origin.
for (int CurTri = 0; CurTri < triangles.Length; CurTri += 3)
{
if (CurTri + 2 >= triangles.Length) continue;
if (triangles[CurTri + 2] >= vertices.Length) continue;
vert0 = vertices[triangles[CurTri+0]];
vert1 = vertices[triangles[CurTri+1]];
vert2 = vertices[triangles[CurTri+2]];
// Second pass, test intersection with triangle
if (Math.RayIntersectsTriangle2(origin, direction, vert0, vert1, vert2, out distance, out hitNormal))
{
if (distance < hit.distance)
{
hit.distance = distance;
hit.triangle = CurTri / 3;
hit.position = InRay.GetPoint(hit.distance);
hit.normal = hitNormal;
}
}
}
return hit.triangle > -1;
}
/// <summary>
/// Returns true if the event is one that should consume the mouse or keyboard.
/// </summary>
/// <param name="e"></param>
/// <returns></returns>
internal static bool SceneViewInUse(Event e)
{
return e.alt
|| Tools.current == Tool.View
|| GUIUtility.hotControl > 0
|| (e.isMouse ? e.button > 1 : false)
|| Tools.viewTool == ViewTool.FPS
|| Tools.viewTool == ViewTool.Orbit;
}
static Vector3[] s_WorldBuffer = new Vector3[4096];
/// <summary>
/// Calculates the per-vertex weight for each raycast hit and fills in brush target weights.
/// </summary>
/// <param name="target"></param>
/// <param name="settings"></param>
/// <param name="tool"></param>
/// <param name="bMode"></param>
internal static void CalculateWeightedVertices(BrushTarget target, BrushSettings settings, BrushTool tool = BrushTool.None, BrushMode bMode = null)
{
if(target == null || settings == null)
return;
if(target.editableObject == null)
return;
bool uniformScale = Math.VectorIsUniform(target.transform.lossyScale);
float scale = uniformScale ? 1f / target.transform.lossyScale.x : 1f;
PolyMesh mesh = target.editableObject.visualMesh;
Transform transform = target.transform;
int vertexCount = mesh.vertexCount;
Vector3[] vertices = mesh.vertices;
if (!uniformScale)
{
// As we only increase size only when it's needed, always make sure to
// use the vertexCount variable in loop statements and not the buffer length.
if (s_WorldBuffer.Length < vertexCount)
System.Array.Resize<Vector3>(ref s_WorldBuffer, vertexCount);
for (int i = 0; i < vertexCount; i++)
s_WorldBuffer[i] = transform.TransformPoint(vertices[i]);
vertices = s_WorldBuffer;
}
AnimationCurve curve = settings.falloffCurve;
float radius = settings.radius * scale, falloff_mag = Mathf.Max((radius - radius * settings.falloff), 0.00001f);
Vector3 hitPosition = Vector3.zero;
PolyRaycastHit hit;
if (tool == BrushTool.Texture && mesh.subMeshCount > 1)
{
var mode = bMode as BrushModeTexture;
int[] submeshIndices = mesh.subMeshes[mode.m_CurrentMeshACIndex].indexes;
for (int n = 0; n < target.raycastHits.Count; n++)
{
hit = target.raycastHits[n];
hit.SetVertexCount(vertexCount);
for(int i = 0; i < vertexCount; i++)
{
hit.weights[i] = 0f;
}
hitPosition = uniformScale ? hit.position : transform.TransformPoint(hit.position);
for (int i = 0; i < submeshIndices.Length; i++)
{
int currentIndex = submeshIndices[i];
float dist = (hitPosition - vertices[currentIndex]).magnitude;
float delta = radius - dist;
if (delta >= 0)
{
float weight = Mathf.Clamp(curve.Evaluate(1f - Mathf.Clamp(delta / falloff_mag, 0f, 1f)), 0f, 1f);
hit.weights[currentIndex] = weight;
}
}
}
}
else
{
int[][] common = PolyMeshUtility.GetCommonVertices(mesh);
Vector3 buf = Vector3.zero;
for (int n = 0; n < target.raycastHits.Count; n++)
{
hit = target.raycastHits[n];
hit.SetVertexCount(vertexCount);
hitPosition = uniformScale ? hit.position : transform.TransformPoint(hit.position);
for (int i = 0; i < common.Length; i++)
{
int[] commonItem = common[i];
int commonArrayCount = commonItem.Length;
Math.Subtract(vertices[commonItem[0]], hitPosition, ref buf);
float sqrDist = buf.sqrMagnitude;
if (sqrDist > radius * radius)
{
for (int j = 0; j < commonArrayCount; j++)
hit.weights[commonItem[j]] = 0f;
}
else
{
float weight = Mathf.Clamp(curve.Evaluate(1f - Mathf.Clamp((radius - Mathf.Sqrt(sqrDist)) / falloff_mag, 0f, 1f)), 0f, 1f);
for (int j = 0; j < commonArrayCount; j++)
{
hit.weights[commonItem[j]] = weight;
}
}
}
}
}
target.GetAllWeights(true);
}
internal static IEnumerable<GameObject> FindInstancesInScene(IEnumerable<GameObject> match, System.Func<GameObject, string> instanceNamingFunc)
{
//null checks
if(match == null || instanceNamingFunc == null)
{
return null;
}
IEnumerable<string> matches = match.Where(x => x != null).Select(y => instanceNamingFunc(y));
return UnityEngine.Object.FindObjectsOfType<GameObject>().Where(x => {
return matches.Contains( x.name );
});
}
/// <summary>
/// Store the previous GIWorkflowMode and set the current value to OnDemand (or leave it Legacy).
/// </summary>
internal static void PushGIWorkflowMode()
{
#pragma warning disable 618
s_GIWorkflowMode.value = (int)Lightmapping.giWorkflowMode;
PolybrushSettings.Save();
if (Lightmapping.giWorkflowMode != Lightmapping.GIWorkflowMode.Legacy)
Lightmapping.giWorkflowMode = Lightmapping.GIWorkflowMode.OnDemand;
#pragma warning restore 618
}
/// <summary>
/// Return GIWorkflowMode to it's prior state.
/// </summary>
internal static void PopGIWorkflowMode()
{
#pragma warning disable 618
Lightmapping.giWorkflowMode = (Lightmapping.GIWorkflowMode)s_GIWorkflowMode.value;
#pragma warning restore 618
}
}
}