/// <summary> /// Snap the prefab to ground (y-axis). Sets the prefab position to 0 on y-axis. /// If isConsiderPrefabExtents is true, set ground to be the bottom of the prefab /// rather than its pivot point. /// NOTE: Currently doesn't consider scaling. /// </summary> /// <param name="isConsiderPrefabExtents"></param> private void SnapToGround(bool isConsiderPrefabExtents = false) { if (lbGroupMember != null && lbGroupDesignerItem != null) { // Default lbGroupMember.minOffsetY = 0f; lbGroupMember.maxOffsetY = 0f; if (isConsiderPrefabExtents && lbGroupMember.prefab != null && lbGroupMember.lbMemberType == LBGroupMember.LBMemberType.Prefab) { // Need to reset y value for GetBounds to work correctly Vector3 tempPos = lbGroupMember.prefab.transform.position; tempPos.y = 0f; lbGroupMember.prefab.transform.position = tempPos; Bounds bounds = LBMeshOperations.GetBounds(lbGroupMember.prefab.transform, false, true); if (bounds.extents.y != 0f) { // bounds.extents.y is half the height of the prefab. lbGroupMember.minOffsetY = bounds.extents.y - bounds.center.y; lbGroupMember.maxOffsetY = lbGroupMember.minOffsetY; } } lbGroupDesignerItem.lbGroupDesigner.UpdateGroupMember(lbGroupMember); LBEditorHelper.RepaintEditorWindow(typeof(LandscapeBuilderWindow)); } }
/// <summary> /// Add a zone at the same location as an object/prefab using its extents. /// NOTE: Rectangular zones cannot be rotated, so only width and length can be switched /// </summary> private void AddZoneToObject() { if (lbGroup != null && lbGroupMember != null && lbGroupDesignerItem != null && lbGroupMember.prefab != null && lbGroupMember.lbMemberType == LBGroupMember.LBMemberType.Prefab) { // Need to reset postion for GetBounds to work correctly Vector3 tempPos = lbGroupMember.prefab.transform.position; lbGroupMember.prefab.transform.position = Vector3.zero; Bounds bounds = LBMeshOperations.GetBounds(lbGroupMember.prefab.transform, false, true); lbGroupMember.prefab.transform.position = tempPos; if (bounds.extents.x != 0f && bounds.extents.z != 0f && lbGroup.maxClearingRadius > 0f) { LBGroupZone lbGroupZone = new LBGroupZone(); if (lbGroupZone != null) { if (bounds.extents.x == bounds.extents.z) { lbGroupZone.zoneType = LBGroupZone.LBGroupZoneType.circle; lbGroupZone.centrePointX = lbGroupMember.minOffsetX / lbGroup.maxClearingRadius; lbGroupZone.centrePointZ = lbGroupMember.minOffsetZ / lbGroup.maxClearingRadius; lbGroupZone.width = (bounds.extents.x - bounds.center.x) / lbGroup.maxClearingRadius; lbGroupZone.length = lbGroupZone.width; } else { lbGroupZone.zoneType = LBGroupZone.LBGroupZoneType.rectangle; lbGroupZone.centrePointX = lbGroupMember.minOffsetX / lbGroup.maxClearingRadius; lbGroupZone.centrePointZ = lbGroupMember.minOffsetZ / lbGroup.maxClearingRadius; lbGroupZone.width = (bounds.extents.x - bounds.center.x) * 2f / lbGroup.maxClearingRadius; lbGroupZone.length = (bounds.extents.z - bounds.center.z) * 2f / lbGroup.maxClearingRadius; float rotationY = lbGroupDesignerItem.transform.rotation.eulerAngles.y; // Get the absolute rotation on y-axis if (rotationY < 0) { rotationY = -rotationY; } // cater for rotation by switching width and length if ((rotationY > 45f && rotationY < 135f) || (rotationY > 225f && rotationY < 315f)) { float width = lbGroupZone.width; lbGroupZone.width = lbGroupZone.length; lbGroupZone.length = width; } } lbGroupZone.zoneName = lbGroupMember.prefab.name + " zone"; lbGroup.zoneList.Add(lbGroupZone); } } LBEditorHelper.RepaintEditorWindow(typeof(LandscapeBuilderWindow)); } }
// Mesh Operations Class #region Submesh Methods /// <summary> /// Create a new mesh from a submesh of baseMesh with index submeshIndex /// </summary> public static Mesh CreateMeshFromSubmesh(Mesh baseMesh, int submeshIndex) { // Function adapted from the MeshCreationHelper.cs script on the Unify Community Wiki Mesh newMesh = new Mesh(); List <int> triangles = new List <int>(); triangles.AddRange(baseMesh.GetTriangles(submeshIndex)); // the triangles of the sub mesh List <Vector3> newVertices = new List <Vector3>(); List <Vector2> newUvs = new List <Vector2>(); List <Vector2> newUv2s = new List <Vector2>(); List <Vector2> newUv3s = new List <Vector2>(); List <Vector2> newUv4s = new List <Vector2>(); List <Vector3> newNormals = new List <Vector3>(); List <Color> newColours = new List <Color>(); List <Vector4> newTangents = new List <Vector4>(); Dictionary <int, int> oldToNewIndices = new Dictionary <int, int>(); int newIndex = 0; int i = 0; // Improve performance by almost 50% by pre-defining variables and only getting the length of an array once. int numBaseMeshVerts = baseMesh.vertices.Length; int numBaseMeshColours = (baseMesh.colors == null ? 0 : baseMesh.colors.Length); int numBaseMeshNormals = (baseMesh.normals == null ? 0 : baseMesh.normals.Length); int numBaseMeshTangents = (baseMesh.tangents == null ? 0 : baseMesh.tangents.Length); int numBaseMeshuvs = (baseMesh.uv == null ? 0 : baseMesh.uv.Length); int numBaseMeshuv2s = (baseMesh.uv2 == null ? 0 : baseMesh.uv2.Length); int numBaseMeshuv3s = (baseMesh.uv3 == null ? 0 : baseMesh.uv3.Length); int numBaseMeshuv4s = (baseMesh.uv4 == null ? 0 : baseMesh.uv4.Length); // Collect the vertices and uvs for (i = 0; i < numBaseMeshVerts; i++) { if (triangles.Contains(i)) { newVertices.Add(baseMesh.vertices[i]); if (numBaseMeshColours > i) { newColours.Add(baseMesh.colors[i]); } else { newColours.Add(Color.white); } if (numBaseMeshNormals > i) { newNormals.Add(baseMesh.normals[i]); } if (numBaseMeshTangents > i) { newTangents.Add(baseMesh.tangents[i]); } if (numBaseMeshuvs > i) { newUvs.Add(baseMesh.uv[i]); } if (numBaseMeshuv2s > i) { newUv2s.Add(baseMesh.uv2[i]); } if (numBaseMeshuv3s > i) { newUv3s.Add(baseMesh.uv2[i]); } if (numBaseMeshuv4s > i) { newUv4s.Add(baseMesh.uv2[i]); } oldToNewIndices.Add(i, newIndex); ++newIndex; } } int[] newTriangles = new int[triangles.Count]; int numNewTriangles = (newTriangles == null ? 0 : newTriangles.Length); // Collect the new triangles indicies for (i = 0; i < numNewTriangles; i++) { newTriangles[i] = oldToNewIndices[triangles[i]]; } // Assemble the new mesh with the new vertices/uv/triangles. newMesh.vertices = newVertices.ToArray(); newMesh.triangles = newTriangles; if (numBaseMeshuvs > 0) { newMesh.uv = newUvs.ToArray(); } if (numBaseMeshuv2s > 0) { newMesh.uv2 = newUv2s.ToArray(); } if (numBaseMeshuv3s > 0) { newMesh.uv3 = newUv3s.ToArray(); } if (numBaseMeshuv4s > 0) { newMesh.uv4 = newUv4s.ToArray(); } if (newMesh.colors.Length > 0) { newMesh.colors = newColours.ToArray(); } if (newMesh.normals.Length > 0) { newMesh.normals = newNormals.ToArray(); } else { newMesh.RecalculateNormals(); } // If tangents don't exist, recalculate tangents so that normalmaps work on combined meshes if (newMesh.tangents.Length > 0) { newMesh.tangents = newTangents.ToArray(); } else { #if UNITY_5_6_OR_NEWER newMesh.RecalculateTangents(); #else newMesh = LBMeshOperations.CalculateTangents(newMesh); #endif } // Re-calculate bounds for the renderer. newMesh.RecalculateBounds(); return(newMesh); }
/// <summary> /// Update the mesh with the vert, triangle, normal data. /// Recalculate Bounds /// Recalculate Normals if required /// NOTE: MegaSplat uses color.a, uv4.x, uv4.w /// Currently doesn't update uvs as vector4s.. /// </summary> /// <param name="flipNormals"></param> /// <param name="showErrors"></param> /// <returns></returns> public bool UpdateMesh(bool flipNormals, bool showErrors) { bool isSuccess = false; // Do some validation if (mesh == null) { if (showErrors) { Debug.Log("LBMesh.UpdateMesh - mesh cannot be null"); } } else if (verts == null) { if (showErrors) { Debug.Log("LBMesh.UpdateMesh - verts cannot be null"); } } else if (verts.Count > 65000) { if (showErrors) { Debug.Log("LBMesh.UpdateMesh - vert count of " + verts.Count + " is greater than 65000"); } } else if (normals == null) { if (showErrors) { Debug.Log("LBMesh.UpdateMesh - normals cannot be null"); } } else if (uvs == null) { if (showErrors) { Debug.Log("LBMesh.UpdateMesh - uvs cannot be null"); } } else if (triangles == null) { if (showErrors) { Debug.Log("LBMesh.UpdateMesh - triangles cannot be null"); } } //else if (colours == null) { if (showErrors) { Debug.Log("LBMesh.UpdateMesh - colours cannot be null"); } } else { mesh.Clear(); mesh.SetVertices(verts); mesh.SetTriangles(triangles, 0, true); if (colours != null) { if (colours.Count > 0) { // Populate an array with the Vector4 "colour" data from lbMesh. UnityEngine.Color[] colorsArray = new Color[colours.Count]; if (colorsArray != null) { for (int c = 0; c < colours.Count; c++) { Vector4 colour = colours[c]; // TODO A color has direct conversion to vector4 so probably just setting to vector4 would // be faster. Need to test. colorsArray[c] = new Color(colour.x, colour.y, colour.z, colour.w); } mesh.colors = colorsArray; } } } // If they exist, apply the UVs if (uvs != null && uvs.Count > 0) { mesh.SetUVs(0, uvs); } if (uv2s != null && uv2s.Count > 0) { mesh.SetUVs(1, uv2s); } if (uv3s != null && uv3s.Count > 0) { mesh.SetUVs(2, uv3s); } if (uv4s != null && uv4s.Count > 0) { mesh.SetUVs(3, uv4s); } // uv3,4 can also be Vector4s... if (normals != null && normals.Count > 0) { mesh.SetNormals(normals); } else { mesh.RecalculateNormals(); } // flip normals on the mesh itself after normals have been either added from LBMesh // or by recalculating the normals. Don't update the original LBMesh normals list if (flipNormals) { LBMeshOperations.FlipNormals(mesh); } // Recalc gets done with SetTriangles() above //mesh.RecalculateBounds(); if (tangents != null && tangents.Count > 0) { mesh.SetTangents(tangents); } else { //mesh = LBMeshOperations.ReCalculateTangents(mesh); mesh = LBMeshOperations.CalculateTangents(mesh); } isSuccess = true; } return(isSuccess); }