// Build voxel object public override float Build(Storage voxels, Bounds bounds, Informer informer, object parameter) { // Check for given array if (voxels != null) { GameObject subContainer; int width = voxels.Width; int height = voxels.Height; int depth = voxels.Depth; int sides = voxels.FacesCount; int x, y, z; // Check for non-empty array if (width * height * depth * sides > 0) { if (mainContainer == null) { // Check if texture is required if (mainTextureTarget || emissiveTextureTarget) { // Create voxel texture, if required if (voxelTexture2D == null) { voxelTexture2D = new Texture2D.Process(); } // Build texture if (voxelTexture2D != null && voxelTexture2D.CurrentProgress < 1) { return(voxelTexture2D.Build(voxels, bounds) * 0.5f); } } else { voxelTexture2D = null; } // Get iterator iterator = voxels.GetIterator(); // Create empty game object mainContainer = new GameObject(targetName); if (mainContainer != null) { // Hide new container mainContainer.hideFlags |= HideFlags.HideAndDontSave; // Create empty list to store groups to groups = new Dictionary <Material, GameObject>(); // Create empty list to store mesh data interfaces to meshDataInterfaces = new List <IMeshData>(); currentMeshDataInterface = 0; // Copy position from source object mainContainer.transform.position = gameObject.transform.position; // Copy static flag mainContainer.isStatic = staticContainers; // Calculate total scaling for one block globalScaling = new Vector3(2.0f * bounds.extents.x / (float)width, 2.0f * bounds.extents.y / (float)height, 2.0f * bounds.extents.z / (float)depth); // Check for given mesh if (mesh != null) { // Calculate offset and scaling for one voxel mesh offset = -mesh.bounds.center; scaling.x = 0.5f / mesh.bounds.extents.x; scaling.y = 0.5f / mesh.bounds.extents.y; scaling.z = 0.5f / mesh.bounds.extents.z; offset.x *= scaling.x; offset.y *= scaling.y; offset.z *= scaling.z; scaling.x *= globalScaling.x; scaling.y *= globalScaling.y; scaling.z *= globalScaling.z; } else { // Unset translation und scaling offset = Vector3.zero; scaling = Vector3.one; } // Add offset for half voxel offset += new Vector3(0.5f * globalScaling.x, 0.5f * globalScaling.y, 0.5f * globalScaling.z); // Move to match position of the original object offset += bounds.center - gameObject.transform.position - bounds.extents; } } // Check for main container and voxel iterator if (mainContainer != null && iterator != null) { // Process voxels in steps for (int number = 0; number < 10; ++number) { // Retrieve material for current coordinate int iteratorIndex = iterator.Number; Color color; Material material = iterator.GetNextMaterial(out color, out x, out y, out z); // Check for valid voxel if (material != null) { //// Replace material, if texture template is set //if (textureMaterialTemplate != null && voxelTexture != null) //{ // material = textureMaterialTemplate; // material.SetTexture("_VoxelTex", voxelTexture.target); //} // Check for existing material groups if (groups != null) { // Check for new group if (!groups.TryGetValue(material, out subContainer) || (subContainer == null)) { // Create empty game object subContainer = new GameObject(material.name == null || material.name.Length == 0 ? (groups.Count + 1).ToString() : material.name); if (subContainer != null) { // Attach it to this main object subContainer.transform.parent = mainContainer.transform; // Unset local transformation subContainer.transform.localPosition = Vector3.zero; subContainer.transform.localScale = Vector3.one; subContainer.transform.localRotation = Quaternion.identity; // Copy static flag subContainer.isStatic = staticContainers; // Set layer number by transparency property if (material.HasProperty("_Color")) { if (material.color.a < 1) { subContainer.layer = 1; } else { subContainer.layer = 0; } } try { // Add group to list groups.Add(material, subContainer); } catch (System.Exception exception) { Debug.Log(exception.Message); } } } } else { // Unset container for first material subContainer = null; } // Calculate current voxel position Vector3 currentPosition = new Vector3((float)x * globalScaling.x + offset.x, (float)y * globalScaling.y + offset.y, (float)z * globalScaling.z + offset.z); // material container as parent for the current voxel GameObject parent = subContainer; // Create empty game object if (containerTemplate != null) { subContainer = Instantiate(containerTemplate); } else { subContainer = new GameObject(); } if (subContainer != null) { // Change name of the voxel container to current coordinate subContainer.name = x.ToString() + ", " + y.ToString() + ", " + z.ToString(); if (parent != null) { // Attach it to material container subContainer.transform.parent = parent.transform; } else { // Attach it to main object subContainer.transform.parent = mainContainer.transform; } // Set transformation to position and scale current cell subContainer.transform.localPosition = currentPosition; subContainer.transform.localScale = scaling * sizeFactor; subContainer.transform.localRotation = Quaternion.identity; // Copy static flag if (containerTemplate == null) { subContainer.isStatic = staticContainers; } // Set layer number by transparency property if (material.HasProperty("_Color")) { if (material.color.a < 1) { subContainer.layer = 1; } else { subContainer.layer = 0; } } // Check for valid mesh if (mesh != null) { // Add mesh filter MeshFilter meshFilter = subContainer.AddComponent <MeshFilter>(); if (meshFilter != null) { // Apply specified mesh as shared one for the sub container meshFilter.sharedMesh = mesh; // Check for vertex color utilization if (vertexColors) { // Create new array, if size does not match if (colors == null || colors.Length != mesh.vertexCount) { colors = new Color[mesh.vertexCount]; lastColor = colors[0]; } // Fill and apply new vertex colors array if (colors != null) { if (lastColor != color) { for (int index = 0; index < colors.Length; ++index) { colors[index] = color; } lastColor = color; } meshFilter.mesh.colors = colors; } } // Check for existing voxel map if (voxelTexture2D != null) { // Retrieve texture coordinate for current voxel Vector2 textureCoordinate = voxelTexture2D.GetTextureCoordinate(voxels, iteratorIndex); if (!float.IsNaN(textureCoordinate.x)) { //// Encode iterator index into texture coordinate //textureCoordinate.x += (float)iteratorIndex; // Create new array, if size does not match if (textureCoordinates == null || textureCoordinates.Length != mesh.vertexCount) { textureCoordinates = new Vector2[mesh.vertexCount]; } // Fill and apply new UV coordinates array if (textureCoordinates != null) { for (int index = 0; index < textureCoordinates.Length; ++index) { textureCoordinates[index] = textureCoordinate; } meshFilter.mesh.uv = textureCoordinates; } //Debug.Log(textureCoordinate); // Apply texture to material if (mainTextureTarget) { material.SetTexture("_MainTex", voxelTexture2D.Texture); } if (emissiveTextureTarget) { material.SetTexture("_EmissionMap", voxelTexture2D.Texture); } } } //// Check for active volume texture //if (voxelTexture != null && voxelTexture.target != null) //{ // // Instantiate mesh // Mesh newMesh = meshFilter.mesh; // // Compute texture coordinate center for current voxel // Vector3 textureCoordinate = new Vector3((x + 0.5f) / (float)voxelTexture.target.width, (y + 0.5f) / (float)voxelTexture.target.height, (z + 0.5f) / (float)voxelTexture.target.depth); // // Fill list of UVs for every vertex // List<Vector3> textureCoordinates = new List<Vector3>(newMesh.vertexCount); // for (int vertex = 0; vertex < mesh.vertexCount; ++vertex) // { // textureCoordinates.Add(textureCoordinate); // } // // Apply them to the mesh // newMesh.SetUVs(0, textureCoordinates); //} // Add mesh renderer MeshRenderer meshRenderer = subContainer.AddComponent <MeshRenderer>(); if (meshRenderer != null) { // Hide object meshRenderer.enabled = false; // Set material to renderer if (material != null) { meshRenderer.material = material; } } // Check for mesh data and add indices IMeshData[] meshDataComponents = subContainer.GetComponents <IMeshData>(); if (meshDataComponents != null) { foreach (IMeshData meshData in meshDataComponents) { meshData.SetVoxelIndices(new int[1] { iteratorIndex }); meshDataInterfaces.Add(meshData); } } } } } } else { iterator = null; break; } } // Return current progress when building has not been finished if (iterator != null) { return(((float)iterator.Number / (float)(voxels.Count + 1) * (mergeMeshes ? 0.5f : 1.0f)) * ((voxelTexture2D != null) ? 0.5f : 1.0f) + ((voxelTexture2D != null) ? 0.5f : 0.0f)); } } } } // Check for groups of materials if (groups != null && groups.Count > 0) { GameObject meshContainer; // Initialize group enumerator if (currentGroup == null) { currentGroup = groups.GetEnumerator(); if (currentGroup != null) { if (!currentGroup.MoveNext()) { currentGroup = null; } } } // Check for mesh baking if (mergeMeshes) { int count, index; int vertexCount, indexCount; int iteratorNumber; // Process collected material groups if (currentGroup != null) { // Check if semi-transparent meshes should be merged or if current group is opaque if (!opaqueOnly || (textureMaterialTemplate != null && voxelTexture != null) || (!currentGroup.Current.Key.HasProperty("_Color") || currentGroup.Current.Key.color.a <= 0 || currentGroup.Current.Key.color.a >= 1)) { if (materialContainer == null) { // Create empty game object for the material materialContainer = new GameObject(currentGroup.Current.Value.name); if (materialContainer != null) { // Attach it to this main object materialContainer.transform.parent = mainContainer.transform; // Unset relative transformation materialContainer.transform.localPosition = Vector3.zero; materialContainer.transform.localScale = Vector3.one; materialContainer.transform.localRotation = Quaternion.identity; // Copy static flag materialContainer.isStatic = staticContainers; // Get meshes of the current group and create array to store flags of processed meshes meshFilters = currentGroup.Current.Value.GetComponentsInChildren <MeshFilter>(); processedMeshes = new bool[meshFilters.Length]; // Initialize processing flags and count meshes to merge for (meshCount = 0, index = 0; index < meshFilters.Length; ++index) { if (meshFilters[index].gameObject != currentGroup.Current.Value) { processedMeshes[index] = false; ++meshCount; } else { processedMeshes[index] = true; } } if (meshCount == 0) { materialContainer = null; } currentMesh = 0; } } } else { materialContainer = null; } if (materialContainer != null) { // Count number of meshes and total vertices and indices count for current target for (iteratorNumber = 0, vertexCount = 0, indexCount = 0, count = 0, index = 0; index < meshFilters.Length; ++index) { // Check if mesh has not already been processed if (!processedMeshes[index]) { // Check for vertex, index and voxels count limit if (vertexCount + meshFilters[index].sharedMesh.vertexCount < 65536 && indexCount + meshFilters[index].sharedMesh.triangles.Length < 65536 && (objectVoxelLimit == 0 || count < objectVoxelLimit)) { // Increase number of vertices and indices vertexCount += meshFilters[index].sharedMesh.vertexCount; indexCount += meshFilters[index].sharedMesh.triangles.Length; // Increase voxels count ++count; // Get mesh data interface IMeshData meshData = meshFilters[index].gameObject.GetComponent <IMeshData>(); if (meshData != null) { // Get indices int[] indices = meshData.GetVoxelIndices(); // Increase number of voxel indices if (indices != null) { iteratorNumber += indices.Length; } } } else { break; } } } // Create array to store meshes to merge to CombineInstance[] subMeshes = new CombineInstance[count]; // Create array to store voxel indices for current mesh to int[] iteratorIndices = new int[iteratorNumber]; // Create empty list to store unused mesh data interface to List <IMeshData> unusedMeshDataInterfaces = new List <IMeshData>(); // Process meshes of the current group for (iteratorNumber = 0, vertexCount = 0, indexCount = 0, count = 0, index = 0; index < meshFilters.Length; ++index) { // Check if mesh is not already processed if (!processedMeshes[index]) { // Check for vertex and index limit if (vertexCount + meshFilters[index].sharedMesh.vertexCount < 65536 && indexCount + meshFilters[index].sharedMesh.triangles.Length < 65536 && (objectVoxelLimit == 0 || count < objectVoxelLimit)) { // Increase vertices and indices counts for current target mesh vertexCount += meshFilters[index].sharedMesh.vertexCount; indexCount += meshFilters[index].sharedMesh.triangles.Length; // Store mesh instance and calculate transformation relative to parent object subMeshes[count].mesh = meshFilters[index].sharedMesh; subMeshes[count].transform = currentGroup.Current.Value.transform.worldToLocalMatrix * meshFilters[index].transform.localToWorldMatrix; // Set flag to skip mesh at next iteration processedMeshes[index] = true; // Get mesh data interface IMeshData[] meshDataComponents = meshFilters[index].gameObject.GetComponents <IMeshData>(); if (meshDataComponents != null && meshDataComponents.Length >= 1) { // Get indices int[] indices = meshDataComponents[0].GetVoxelIndices(); // Check for valid voxel indices if (indices != null) { // Copy indices System.Array.Copy(indices, 0, iteratorIndices, iteratorNumber, indices.Length); iteratorNumber += indices.Length; } // Add interfaces for mesh data to list of unused ones unusedMeshDataInterfaces.AddRange(meshDataComponents); } // Increase sub meshes count for current merge target ++count; } } } // Create object for current mesh to merge if (containerTemplate != null) { meshContainer = Instantiate(containerTemplate); } else { meshContainer = new GameObject("Part"); } if (meshContainer != null) { // Attach it to this material object meshContainer.transform.parent = materialContainer.transform; // Unset relative transformation meshContainer.transform.localPosition = Vector3.zero; meshContainer.transform.localScale = Vector3.one; meshContainer.transform.localRotation = Quaternion.identity; // Copy static flag if (containerTemplate == null) { meshContainer.isStatic = staticContainers; } // Add mesh filter MeshFilter meshFilter = meshContainer.GetComponent <MeshFilter>(); if (meshFilter == null) { meshFilter = meshContainer.AddComponent <MeshFilter>(); } if (meshFilter != null) { // Create empty mesh object UnityEngine.Mesh mesh = new UnityEngine.Mesh(); if (mesh != null) { // Merge all collected meshes into new one mesh.CombineMeshes(subMeshes, true, true); // Set mesh to filter meshFilter.mesh = mesh; // Add mesh renderer MeshRenderer meshRenderer = meshContainer.GetComponent <MeshRenderer>(); if (meshRenderer == null) { meshRenderer = meshContainer.AddComponent <MeshRenderer>(); } // Set material if (meshRenderer != null) { meshRenderer.material = currentGroup.Current.Key; //meshRenderer.material = groups[group].material; meshRenderer.enabled = false; } // Get texture coordinates to manipulate them textureCoordinates = mesh.uv2; // Encode iterator indices into texture coordinates iteratorNumber = 0; vertexCount = 0; foreach (CombineInstance subMesh in subMeshes) { for (int vertexNumber = 0; vertexNumber < subMesh.mesh.vertexCount; ++vertexNumber, ++vertexCount) { textureCoordinates[vertexCount].x += (float)iteratorNumber; } ++iteratorNumber; } // Store manipulated UVs mesh.uv2 = textureCoordinates; // Remove mesh data interface of the merge meshes if (unusedMeshDataInterfaces != null) { foreach (IMeshData meshData in unusedMeshDataInterfaces) { meshDataInterfaces.Remove(meshData); } } // Check for mesh data and add indices IMeshData[] meshDataComponents = meshContainer.GetComponents <IMeshData>(); if (meshDataComponents != null) { foreach (IMeshData meshData in meshDataComponents) { meshData.SetVoxelIndices(iteratorIndices); meshDataInterfaces.Add(meshData); } } } } } // Decrease number of remaining objects currentMesh += count; if (currentMesh >= meshCount) { // Unset objects for current group materialContainer = null; // Remove original game object DestroyImmediate(currentGroup.Current.Value); //DestroyImmediate(groups[group].gameObject); } } } // Increase number of the current group, if it has been finished if (materialContainer == null) { if (currentGroup.MoveNext()) { ++groupNumber; } else { currentGroup = null; } } // Return current progress when building has not been finished if (currentGroup != null) { return((((float)groupNumber + ((float)currentMesh / (float)(meshCount + 1))) / (float)groups.Count * 0.5f + 0.5f) * ((voxelTexture2D != null) ? 0.5f : 1.0f) + ((voxelTexture2D != null) ? 0.5f : 0.0f)); } } // Clear groups list groups.Clear(); groups = null; } // Check for mesh data interfaces if (currentMeshDataInterface < meshDataInterfaces.Count) { // Transfer given voxels using the current interface float progress = meshDataInterfaces[currentMeshDataInterface].ProcessVoxels(voxels, bounds); if (progress >= 1) { ++currentMeshDataInterface; progress = 0; } if (currentMeshDataInterface < meshDataInterfaces.Count) { return(((float)currentMeshDataInterface + progress) / (float)meshDataInterfaces.Count); } } // Reset current processing data //currentDepth = 0; //currentHeight = 0; currentGroup = null; groupNumber = 0; meshFilters = null; processedMeshes = null; colors = null; voxelTexture2D = null; meshDataInterfaces = null; if (mainContainer != null) { // Show new main container and enable its renderers mainContainer.hideFlags &= ~HideFlags.HideAndDontSave; ShowRenderer(mainContainer); //Debug.Log("mainContainer = " + mainContainer.gameObject ); //GameObject prefab = PrefabUtility.CreatePrefab("Assets/Prefabs/test.prefab", mainContainer.gameObject, ReplacePrefabOptions.ReplaceNameBased); //StaticBatchingUtility.Combine(mainContainer); #if UNITY_EDITOR // Add object creation undo operation if (!Application.isPlaying) { UnityEditor.Undo.RegisterCreatedObjectUndo(mainContainer, "\"" + targetName + "\" Creation"); } #endif // Execute informer callback if (informer != null) { informer(new UnityEngine.Object[] { mainContainer }, parameter); } mainContainer = null; } return(1); }
// Build voxel object public virtual float Build(Storage voxels, Bounds bounds) { // Check for given array if (voxels != null) { if (!building) { int existingIndex; int x, y, z; // Get iterator if (iterator == null) { iterator = voxels.GetIterator(); currentIndex = 0; currentProgress = 0; } if (colorAssignments == null) { // Create empty list to color assignments to colorAssignments = new Dictionary <Color, int>(); } else { // Get current color index from existing hash map currentIndex = colorAssignments.Count; } // Process voxels in steps for (int number = 0; number < 256; ++number) { // Retrieve color and coordinate for current cell Color color = iterator.GetNextColor(out x, out y, out z); // Check for valid voxel if (color.a > 0) { // Add assignment between color and vertex index, if it is not already included if (!colorAssignments.TryGetValue(color, out existingIndex)) { colorAssignments.Add(color, currentIndex++); } } else { iterator = null; break; } } // Return current progress when building has not been finished if (iterator != null) { return(currentProgress = (float)iterator.Number / (float)(voxels.Count + 1) * 0.5f); } else { building = true; texture = null; } } if (colorAssignments != null) { CoordinateAssignment assignment; int column = 0, line = 0; // Compute resolution to fit all voxels into a 2D surface int textureWidth = (int)Math.Pow(2, Math.Ceiling(Math.Log(Math.Sqrt(colorAssignments.Count)) / Math.Log(2))); int textureHeight = (int)Math.Ceiling((double)colorAssignments.Count / (double)textureWidth); // Make height 2^n, too, if flag is set if (powerOfTwo) { textureHeight = (int)Math.Pow(2, Math.Ceiling(Math.Log((float)textureHeight) / Math.Log(2))); } //// Change resolution, if current does not match the required resolution //if (texture != null && ((texture.width != textureWidth) || (texture.height != textureHeight))) //{ // try // { // texture.Resize(textureWidth, textureHeight, TextureFormat.ARGB32, false); // // Fill texture and calculate texture coordinates // foreach (KeyValuePair<Color, int> currentPixel in colorAssignments) // { // texture.SetPixel(column = currentPixel.Value % texture.width, line = currentPixel.Value / texture.width, currentPixel.Key); // } // iterator = null; // } // catch (System.Exception) // { // texture = null; // } //} if (texture == null) { if (textureWidth != 0 && textureHeight != 0) { // Create new texture instance texture = new UnityEngine.Texture2D(textureWidth, textureHeight, TextureFormat.ARGB32, false); if (texture != null) { texture.filterMode = FilterMode.Point; texture.wrapMode = TextureWrapMode.Clamp; } } iterator = null; } if (texture != null) { // Check for non-empty array if (voxels.Count > 0) { // Get iterator if (iterator == null) { iterator = voxels.GetIterator(); currentIndex = 0; currentProgress = 0; // Create array to store coordinates to coordinateAssignments = new CoordinateAssignment[voxels.Count]; } // Process voxels in steps for (int number = 0; number < texture.width; ++number) { // Retrieve color and coordinate for current cell int index = iterator.Number; Color color = iterator.GetNextColor(out assignment.source.x, out assignment.source.y, out assignment.source.z); // Check for valid voxel if (color.a > 0) { // Get index for current color if (colorAssignments.TryGetValue(color, out currentIndex)) { // Store color as pixel texture.SetPixel(column = currentIndex % texture.width, line = currentIndex / texture.width, color); // Calculate coordinate for center of the current texel assignment.target.x = ((float)column + 0.5f) / (float)texture.width; assignment.target.y = ((float)line + 0.5f) / (float)texture.height; // Store assigned coordinates to array coordinateAssignments[index] = assignment; } } else { iterator = null; break; } } // Return current progress when building has not been finished if (iterator != null) { return(currentProgress = (float)iterator.Number / (float)(voxels.Count + 1) * 0.5f + 0.5f); } // Unset remaining texels for (column = colorAssignments.Count % texture.width, line = colorAssignments.Count / texture.width; line < texture.height; ++line) { for (; column < texture.width; ++column) { texture.SetPixel(column, line, Color.clear); } column = 0; } } } } } // Check for texture and color array if (texture != null) { // Apply color changes on texture texture.Apply(); } // Reset current processing data currentIndex = 0; iterator = null; colorAssignments = null; building = false; return(currentProgress = 1); }
// Build voxel object public virtual float Build(Storage voxels, Bounds bounds) { // Check for given array if (voxels != null) { //if (colorAssignments != null) { // Check for non-empty array if (voxels.Count > 0) { // Get iterator if (iterator == null) { iterator = voxels.GetIterator(); currentProgress = 0; } if (texture == null) { if (superSamplingCount <= 0) { superSamplingCount = 1; } // Calculate target resolution int textureWidth = (voxels.Width + superSamplingCount - 1) / superSamplingCount; int textureHeight = (voxels.Height + superSamplingCount - 1) / superSamplingCount; int textureDepth = (voxels.Depth + superSamplingCount - 1) / superSamplingCount; // Make resolution 2^n, if flag is set if (powerOfTwo) { textureWidth = (int)Math.Pow(2, Math.Ceiling(Math.Log((float)textureWidth) / Math.Log(2))); textureHeight = (int)Math.Pow(2, Math.Ceiling(Math.Log((float)textureHeight) / Math.Log(2))); textureDepth = (int)Math.Pow(2, Math.Ceiling(Math.Log((float)textureDepth) / Math.Log(2))); } if (textureWidth != 0 && textureHeight != 0 && textureDepth != 0) { texels = new Color[textureWidth * textureHeight * textureDepth]; counts = new float[textureWidth * textureHeight * textureDepth]; hdr |= voxels.HasHDR(); // Create new texture instance texture = new UnityEngine.Texture3D(textureWidth, textureHeight, textureDepth, hdr ? TextureFormat.RGBAHalf : TextureFormat.RGBA32, 4); if (texture != null) { //texture.filterMode = FilterMode.Point; texture.wrapMode = TextureWrapMode.Clamp; } } } if (texture != null) { // Process voxels in steps for (int number = 0; number < 10; ++number) { // Retrieve color and coordinate for current cell int x, y, z; Color color = iterator.GetNextColor(out x, out y, out z); // Check for valid voxel if (color.a > 0) { var index = x / superSamplingCount + (y / superSamplingCount + z / superSamplingCount * texture.height) * texture.width; // Store color to texels array texels[index] += color; ++counts[index]; } else { iterator = null; break; } } // Return current progress when building has not been finished if (iterator != null) { return(currentProgress = (float)iterator.Number / (float)(voxels.Count + 1)); } else { // Calculate weight factor for every source cell var samplingFactor = 1f / (superSamplingCount * superSamplingCount * superSamplingCount); // Normalize colors and expand edges or blend with background for (int index = 0; index < texels.Length; ++index) { if (counts[index] > 0) { if (expandEdges) { texels[index].r /= counts[index]; texels[index].g /= counts[index]; texels[index].b /= counts[index]; texels[index].a *= samplingFactor; } else { texels[index] /= counts[index]; texels[index] += backgroundColor * (1 - texels[index].a); } } else { if (!expandEdges) { texels[index] = backgroundColor; } } } if (expandEdges) { bool repeat; do { repeat = false; // Process all cells for (int index = 0; index < texels.Length; ++index) { // Check if current cell is empty if (counts[index] == 0) { var column = index % texture.width; var row = index / texture.width % texture.height; var slice = index / texture.width / texture.height; var color = new Color(0, 0, 0, 0); var count = 0f; // Sum up all colors of direct neighbor cells for (int offset = 0; offset < 6; ++offset) { // Get offset by current index var offsetX = offset == 0 ? -1 : offset == 1 ? 1 : 0; var offsetY = offset == 2 ? -1 : offset == 3 ? 1 : 0; var offsetZ = offset == 4 ? -1 : offset == 5 ? 1 : 0; var offsetColumn = column + offsetX; if (offsetColumn >= 0 && offsetColumn < texture.width) { var offsetRow = row + offsetY; if (offsetRow >= 0 && offsetRow < texture.height) { var offsetSlice = slice + offsetZ; if (offsetSlice >= 0 && offsetSlice < texture.depth) { var offsetIndex = offsetColumn + (offsetRow + offsetSlice * texture.height) * texture.width; // Check if neighbor includes an original color or one that has been set in a previous iteration if (counts[offsetIndex] > 0) { // Sum color components and increase quantity counter for later normalization color += texels[offsetIndex]; ++count; } } } } } if (count > 0) { // Normalize target color but set full transparency texels[index].r = color.r / count; texels[index].g = color.g / count; texels[index].b = color.b / count; texels[index].a = 0; // Flag index as processed in this loop and enable next one counts[index] = -count; repeat = true; } } } // Unset processing flags for next iteration for (int index = 0; index < texels.Length; ++index) { if (counts[index] < 0) { counts[index] = -counts[index]; } } }while (repeat); } // Transfer all texel colors to the texture texture.SetPixels(texels); } } } } } // Check for texture and color array if (texture != null) { // Apply color changes on texture texture.Apply(); } // Reset current processing data iterator = null; return(currentProgress = 1); }