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 s_GIWorkflowMode = new Pref("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); } /// /// 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. /// /// /// /// /// /// /// /// /// 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); } /// /// Cast a ray (in model space) against a mesh. /// 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; } /// /// Returns true if the event is one that should consume the mouse or keyboard. /// /// /// 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]; /// /// Calculates the per-vertex weight for each raycast hit and fills in brush target weights. /// /// /// /// /// 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(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 FindInstancesInScene(IEnumerable match, System.Func instanceNamingFunc) { //null checks if(match == null || instanceNamingFunc == null) { return null; } IEnumerable matches = match.Where(x => x != null).Select(y => instanceNamingFunc(y)); return UnityEngine.Object.FindObjectsOfType().Where(x => { return matches.Contains( x.name ); }); } /// /// Store the previous GIWorkflowMode and set the current value to OnDemand (or leave it Legacy). /// 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 } /// /// Return GIWorkflowMode to it's prior state. /// internal static void PopGIWorkflowMode() { #pragma warning disable 618 Lightmapping.giWorkflowMode = (Lightmapping.GIWorkflowMode)s_GIWorkflowMode.value; #pragma warning restore 618 } } }