private WorkingMaterial CreateBakedMaterial(string name, Bounds bounds, bool useHighMaterial) { int matInstanceID = useHighMaterial ? m_terrainMaterialInstanceId : m_terrainMaterialLowInstanceId; string matName = useHighMaterial ? m_terrainMaterialName : m_terrainMaterialLowName; WorkingMaterial material = new WorkingMaterial(Allocator.Persistent, matInstanceID, matName); material.Name = name + "_Material"; m_queue.EnqueueJob(() => { WorkingTexture albedo = BakeAlbedo(name, bounds, m_hlod.TextureSize); material.AddTexture(m_hlod.AlbedoPropertyName, albedo); }); if (m_hlod.UseNormal) { m_queue.EnqueueJob(() => { WorkingTexture normal = BakeNormal(name, bounds, m_hlod.TextureSize); material.AddTexture(m_hlod.NormalPropertyName, normal); }); } if (m_hlod.UseMask) { m_queue.EnqueueJob(() => { WorkingTexture mask = BakeMask(name, bounds, m_hlod.TextureSize); material.AddTexture(m_hlod.MaskPropertyName, mask); }); } return(material); }
private WorkingTexture GenerateMipmap(WorkingTexture source) { int sx = Mathf.Max(source.Width >> 1, 1); int sy = Mathf.Max(source.Height >> 1, 1); WorkingTexture mipmap = new WorkingTexture(Allocator.Persistent, source.Format, sx, sy, source.Linear); mipmap.Name = source.Name; for (int y = 0; y < sy; ++y) { for (int x = 0; x < sx; ++x) { Color color = new Color(); int x1 = Mathf.Min(x * 2 + 0, source.Width - 1); int x2 = Mathf.Min(x * 2 + 1, source.Width - 1); int y1 = Mathf.Min(y * 2 + 0, source.Height - 1); int y2 = Mathf.Min(y * 2 + 1, source.Height - 1); color += source.GetPixel(x1, y1); color += source.GetPixel(x1, y2); color += source.GetPixel(x2, y1); color += source.GetPixel(x2, y2); color /= 4; mipmap.SetPixel(x, y, color); } } return(mipmap); }
private void RemapTexture(WorkingTexture source, Color min, Color max) { for (int y = 0; y < source.Height; ++y) { for (int x = 0; x < source.Width; ++x) { var color = source.GetPixel(x, y); color = color * max + min; source.SetPixel(x, y, color); } } }
static private WorkingTexture CreateEmptyTexture(int width, int height, Color color, bool linear) { WorkingTexture texture = new WorkingTexture(Allocator.Persistent, TextureFormat.RGB24, width, height, linear); for (int y = 0; y < height; ++y) { for (int x = 0; x < width; ++x) { texture.SetPixel(x, y, color); } } return(texture); }
private void ApplyTintColor(WorkingTexture texture, Color tintColor) { for (int ty = 0; ty < texture.Height; ++ty) { for (int tx = 0; tx < texture.Width; ++tx) { Color c = texture.GetPixel(tx, ty); c.r = c.r * tintColor.r; c.g = c.g * tintColor.g; c.b = c.b * tintColor.b; c.a = c.a * tintColor.a; texture.SetPixel(tx, ty, c); } } }
private WorkingTexture BakeMask(string name, Bounds bounds, int resolution) { WorkingTexture maskTexture = new WorkingTexture(Allocator.Persistent, TextureFormat.ARGB32, resolution, resolution, false); maskTexture.Name = name + "_Mask"; maskTexture.WrapMode = TextureWrapMode.Clamp; EnqueueBlendTextureJob(maskTexture, bounds, resolution, (layer, wx, wz, sx, sz, linear) => { Color c = m_layers[layer].GetMaskByWorld(wx, wz, sx, sz); if (!linear) { c = c.linear; } return(c); }); return(maskTexture); }
private void AddToCache(WorkingMaterial material) { string inputName = m_textureInfoList[0].InputName; WorkingTexture texture = material.GetTexture(inputName); if (texture == null) { texture = m_defaultTextures[m_textureInfoList[0].Type]; } TexturePacker.MaterialTexture materialTexture = new TexturePacker.MaterialTexture(); if (m_enableTintColor) { Color tintColor = material.GetColor(m_tintColorName); texture = texture.Clone(); ApplyTintColor(texture, tintColor); materialTexture.Add(texture); texture.Dispose(); } else { materialTexture.Add(texture); } for (int ti = 1; ti < m_textureInfoList.Count; ++ti) { string input = m_textureInfoList[ti].InputName; WorkingTexture tex = material.GetTexture(input); if (tex == null) { tex = m_defaultTextures[m_textureInfoList[ti].Type]; } materialTexture.Add(tex); } m_textureCache.Add(material.Guid, materialTexture); }
private WorkingTexture BakeAlbedo(string name, Bounds bounds, int resolution) { WorkingTexture albedoTexture = new WorkingTexture(Allocator.Persistent, TextureFormat.RGB24, resolution, resolution, false); albedoTexture.Name = name + "_Albedo"; albedoTexture.WrapMode = TextureWrapMode.Clamp; EnqueueBlendTextureJob(albedoTexture, bounds, resolution, (layer, wx, wz, sx, sz, linear) => { Color c = m_layers[layer].GetColorByWorld(wx, wz, sx, sz); if (!linear) { c = c.linear; } return(c); }); return(albedoTexture); }
private WorkingTexture BakeNormal(string name, Bounds bounds, int resolution) { WorkingTexture normalTexture = new WorkingTexture(Allocator.Persistent, TextureFormat.RGB24, resolution, resolution, true); normalTexture.Name = name + "_Normal"; normalTexture.WrapMode = TextureWrapMode.Clamp; EnqueueBlendTextureJob(normalTexture, bounds, resolution, (layer, wx, wz, sx, sz, linear) => { Color c = m_layers[layer].GetNormalByWorld(wx, wz, sx, sz); c = UnPackNormal(c, m_layers[layer].NormalScale); return(c); }, (c) => { Vector3 n = new Vector3(c.r, c.g, c.b); n.Normalize(); c = new Color(n.x, n.y, n.z); return(PackNormal(c)); }); return(normalTexture); }
private DisposableList <MaterialTexture> CreateResizedTextures(int newWidth, int newHeight) { DisposableList <MaterialTexture> resized = new DisposableList <MaterialTexture>(); for (int i = 0; i < m_textures.Count; ++i) { MaterialTexture newMT = new MaterialTexture(); for (int k = 0; k < m_textures[i].Count; ++k) { int targetWidth = Mathf.Min(newWidth, m_textures[i][k].Width); int targetHeight = Mathf.Min(newHeight, m_textures[i][k].Height); WorkingTexture resizedTexture = m_textures[i][k].Resize(Allocator.Persistent, targetWidth, targetHeight); newMT.Add(resizedTexture); resizedTexture.Dispose(); } resized.Add(newMT); } return(resized); }
private WorkingMaterial CreateBakedMaterial(string name, Bounds bounds) { WorkingMaterial material = new WorkingMaterial(Allocator.Persistent, m_terrainMaterialInstanceId, m_terrainMaterialName); material.Name = name + "_Material"; m_queue.EnqueueJob(() => { WorkingTexture albedo = BakeAlbedo(name, bounds, m_hlod.TextureSize); material.AddTexture(m_hlod.AlbedoPropertyName, albedo); }); if (m_hlod.UseNormal) { m_queue.EnqueueJob(() => { WorkingTexture normal = BakeNormal(name, bounds, m_hlod.TextureSize); material.AddTexture(m_hlod.NormalPropertyName, normal); }); } return(material); }
private WorkingTexture BakeNormal(string name, Bounds bounds, int resolution) { WorkingTexture normalTexture = new WorkingTexture(Allocator.Persistent, TextureFormat.RGB24, resolution, resolution, true); normalTexture.Name = name + "_Normal"; normalTexture.WrapMode = TextureWrapMode.Clamp; m_queue.EnqueueJob(() => { float ustart = (bounds.min.x) / m_size.x; float vstart = (bounds.min.z) / m_size.z; float usize = (bounds.max.x - bounds.min.x) / m_size.x; float vsize = (bounds.max.z - bounds.min.z) / m_size.z; for (int y = 0; y < resolution; ++y) { for (int x = 0; x < resolution; ++x) { float u = (float)x / (float)resolution * usize + ustart; float v = (float)y / (float)resolution * vsize + vstart; Vector3 normal = m_heightmap.GetInterpolatedNormal(u, v); Color color = new Color(0.0f, 0.0f, 0.0f, 0.0f); color.r = normal.x * 0.5f + 0.5f; color.g = -normal.z * 0.5f + 0.5f; color.b = normal.y * 0.5f + 0.5f; color.a = 1.0f; normalTexture.SetPixel(x, y, color); } } }); return(normalTexture); }
private WorkingTexture BakeAlbedo(string name, Bounds bounds, int resolution) { WorkingTexture albedoTexture = new WorkingTexture(Allocator.Persistent, TextureFormat.RGB24, resolution, resolution, false); albedoTexture.Name = name + "_Albedo"; albedoTexture.WrapMode = TextureWrapMode.Clamp; m_queue.EnqueueJob(() => { float ustart = (bounds.min.x) / m_size.x; float vstart = (bounds.min.z) / m_size.z; float usize = (bounds.max.x - bounds.min.x) / m_size.x; float vsize = (bounds.max.z - bounds.min.z) / m_size.z; for (int y = 0; y < resolution; ++y) { for (int x = 0; x < resolution; ++x) { float u = (float)x / (float)resolution * usize + ustart; float v = (float)y / (float)resolution * vsize + vstart; Color color = new Color(0.0f, 0.0f, 0.0f, 0.0f); for (int li = 0; li < m_layers.Count; ++li) { float weight = 0.0f; switch (li % 4) { case 0: weight = m_alphamaps[li / 4].GetPixel(u, v).r; break; case 1: weight = m_alphamaps[li / 4].GetPixel(u, v).g; break; case 2: weight = m_alphamaps[li / 4].GetPixel(u, v).b; break; case 3: weight = m_alphamaps[li / 4].GetPixel(u, v).a; break; } //optimize to skip not effect pixels. if (weight < 0.01f) { continue; } float wx = (float)x / (float)resolution * bounds.size.x + bounds.min.x; float wy = (float)y / (float)resolution * bounds.size.z + bounds.min.z; Color c = m_layers[li].GetColorByWorld(wx, wy, bounds.size.x, bounds.size.z); color.r += Mathf.Pow(c.r, 2.2f) * weight; color.g += Mathf.Pow(c.g, 2.2f) * weight; color.b += Mathf.Pow(c.b, 2.2f) * weight; } color.r = Mathf.Pow(color.r, 0.454545f); color.g = Mathf.Pow(color.g, 0.454545f); color.b = Mathf.Pow(color.b, 0.454545f); color.a = 1.0f; albedoTexture.SetPixel(x, y, color); } } }); return(albedoTexture); }
private void EnqueueBlendTextureJob(WorkingTexture texture, Bounds bounds, int resolution, System.Func <int, float, float, float, float, bool, Color> getColor, System.Func <Color, Color> packColor = null) { bool linear = texture.Linear; m_queue.EnqueueJob(() => { float ustart = (bounds.min.x) / m_size.x; float vstart = (bounds.min.z) / m_size.z; float usize = (bounds.max.x - bounds.min.x) / m_size.x; float vsize = (bounds.max.z - bounds.min.z) / m_size.z; for (int y = 0; y < resolution; ++y) { for (int x = 0; x < resolution; ++x) { float u = (float)x / (float)resolution * usize + ustart; float v = (float)y / (float)resolution * vsize + vstart; Color color = new Color(0.0f, 0.0f, 0.0f, 0.0f); for (int li = 0; li < m_layers.Count; ++li) { float weight = 0.0f; switch (li % 4) { case 0: weight = m_alphamaps[li / 4].GetPixel(u, v).r; break; case 1: weight = m_alphamaps[li / 4].GetPixel(u, v).g; break; case 2: weight = m_alphamaps[li / 4].GetPixel(u, v).b; break; case 3: weight = m_alphamaps[li / 4].GetPixel(u, v).a; break; } //optimize to skip not effect pixels. if (weight < 0.01f) { continue; } float wx = (float)x / (float)resolution * bounds.size.x + bounds.min.x; float wy = (float)y / (float)resolution * bounds.size.z + bounds.min.z; Color c = getColor(li, wx, wy, bounds.size.x, bounds.size.z, linear); // blend in linear space. color.r += c.r * weight; color.g += c.g * weight; color.b += c.b * weight; color.a += c.a * weight; } if (packColor != null) { color = packColor(color); } if (!linear) { color = color.gamma; } texture.SetPixel(x, y, color); } } }); }
public TextureCombiner(Allocator allocator, TextureFormat format, int width, int height, bool linear) { m_texture = new WorkingTexture(allocator, format, width, height, linear); }
public void SetTexture(WorkingTexture source, int x, int y) { m_texture.Blit(source, x, y); }
private void PackingTexture(TexturePacker packer, DisposableList <HLODBuildInfo> targets, dynamic options, Action <float> onProgress) { List <TextureInfo> textureInfoList = options.TextureInfoList; using (MaterialTextureCache cache = new MaterialTextureCache(options)) { for (int i = 0; i < targets.Count; ++i) { var workingObjects = targets[i].WorkingObjects; Dictionary <Guid, TexturePacker.MaterialTexture> textures = new Dictionary <Guid, TexturePacker.MaterialTexture>(); for (int oi = 0; oi < workingObjects.Count; ++oi) { var materials = workingObjects[oi].Materials; for (int m = 0; m < materials.Count; ++m) { var materialTextures = cache.GetMaterialTextures(materials[m]); if (materialTextures == null) { continue; } if (textures.ContainsKey(materialTextures[0].GetGUID()) == true) { continue; } textures.Add(materialTextures[0].GetGUID(), materialTextures); } } packer.AddTextureGroup(targets[i], textures.Values.ToList()); if (onProgress != null) { onProgress(((float)i / targets.Count) * 0.1f); } } } packer.Pack(TextureFormat.RGBA32, options.PackTextureSize, options.LimitTextureSize, false); if (onProgress != null) { onProgress(0.3f); } int index = 1; var atlases = packer.GetAllAtlases(); foreach (var atlas in atlases) { Dictionary <string, WorkingTexture> textures = new Dictionary <string, WorkingTexture>(); for (int i = 0; i < atlas.Textures.Count; ++i) { WorkingTexture wt = atlas.Textures[i]; wt.Name = "CombinedTexture " + index + "_" + i; if (textureInfoList[i].Type == PackingType.Normal) { wt.Linear = true; } textures.Add(textureInfoList[i].OutputName, wt); } WorkingMaterial mat = CreateMaterial(options.MaterialGUID, textures); mat.Name = "CombinedMaterial " + index; m_createdMaterials.Add(atlas, mat); index += 1; } }
public void Add(WorkingTexture texture) { m_textures.Add(texture.Clone()); }
public Layer(TerrainLayer layer) { var texture = layer.diffuseTexture; bool linear = !GraphicsFormatUtility.IsSRGBFormat(texture.graphicsFormat); m_diffuseTexstures = new DisposableList <WorkingTexture>(); m_offset = layer.tileOffset; m_size = layer.tileSize; var diffuseRemapMin = layer.diffuseRemapMin; var diffuseRemapMax = layer.diffuseRemapMax; diffuseRemapMin.x = Mathf.Pow(diffuseRemapMin.x, 0.45f); diffuseRemapMin.y = Mathf.Pow(diffuseRemapMin.y, 0.45f); diffuseRemapMin.z = Mathf.Pow(diffuseRemapMin.z, 0.45f); diffuseRemapMax.x = Mathf.Pow(diffuseRemapMax.x, 0.45f); diffuseRemapMax.y = Mathf.Pow(diffuseRemapMax.y, 0.45f); diffuseRemapMax.z = Mathf.Pow(diffuseRemapMax.z, 0.45f); //make to texture readable. var assetImporter = AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(texture)); var textureImporter = assetImporter as TextureImporter; TextureImporterType type = TextureImporterType.Default; if (textureImporter) { type = textureImporter.textureType; textureImporter.isReadable = true; textureImporter.textureType = TextureImporterType.Default; textureImporter.SaveAndReimport(); } try { for (int i = 0; i < texture.mipmapCount; ++i) { int width = texture.width >> i; int height = texture.height >> i; WorkingTexture workingTexture = new WorkingTexture(Allocator.Persistent, texture.format, width, height, linear); Color[] colors = texture.GetPixels(i); for (int y = 0; y < height; ++y) { for (int x = 0; x < width; ++x) { workingTexture.SetPixel(x, y, colors[y * width + x]); } } RemapTexture(workingTexture, diffuseRemapMin, diffuseRemapMax); m_diffuseTexstures.Add(workingTexture); } } finally { if (textureImporter) { textureImporter.isReadable = false; textureImporter.textureType = type; textureImporter.SaveAndReimport(); } } }
public IEnumerator CreateImpl() { try { using (m_queue = new JobQueue(8)) { Stopwatch sw = new Stopwatch(); AssetDatabase.Refresh(); AssetDatabase.SaveAssets(); sw.Reset(); sw.Start(); EditorUtility.DisplayProgressBar("Bake HLOD", "Initialize Bake", 0.0f); TerrainData data = m_hlod.TerrainData; m_size = data.size; m_heightmap = new Heightmap(data.heightmapResolution, data.heightmapResolution, data.size, data.GetHeights(0, 0, data.heightmapResolution, data.heightmapResolution)); string materialPath = AssetDatabase.GUIDToAssetPath(m_hlod.MaterialGUID); m_terrainMaterial = AssetDatabase.LoadAssetAtPath <Material>(materialPath); if (m_terrainMaterial == null) { m_terrainMaterial = new Material(Shader.Find("Standard")); } m_terrainMaterialInstanceId = m_terrainMaterial.GetInstanceID(); m_terrainMaterialName = m_terrainMaterial.name; using (m_alphamaps = new DisposableList <WorkingTexture>()) using (m_layers = new DisposableList <Layer>()) { for (int i = 0; i < data.alphamapTextures.Length; ++i) { m_alphamaps.Add(new WorkingTexture(Allocator.Persistent, data.alphamapTextures[i])); } for (int i = 0; i < data.terrainLayers.Length; ++i) { m_layers.Add(new Layer(data.terrainLayers[i])); } QuadTreeSpaceSplitter splitter = new QuadTreeSpaceSplitter(0.0f); SpaceNode rootNode = splitter.CreateSpaceTree(m_hlod.GetBounds(), m_hlod.ChunkSize * 2.0f, m_hlod.transform.position, null, progress => { }); EditorUtility.DisplayProgressBar("Bake HLOD", "Create mesh", 0.0f); using (DisposableList <HLODBuildInfo> buildInfos = CreateBuildInfo(data, rootNode)) { yield return(m_queue.WaitFinish()); //Write material & textures for (int i = 0; i < buildInfos.Count; ++i) { int curIndex = i; m_queue.EnqueueJob(() => { ISimplifier simplifier = (ISimplifier)Activator.CreateInstance(m_hlod.SimplifierType, new object[] { m_hlod.SimplifierOptions }); simplifier.SimplifyImmidiate(buildInfos[curIndex]); }); } EditorUtility.DisplayProgressBar("Bake HLOD", "Simplify meshes", 0.0f); yield return(m_queue.WaitFinish()); Debug.Log("[TerrainHLOD] Simplify: " + sw.Elapsed.ToString("g")); sw.Reset(); sw.Start(); EditorUtility.DisplayProgressBar("Bake HLOD", "Make border", 0.0f); for (int i = 0; i < buildInfos.Count; ++i) { HLODBuildInfo info = buildInfos[i]; m_queue.EnqueueJob(() => { for (int oi = 0; oi < info.WorkingObjects.Count; ++oi) { WorkingObject o = info.WorkingObjects[oi]; int borderVertexCount = m_hlod.BorderVertexCount * Mathf.RoundToInt(Mathf.Pow(2.0f, (float)info.Distances[oi])); using (WorkingMesh m = MakeBorder(o.Mesh, info.Heightmap, borderVertexCount)) { ReampUV(m, info.Heightmap); o.SetMesh(MakeFillHoleMesh(m)); } } }); } yield return(m_queue.WaitFinish()); Debug.Log("[TerrainHLOD] Make Border: " + sw.Elapsed.ToString("g")); sw.Reset(); sw.Start(); for (int i = 0; i < buildInfos.Count; ++i) { SpaceNode node = buildInfos[i].Target; HLODBuildInfo info = buildInfos[i]; if (node.HasChild() == false) { SpaceNode parent = node.ParentNode; node.ParentNode = null; GameObject go = new GameObject(buildInfos[i].Name); for (int wi = 0; wi < info.WorkingObjects.Count; ++wi) { WorkingObject wo = info.WorkingObjects[wi]; GameObject targetGO = null; if (wi == 0) { targetGO = go; } else { targetGO = new GameObject(wi.ToString()); targetGO.transform.SetParent(go.transform, false); } List <Material> materials = new List <Material>(); for (int mi = 0; mi < wo.Materials.Count; ++mi) { WorkingMaterial wm = wo.Materials[mi]; if (wm.NeedWrite() == false) { materials.Add(wm.ToMaterial()); continue; } Material mat = new Material(wm.ToMaterial()); string[] textureNames = wm.GetTextureNames(); for (int ti = 0; ti < textureNames.Length; ++ti) { WorkingTexture wt = wm.GetTexture(textureNames[ti]); Texture2D tex = wt.ToTexture(); tex.wrapMode = wt.WrapMode; mat.name = targetGO.name + "_Mat"; mat.SetTexture(textureNames[ti], tex); } mat.EnableKeyword("_NORMALMAP"); materials.Add(mat); } targetGO.AddComponent <MeshFilter>().sharedMesh = wo.Mesh.ToMesh(); targetGO.AddComponent <MeshRenderer>().sharedMaterials = materials.ToArray(); } go.transform.SetParent(m_hlod.transform, false); m_hlod.AddGeneratedResource(go); parent.Objects.Add(go); buildInfos.RemoveAt(i); i -= 1; } } //controller IStreamingBuilder builder = (IStreamingBuilder)Activator.CreateInstance(m_hlod.StreamingType, new object[] { m_hlod, m_hlod.StreamingOptions }); builder.Build(rootNode, buildInfos, m_hlod.gameObject, m_hlod.CullDistance, m_hlod.LODDistance, true, false, progress => { EditorUtility.DisplayProgressBar("Bake HLOD", "Storing results.", 0.75f + progress * 0.25f); }); Debug.Log("[TerrainHLOD] Build: " + sw.Elapsed.ToString("g")); } } EditorUtility.SetDirty(m_hlod.gameObject); } } finally { EditorUtility.ClearProgressBar(); GC.Collect(); } }
void MakeTexture(TerrainLayer layer, Texture2D texture, Vector4 min, Vector4 max, DisposableList <WorkingTexture> results) { bool linear = !GraphicsFormatUtility.IsSRGBFormat(texture.graphicsFormat); var offset = layer.tileOffset; var size = layer.tileSize; if (!linear) { min.x = Mathf.Pow(min.x, 0.45f); min.y = Mathf.Pow(min.y, 0.45f); min.z = Mathf.Pow(min.z, 0.45f); max.x = Mathf.Pow(max.x, 0.45f); max.y = Mathf.Pow(max.y, 0.45f); max.z = Mathf.Pow(max.z, 0.45f); } //make to texture readable. var assetImporter = AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(texture)); var textureImporter = assetImporter as TextureImporter; TextureImporterType type = TextureImporterType.Default; if (textureImporter) { type = textureImporter.textureType; textureImporter.isReadable = true; textureImporter.textureType = TextureImporterType.Default; textureImporter.SaveAndReimport(); } try { for (int i = 0; i < texture.mipmapCount; ++i) { int width = texture.width >> i; int height = texture.height >> i; WorkingTexture workingTexture = new WorkingTexture(Allocator.Persistent, texture.format, width, height, linear); Color[] colors = texture.GetPixels(i); for (int y = 0; y < height; ++y) { for (int x = 0; x < width; ++x) { workingTexture.SetPixel(x, y, colors[y * width + x]); } } RemapTexture(workingTexture, min, max); results.Add(workingTexture); } } finally { if (textureImporter) { textureImporter.isReadable = false; textureImporter.textureType = type; textureImporter.SaveAndReimport(); } } }