/// <summary> /// will set the color if our vertex color is black (IE: not already set), and no adjacent verticies are using that color /// if color already set to the same color, will also return true /// </summary> /// <param name="c"></param> /// <returns></returns> public bool TrySetAvailableColor(Color c, bool force = false) { if (force == true) { mdt.SetVertexColor(vertIdx, c); return(true); } var currentColor = mdt.GetVertexColor(vertIdx); if (currentColor == c) { return(true); } // if (currentColor != Colors.Transparent) { return(false); } foreach (var adjIdx in adjacentVerticies) { var adjColor = mdt.GetVertexColor(adjIdx); if (adjColor == c) { return(false); } } mdt.SetVertexColor(vertIdx, c); return(true); }
/// <summary> /// 4th attempt: set color by most linked, not by color /// </summary> /// <param name="mdt"></param> public static void _SetVertexColorToBarycentric(MeshDataTool mdt) { //store info about our verticies into an array var vertStorage = new VertexInfo[mdt.GetVertexCount()]; for (var vertIdx = 0; vertIdx < vertStorage.Length; vertIdx++) { vertStorage[vertIdx] = new VertexInfo(vertIdx, mdt, vertStorage); //set vert color to alphaBlack mdt.SetVertexColor(vertIdx, Colors.Transparent); } //sort verticies by degree (number of edges). defaults to highest first var sortedVerts = new List <VertexInfo>(vertStorage); sortedVerts.Sort(); //color channels used for verticies. 3 is ideal, but aprox 10% of verts wont be colored. var colorChoices = new Color[] { //encode 5 channels as 20% red each. new Color(0.2f, 0, 0, 0), new Color(0.4f, 0, 0, 0), new Color(0.6f, 0, 0, 0), new Color(0.8f, 0, 0, 0), new Color(1f, 0, 0, 0) }; ////////////// various algorithm choices. best is _WELSH_POWELL_ADJUSTED //_GREEDY_FACE(sortedVerts, colorChoices, mdt); //_GREEDY_BASIC(sortedVerts, colorChoices, mdt); //_CYBERREALITY(sortedVerts, colorChoices, mdt); //_CYBERREALITY_EDIT(sortedVerts, colorChoices, mdt); _WELSH_POWELL_ADJUSTED(sortedVerts, colorChoices, mdt); }
/// <summary> /// 3rd attempt: after researching a bit I realized this is a "graph coloring" problem: https://en.wikipedia.org/wiki/Graph_coloring /// nice overview of some here: https://github.com/Ajaypal91/Graph_Coloring_Algorithms_Implementation /// here we implement welsh-powell algorithm: https://www.youtube.com/watch?v=CQIW2mLfG04 /// </summary> /// <param name="mdt"></param> private static void _SetVertexColorToBarycentric_WP(MeshDataTool mdt) { //store info about our verticies into an array var vertStorage = new VertexInfo[mdt.GetVertexCount()]; for (var vertIdx = 0; vertIdx < vertStorage.Length; vertIdx++) { vertStorage[vertIdx] = new VertexInfo(vertIdx, mdt, vertStorage); //set vert color to black mdt.SetVertexColor(vertIdx, Colors.Transparent); } //sort verticies by degree (number of edges). defaults to highest first var sortedVerts = new List <VertexInfo>(vertStorage); sortedVerts.Sort(); //verts.CopyTo(sortedVerts,0); //Array.Sort(sortedVerts); var colorChoices = new Color[] { Colors.Red, Colors.Green, Colors.Blue }; foreach (var color in colorChoices) { //enumerate in reverse so we inspect our verticies with highest degree first (most edges) //and also lets us remove from the list directly for (int i = sortedVerts.Count - 1; i >= 0; i--) { var vertInfo = sortedVerts[i]; if (vertInfo.TrySetAvailableColor(color)) { sortedVerts.RemoveAt(i); } } } //any remaining verts are uncolored! bad. GD.Print($"Done building mesh. Verticies uncolored count={sortedVerts.Count} / {mdt.GetVertexCount()}"); //for any remaining verticies color alpha var alphaBlack = new Color(0, 0, 0, 0); for (int i = sortedVerts.Count - 1; i >= 0; i--) { var vertInfo = sortedVerts[i]; mdt.SetVertexColor(vertInfo.vertIdx, alphaBlack); } }
/// <summary> /// cyberality's technique. needs some optimization for use with big meshes. /// </summary> /// <param name="sortedVerts"></param> /// <param name="colorChoices"></param> /// <param name="mdt"></param> private static void _CYBERREALITY(List <VertexInfo> sortedVerts, Color[] colorChoices, MeshDataTool mdt) { var done = new Dictionary <int, bool>(); //vertidx/isDone var bary = new Dictionary <int, Color>(); //vertidx/color var rand = new Random(0); var rand_color = new Color((float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble()); var nors = new Dictionary <int, (Vector3 normal, Color color)>(); //normalIdx/normal for (var j = 0; j < mdt.GetFaceCount(); j++) { var fid = mdt.GetFaceVertex(j, 0); var nor = mdt.GetVertexNormal(fid); var coords = new List <Color>() { Colors.Red, Colors.Green, Colors.Blue }; foreach (var n in nors.Keys) { var dot = nor.Dot(nors[n].normal); if (dot == 1) { rand_color = nors[n].color; } else { rand_color = new Color((float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble()); } } nors[fid] = (normal : nor, color : rand_color); for (var k = 0; k < 3; k++) { var vid = mdt.GetFaceVertex(j, k); if (bary.ContainsKey(vid)) { coords.Remove(bary[vid]); } } for (var i = 0; i < 3; i++) { var vid = mdt.GetFaceVertex(j, i); if (!done.ContainsKey(vid) || done[vid] != true) { done[vid] = true; var removal = Colors.Black; var vert_0 = mdt.GetFaceVertex(j, 0); var vert_1 = mdt.GetFaceVertex(j, 1); var vert_2 = mdt.GetFaceVertex(j, 2); var edge_a = mdt.GetVertex(vert_2).DirectionTo(mdt.GetVertex(vert_0)); var edge_b = mdt.GetVertex(vert_0).DirectionTo(mdt.GetVertex(vert_1)); var edge_c = mdt.GetVertex(vert_1).DirectionTo(mdt.GetVertex(vert_2)); if ((edge_a > edge_b) && (edge_a > edge_c)) { removal.g = 1; } else if ((edge_b > edge_c) && (edge_b > edge_a)) { removal.r = 1; } else { removal.b = 1; } if (coords.Count > 0) { var next = coords[0]; coords.RemoveAt(0); bary[vid] = next + removal; } else { var coords2 = new List <Color>() { Colors.Red, Colors.Green, Colors.Blue }; for (var m = 0; m < 3; m++) { if (m == i) { continue; } var vid2 = mdt.GetFaceVertex(j, m); if (bary.ContainsKey(vid2)) { coords2.Remove(bary[vid2]); } bary[vid] = coords2[0] + removal; //BUG? coords was checked to not have any.... maybe means coords2 coords2.RemoveAt(0); } } mdt.SetVertexColor(vid, bary[vid]); } } } }
/// <summary> /// my edited version of Cyberality's technique. /// faster performance, about as good coverage as "greedy" algo. /// </summary> /// <param name="sortedVerts"></param> /// <param name="_colorChoices"></param> /// <param name="mdt"></param> private static void _CYBERREALITY_EDIT(List <VertexInfo> sortedVerts, Color[] _colorChoices, MeshDataTool mdt) { var done = new Dictionary <int, bool>(); //vertidx/isDone var vertColorStorage = new Dictionary <int, Color>(); //vertidx/color var rand = new Random(0); var rand_color = new Color((float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble()); var faceVert0MetaInfo = new Dictionary <int, (Vector3 normal, Color color)>(); //normalIdx/normal for (var faceIdx = 0; faceIdx < mdt.GetFaceCount(); faceIdx++) { var faceVert0Idx = mdt.GetFaceVertex(faceIdx, 0); var faceVert0Norm = mdt.GetVertexNormal(faceVert0Idx); var colorChoices = new List <Color>() { Colors.Red, Colors.Green, Colors.Blue }; //////JASON CLEANUP: this code block isn't actually used in the algo... ////foreach (var n in faceVert0MetaInfo.Keys) ////{ //// var dot = faceVert0Norm.Dot(faceVert0MetaInfo[n].normal); //// if (dot == 1) //// { //// rand_color = faceVert0MetaInfo[n].color; //// } //// else //// { //// rand_color = new Color((float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble()); //// } ////} ////faceVert0MetaInfo[faceVert0Idx] = (normal: faceVert0Norm, color: rand_color); //loop through all verts for the face, and remove colors from our colorChoices if a vert is already using it for (var faceVertId = 0; faceVertId < 3; faceVertId++) { var vertIdx = mdt.GetFaceVertex(faceIdx, faceVertId); if (vertColorStorage.ContainsKey(vertIdx)) { colorChoices.Remove(vertColorStorage[vertIdx]); } } for (var faceVertId = 0; faceVertId < 3; faceVertId++) { var vertIdx = mdt.GetFaceVertex(faceIdx, faceVertId); if (!done.ContainsKey(vertIdx) || done[vertIdx] != true) { done[vertIdx] = true; var removal = Colors.Black; var vert_0 = mdt.GetFaceVertex(faceIdx, 0); var vert_1 = mdt.GetFaceVertex(faceIdx, 1); var vert_2 = mdt.GetFaceVertex(faceIdx, 2); var edge_a = mdt.GetVertex(vert_2).DirectionTo(mdt.GetVertex(vert_0)); var edge_b = mdt.GetVertex(vert_0).DirectionTo(mdt.GetVertex(vert_1)); var edge_c = mdt.GetVertex(vert_1).DirectionTo(mdt.GetVertex(vert_2)); if ((edge_a > edge_b) && (edge_a > edge_c)) { removal.g = 1; } else if ((edge_b > edge_c) && (edge_b > edge_a)) { removal.r = 1; } else { removal.b = 1; } if (colorChoices.Count > 0) { var next = colorChoices[0]; colorChoices.RemoveAt(0); vertColorStorage[vertIdx] = next + removal; } //JASON CLEANUP: this else will never trigger, as there are only 3 verticies else { GD.Print("in else!"); var coords2 = new List <Color>() { Colors.Red, Colors.Green, Colors.Blue }; for (var m = 0; m < 3; m++) { if (m == faceVertId) { continue; } var vid2 = mdt.GetFaceVertex(faceIdx, m); if (vertColorStorage.ContainsKey(vid2)) { coords2.Remove(vertColorStorage[vid2]); } vertColorStorage[vertIdx] = coords2[0] + removal; //BUG? coords was checked to not have any.... maybe means coords2 coords2.RemoveAt(0); } } mdt.SetVertexColor(vertIdx, vertColorStorage[vertIdx]); } } } }
/// <summary> /// /// </summary> /// <param name="sortedVerts"></param> /// <param name="colorChoices"></param> /// <param name="mdt"></param> private static void _WELSH_POWELL_ADJUSTED(List <VertexInfo> sortedVerts, Color[] colorChoices, MeshDataTool mdt) { for (var h = 0; h < colorChoices.Length; h++) { var color = colorChoices[h]; //enumerate in reverse so we inspect our verticies with highest degree first (most edges) //and also lets us remove from the list directly for (int i = sortedVerts.Count - 1; i >= 0; i--) { //if we remove too many, reset our index. this means we might invoke this loop on an element more than once. //but that's ok as it doesn't have negative consiquences. if (i >= sortedVerts.Count) { i = sortedVerts.Count - 1; } var vertInfo = sortedVerts[i]; if (vertInfo.TrySetAvailableColor(color)) { sortedVerts.RemoveAt(i); //preemptively try to set adjacent and adjadj with related colors foreach (var adj0Vert in vertInfo.GetAdjacentVertInfo()) { //JASON OPTIMIZATION: reduces non-colored by aprox 8% on sibnek 100k vert mesh. foreach (var adj1Vert in adj0Vert.GetAdjacentVertInfo()) { if (adj1Vert.adjacentVerticies.Length > vertInfo.adjacentVerticies.Length * 0.75) { adj1Vert.TrySetAvailableColor(color); } } } } } } //any remaining verts are uncolored! bad. GD.Print($"Done building mesh. Verticies uncolored count={sortedVerts.Count} / {mdt.GetVertexCount()}"); //loop through all faces, finding the vertex for the longest edge, //and encode that into green channel = 0.1; //may be used by the shader to remove interrior edges var faceCount = mdt.GetFaceCount(); for (var faceIdx = 0; faceIdx < faceCount; faceIdx++) { var vertIdx0 = mdt.GetFaceVertex(faceIdx, 0); var vertIdx1 = mdt.GetFaceVertex(faceIdx, 1); var vertIdx2 = mdt.GetFaceVertex(faceIdx, 2); var vert0 = mdt.GetVertex(vertIdx0); var vert1 = mdt.GetVertex(vertIdx1); var vert2 = mdt.GetVertex(vertIdx2); var edgeLen1 = vert0.DistanceTo(vert1); var edgeLen2 = vert0.DistanceTo(vert2); var edgeLen3 = vert1.DistanceTo(vert2); int longestEdgeVertIdx = -1; if (edgeLen1 > edgeLen2 && edgeLen1 > edgeLen3) { longestEdgeVertIdx = vertIdx2; } if (edgeLen2 > edgeLen1 && edgeLen2 > edgeLen3) { longestEdgeVertIdx = vertIdx1; } if (edgeLen3 > edgeLen1 && edgeLen3 > edgeLen2) { longestEdgeVertIdx = vertIdx0; } if (longestEdgeVertIdx != -1) { var curCol = mdt.GetVertexColor(longestEdgeVertIdx); //encode that this vertext has longest edge (used in shader code) curCol.g += 0.1f; mdt.SetVertexColor(longestEdgeVertIdx, curCol); } } ////for any remaining verticies color alpha //var alphaBlack = new Color(0, 0, 0, 0); //for (int i = sortedVerts.Count - 1; i >= 0; i--) //{ // var vertInfo = sortedVerts[i]; // mdt.SetVertexColor(vertInfo.vertIdx, alphaBlack); // //vertInfo.TrySetAvailableColor(Colors.White, true); //} }
public override void _Ready() { var mesh = new PlaneMesh(); mesh.Size = new Vector2(size, size); mesh.SubdivideDepth = 3; mesh.SubdivideWidth = (int)position.x == 0 ? (int)size : 3; var surface_tool = new SurfaceTool(); surface_tool.CreateFrom(mesh, 0); var mesh_tool = new MeshDataTool(); mesh_tool.CreateFromSurface(surface_tool.Commit(), 0); var biome = GetBiome(/*hydro_noise.GetNoise2dv(position / size), */ heat_noise.GetNoise2d(position.x / size / 10.0f, position.y)); for (int i = 0; i < mesh_tool.GetVertexCount(); ++i) { var vertex = mesh_tool.GetVertex(i); var vertex_global_pos = position + new Vector2(vertex.x, vertex.z); var height_noise_val = height_noise.GetNoise2dv(vertex_global_pos); vertex.y = height_noise_val * 20; var color_factor = (height_noise_val + 1) / 2.0f; var hydro_val = (int)Math.Round(hydro_noise.GetNoise2dv(vertex_global_pos)); if ((int)vertex.x == 0 && (int)position.x == 0) { mesh_tool.SetVertexColor(i, new Color(color_factor, color_factor / 2, 0.0f)); } else if (hydro_val == -1) { mesh_tool.SetVertexColor(i, biome.dry_color * color_factor); } else if (hydro_val == 1) { mesh_tool.SetVertexColor(i, biome.humid_color * color_factor); } else { mesh_tool.SetVertexColor(i, biome.ground_color * color_factor); } mesh_tool.SetVertex(i, vertex); } /*if (base_tree_mesh == null && ResourceLoader.Exists("res://assets/tree.obj")) * { * base_tree_mesh = ResourceLoader.Load<Mesh>("res://assets/tree.obj"); * } * if (base_tree_material == null && ResourceLoader.Exists("res://assets/tree.tres")) * { * Console.WriteLine("OK"); * base_tree_material = ResourceLoader.Load<SpatialMaterial>("res://assets/tree.tres"); * } * * MultiMesh trees = new MultiMesh(); * trees.Mesh = base_tree_mesh; * trees.TransformFormat = MultiMesh.TransformFormatEnum.Transform3d; * * if ((int)position.x == 0) * { * var points1 = Utility.UniformPoissonDiskSampler.SampleRectangle(-new Vector2(size, size) / 2, new Vector2(-5, size / 2.0f), biome.tree_spacing); * var points2 = Utility.UniformPoissonDiskSampler.SampleRectangle(new Vector2(5, 0), new Vector2(size, size) / 2, biome.tree_spacing); * trees.InstanceCount = points1.Count + points2.Count; * * int i = 0; * foreach (var p in points1) * { * trees.SetInstanceTransform(i, Transform.Identity.Scaled(new Vector3(1, biome.tree_size, 1)).Translated(new Vector3(p.x, height_noise.GetNoise2dv(position + p) * 20, p.y))); ++i; * } * * foreach (var p in points2) * { * trees.SetInstanceTransform(i, Transform.Identity.Scaled(new Vector3(1, biome.tree_size, 1)).Translated(new Vector3(p.x, height_noise.GetNoise2dv(position + p) * 20, p.y))); ++i; * } * } * else * { * var points = Utility.UniformPoissonDiskSampler.SampleRectangle(-new Vector2(size, size) / 2, new Vector2(size, size) / 2, biome.tree_spacing); * trees.InstanceCount = points.Count; * int i = 0; * foreach (var p in points) * { * trees.SetInstanceTransform(i, Transform.Identity.Scaled(new Vector3(1, biome.tree_size, 1)).Translated(new Vector3(p.x, height_noise.GetNoise2dv(position + p) * 20, p.y))); ++i; * } * } * * MultiMeshInstance child = new MultiMeshInstance(); * child.Multimesh = trees; * child.MaterialOverride = base_tree_material; * AddChild(child);*/ var array = new ArrayMesh(); mesh_tool.CommitToSurface(array); Mesh = array; if (base_shader == null && ResourceLoader.Exists("res://assets/chunk_shader.tres")) { base_shader = ResourceLoader.Load <ShaderMaterial>("res://assets/chunk_shader.tres"); } var shader = base_shader; MaterialOverride = shader; }