Inheritance: MonoBehaviour
        /// <summary>
        /// Find the all faces intersected by InWorldRay on this pb_Object.
        /// </summary>
        /// <param name="InWorldRay">A ray in world space.</param>
        /// <param name="mesh">The ProBuilder object to raycast against.</param>
        /// <param name="hits">If the mesh was intersected, hits contains all intersection point RaycastHit information.</param>
        /// <param name="cullingMode">What sides of triangles does the ray intersect with.</param>
        /// <param name="ignore">Optional collection of faces to ignore when raycasting.</param>
        /// <returns>True if the ray intersects with the mesh, false if not.</returns>
        internal static bool FaceRaycast(
            Ray InWorldRay,
            ProBuilderMesh mesh,
            out List <RaycastHit> hits,
            CullingMode cullingMode,
            HashSet <Face> ignore = null)
        {
            // Transform ray into model space
            InWorldRay.origin -= mesh.transform.position;  // Why doesn't worldToLocalMatrix apply translation?

            InWorldRay.origin    = mesh.transform.worldToLocalMatrix * InWorldRay.origin;
            InWorldRay.direction = mesh.transform.worldToLocalMatrix * InWorldRay.direction;

            Vector3[] vertices = mesh.positionsInternal;

            hits = new List <RaycastHit>();

            // Iterate faces, testing for nearest hit to ray origin.  Optionally ignores backfaces.
            for (int CurFace = 0; CurFace < mesh.facesInternal.Length; ++CurFace)
            {
                if (ignore != null && ignore.Contains(mesh.facesInternal[CurFace]))
                {
                    continue;
                }

                int[] indexes = mesh.facesInternal[CurFace].indexesInternal;

                for (int CurTriangle = 0; CurTriangle < indexes.Length; CurTriangle += 3)
                {
                    Vector3 a = vertices[indexes[CurTriangle + 0]];
                    Vector3 b = vertices[indexes[CurTriangle + 1]];
                    Vector3 c = vertices[indexes[CurTriangle + 2]];

                    var     dist = 0f;
                    Vector3 point;

                    if (Math.RayIntersectsTriangle(InWorldRay, a, b, c, out dist, out point))
                    {
                        Vector3 nrm = Vector3.Cross(b - a, c - a);

                        float dot; // vars used in loop
                        switch (cullingMode)
                        {
                        case CullingMode.Front:
                            dot = Vector3.Dot(InWorldRay.direction, nrm);

                            if (dot > 0f)
                            {
                                goto case CullingMode.FrontBack;
                            }
                            break;

                        case CullingMode.Back:
                            dot = Vector3.Dot(InWorldRay.direction, nrm);

                            if (dot < 0f)
                            {
                                goto case CullingMode.FrontBack;
                            }
                            break;

                        case CullingMode.FrontBack:
                            hits.Add(new RaycastHit(dist,
                                                    InWorldRay.GetPoint(dist),
                                                    nrm,
                                                    CurFace));
                            break;
                        }

                        continue;
                    }
                }
            }

            return(hits.Count > 0);
        }
 /// <summary>
 /// Attempt to calculate the UV transform for a face. In cases where the face is auto unwrapped
 /// (manualUV = false), this returns the offset, rotation, and scale from <see cref="Face.uv"/>. If the face is
 /// manually unwrapped, a transform will be calculated by trying to match an unmodified planar projection to the
 /// current UVs. The results
 /// </summary>
 /// <returns></returns>
 internal static UVTransform GetUVTransform(ProBuilderMesh mesh, Face face)
 {
     Projection.PlanarProject(mesh.positionsInternal, face.indexesInternal, Math.Normal(mesh, face), s_UVTransformProjectionBuffer);
     return(CalculateDelta(mesh.texturesInternal, face.indexesInternal, s_UVTransformProjectionBuffer, null));
 }
        internal static bool FaceRaycastBothCullModes(Ray worldRay, ProBuilderMesh mesh, ref SimpleTuple <Face, Vector3> back, ref SimpleTuple <Face, Vector3> front)
        {
            // Transform ray into model space
            worldRay.origin   -= mesh.transform.position; // Why doesn't worldToLocalMatrix apply translation?
            worldRay.origin    = mesh.transform.worldToLocalMatrix * worldRay.origin;
            worldRay.direction = mesh.transform.worldToLocalMatrix * worldRay.direction;

            var positions = mesh.positionsInternal;
            var faces     = mesh.facesInternal;

            back.item1  = null;
            front.item1 = null;

            float backDistance  = Mathf.Infinity;
            float frontDistance = Mathf.Infinity;

            // Iterate faces, testing for nearest hit to ray origin. Optionally ignores backfaces.
            for (int i = 0, fc = faces.Length; i < fc; ++i)
            {
                int[] indexes = mesh.facesInternal[i].indexesInternal;

                for (int j = 0, ic = indexes.Length; j < ic; j += 3)
                {
                    Vector3 a = positions[indexes[j + 0]];
                    Vector3 b = positions[indexes[j + 1]];
                    Vector3 c = positions[indexes[j + 2]];

                    float   dist;
                    Vector3 point;

                    if (Math.RayIntersectsTriangle(worldRay, a, b, c, out dist, out point))
                    {
                        if (dist < backDistance || dist < frontDistance)
                        {
                            Vector3 nrm = Vector3.Cross(b - a, c - a);
                            float   dot = Vector3.Dot(worldRay.direction, nrm);

                            if (dot < 0f)
                            {
                                if (dist < backDistance)
                                {
                                    backDistance = dist;
                                    back.item1   = faces[i];
                                }
                            }
                            else
                            {
                                if (dist < frontDistance)
                                {
                                    frontDistance = dist;
                                    front.item1   = faces[i];
                                }
                            }
                        }
                    }
                }
            }

            if (back.item1 != null)
            {
                back.item2 = worldRay.GetPoint(backDistance);
            }

            if (front.item1 != null)
            {
                front.item2 = worldRay.GetPoint(frontDistance);
            }

            return(back.item1 != null || front.item1 != null);
        }
Exemple #4
0
        static Mesh BuildVertexMesh(ProBuilderMesh pb, Dictionary <uint, SimpleTuple <ProBuilderMesh, int> > map, ref uint index)
        {
            int length = System.Math.Min(pb.sharedVerticesInternal.Length, ushort.MaxValue / 4 - 1);

            Vector3[] t_billboards = new Vector3[length * 4];
            Vector2[] t_uvs        = new Vector2[length * 4];
            Vector2[] t_uv2        = new Vector2[length * 4];
            Color[]   t_col        = new Color[length * 4];
            int[]     t_tris       = new int[length * 6];

            int n = 0;
            int t = 0;

            Vector3 up    = Vector3.up;
            Vector3 right = Vector3.right;

            for (int i = 0; i < length; i++)
            {
                Vector3 v = pb.positionsInternal[pb.sharedVerticesInternal[i][0]];

                t_billboards[t + 0] = v;
                t_billboards[t + 1] = v;
                t_billboards[t + 2] = v;
                t_billboards[t + 3] = v;

                t_uvs[t + 0] = Vector3.zero;
                t_uvs[t + 1] = Vector3.right;
                t_uvs[t + 2] = Vector3.up;
                t_uvs[t + 3] = Vector3.one;

                t_uv2[t + 0] = -up - right;
                t_uv2[t + 1] = -up + right;
                t_uv2[t + 2] = up - right;
                t_uv2[t + 3] = up + right;

                t_tris[n + 0] = t + 0;
                t_tris[n + 1] = t + 1;
                t_tris[n + 2] = t + 2;
                t_tris[n + 3] = t + 1;
                t_tris[n + 4] = t + 3;
                t_tris[n + 5] = t + 2;

                Color32 color = EncodeRGBA(index);
                map.Add(index++, new SimpleTuple <ProBuilderMesh, int>(pb, i));

                t_col[t + 0] = color;
                t_col[t + 1] = color;
                t_col[t + 2] = color;
                t_col[t + 3] = color;

                t += 4;
                n += 6;
            }

            Mesh mesh = new Mesh();

            mesh.name      = "Vertex Billboard";
            mesh.vertices  = t_billboards;
            mesh.uv        = t_uvs;
            mesh.uv2       = t_uv2;
            mesh.colors    = t_col;
            mesh.triangles = t_tris;

            return(mesh);
        }
Exemple #5
0
 /// <summary>
 /// Creates a SceneSelection object in the [Edge editing mode](../manual/modes.html) from the specified mesh.
 /// </summary>
 /// <param name="mesh">The ProBuilderMesh containing the edge to select.</param>
 /// <param name="edge">The Edge to set as the SceneSelection.</param>
 public SceneSelection(ProBuilderMesh mesh, Edge edge) : this(mesh, new List <Edge>() { edge })
 {
 }
Exemple #6
0
 internal static void CreateFaceMesh(ProBuilderMesh mesh, Mesh target)
 {
     target.Clear();
     target.vertices  = mesh.positionsInternal;
     target.triangles = mesh.selectedFacesInternal.SelectMany(x => x.indexes).ToArray();
 }
Exemple #7
0
        /// <summary>
        /// Calculates the tangents for a mesh.
        /// </summary>
        /// <param name="mesh">The mesh to calculate tangents for.</param>
        public static void CalculateTangents(ProBuilderMesh mesh)
        {
            int vc = mesh.vertexCount;

            if (!mesh.HasArrays(MeshArrays.Tangent))
            {
                mesh.tangentsInternal = new Vector4[vc];
            }

            if (!mesh.HasArrays(MeshArrays.Position) || !mesh.HasArrays(MeshArrays.Texture0))
            {
                return;
            }

            // http://answers.unity3d.com/questions/7789/calculating-tangents-vector4.html
            Vector3[] normals = mesh.GetNormals();

            var positions = mesh.positionsInternal;
            var textures  = mesh.texturesInternal;

            var tan1 = new Vector3[vc];
            var tan2 = new Vector3[vc];

            var tangents = mesh.tangentsInternal;

            foreach (var face in mesh.facesInternal)
            {
                int[] triangles = face.indexesInternal;

                for (int a = 0, c = triangles.Length; a < c; a += 3)
                {
                    long i1 = triangles[a + 0];
                    long i2 = triangles[a + 1];
                    long i3 = triangles[a + 2];

                    Vector3 v1 = positions[i1];
                    Vector3 v2 = positions[i2];
                    Vector3 v3 = positions[i3];

                    Vector2 w1 = textures[i1];
                    Vector2 w2 = textures[i2];
                    Vector2 w3 = textures[i3];

                    float x1 = v2.x - v1.x;
                    float x2 = v3.x - v1.x;
                    float y1 = v2.y - v1.y;
                    float y2 = v3.y - v1.y;
                    float z1 = v2.z - v1.z;
                    float z2 = v3.z - v1.z;

                    float s1 = w2.x - w1.x;
                    float s2 = w3.x - w1.x;
                    float t1 = w2.y - w1.y;
                    float t2 = w3.y - w1.y;

                    float r = 1.0f / (s1 * t2 - s2 * t1);

                    Vector3 sdir = new Vector3((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, (t2 * z1 - t1 * z2) * r);
                    Vector3 tdir = new Vector3((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r, (s1 * z2 - s2 * z1) * r);

                    tan1[i1] += sdir;
                    tan1[i2] += sdir;
                    tan1[i3] += sdir;

                    tan2[i1] += tdir;
                    tan2[i2] += tdir;
                    tan2[i3] += tdir;
                }
            }

            for (long a = 0; a < vc; ++a)
            {
                Vector3 n = normals[a];
                Vector3 t = Math.EnsureUnitVector(tan1[a]);

                Vector3.OrthoNormalize(ref n, ref t);

                tangents[a].x = t.x;
                tangents[a].y = t.y;
                tangents[a].z = t.z;
                tangents[a].w = (Vector3.Dot(Vector3.Cross(n, t), tan2[a]) < 0.0f) ? -1.0f : 1.0f;
            }
        }
Exemple #8
0
 /// <summary>
 /// Generate smoothing groups for a set of faces by comparing adjacent faces with normal differences less than angleThreshold (in degrees).
 /// </summary>
 /// <param name="mesh">The source mesh.</param>
 /// <param name="faces">Faces to be considered for smoothing.</param>
 /// <param name="angleThreshold">The maximum angle difference in degrees between adjacent face normals for the shared edge to be considered smooth.</param>
 public static void ApplySmoothingGroups(ProBuilderMesh mesh, IEnumerable <Face> faces, float angleThreshold)
 {
     ApplySmoothingGroups(mesh, faces, angleThreshold, null);
 }
Exemple #9
0
        internal static void ApplySmoothingGroups(ProBuilderMesh mesh, IEnumerable <Face> faces, float angleThreshold, Vector3[] normals)
        {
            if (mesh == null || faces == null)
            {
                throw new System.ArgumentNullException("mesh");
            }

            // Reset the selected faces to no smoothing group
            bool anySmoothed = false;

            foreach (Face face in faces)
            {
                if (face.smoothingGroup != smoothingGroupNone)
                {
                    anySmoothed = true;
                }

                face.smoothingGroup = Smoothing.smoothingGroupNone;
            }

            // if a set of normals was not supplied, get a new set of normals
            // with no prior smoothing groups applied.
            if (normals == null)
            {
                if (anySmoothed)
                {
                    mesh.mesh.normals = null;
                }
                normals = mesh.GetNormals();
            }

            float             threshold = Mathf.Abs(Mathf.Cos(Mathf.Clamp(angleThreshold, 0f, 89.999f) * Mathf.Deg2Rad));
            HashSet <int>     used      = new HashSet <int>(mesh.facesInternal.Select(x => x.smoothingGroup));
            int               group     = GetNextUnusedSmoothingGroup(1, used);
            HashSet <Face>    processed = new HashSet <Face>();
            List <WingedEdge> wings     = WingedEdge.GetWingedEdges(mesh, faces, true);

            try
            {
                foreach (WingedEdge wing in wings)
                {
                    // Already part of a group
                    if (!processed.Add(wing.face))
                    {
                        continue;
                    }

                    wing.face.smoothingGroup = group;
                    if (FindSoftEdgesRecursive(normals, wing, threshold, processed))
                    {
                        used.Add(group);
                        group = GetNextUnusedSmoothingGroup(group, used);
                    }
                    else
                    {
                        wing.face.smoothingGroup = Smoothing.smoothingGroupNone;
                    }
                }
            }
            catch (System.StackOverflowException e)
            {
                Debug.LogWarning("Smoothing has been aborted: Too many edges in the analyzed mesh");
            }
        }
 internal static void Unwrap(ProBuilderMesh mesh, Face face, Vector3 projection = default)
 {
     Projection.PlanarProject(mesh, face, projection != Vector3.zero? projection : Vector3.zero);
     ApplyUVSettings(mesh.texturesInternal, face.distinctIndexesInternal, face.uv);
 }
 internal static string SanityCheck(ProBuilderMesh mesh)
 {
     return(SanityCheck(mesh.GetVertices()));
 }
Exemple #12
0
        internal static void BuildVertexMeshLegacy(ProBuilderMesh mesh, Mesh target, IList <int> indexes)
        {
            const ushort k_MaxPointCount = ushort.MaxValue / 4;

            int billboardCount = indexes.Count;

            if (billboardCount > k_MaxPointCount)
            {
                billboardCount = k_MaxPointCount;
            }

            var positions = mesh.positionsInternal;

            Vector3[] t_billboards = new Vector3[billboardCount * 4];
            Vector3[] t_nrm        = new Vector3[billboardCount * 4];
            Vector2[] t_uvs        = new Vector2[billboardCount * 4];
            Vector2[] t_uv2        = new Vector2[billboardCount * 4];
            int[]     t_tris       = new int[billboardCount * 6];

            int n = 0;
            int t = 0;

            Vector3 up    = Vector3.up;
            Vector3 right = Vector3.right;

            for (int i = 0; i < billboardCount; i++)
            {
                t_billboards[t + 0] = positions[indexes[i]];
                t_billboards[t + 1] = positions[indexes[i]];
                t_billboards[t + 2] = positions[indexes[i]];
                t_billboards[t + 3] = positions[indexes[i]];

                t_uvs[t + 0] = Vector3.zero;
                t_uvs[t + 1] = Vector3.right;
                t_uvs[t + 2] = Vector3.up;
                t_uvs[t + 3] = Vector3.one;

                t_uv2[t + 0] = -up - right;
                t_uv2[t + 1] = -up + right;
                t_uv2[t + 2] = up - right;
                t_uv2[t + 3] = up + right;

                t_nrm[t + 0] = Vector3.forward;
                t_nrm[t + 1] = Vector3.forward;
                t_nrm[t + 2] = Vector3.forward;
                t_nrm[t + 3] = Vector3.forward;

                t_tris[n + 0] = t + 0;
                t_tris[n + 1] = t + 1;
                t_tris[n + 2] = t + 2;
                t_tris[n + 3] = t + 1;
                t_tris[n + 4] = t + 3;
                t_tris[n + 5] = t + 2;

                t += 4;
                n += 6;
            }

            target.Clear();
            target.vertices  = t_billboards;
            target.normals   = t_nrm;
            target.uv        = t_uvs;
            target.uv2       = t_uv2;
            target.triangles = t_tris;
        }
Exemple #13
0
 public NonVersionedEditScope(ProBuilderMesh mesh)
 {
     m_Mesh         = mesh;
     m_VersionIndex = mesh.versionIndex;
 }
Exemple #14
0
 /// <summary>
 /// Creates a SceneSelection object in the [Face editing mode](../manual/modes.html) from the specified mesh.
 /// </summary>
 /// <param name="mesh">The ProBuilderMesh containing the face to select.</param>
 /// <param name="face">The Face to set as the SceneSelection.</param>
 public SceneSelection(ProBuilderMesh mesh, Face face) : this(mesh, new List <Face>() { face })
 {
 }
 /// <summary>
 /// Find a triangle intersected by InRay on InMesh.  InRay is in world space.
 /// Returns the index in mesh.faces of the hit face, or -1.  Optionally can ignore backfaces.
 /// </summary>
 /// <param name="worldRay"></param>
 /// <param name="mesh"></param>
 /// <param name="hit"></param>
 /// <param name="ignore"></param>
 /// <returns></returns>
 internal static bool FaceRaycast(Ray worldRay, ProBuilderMesh mesh, out RaycastHit hit, HashSet <Face> ignore = null)
 {
     return(FaceRaycast(worldRay, mesh, out hit, Mathf.Infinity, CullingMode.Back, ignore));
 }
 internal static Vector3 GetActiveElementPosition(ProBuilderMesh mesh, IEnumerable <Face> faces)
 {
     return(mesh.transform.TransformPoint(Math.GetBounds(mesh.positionsInternal, faces.Last().distinctIndexesInternal).center));
 }
        /// <summary>
        /// Find the nearest face intersected by InWorldRay on this pb_Object.
        /// </summary>
        /// <param name="worldRay">A ray in world space.</param>
        /// <param name="mesh">The ProBuilder object to raycast against.</param>
        /// <param name="hit">If the mesh was intersected, hit contains information about the intersect point in local coordinate space.</param>
        /// <param name="distance">The distance from the ray origin to the intersection point.</param>
        /// <param name="cullingMode">Which sides of a face are culled when hit testing. Default is back faces are culled.</param>
        /// <param name="ignore">Optional collection of faces to ignore when raycasting.</param>
        /// <returns>True if the ray intersects with the mesh, false if not.</returns>
        internal static bool FaceRaycast(Ray worldRay, ProBuilderMesh mesh, out RaycastHit hit, float distance, CullingMode cullingMode, HashSet <Face> ignore = null)
        {
            // Transform ray into model space
            worldRay.origin   -= mesh.transform.position; // Why doesn't worldToLocalMatrix apply translation?
            worldRay.origin    = mesh.transform.worldToLocalMatrix * worldRay.origin;
            worldRay.direction = mesh.transform.worldToLocalMatrix * worldRay.direction;

            var positions = mesh.positionsInternal;
            var faces     = mesh.facesInternal;

            float   OutHitPoint = Mathf.Infinity;
            int     OutHitFace  = -1;
            Vector3 OutNrm      = Vector3.zero;

            // Iterate faces, testing for nearest hit to ray origin. Optionally ignores backfaces.
            for (int i = 0, fc = faces.Length; i < fc; ++i)
            {
                if (ignore != null && ignore.Contains(faces[i]))
                {
                    continue;
                }

                int[] indexes = mesh.facesInternal[i].indexesInternal;

                for (int j = 0, ic = indexes.Length; j < ic; j += 3)
                {
                    Vector3 a = positions[indexes[j + 0]];
                    Vector3 b = positions[indexes[j + 1]];
                    Vector3 c = positions[indexes[j + 2]];

                    Vector3 nrm = Vector3.Cross(b - a, c - a);
                    float   dot = Vector3.Dot(worldRay.direction, nrm);

                    bool skip = false;

                    switch (cullingMode)
                    {
                    case CullingMode.Front:
                        if (dot < 0f)
                        {
                            skip = true;
                        }
                        break;

                    case CullingMode.Back:
                        if (dot > 0f)
                        {
                            skip = true;
                        }
                        break;
                    }

                    var dist = 0f;

                    Vector3 point;
                    if (!skip && Math.RayIntersectsTriangle(worldRay, a, b, c, out dist, out point))
                    {
                        if (dist > OutHitPoint || dist > distance)
                        {
                            continue;
                        }

                        OutNrm      = nrm;
                        OutHitFace  = i;
                        OutHitPoint = dist;
                    }
                }
            }

            hit = new RaycastHit(OutHitPoint,
                                 worldRay.GetPoint(OutHitPoint),
                                 OutNrm,
                                 OutHitFace);

            return(OutHitFace > -1);
        }
        internal static Vector3 GetActiveElementPosition(ProBuilderMesh mesh, IEnumerable <Edge> edges)
        {
            var edge = edges.Last();

            return(mesh.transform.TransformPoint(Math.GetBounds(mesh.positionsInternal, new int[] { edge.a, edge.b }).center));
        }
Exemple #19
0
        /// <summary>
        /// Calculates the normals for a mesh, taking into account smoothing groups.
        /// </summary>
        /// <param name="mesh">The mesh to calculate normals for.</param>
        public static void CalculateNormals(ProBuilderMesh mesh)
        {
            CalculateHardNormals(mesh);

            var sharedVertices = mesh.sharedVerticesInternal;
            var faces          = mesh.facesInternal;
            // CalculateHardNormals ensures that normals array is initialized
            var normals        = mesh.normalsInternal;
            int smoothGroupMax = 24;

            // s_CachedIntArray acts as the smoothingGroup lookup for each vertex
            ClearIntArray(mesh.vertexCount);

            // Create a lookup of each triangles smoothing group.
            for (int i = 0, c = mesh.faceCount; i < c; i++)
            {
                var face    = faces[i];
                var indices = face.distinctIndexesInternal;

                for (int n = 0, d = indices.Length; n < d; n++)
                {
                    s_CachedIntArray[indices[n]] = face.smoothingGroup;

                    if (face.smoothingGroup >= smoothGroupMax)
                    {
                        smoothGroupMax = face.smoothingGroup + 1;
                    }
                }
            }

            // Increase buffers size if we have more smoothing groups than usual.
            if (smoothGroupMax > s_SmoothAvg.Length)
            {
                Array.Resize(ref s_SmoothAvg, smoothGroupMax);
                Array.Resize(ref s_SmoothAvgCount, smoothGroupMax);
            }

            // For each sharedIndexes group (individual vertex), find vertices that are in the same smoothing
            // group and average their normals.
            for (var i = 0; i < sharedVertices.Length; i++)
            {
                for (var n = 0; n < smoothGroupMax; n++)
                {
                    s_SmoothAvg[n].x    = 0f;
                    s_SmoothAvg[n].y    = 0f;
                    s_SmoothAvg[n].z    = 0f;
                    s_SmoothAvgCount[n] = 0f;
                }

                for (var n = 0; n < sharedVertices[i].Count; n++)
                {
                    int index = sharedVertices[i][n];
                    int group = s_CachedIntArray[index];

                    // Ideally this should only continue on group == NONE, but historically negative values have also
                    // been treated as no smoothing.
                    if (group <= Smoothing.smoothingGroupNone ||
                        (group > Smoothing.smoothRangeMax && group < Smoothing.hardRangeMax))
                    {
                        continue;
                    }

                    s_SmoothAvg[group].x    += normals[index].x;
                    s_SmoothAvg[group].y    += normals[index].y;
                    s_SmoothAvg[group].z    += normals[index].z;
                    s_SmoothAvgCount[group] += 1f;
                }

                for (int n = 0; n < sharedVertices[i].Count; n++)
                {
                    int index = sharedVertices[i][n];
                    int group = s_CachedIntArray[index];

                    if (group <= Smoothing.smoothingGroupNone ||
                        (group > Smoothing.smoothRangeMax && group < Smoothing.hardRangeMax))
                    {
                        continue;
                    }

                    normals[index].x = s_SmoothAvg[group].x / s_SmoothAvgCount[group];
                    normals[index].y = s_SmoothAvg[group].y / s_SmoothAvgCount[group];
                    normals[index].z = s_SmoothAvg[group].z / s_SmoothAvgCount[group];
                    normals[index]   = Math.EnsureUnitVector(normals[index]);
                }
            }
        }
 internal static Vector3 GetActiveElementPosition(ProBuilderMesh mesh, IEnumerable <int> vertices)
 {
     return(mesh.transform.TransformPoint(mesh.positionsInternal[vertices.First()]));
 }
Exemple #21
0
        /// <summary>
        /// Find a plane that best fits a set of faces within a texture group.
        /// </summary>
        /// <returns>A plane that best matches the layout of the points array.</returns>
        internal static Plane FindBestPlane(ProBuilderMesh mesh, int textureGroup)
        {
            float xx = 0f, xy = 0f, xz = 0f,
                  yy = 0f, yz = 0f, zz = 0f;

            if (mesh == null)
            {
                throw new System.ArgumentNullException("mesh");
            }

            Vector3 c   = Vector3.zero;
            int     len = 0;

            Vector3[] positions = mesh.positionsInternal;
            int       faceCount = mesh.faceCount;

            Face[] faces = mesh.facesInternal;

            for (int faceIndex = 0; faceIndex < faceCount; faceIndex++)
            {
                if (faces[faceIndex].textureGroup != textureGroup)
                {
                    continue;
                }

                int[] indexes = faces[faceIndex].distinctIndexesInternal;

                for (int index = 0, indexCount = indexes.Length; index < indexCount; index++)
                {
                    c.x += positions[indexes[index]].x;
                    c.y += positions[indexes[index]].y;
                    c.z += positions[indexes[index]].z;

                    len++;
                }
            }

            c.x /= len;
            c.y /= len;
            c.z /= len;

            for (int faceIndex = 0; faceIndex < faceCount; faceIndex++)
            {
                if (faces[faceIndex].textureGroup != textureGroup)
                {
                    continue;
                }

                int[] indexes = faces[faceIndex].distinctIndexesInternal;

                for (int index = 0, indexCount = indexes.Length; index < indexCount; index++)
                {
                    Vector3 r = positions[indexes[index]] - c;

                    xx += r.x * r.x;
                    xy += r.x * r.y;
                    xz += r.x * r.z;
                    yy += r.y * r.y;
                    yz += r.y * r.z;
                    zz += r.z * r.z;
                }
            }

            float   det_x = yy * zz - yz * yz;
            float   det_y = xx * zz - xz * xz;
            float   det_z = xx * yy - xy * xy;
            Vector3 n     = Vector3.zero;

            if (det_x > det_y && det_x > det_z)
            {
                n.x = 1f;
                n.y = (xz * yz - xy * zz) / det_x;
                n.z = (xy * yz - xz * yy) / det_x;
            }
            else if (det_y > det_z)
            {
                n.x = (yz * xz - xy * zz) / det_y;
                n.y = 1f;
                n.z = (xy * xz - yz * xx) / det_y;
            }
            else
            {
                n.x = (yz * xy - xz * yy) / det_z;
                n.y = (xz * xy - yz * xx) / det_z;
                n.z = 1f;
            }

            n.Normalize();

            return(new Plane(n, c));
        }
Exemple #22
0
        /// <summary>
        /// Builds a list of predecessors from a given face index to all other faces
        /// Uses the Djikstra pathfinding algorithm
        /// </summary>
        /// <param name="mesh">The mesh of the object</param>
        /// <param name="start">The index of the starting face</param>
        /// <returns>A list of predecessors from a face index to all other faces</returns>
        private static int[] Dijkstra(ProBuilderMesh mesh, int start)
        {
            HashSet <int> visited = new HashSet <int>();
            HashSet <int> toVisit = new HashSet <int>();

            if (s_cachedMesh != mesh || s_cachedFacesCount != mesh.faceCount)
            {
                s_cachedWings = WingedEdge.GetWingedEdges(mesh, true);
                s_cachedFacesIndex.Clear();
                s_cachedFacesCount = mesh.faceCount;

                for (int i = 0; i < mesh.facesInternal.Length; i++)
                {
                    s_cachedFacesIndex.Add(mesh.facesInternal[i], i);
                }
            }
            int wingCount = s_cachedWings.Count;

            float[] weights      = new float[wingCount];
            int[]   predecessors = new int[wingCount];

            for (int i = 0; i < wingCount; i++)
            {
                weights[i]      = float.MaxValue;
                predecessors[i] = -1;
            }

            int current = start;

            weights[current] = 0;
            visited.Add(current);

            // Construct the paths between the start face and every other faces
            while (visited.Count < wingCount)
            {
                var currentWing = s_cachedWings[current];
                var otherWing   = currentWing;
                // Update the weight array for each face next to the current one
                do
                {
                    var opposite = otherWing.opposite;
                    if (opposite == null)
                    {
                        otherWing = otherWing.next;
                        continue;
                    }

                    var idx    = s_cachedFacesIndex[opposite.face];
                    var weight = GetWeight(current, idx, mesh);
                    // Change the predecessor and weight if the new path found if shorter
                    if (weights[current] + weight < weights[idx])
                    {
                        weights[idx]      = weights[current] + weight;
                        predecessors[idx] = current;
                    }
                    // Add the face to the ones we can visit next, if not yet visited
                    if (!toVisit.Contains(idx) && !visited.Contains(idx))
                    {
                        toVisit.Add(idx);
                    }

                    otherWing = otherWing.next;
                } while (otherWing != currentWing);

                // This means there is an isolated face
                if (toVisit.Count == 0)
                {
                    return(predecessors);
                }
                // Look for the next face to visit, choosing the one with less weight
                float min = float.MaxValue;
                foreach (var i in toVisit)
                {
                    if (weights[i] < min)
                    {
                        min     = weights[i];
                        current = i;
                    }
                }
                visited.Add(current);
                toVisit.Remove(current);
            }

            return(predecessors);
        }
        /// <summary>
        /// Create a new list of WingedEdge values for a ProBuilder mesh.
        /// </summary>
        /// <param name="mesh">Target ProBuilderMesh.</param>
        /// <param name="faces">Which faces to include in the WingedEdge list.</param>
        /// <param name="oneWingPerFace">If `oneWingPerFace` is true the returned list will contain a single winged edge per-face (but still point to all edges).</param>
        /// <returns>A new list of WingedEdge values gathered from faces.</returns>
        public static List <WingedEdge> GetWingedEdges(ProBuilderMesh mesh, IEnumerable <Face> faces, bool oneWingPerFace = false)
        {
            if (mesh == null)
            {
                throw new ArgumentNullException("mesh");
            }

            var lookup = mesh.sharedVertexLookup;

            List <WingedEdge>             winged    = new List <WingedEdge>();
            Dictionary <Edge, WingedEdge> opposites = new Dictionary <Edge, WingedEdge>();

            foreach (Face f in faces)
            {
                List <Edge> edges = SortEdgesByAdjacency(f);
                int         edgeLength = edges.Count;
                WingedEdge  first = null, prev = null;

                for (int n = 0; n < edgeLength; n++)
                {
                    Edge e = edges[n];

                    WingedEdge w = new WingedEdge();
                    w.edge = new EdgeLookup(lookup[e.a], lookup[e.b], e.a, e.b);
                    w.face = f;
                    if (n < 1)
                    {
                        first = w;
                    }

                    if (n > 0)
                    {
                        w.previous = prev;
                        prev.next  = w;
                    }

                    if (n == edgeLength - 1)
                    {
                        w.next         = first;
                        first.previous = w;
                    }

                    prev = w;

                    WingedEdge opp;

                    if (opposites.TryGetValue(w.edge.common, out opp))
                    {
                        opp.opposite = w;
                        w.opposite   = opp;
                    }
                    else
                    {
                        w.opposite = null;
                        opposites.Add(w.edge.common, w);
                    }

                    if (!oneWingPerFace || n < 1)
                    {
                        winged.Add(w);
                    }
                }
            }

            return(winged);
        }
Exemple #24
0
 /// <summary>
 /// Creates a SceneSelection object in the [Vertex editing mode](../manual/modes.html) from the specified mesh.
 /// </summary>
 /// <param name="mesh">The ProBuilderMesh containing the vertex to select.</param>
 /// <param name="vertex">The index of the vertex to set as the SceneSelection.</param>
 public SceneSelection(ProBuilderMesh mesh, int vertex) : this(mesh, new List <int>() { vertex })
 {
 }