/// <summary> /// Clone constructor /// </summary> /// <param name="lbMesh"></param> public LBMesh(LBMesh lbMesh) { // A Mesh cannot be created outside the main Unity thread and must be called // from that is typically inherited from MonoBehaviour this.mesh = null; this.title = lbMesh.title; this.verts = new List <Vector3>(lbMesh.verts); this.normals = new List <Vector3>(lbMesh.normals); this.triangles = new List <int>(lbMesh.triangles); if (lbMesh.uvs != null) { this.uvs = new List <Vector2>(lbMesh.uvs); } else { uvs = new List <Vector2>(); } if (lbMesh.uv2s != null) { this.uv2s = new List <Vector2>(lbMesh.uv2s); } else { uv2s = new List <Vector2>(); } if (lbMesh.uv3s != null) { this.uv3s = new List <Vector2>(lbMesh.uv3s); } else { uv3s = new List <Vector2>(); } if (lbMesh.uv4s != null) { this.uv4s = new List <Vector2>(lbMesh.uv4s); } else { uv4s = new List <Vector2>(); } if (lbMesh.tangents != null) { this.tangents = new List <Vector4>(lbMesh.tangents); } else { tangents = new List <Vector4>(); } minExtent = lbMesh.minExtent; maxExtent = lbMesh.maxExtent; }
/// <summary> /// Delete the Unity Mesh class instance and release memory. Clears all LBMesh data but doesn't delete the class instance. /// NOTE: Memory will be released by LB back to the available heap within Unity. However, whether or not Unity /// releases that memory back to the OS or not is beyond the control of LB. /// </summary> /// <param name="lbMesh"></param> public static void DeleteMesh(LBMesh lbMesh) { if (lbMesh != null) { if (lbMesh.mesh != null) { lbMesh.mesh.Clear(false); lbMesh.mesh = null; } if (lbMesh.normals != null) { lbMesh.normals.Clear(); lbMesh.normals.TrimExcess(); lbMesh.normals = null; } if (lbMesh.tangents != null) { lbMesh.tangents.Clear(); lbMesh.tangents.TrimExcess(); lbMesh.tangents = null; } if (lbMesh.uvs != null) { lbMesh.uvs.Clear(); lbMesh.uvs.TrimExcess(); lbMesh.uvs = null; } if (lbMesh.uv2s != null) { lbMesh.uv2s.Clear(); lbMesh.uv2s.TrimExcess(); lbMesh.uv2s = null; } if (lbMesh.uv3s != null) { lbMesh.uv3s.Clear(); lbMesh.uv3s.TrimExcess(); lbMesh.uv3s = null; } if (lbMesh.uv4s != null) { lbMesh.uv4s.Clear(); lbMesh.uv4s.TrimExcess(); lbMesh.uv4s = null; } if (lbMesh.triangles != null) { lbMesh.triangles.Clear(); lbMesh.triangles.TrimExcess(); lbMesh.triangles = null; } if (lbMesh.verts != null) { lbMesh.verts.Clear(); lbMesh.verts.TrimExcess(); lbMesh.verts = null; } System.GC.Collect(); } }
/// <summary> /// Create an LBMesh object to hold the water mesh for a Topography Image Modifier Layer. /// </summary> /// <param name="landscape"></param> /// <param name="lbLayer"></param> /// <param name="layerIdx"></param> /// <param name="meshTitle"></param> /// <param name="showErrors"></param> /// <returns></returns> public static bool CreateMeshForWaterFromLayer(LBLandscape landscape, LBLayer lbLayer, int layerIdx, string meshTitle, bool showErrors) { bool isSuccessful = false; string methodName = "LBMeshOperations.CreateMeshForWaterFromLayer"; if (landscape == null) { if (showErrors) { Debug.LogWarning("ERROR: " + methodName + " landscape is null. Please Report"); } } else if (lbLayer == null) { if (showErrors) { Debug.LogWarning("ERROR: " + methodName + " lbLayer is null. Please Report"); } } else if (lbLayer.type != LBLayer.LayerType.ImageModifier) { if (showErrors) { Debug.LogWarning("ERROR: " + methodName + " LayerType must be ImageModifier. Please Report"); } } { // TODO get the minium area below zero for the RAW modifier file to use for meshWidth/Length // Get the absolute width and length Vector2 meshSize = Vector2.zero; meshSize.x = lbLayer.areaRect.width < 0 ? -lbLayer.areaRect.width : lbLayer.areaRect.width; meshSize.y = lbLayer.areaRect.height < 0 ? -lbLayer.areaRect.height : lbLayer.areaRect.height; // Get the bottom-left point of mesh with landscape (it might be outside the landscape) Vector2 meshBottomLeft = Vector2.zero; meshBottomLeft.x = lbLayer.areaRect.x - (meshSize.x / 2f); meshBottomLeft.y = lbLayer.areaRect.y - (meshSize.y / 2f); if (meshBottomLeft.x < 0) { meshBottomLeft.x += meshSize.x; } else if (meshBottomLeft.x == meshSize.x) { meshBottomLeft.x = 0f; } if (meshBottomLeft.y < 0) { meshBottomLeft.y += meshSize.y; } else if (meshBottomLeft.y == meshSize.y) { meshBottomLeft.y = 0f; } //Debug.Log("INFO: " + methodName + " areaRect xy: " + lbLayer.areaRect.x + "," + lbLayer.areaRect.y + " b-l:" + meshBottomLeft); // If there is an existing LBMesh instance, remove all the mesh data if (lbLayer.modifierWaterLBMesh != null) { LBMesh.DeleteMesh(lbLayer.modifierWaterLBMesh); } else { lbLayer.modifierWaterLBMesh = new LBMesh(); } if (lbLayer.modifierWaterLBMesh == null) { if (showErrors) { Debug.LogWarning("ERROR: " + methodName + " Could not create LBMesh for the water in Layer " + (layerIdx + 1) + ". Please Report"); } } else if (meshSize.x < 0.1f || meshSize.y < 0.1f) { { if (showErrors) { Debug.LogWarning("ERROR: " + methodName + " LayerType " + (layerIdx + 1) + " has an invalid water mesh size " + meshSize.x + "," + meshSize.y); } } } { lbLayer.modifierWaterLBMesh.title = meshTitle; // Initialise mesh lists List <Vector3> verts = new List <Vector3>(); List <Vector2> uvs = new List <Vector2>(); List <int> triangles = new List <int>(); List <Vector3> normals = new List <Vector3>(); List <Vector4> tangents = new List <Vector4>(); List <Vector4> colours = new List <Vector4>(); // Store as Vector4s rather than Color so they are serializable // Declare outside loops for less garbage collection // We are creating a flat plane, so can use Vector2. Vector2 vertPositionN = Vector2.zero; // Normalised position within mesh Vector2 vertLandscapePosN = Vector2.zero; // Normalised postion within the landscape int vertCount = 0; // Default colour of each vert (stored in LB as a Vector4) Vector4 defaultVertColour = new Vector4(1f, 1f, 1f, 1f); // The number of cells wide and long in the mesh int meshWidth = 10; int meshLength = 10; for (int x = 0; x < meshWidth; x++) { for (int z = 0; z < meshLength; z++) { // Create the vert as a 0-1 position // Normalise the RAW Pixel - converting it to a range of 0 to 1 vertPositionN.x = (float)x / (float)(meshWidth - 1); vertPositionN.y = (float)z / (float)(meshLength - 1); verts.Add(new Vector3(vertPositionN.x * meshSize.x, 0f, vertPositionN.y * meshSize.y)); normals.Add(Vector3.up); if (lbLayer.modifierWaterIsMeshLandscapeUV) { // UVs normalised to the landscape vertLandscapePosN.x = ((vertPositionN.x * meshSize.x) + meshBottomLeft.x) / landscape.size.x; vertLandscapePosN.y = ((vertPositionN.y * meshSize.y) + meshBottomLeft.y) / landscape.size.y; uvs.Add(new Vector2(vertLandscapePosN.x / lbLayer.modifierWaterMeshUVTileScale.x, vertLandscapePosN.y / lbLayer.modifierWaterMeshUVTileScale.y)); } else { // Generic uvs (simply 0-1 coordinates of vert position) uvs.Add(new Vector2(vertPositionN.x / lbLayer.modifierWaterMeshUVTileScale.x, vertPositionN.y / lbLayer.modifierWaterMeshUVTileScale.y)); } // Create tangent tangents.Add(new Vector4(1f, 0f, 0f, 1f)); // Add the two triangles for the quad // Not required if on left or bottom edges of the mesh if (x < meshWidth - 1 && z < meshLength - 1) { // Bottom (left) triangle // Bottom left of quad triangles.Add(vertCount); // Bottom right of quad triangles.Add(vertCount + 1); // Top right of quad triangles.Add(vertCount + meshWidth + 1); // Top (right) triangle // Top left of quad triangles.Add(vertCount + meshWidth); // Bottom left of quad triangles.Add(vertCount); // Top right of quad triangles.Add(vertCount + meshWidth + 1); } // Set default vert colour colours.Add(defaultVertColour); // Increment the vert count vertCount++; } } lbLayer.modifierWaterLBMesh.verts = verts; lbLayer.modifierWaterLBMesh.triangles = triangles; lbLayer.modifierWaterLBMesh.normals = normals; lbLayer.modifierWaterLBMesh.uvs = uvs; lbLayer.modifierWaterLBMesh.tangents = tangents; //Debug.Log("INFO " + methodName + " - created Mesh (verts " + verts.Count + " tris " + (triangles.Count / 3) + ")"); isSuccessful = true; } } return(isSuccessful); }
/// <summary> /// Add a LBMesh mesh to a new gameobject in the scene as a child of the parentTransform /// </summary> /// <param name="lbMesh"></param> /// <param name="position"></param> /// <param name="gameobjectName"></param> /// <param name="parentTransform"></param> /// <param name="meshMaterial"></param> /// <param name="isStatic"></param> /// <param name="checkForExisting"></param> /// <returns></returns> public static Transform AddMeshToScene(LBMesh lbMesh, Vector3 position, string gameobjectName, Transform parentTransform, Material meshMaterial, bool isStatic, bool checkForExisting) { Transform meshTranform = null; string methodName = "LBMeshOperations.AddMeshToScene"; // Basic validation if (lbMesh == null) { Debug.LogWarning("ERROR: " + methodName + " - LBMesh cannot be null"); } else if (string.IsNullOrEmpty(gameobjectName)) { Debug.LogWarning("ERROR: " + methodName + " - no name specified"); } else if (parentTransform == null) { Debug.LogWarning("ERROR: " + methodName + " - no parent transform is specified"); } else { GameObject meshGameObject; Transform tfrm = null; // Check to see if the GameObject already exists if (checkForExisting) { tfrm = parentTransform.Find(gameobjectName); } // If it doesn't exist or checkForExisting is false, create a new child object. if (tfrm == null) { meshGameObject = new GameObject(gameobjectName); if (meshGameObject == null) { Debug.LogWarning("ERROR: " + methodName + " - could not create GameObject " + gameobjectName); } else { meshGameObject.transform.SetParent(parentTransform); } } else { meshGameObject = tfrm.gameObject; } if (meshGameObject != null) { meshGameObject.isStatic = isStatic; meshGameObject.transform.position = position; // If the Mesh Filter doesn't exist, add it MeshFilter meshFilter = meshGameObject.GetComponent <MeshFilter>(); if (meshFilter == null) { meshFilter = meshGameObject.AddComponent <MeshFilter>(); } // If the Mesh Renderer doesn't exist, add it MeshRenderer meshRenderer = meshGameObject.GetComponent <MeshRenderer>(); if (meshRenderer == null) { meshRenderer = meshGameObject.AddComponent <MeshRenderer>(); } if (meshFilter == null) { Debug.LogWarning("ERROR: " + methodName + " - Could not add MeshFilter to " + gameobjectName); } else if (meshRenderer == null) { Debug.LogWarning("ERROR: " + methodName + " - Could not add MeshRenderer to " + gameobjectName); } else { meshFilter.sharedMesh = lbMesh.mesh; meshRenderer.sharedMaterial = meshMaterial; //lbMesh.mesh.RecalculateBounds(); // We may already have the trfm (see above) but we want to only return if everything succeeds meshTranform = meshGameObject.transform; } } } return(meshTranform); }