From ddbd69ea2570de952c002e0e6fb980b824a42e5c Mon Sep 17 00:00:00 2001 From: Rodolphe Suescun Date: Sun, 19 Aug 2018 15:23:11 +0200 Subject: [PATCH] Added new patterns for bricks Added a Running Bond variant, HerringBone, Basket Weave and Spanish bond patterns to the bricks generator. Shader was refactored to make it easy to add new patterns made of axis-aligned bricks. --- addons/procedural_material/common.shader | 104 ++++++++++++-- addons/procedural_material/nodes/bricks.gd | 27 +++- addons/procedural_material/nodes/bricks.tscn | 137 +++++++++++++++++-- 3 files changed, 234 insertions(+), 34 deletions(-) diff --git a/addons/procedural_material/common.shader b/addons/procedural_material/common.shader index 68b6495..ae2b392 100644 --- a/addons/procedural_material/common.shader +++ b/addons/procedural_material/common.shader @@ -145,21 +145,99 @@ vec2 transform_norepeat(vec2 uv, vec2 translate, float rotate, vec2 scale) { return clamp(transform(uv, translate, rotate, scale), vec2(0.0), vec2(1.0)); } -vec3 bricks(vec2 uv, vec2 count, float offset, float mortar, float bevel) { +vec3 brick(vec2 uv, vec2 bmin, vec2 bmax, float mortar, float bevel) { + float color = 0.5; + vec2 c1 = (uv-bmin-vec2(mortar))/bevel; + vec2 c2 = (bmax-uv-vec2(mortar))/bevel; + vec2 c = min(c1, c2); + color = clamp(min(c.x, c.y), 0.0, 1.0); + return vec3(color, bmin); +} + +vec3 bricks_rb(vec2 uv, vec2 count, float repeat, float offset, float mortar, float bevel) { + count *= repeat; mortar /= max(count.x, count.y); bevel /= max(count.x, count.y); - float x = uv.x*count.x+offset*step(0.5, fract(uv.y*count.y*0.5)); - float fract_x = fract(x); - float slope_x = 1.0/(bevel*count.x); - float off = 0.5*mortar/bevel; - float f1 = fract_x*slope_x-off; - float f2 = (1.0-fract_x)*slope_x-off; - float y = uv.y*count.y; - float fract_y = fract(uv.y*count.y); - float slope_y = 1.0/(bevel*count.y); - float f3 = fract_y*slope_y-off; - float f4 = (1.0-fract_y)*slope_y-off; - return vec3(max(0.0, min(1.0, min(min(f1, f2), min(f3, f4)))), floor(mod(x, count.x)), floor(mod(y, count.y))); + float x_offset = offset*step(0.5, fract(uv.y*count.y*0.5)); + vec2 bmin = floor(vec2(uv.x*count.x-x_offset, uv.y*count.y)); + bmin.x += x_offset; + bmin /= count; + return brick(uv, bmin, bmin+vec2(1.0)/count, mortar, bevel); +} + +vec3 bricks_rb2(vec2 uv, vec2 count, float repeat, float offset, float mortar, float bevel) { + count *= repeat; + mortar /= max(2.0*count.x, count.y); + bevel /= max(2.0*count.x, count.y); + float x_offset = offset*step(0.5, fract(uv.y*count.y*0.5)); + count.x = count.x*(1.0+step(0.5, fract(uv.y*count.y*0.5))); + vec2 bmin = floor(vec2(uv.x*count.x-x_offset, uv.y*count.y)); + bmin.x += x_offset; + bmin /= count; + return brick(uv, bmin, bmin+vec2(1.0)/count, mortar, bevel); +} + +vec3 bricks_hb(vec2 uv, vec2 count, float repeat, float offset, float mortar, float bevel) { + float pc = count.x+count.y; + float c = pc*repeat; + mortar /= c; + bevel /= c; + vec2 corner = floor(uv*c); + float cdiff = mod(corner.x-corner.y, pc); + if (cdiff < count.x) { + return brick(uv, (corner-vec2(cdiff, 0.0))/c, (corner-vec2(cdiff, 0.0)+vec2(count.x, 1.0))/c, mortar, bevel); + } else { + return brick(uv, (corner-vec2(0.0, pc-cdiff-1.0))/c, (corner-vec2(0.0, pc-cdiff-1.0)+vec2(1.0, count.y))/c, mortar, bevel); + } +} + +vec3 bricks_bw(vec2 uv, vec2 count, float repeat, float offset, float mortar, float bevel) { + vec2 c = 2.0*count*repeat; + float mc = max(c.x, c.y); + mortar /= mc; + bevel /= mc; + vec2 corner1 = floor(uv*c); + vec2 corner2 = count*floor(repeat*2.0*uv); + float cdiff = mod(dot(floor(repeat*2.0*uv), vec2(1.0)), 2.0); + vec2 corner; + vec2 size; + if (cdiff == 0.0) { + corner = vec2(corner1.x, corner2.y); + size = vec2(1.0, count.y); + } else { + corner = vec2(corner2.x, corner1.y); + size = vec2(count.x, 1.0); + } + return brick(uv, corner/c, (corner+size)/c, mortar, bevel); +} + +vec3 bricks_sb(vec2 uv, vec2 count, float repeat, float offset, float mortar, float bevel) { + vec2 c = (count+vec2(1.0))*repeat; + float mc = max(c.x, c.y); + mortar /= mc; + bevel /= mc; + vec2 corner1 = floor(uv*c); + vec2 corner2 = (count+vec2(1.0))*floor(repeat*uv); + vec2 rcorner = corner1 - corner2; + vec2 corner; + vec2 size; + if (rcorner.x == 0.0 && rcorner.y < count.y) { + corner = corner2; + size = vec2(1.0, count.y); + } else if (rcorner.y == 0.0) { + corner = corner2+vec2(1.0, 0.0); + size = vec2(count.x, 1.0); + } else if (rcorner.x == count.x) { + corner = corner2+vec2(count.x, 1.0); + size = vec2(1.0, count.y); + } else if (rcorner.y == count.y) { + corner = corner2+vec2(0.0, count.y); + size = vec2(count.x, 1.0); + } else { + corner = corner2+vec2(1.0); + size = vec2(count.x-1.0, count.y-1.0); + } + return brick(uv, corner/c, (corner+size)/c, mortar, bevel); } float colored_bricks(vec2 uv, vec2 count, float offset) { diff --git a/addons/procedural_material/nodes/bricks.gd b/addons/procedural_material/nodes/bricks.gd index 3a39c01..5141596 100644 --- a/addons/procedural_material/nodes/bricks.gd +++ b/addons/procedural_material/nodes/bricks.gd @@ -1,27 +1,42 @@ tool extends "res://addons/procedural_material/node_base.gd" +var pattern = 0 +var repeat var rows var columns var row_offset var mortar var bevel +const BRICK_PATTERNS = [ + { name="Running bond", suffix="rb", has_offset=true, has_repeat=false }, + { name="Running bond (2)", suffix="rb2", has_offset=true, has_repeat=false }, + { name="HerringBone", suffix="hb", has_offset=false, has_repeat=true }, + { name="Basket weave", suffix="bw", has_offset=false, has_repeat=true }, + { name="Spanish bond", suffix="sb", has_offset=false, has_repeat=true } +] + func _ready(): - set_slot(0, false, 0, Color(0.5, 0.5, 1), true, 0, Color(0.5, 0.5, 1)) - initialize_properties([ $HBoxContainer1/rows, $HBoxContainer2/columns, $HBoxContainer3/row_offset, $HBoxContainer4/mortar, $HBoxContainer5/bevel ]) + $pattern.clear() + for p in BRICK_PATTERNS: + $pattern.add_item(p.name) + initialize_properties([ $pattern, $HBoxContainer1/rows, $HBoxContainer2/columns, $HBoxContainer6/repeat, $HBoxContainer3/row_offset, $HBoxContainer4/mortar, $HBoxContainer5/bevel ]) func _get_shader_code(uv, slot = 0): var rv = { defs="", code="" } if generated_variants.empty(): - rv.defs = "vec3 "+name+"_xyz(vec2 uv) { return bricks(uv, vec2("+str(columns)+", "+str(rows)+"), "+str(row_offset)+", "+str(mortar)+", "+str(max(0.001, bevel))+"); }\n" + rv.defs = "vec3 %s_xyz(vec2 uv) { return bricks_%s(uv, vec2(%d, %d), %.9f, %.9f, %.9f, %.9f); }\n" % [ name, BRICK_PATTERNS[pattern].suffix, columns, rows, repeat, row_offset, mortar, max(0.001, bevel) ] var variant_index = generated_variants.find(uv) if variant_index == -1: variant_index = generated_variants.size() generated_variants.append(uv) - rv.code = "vec3 "+name+"_"+str(variant_index)+"_xyz = "+name+"_xyz("+uv+");\n" + rv.code = "vec3 %s_%d_xyz = %s_xyz(%s);\n" % [ name, variant_index, name, uv ] if slot == 0: - rv.f = name+"_"+str(variant_index)+"_xyz.x" + rv.f = "%s_%d_xyz.x" % [ name, variant_index ] else: - rv.rgb = "rand3("+name+"_"+str(variant_index)+"_xyz.yz)" + rv.rgb = "rand3(%s_%d_xyz.yz+vec2(%.9f))" % [ name, variant_index, get_seed() ] return rv + +func _on_offset_changed(): + update_shaders() diff --git a/addons/procedural_material/nodes/bricks.tscn b/addons/procedural_material/nodes/bricks.tscn index dba834f..66f9fb7 100644 --- a/addons/procedural_material/nodes/bricks.tscn +++ b/addons/procedural_material/nodes/bricks.tscn @@ -5,7 +5,7 @@ [sub_resource type="Theme" id=1] -[node name="Bricks" type="GraphNode"] +[node name="Bricks" type="GraphNode" index="0"] anchor_left = 0.0 anchor_top = 0.0 @@ -58,10 +58,22 @@ slot/4/left_color = Color( 1, 1, 1, 1 ) slot/4/right_enabled = false slot/4/right_type = 0 slot/4/right_color = Color( 1, 1, 1, 1 ) +slot/5/left_enabled = false +slot/5/left_type = 0 +slot/5/left_color = Color( 1, 1, 1, 1 ) +slot/5/right_enabled = false +slot/5/right_type = 0 +slot/5/right_color = Color( 1, 1, 1, 1 ) +slot/6/left_enabled = false +slot/6/left_type = 0 +slot/6/left_color = Color( 1, 1, 1, 1 ) +slot/6/right_enabled = false +slot/6/right_type = 0 +slot/6/right_color = Color( 1, 1, 1, 1 ) script = ExtResource( 1 ) _sections_unfolded = [ "Mouse", "Theme", "slot", "slot/0", "slot/1" ] -[node name="HBoxContainer1" type="HBoxContainer" parent="." index="0"] +[node name="pattern" type="OptionButton" parent="." index="0"] anchor_left = 0.0 anchor_top = 0.0 @@ -70,7 +82,100 @@ anchor_bottom = 0.0 margin_left = 16.0 margin_top = 24.0 margin_right = 154.0 -margin_bottom = 48.0 +margin_bottom = 44.0 +rect_pivot_offset = Vector2( 0, 0 ) +rect_clip_content = false +focus_mode = 2 +mouse_filter = 0 +mouse_default_cursor_shape = 0 +size_flags_horizontal = 3 +size_flags_vertical = 1 +toggle_mode = false +action_mode = 0 +enabled_focus_mode = 2 +shortcut = null +group = null +flat = false +align = 0 +items = [ ] +selected = -1 +_sections_unfolded = [ "Size Flags" ] + +[node name="HBoxContainer6" type="HBoxContainer" parent="." index="1"] + +anchor_left = 0.0 +anchor_top = 0.0 +anchor_right = 0.0 +anchor_bottom = 0.0 +margin_left = 16.0 +margin_top = 44.0 +margin_right = 154.0 +margin_bottom = 68.0 +rect_pivot_offset = Vector2( 0, 0 ) +rect_clip_content = false +mouse_filter = 1 +mouse_default_cursor_shape = 0 +size_flags_horizontal = 1 +size_flags_vertical = 1 +alignment = 0 + +[node name="Label1" type="Label" parent="HBoxContainer6" index="0"] + +anchor_left = 0.0 +anchor_top = 0.0 +anchor_right = 0.0 +anchor_bottom = 0.0 +margin_top = 5.0 +margin_right = 60.0 +margin_bottom = 19.0 +rect_pivot_offset = Vector2( 0, 0 ) +rect_clip_content = false +mouse_filter = 2 +mouse_default_cursor_shape = 0 +size_flags_horizontal = 3 +size_flags_vertical = 4 +text = "Repeat:" +percent_visible = 1.0 +lines_skipped = 0 +max_lines_visible = -1 + +[node name="repeat" type="SpinBox" parent="HBoxContainer6" index="1"] + +anchor_left = 0.0 +anchor_top = 0.0 +anchor_right = 0.0 +anchor_bottom = 0.0 +margin_left = 64.0 +margin_right = 138.0 +margin_bottom = 24.0 +rect_pivot_offset = Vector2( 0, 0 ) +rect_clip_content = false +mouse_filter = 0 +mouse_default_cursor_shape = 0 +size_flags_horizontal = 9 +size_flags_vertical = 1 +min_value = 1.0 +max_value = 8.0 +step = 1.0 +page = 0.0 +value = 1.0 +exp_edit = false +rounded = false +editable = true +prefix = "" +suffix = "" +_sections_unfolded = [ "Caret", "Placeholder" ] + +[node name="HBoxContainer1" type="HBoxContainer" parent="." index="2"] + +anchor_left = 0.0 +anchor_top = 0.0 +anchor_right = 0.0 +anchor_bottom = 0.0 +margin_left = 16.0 +margin_top = 69.0 +margin_right = 154.0 +margin_bottom = 93.0 rect_pivot_offset = Vector2( 0, 0 ) rect_clip_content = false mouse_filter = 1 @@ -126,16 +231,16 @@ prefix = "" suffix = "" _sections_unfolded = [ "Caret", "Placeholder" ] -[node name="HBoxContainer2" type="HBoxContainer" parent="." index="1"] +[node name="HBoxContainer2" type="HBoxContainer" parent="." index="3"] anchor_left = 0.0 anchor_top = 0.0 anchor_right = 0.0 anchor_bottom = 0.0 margin_left = 16.0 -margin_top = 48.0 +margin_top = 94.0 margin_right = 154.0 -margin_bottom = 72.0 +margin_bottom = 118.0 rect_pivot_offset = Vector2( 0, 0 ) rect_clip_content = false mouse_filter = 1 @@ -191,16 +296,16 @@ prefix = "" suffix = "" _sections_unfolded = [ "Caret", "Placeholder" ] -[node name="HBoxContainer3" type="HBoxContainer" parent="." index="2"] +[node name="HBoxContainer3" type="HBoxContainer" parent="." index="4"] anchor_left = 0.0 anchor_top = 0.0 anchor_right = 0.0 anchor_bottom = 0.0 margin_left = 16.0 -margin_top = 73.0 +margin_top = 119.0 margin_right = 154.0 -margin_bottom = 97.0 +margin_bottom = 143.0 rect_pivot_offset = Vector2( 0, 0 ) rect_clip_content = false mouse_filter = 1 @@ -256,16 +361,16 @@ prefix = "" suffix = "" _sections_unfolded = [ "Caret", "Placeholder" ] -[node name="HBoxContainer4" type="HBoxContainer" parent="." index="3"] +[node name="HBoxContainer4" type="HBoxContainer" parent="." index="5"] anchor_left = 0.0 anchor_top = 0.0 anchor_right = 0.0 anchor_bottom = 0.0 margin_left = 16.0 -margin_top = 98.0 +margin_top = 144.0 margin_right = 154.0 -margin_bottom = 122.0 +margin_bottom = 168.0 rect_pivot_offset = Vector2( 0, 0 ) rect_clip_content = false mouse_filter = 1 @@ -321,16 +426,16 @@ prefix = "" suffix = "" _sections_unfolded = [ "Caret", "Placeholder" ] -[node name="HBoxContainer5" type="HBoxContainer" parent="." index="4"] +[node name="HBoxContainer5" type="HBoxContainer" parent="." index="6"] anchor_left = 0.0 anchor_top = 0.0 anchor_right = 0.0 anchor_bottom = 0.0 margin_left = 16.0 -margin_top = 123.0 +margin_top = 169.0 margin_right = 154.0 -margin_bottom = 147.0 +margin_bottom = 193.0 rect_pivot_offset = Vector2( 0, 0 ) rect_clip_content = false mouse_filter = 1 @@ -386,4 +491,6 @@ prefix = "" suffix = "" _sections_unfolded = [ "Caret", "Placeholder" ] +[connection signal="offset_changed" from="." to="." method="_on_offset_changed"] +