mirror of
https://github.com/Relintai/material-maker.git
synced 2024-11-13 06:27:18 +01:00
Improved integration in Godot
Exporting a material will now create a SpatialMaterial. The Material graph node now generates different textures when used as a Godot addon (metallic, roughness and ambient occlusion are merged into a single texture). Rendering code (that was in graph_edit and node_base scenes) is now in a single "renderer scene" attached to the main window.
This commit is contained in:
parent
1c56fc60d8
commit
2621ff4b46
@ -1 +1 @@
|
||||
{"connections":[{"from":"perlin_0","from_port":0,"to":"warp_0","to_port":0},{"from":"perlin_1","from_port":0,"to":"warp_0","to_port":1},{"from":"perlin_2","from_port":0,"to":"blend_0","to_port":0},{"from":"blend_0","from_port":0,"to":"normal_map_0","to_port":0},{"from":"normal_map_0","from_port":0,"to":"Material","to_port":4},{"from":"blend_0","from_port":0,"to":"colorize_0","to_port":0},{"from":"colorize_0","from_port":0,"to":"Material","to_port":2},{"from":"blend_0","from_port":0,"to":"Material","to_port":1},{"from":"voronoi_0","from_port":0,"to":"colorize_1","to_port":0},{"from":"warp_0","from_port":0,"to":"warp_1","to_port":0},{"from":"colorize_1","from_port":0,"to":"warp_1","to_port":1},{"from":"warp_1","from_port":0,"to":"blend_0","to_port":1},{"from":"combine_0","from_port":0,"to":"export_0","to_port":0},{"from":"colorize_0","from_port":0,"to":"combine_0","to_port":1},{"from":"blend_0","from_port":0,"to":"colorize_2","to_port":0},{"from":"blend_0","from_port":0,"to":"combine_0","to_port":0},{"from":"colorize_2","from_port":0,"to":"Material","to_port":0}],"nodes":[{"amount":0.1,"name":"normal_map_0","node_position":{"x":319,"y":265.5},"type":"normal_map"},{"gradient":[{"b":0.53125,"g":0.53125,"pos":0,"r":0.53125},{"b":0.708333,"g":0.708333,"pos":1,"r":0.708333}],"name":"colorize_0","node_position":{"x":313,"y":176.5},"type":"colorize"},{"iterations":6,"name":"perlin_2","node_position":{"x":-312,"y":2.5},"persistence":1,"scale_x":32,"scale_y":4,"type":"perlin"},{"iterations":3,"name":"perlin_1","node_position":{"x":-400,"y":359.5},"persistence":0.5,"scale_x":4,"scale_y":4,"type":"perlin"},{"iterations":3,"name":"perlin_0","node_position":{"x":-393,"y":203.5},"persistence":0.5,"scale_x":32,"scale_y":4,"type":"perlin"},{"amount":0.3,"name":"warp_0","node_position":{"x":-180,"y":317.5},"type":"warp"},{"gradient":[{"b":0.432292,"g":0.432292,"pos":0,"r":0.432292},{"b":0,"g":0,"pos":0.345455,"r":0}],"name":"colorize_1","node_position":{"x":-194,"y":466.5},"type":"colorize"},{"amount":0.1,"name":"warp_1","node_position":{"x":-31,"y":336.5},"type":"warp"},{"intensity":1,"name":"voronoi_0","node_position":{"x":-381,"y":505.5},"scale_x":5,"scale_y":4,"type":"voronoi"},{"amount":1,"blend_type":2,"name":"blend_0","node_position":{"x":83,"y":245.5},"type":"blend"},{"name":"combine_0","node_position":{"x":515.35144,"y":-15.818176},"type":"combine"},{"name":"export_0","node_position":{"x":656.35144,"y":-20.818176},"suffix":"mr","type":"export"},{"name":"Material","node_position":{"x":544,"y":79},"type":"material"},{"gradient":[{"b":0.071126,"g":0.34877,"pos":0,"r":0.59375},{"b":0.013021,"g":0.144043,"pos":1,"r":0.3125}],"name":"colorize_2","node_position":{"x":305.35144,"y":76.181824},"type":"colorize"}]}
|
||||
{"connections":[{"from":"perlin_0","from_port":0,"to":"warp_0","to_port":0},{"from":"perlin_1","from_port":0,"to":"warp_0","to_port":1},{"from":"perlin_2","from_port":0,"to":"blend_0","to_port":0},{"from":"blend_0","from_port":0,"to":"normal_map_0","to_port":0},{"from":"normal_map_0","from_port":0,"to":"Material","to_port":4},{"from":"blend_0","from_port":0,"to":"colorize_0","to_port":0},{"from":"colorize_0","from_port":0,"to":"Material","to_port":2},{"from":"blend_0","from_port":0,"to":"Material","to_port":1},{"from":"voronoi_0","from_port":0,"to":"colorize_1","to_port":0},{"from":"warp_0","from_port":0,"to":"warp_1","to_port":0},{"from":"colorize_1","from_port":0,"to":"warp_1","to_port":1},{"from":"warp_1","from_port":0,"to":"blend_0","to_port":1},{"from":"combine_0","from_port":0,"to":"export_0","to_port":0},{"from":"colorize_0","from_port":0,"to":"combine_0","to_port":1},{"from":"blend_0","from_port":0,"to":"colorize_2","to_port":0},{"from":"blend_0","from_port":0,"to":"combine_0","to_port":0},{"from":"colorize_2","from_port":0,"to":"Material","to_port":0}],"nodes":[{"iterations":6,"name":"perlin_2","node_position":{"x":-312,"y":2.5},"persistence":1,"scale_x":32,"scale_y":4,"type":"perlin"},{"iterations":3,"name":"perlin_1","node_position":{"x":-400,"y":359.5},"persistence":0.5,"scale_x":4,"scale_y":4,"type":"perlin"},{"iterations":3,"name":"perlin_0","node_position":{"x":-393,"y":203.5},"persistence":0.5,"scale_x":32,"scale_y":4,"type":"perlin"},{"amount":0.3,"name":"warp_0","node_position":{"x":-180,"y":317.5},"type":"warp"},{"gradient":[{"b":0.432292,"g":0.432292,"pos":0,"r":0.432292},{"b":0,"g":0,"pos":0.345455,"r":0}],"name":"colorize_1","node_position":{"x":-194,"y":466.5},"type":"colorize"},{"amount":0.1,"name":"warp_1","node_position":{"x":-31,"y":336.5},"type":"warp"},{"intensity":1,"name":"voronoi_0","node_position":{"x":-381,"y":505.5},"scale_x":5,"scale_y":4,"type":"voronoi"},{"amount":1,"blend_type":2,"name":"blend_0","node_position":{"x":83,"y":245.5},"type":"blend"},{"name":"combine_0","node_position":{"x":515.35144,"y":-15.818176},"type":"combine"},{"name":"export_0","node_position":{"x":656.35144,"y":-20.818176},"resolution":1,"suffix":"mr","type":"export"},{"albedo_color":{"a":1,"b":1,"g":1,"r":1,"type":"Color"},"ao_light_affect":1,"depth_scale":1,"emission_energy":1,"metallic":1,"name":"Material","node_position":{"x":544,"y":79},"normal_scale":1,"resolution":1,"roughness":1,"type":"material"},{"gradient":[{"b":0.071126,"g":0.34877,"pos":0,"r":0.59375},{"b":0.013021,"g":0.144043,"pos":1,"r":0.3125}],"name":"colorize_2","node_position":{"x":305.35144,"y":76.181824},"type":"colorize"},{"amount":0.1,"name":"normal_map_0","node_position":{"x":319,"y":265.5},"size":5,"type":"normal_map"},{"gradient":[{"b":0.53125,"g":0.53125,"pos":0,"r":0.53125},{"b":0.708333,"g":0.708333,"pos":1,"r":0.708333}],"name":"colorize_0","node_position":{"x":313,"y":176.5},"type":"colorize"}]}
|
@ -1,6 +1,9 @@
|
||||
tool
|
||||
extends GraphEdit
|
||||
|
||||
var editor_interface = null
|
||||
var renderer = null
|
||||
|
||||
var save_path = null
|
||||
var need_save = false
|
||||
|
||||
@ -8,7 +11,6 @@ signal save_path_changed
|
||||
signal graph_changed
|
||||
|
||||
func _ready():
|
||||
$SaveViewport/ColorRect.material = $SaveViewport/ColorRect.material.duplicate(true)
|
||||
OS.low_processor_usage_mode = true
|
||||
center_view()
|
||||
|
||||
@ -284,59 +286,3 @@ func drop_data(position, data):
|
||||
Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
|
||||
create_nodes(data, offset_from_global_position(get_global_transform().xform(position)))
|
||||
return true
|
||||
|
||||
# Save shader to image, create image texture
|
||||
|
||||
func setup_material(shader_material, textures, shader_code):
|
||||
for k in textures.keys():
|
||||
shader_material.set_shader_param(k+"_tex", textures[k])
|
||||
shader_material.shader.code = shader_code
|
||||
|
||||
var render_queue = []
|
||||
|
||||
func render_shader_to_viewport(shader, textures, size, method, args):
|
||||
render_queue.append( { shader=shader, textures=textures, size=size, method=method, args=args } )
|
||||
if render_queue.size() == 1:
|
||||
while !render_queue.empty():
|
||||
var job = render_queue.front()
|
||||
$SaveViewport.size = Vector2(job.size, job.size)
|
||||
$SaveViewport/ColorRect.rect_position = Vector2(0, 0)
|
||||
$SaveViewport/ColorRect.rect_size = Vector2(job.size, job.size)
|
||||
var shader_material = $SaveViewport/ColorRect.material
|
||||
shader_material.shader.code = job.shader
|
||||
if job.textures != null:
|
||||
for k in job.textures.keys():
|
||||
shader_material.set_shader_param(k+"_tex", job.textures[k])
|
||||
$SaveViewport.render_target_update_mode = Viewport.UPDATE_ONCE
|
||||
$SaveViewport.update_worlds()
|
||||
yield(get_tree(), "idle_frame")
|
||||
yield(get_tree(), "idle_frame")
|
||||
callv(job.method, job.args)
|
||||
render_queue.pop_front()
|
||||
|
||||
func render_to_viewport(node, size, method, args):
|
||||
render_shader_to_viewport(node.generate_shader(), node.get_textures(), size, method, args)
|
||||
|
||||
func export_texture(node, filename, size = 256):
|
||||
if node == null:
|
||||
return null
|
||||
render_to_viewport(node, size, "do_export_texture", [ filename ])
|
||||
|
||||
func do_export_texture(filename):
|
||||
var viewport_texture = $SaveViewport.get_texture()
|
||||
var viewport_image = viewport_texture.get_data()
|
||||
viewport_image.save_png(filename)
|
||||
|
||||
func precalculate_node(node, size, target_texture, object, method, args):
|
||||
if node == null:
|
||||
return null
|
||||
render_to_viewport(node, size, "do_precalculate_texture", [ object, method, args ])
|
||||
|
||||
func precalculate_shader(shader, textures, size, target_texture, object, method, args):
|
||||
render_shader_to_viewport(shader, textures, size, "do_precalculate_texture", [ target_texture, object, method, args ])
|
||||
|
||||
func do_precalculate_texture(target_texture, object, method, args):
|
||||
var viewport_texture = $SaveViewport.get_texture()
|
||||
target_texture.create_from_image(viewport_texture.get_data())
|
||||
object.callv(method, args)
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
[gd_scene load_steps=6 format=2]
|
||||
[gd_scene load_steps=4 format=2]
|
||||
|
||||
[ext_resource path="res://addons/material_maker/graph_edit.gd" type="Script" id=1]
|
||||
[ext_resource path="res://addons/material_maker/nodes/material.tscn" type="PackedScene" id=2]
|
||||
@ -6,20 +6,6 @@
|
||||
[sub_resource type="Theme" id=1]
|
||||
|
||||
|
||||
[sub_resource type="Shader" id=2]
|
||||
|
||||
code = "shader_type canvas_item;
|
||||
|
||||
void fragment() {
|
||||
COLOR = vec4(1.0);
|
||||
}
|
||||
"
|
||||
|
||||
[sub_resource type="ShaderMaterial" id=3]
|
||||
|
||||
render_priority = 0
|
||||
shader = SubResource( 2 )
|
||||
|
||||
[node name="GraphEdit" type="GraphEdit" index="0"]
|
||||
|
||||
self_modulate = Color( 1, 1, 1, 0 )
|
||||
@ -35,7 +21,7 @@ mouse_default_cursor_shape = 0
|
||||
size_flags_horizontal = 1
|
||||
size_flags_vertical = 1
|
||||
right_disconnects = true
|
||||
scroll_offset = Vector2( 0, 0 )
|
||||
scroll_offset = Vector2( -525, -250 )
|
||||
snap_distance = 20
|
||||
use_snap = false
|
||||
zoom = 1.0
|
||||
@ -44,8 +30,12 @@ _sections_unfolded = [ "Material", "Mouse", "Visibility" ]
|
||||
|
||||
[node name="Material" parent="." index="0" instance=ExtResource( 2 )]
|
||||
|
||||
margin_left = 525.0
|
||||
margin_top = 250.0
|
||||
margin_right = 755.0
|
||||
margin_bottom = 470.0
|
||||
theme = SubResource( 1 )
|
||||
_sections_unfolded = [ "Anchor", "Margin", "Mouse", "Theme", "slot", "slot/2", "slot/3", "slot/4", "slot/5" ]
|
||||
_sections_unfolded = [ "Anchor", "Margin", "Mouse", "Theme", "slot", "slot/0", "slot/1", "slot/2", "slot/3", "slot/4", "slot/5" ]
|
||||
|
||||
[node name="Timer" type="Timer" parent="." index="1"]
|
||||
|
||||
@ -54,58 +44,6 @@ wait_time = 0.2
|
||||
one_shot = true
|
||||
autostart = false
|
||||
|
||||
[node name="SaveViewport" type="Viewport" parent="." index="2"]
|
||||
|
||||
arvr = false
|
||||
size = Vector2( 0, 0 )
|
||||
own_world = true
|
||||
world = null
|
||||
transparent_bg = false
|
||||
msaa = 2
|
||||
hdr = false
|
||||
disable_3d = false
|
||||
usage = 2
|
||||
debug_draw = 0
|
||||
render_target_v_flip = true
|
||||
render_target_clear_mode = 0
|
||||
render_target_update_mode = 1
|
||||
audio_listener_enable_2d = false
|
||||
audio_listener_enable_3d = false
|
||||
physics_object_picking = false
|
||||
gui_disable_input = true
|
||||
gui_snap_controls_to_pixels = true
|
||||
shadow_atlas_size = 0
|
||||
shadow_atlas_quad_0 = 2
|
||||
shadow_atlas_quad_1 = 2
|
||||
shadow_atlas_quad_2 = 3
|
||||
shadow_atlas_quad_3 = 4
|
||||
_sections_unfolded = [ "GUI", "Render Target", "Rendering" ]
|
||||
|
||||
[node name="ColorRect" type="ColorRect" parent="SaveViewport" index="0"]
|
||||
|
||||
material = SubResource( 3 )
|
||||
anchor_left = 0.0
|
||||
anchor_top = 0.0
|
||||
anchor_right = 0.0
|
||||
anchor_bottom = 0.0
|
||||
margin_right = 40.0
|
||||
margin_bottom = 40.0
|
||||
rect_pivot_offset = Vector2( 0, 0 )
|
||||
rect_clip_content = false
|
||||
mouse_filter = 0
|
||||
mouse_default_cursor_shape = 0
|
||||
size_flags_horizontal = 1
|
||||
size_flags_vertical = 1
|
||||
color = Color( 1, 1, 1, 1 )
|
||||
_sections_unfolded = [ "Material", "Rect" ]
|
||||
|
||||
[node name="Timer" type="Timer" parent="SaveViewport" index="1"]
|
||||
|
||||
process_mode = 1
|
||||
wait_time = 0.1
|
||||
one_shot = true
|
||||
autostart = false
|
||||
|
||||
[connection signal="connection_request" from="." to="." method="connect_node"]
|
||||
|
||||
[connection signal="disconnection_request" from="." to="." method="disconnect_node"]
|
||||
|
@ -1,6 +1,7 @@
|
||||
tool
|
||||
extends Panel
|
||||
|
||||
var editor_interface = null
|
||||
var current_tab = null
|
||||
|
||||
const MENU = [
|
||||
@ -26,6 +27,8 @@ const MENU = [
|
||||
{ menu="Help", command="about", description="About" }
|
||||
]
|
||||
|
||||
signal quit
|
||||
|
||||
func _ready():
|
||||
OS.set_window_title(ProjectSettings.get_setting("application/config/name")+" v"+ProjectSettings.get_setting("application/config/release"))
|
||||
for m in $VBoxContainer/Menu.get_children():
|
||||
@ -76,6 +79,8 @@ func menu_about_to_show(name, menu):
|
||||
|
||||
func new_pane():
|
||||
var graph_edit = preload("res://addons/material_maker/graph_edit.tscn").instance()
|
||||
graph_edit.renderer = $Renderer
|
||||
graph_edit.editor_interface = editor_interface
|
||||
$VBoxContainer/HBoxContainer/Projects.add_child(graph_edit)
|
||||
$VBoxContainer/HBoxContainer/Projects.current_tab = graph_edit.get_index()
|
||||
return graph_edit
|
||||
@ -140,8 +145,7 @@ func export_material_is_disabled():
|
||||
|
||||
func quit():
|
||||
if Engine.editor_hint:
|
||||
get_parent().hide()
|
||||
get_parent().queue_free()
|
||||
emit_signal("quit")
|
||||
else:
|
||||
get_tree().quit()
|
||||
|
||||
@ -245,7 +249,7 @@ func update_preview_2d(node = null):
|
||||
node = n
|
||||
break
|
||||
if node != null:
|
||||
graph_edit.setup_material(preview.get_2d_material(), node.get_textures(), node.generate_shader())
|
||||
graph_edit.renderer.setup_material(preview.get_2d_material(), node.get_textures(), node.generate_shader())
|
||||
|
||||
func _on_Projects_tab_changed(tab):
|
||||
var new_tab = $VBoxContainer/HBoxContainer/Projects.get_current_tab_control()
|
||||
|
@ -1,9 +1,10 @@
|
||||
[gd_scene load_steps=5 format=2]
|
||||
[gd_scene load_steps=6 format=2]
|
||||
|
||||
[ext_resource path="res://addons/material_maker/main_window.gd" type="Script" id=1]
|
||||
[ext_resource path="res://addons/material_maker/library.gd" type="Script" id=2]
|
||||
[ext_resource path="res://addons/material_maker/preview.tscn" type="PackedScene" id=3]
|
||||
[ext_resource path="res://addons/material_maker/widgets/tabs.gd" type="Script" id=4]
|
||||
[ext_resource path="res://addons/material_maker/renderer.tscn" type="PackedScene" id=5]
|
||||
|
||||
[node name="MainWindow" type="Panel" index="0"]
|
||||
|
||||
@ -260,6 +261,8 @@ tab_align = 0
|
||||
tab_close_display_policy = 1
|
||||
scrolling_enabled = true
|
||||
|
||||
[node name="Renderer" parent="." index="1" instance=ExtResource( 5 )]
|
||||
|
||||
[connection signal="no_more_tabs" from="VBoxContainer/HBoxContainer/Projects" to="." method="new_material"]
|
||||
|
||||
[connection signal="resized" from="VBoxContainer/HBoxContainer/Projects" to="VBoxContainer/HBoxContainer/Projects" method="_on_Projects_resized"]
|
||||
|
@ -155,28 +155,12 @@ func deserialize_element(e):
|
||||
return Color(e.r, e.g, e.b, e.a)
|
||||
return e
|
||||
|
||||
func do_generate_shader(src_code):
|
||||
var code
|
||||
code = "shader_type canvas_item;\n"
|
||||
var file = File.new()
|
||||
file.open("res://addons/material_maker/common.shader", File.READ)
|
||||
code += file.get_as_text()
|
||||
code += "\n"
|
||||
var shader_code = src_code.defs
|
||||
shader_code += "void fragment() {\n"
|
||||
shader_code += src_code.code
|
||||
shader_code += "COLOR = vec4("+src_code.rgb+", 1.0);\n"
|
||||
shader_code += "}\n"
|
||||
#print("GENERATED SHADER:\n"+shader_code)
|
||||
code += shader_code
|
||||
return code
|
||||
|
||||
func generate_shader(slot = 0):
|
||||
# Reset all nodes
|
||||
for c in get_parent().get_children():
|
||||
if c is GraphNode:
|
||||
c.reset()
|
||||
return do_generate_shader(get_shader_code("UV", slot))
|
||||
return get_parent().renderer.generate_shader(get_shader_code("UV", slot))
|
||||
|
||||
func serialize():
|
||||
var type = get_script().resource_path
|
||||
|
@ -6,9 +6,7 @@ var direction = 0
|
||||
var sigma = 1.0
|
||||
|
||||
var input_shader = ""
|
||||
var input_texture
|
||||
var mid_texture
|
||||
var final_texture
|
||||
var saved_texture
|
||||
|
||||
const DIRECTION_H = 1
|
||||
const DIRECTION_V = 2
|
||||
@ -30,9 +28,7 @@ func _ready():
|
||||
$HBoxContainer2/direction.add_item(d.name)
|
||||
$HBoxContainer2/direction.selected = direction
|
||||
initialize_properties([ $HBoxContainer1/size, $HBoxContainer2/direction, $HBoxContainer3/sigma ])
|
||||
input_texture = ImageTexture.new()
|
||||
mid_texture = ImageTexture.new()
|
||||
final_texture = ImageTexture.new()
|
||||
saved_texture = ImageTexture.new()
|
||||
|
||||
func get_gaussian_blur_shader(horizontal):
|
||||
var convolution = { x=0, y=0, kernel=[], epsilon=1.0/pow(2, 5+size) }
|
||||
@ -53,23 +49,23 @@ func get_gaussian_blur_shader(horizontal):
|
||||
|
||||
func _rerender():
|
||||
if DIRECTIONS[direction].mask & DIRECTION_H != 0:
|
||||
get_parent().precalculate_shader(input_shader, get_source().get_textures(), int(pow(2, 5+size)), input_texture, self, "pass_1", [])
|
||||
get_parent().renderer.precalculate_shader(input_shader, get_source().get_textures(), int(pow(2, 5+size)), saved_texture, self, "pass_1", [])
|
||||
else:
|
||||
get_parent().precalculate_shader(input_shader, get_source().get_textures(), int(pow(2, 5+size)), mid_texture, self, "pass_2", [])
|
||||
get_parent().renderer.precalculate_shader(input_shader, get_source().get_textures(), int(pow(2, 5+size)), saved_texture, self, "pass_2", [])
|
||||
|
||||
func pass_1():
|
||||
if DIRECTIONS[direction].mask & DIRECTION_V != 0:
|
||||
get_parent().precalculate_shader(get_gaussian_blur_shader(true), { input=input_texture}, int(pow(2, 5+size)), mid_texture, self, "pass_2", [])
|
||||
get_parent().renderer.precalculate_shader(get_gaussian_blur_shader(true), { input=saved_texture }, int(pow(2, 5+size)), saved_texture, self, "pass_2", [])
|
||||
else:
|
||||
get_parent().precalculate_shader(get_gaussian_blur_shader(true), { input=input_texture}, int(pow(2, 5+size)), final_texture, self, "rerender_targets", [])
|
||||
get_parent().renderer.precalculate_shader(get_gaussian_blur_shader(true), { input=saved_texture }, int(pow(2, 5+size)), saved_texture, self, "rerender_targets", [])
|
||||
|
||||
|
||||
func pass_2():
|
||||
get_parent().precalculate_shader(get_gaussian_blur_shader(false), { input=mid_texture}, int(pow(2, 5+size)), final_texture, self, "rerender_targets", [])
|
||||
get_parent().renderer.precalculate_shader(get_gaussian_blur_shader(false), { input=saved_texture }, int(pow(2, 5+size)), saved_texture, self, "rerender_targets", [])
|
||||
|
||||
func get_textures():
|
||||
var list = {}
|
||||
list[name] = final_texture
|
||||
list[name] = saved_texture
|
||||
return list
|
||||
|
||||
func _get_shader_code(uv):
|
||||
@ -77,7 +73,7 @@ func _get_shader_code(uv):
|
||||
var src = get_source()
|
||||
if src == null:
|
||||
return rv
|
||||
input_shader = do_generate_shader(src.get_shader_code("UV"))
|
||||
input_shader = get_parent().renderer.generate_shader(src.get_shader_code("UV"))
|
||||
_rerender()
|
||||
if generated_variants.empty():
|
||||
rv.defs = "uniform sampler2D "+name+"_tex;\n"
|
||||
|
@ -34,14 +34,14 @@ func _ready():
|
||||
initialize_properties([ $HBoxContainer1/size, $HBoxContainer2/direction ])
|
||||
|
||||
func _rerender():
|
||||
get_parent().precalculate_shader(input_shader, get_source().get_textures(), int(pow(2, 5+size)), input_texture, self, "pass_1", [])
|
||||
get_parent().renderer.precalculate_shader(input_shader, get_source().get_textures(), int(pow(2, 5+size)), input_texture, self, "pass_1", [])
|
||||
|
||||
func pass_1():
|
||||
var convolution = CONVOLUTION
|
||||
convolution.epsilon=1.0/pow(2, 5+size)
|
||||
for i in range(8):
|
||||
convolution.kernel[INDICES[i]] = COEFS[(i+8-int(direction))%8]
|
||||
get_parent().precalculate_shader(get_convolution_shader(convolution), {input=input_texture}, int(pow(2, 5+size)), final_texture, self, "rerender_targets", [])
|
||||
get_parent().renderer.precalculate_shader(get_convolution_shader(convolution), {input=input_texture}, int(pow(2, 5+size)), final_texture, self, "rerender_targets", [])
|
||||
|
||||
func get_textures():
|
||||
var list = {}
|
||||
@ -53,7 +53,7 @@ func _get_shader_code(uv):
|
||||
var src = get_source()
|
||||
if src == null:
|
||||
return rv
|
||||
input_shader = do_generate_shader(src.get_shader_code("UV"))
|
||||
input_shader = get_parent().generate_shader(src.get_shader_code("UV"))
|
||||
_rerender()
|
||||
if generated_variants.empty():
|
||||
rv.defs = "uniform sampler2D %s_tex;\n" % [ name ]
|
||||
|
@ -19,7 +19,7 @@ func export_textures(prefix, size = null):
|
||||
if suffix != "":
|
||||
if size == null:
|
||||
size = int(pow(2, 8+resolution))
|
||||
get_parent().export_texture(get_source(), "%s_%s.png" % [ prefix, suffix ], size)
|
||||
get_parent().renderer.export_texture(get_source(), "%s_%s.png" % [ prefix, suffix ], size)
|
||||
|
||||
func serialize():
|
||||
var data = .serialize()
|
||||
|
@ -10,6 +10,8 @@ var normal_scale
|
||||
var ao_light_affect
|
||||
var depth_scale
|
||||
|
||||
var texture_list
|
||||
|
||||
var current_material_list = []
|
||||
var generated_textures = {}
|
||||
|
||||
@ -27,24 +29,30 @@ const ADDON_TEXTURE_LIST = [
|
||||
{ port=0, texture="albedo" },
|
||||
{ port=3, texture="emission" },
|
||||
{ port=4, texture="normal_map" },
|
||||
{ ports=[1, 2, 5], texture="mrao" },
|
||||
{ ports=[1, 2, 5], default_values=["0.0", "1.0", "1.0"], texture="mrao" },
|
||||
{ port=6, texture="depth_map" }
|
||||
]
|
||||
|
||||
func _ready():
|
||||
for t in TEXTURE_LIST:
|
||||
texture_list = TEXTURE_LIST
|
||||
if Engine.editor_hint:
|
||||
texture_list = ADDON_TEXTURE_LIST
|
||||
for t in texture_list:
|
||||
generated_textures[t.texture] = { shader=null, source=null, texture=null }
|
||||
initialize_properties([ $resolution, $Albedo/albedo_color, $Metallic/metallic, $Roughness/roughness, $Emission/emission_energy, $NormalMap/normal_scale, $AmbientOcclusion/ao_light_affect, $DepthMap/depth_scale ])
|
||||
|
||||
func _rerender():
|
||||
var size = int(pow(2, 8+resolution))
|
||||
var has_textures = false
|
||||
for t in TEXTURE_LIST:
|
||||
for t in texture_list:
|
||||
var shader = generated_textures[t.texture].shader
|
||||
if shader != null:
|
||||
var input_textures = null
|
||||
if generated_textures[t.texture].source != null:
|
||||
input_textures = generated_textures[t.texture].source.get_textures()
|
||||
get_parent().precalculate_shader(shader, input_textures, 1024, generated_textures[t.texture].texture, self, "do_update_materials", [ current_material_list ])
|
||||
var input_textures = {}
|
||||
for s in generated_textures[t.texture].sources:
|
||||
var source_textures = s.get_textures()
|
||||
for st in source_textures.keys():
|
||||
input_textures[st] = source_textures[st]
|
||||
get_parent().renderer.precalculate_shader(shader, input_textures, size, generated_textures[t.texture].texture, self, "do_update_materials", [ current_material_list ])
|
||||
has_textures = true
|
||||
if !has_textures:
|
||||
do_update_materials(current_material_list)
|
||||
@ -59,63 +67,123 @@ func _get_shader_code(uv):
|
||||
func update_materials(material_list):
|
||||
current_material_list = material_list
|
||||
var has_textures = false
|
||||
for t in TEXTURE_LIST:
|
||||
for t in texture_list:
|
||||
var shader = null
|
||||
var sources = []
|
||||
if t.has("port"):
|
||||
var source = get_source(t.port)
|
||||
if source == null:
|
||||
generated_textures[t.texture].shader = null
|
||||
generated_textures[t.texture].source = null
|
||||
if source != null:
|
||||
shader = source.generate_shader()
|
||||
sources.append(source)
|
||||
elif t.has("ports"):
|
||||
var source = [ null, null, null ]
|
||||
generated_textures[t.texture].mask = 0
|
||||
for i in range(3):
|
||||
source[i] = get_source(t.ports[i])
|
||||
if source[i] != null:
|
||||
sources.append(source[i])
|
||||
generated_textures[t.texture].mask |= 1 << i
|
||||
if !sources.empty():
|
||||
for c in get_parent().get_children():
|
||||
if c is GraphNode:
|
||||
c.reset()
|
||||
var source_code = [ null, null, null ]
|
||||
for i in range(3):
|
||||
if source[i] != null:
|
||||
source_code[i] = source[i].get_shader_code("UV")
|
||||
else:
|
||||
source_code[i] = { defs="", code="", f=t.default_values[i] }
|
||||
shader = get_parent().renderer.generate_combined_shader(source_code[0], source_code[1], source_code[2])
|
||||
generated_textures[t.texture].shader = shader
|
||||
generated_textures[t.texture].sources = sources
|
||||
if shader == null:
|
||||
if generated_textures[t.texture].texture != null:
|
||||
generated_textures[t.texture].texture = null
|
||||
else:
|
||||
generated_textures[t.texture].shader = source.generate_shader()
|
||||
generated_textures[t.texture].source = source
|
||||
if generated_textures[t.texture].texture == null:
|
||||
generated_textures[t.texture].texture = ImageTexture.new()
|
||||
_rerender()
|
||||
|
||||
func get_generated_texture(slot, file_prefix = null):
|
||||
if file_prefix != null:
|
||||
var file_name = "%s_%s.png" % [ file_prefix, slot ]
|
||||
if File.new().file_exists(file_name):
|
||||
return load(file_name)
|
||||
else:
|
||||
return null
|
||||
else:
|
||||
return generated_textures[slot].texture
|
||||
|
||||
func update_spatial_material(m, file_prefix = null):
|
||||
var texture
|
||||
m.albedo_color = albedo_color
|
||||
m.albedo_texture = get_generated_texture("albedo", file_prefix)
|
||||
m.metallic = metallic
|
||||
m.roughness = roughness
|
||||
if Engine.editor_hint:
|
||||
texture = get_generated_texture("mrao", file_prefix)
|
||||
m.metallic_texture = texture
|
||||
m.metallic_texture_channel = SpatialMaterial.TEXTURE_CHANNEL_RED
|
||||
m.roughness_texture = texture
|
||||
m.roughness_texture_channel = SpatialMaterial.TEXTURE_CHANNEL_GREEN
|
||||
else:
|
||||
m.metallic_texture = get_generated_texture("metallic", file_prefix)
|
||||
m.roughness_texture = get_generated_texture("roughness", file_prefix)
|
||||
texture = get_generated_texture("emission", file_prefix)
|
||||
if texture != null:
|
||||
m.emission_enabled = true
|
||||
m.emission_energy = emission_energy
|
||||
m.emission_texture = texture
|
||||
else:
|
||||
m.emission_enabled = false
|
||||
texture = get_generated_texture("normal_map", file_prefix)
|
||||
if texture != null:
|
||||
m.normal_enabled = true
|
||||
m.normal_texture = texture
|
||||
else:
|
||||
m.normal_enabled = false
|
||||
if Engine.editor_hint:
|
||||
if (generated_textures.mrao.mask & (1 << 2)) != 0:
|
||||
m.ao_enabled = true
|
||||
m.ao_light_affect = ao_light_affect
|
||||
m.ao_texture = m.metallic_texture
|
||||
m.ao_texture_channel = SpatialMaterial.TEXTURE_CHANNEL_BLUE
|
||||
else:
|
||||
m.ao_enabled = false
|
||||
else:
|
||||
texture = get_generated_texture("ambient_occlusion", file_prefix)
|
||||
if texture != null:
|
||||
m.ao_enabled = true
|
||||
m.ao_light_affect = ao_light_affect
|
||||
m.ao_texture = texture
|
||||
else:
|
||||
m.ao_enabled = false
|
||||
texture = get_generated_texture("depth_map", file_prefix)
|
||||
if texture != null:
|
||||
m.depth_enabled = true
|
||||
m.depth_scale = depth_scale
|
||||
m.depth_texture = texture
|
||||
else:
|
||||
m.depth_enabled = false
|
||||
|
||||
func do_update_materials(material_list):
|
||||
for m in material_list:
|
||||
if m is SpatialMaterial:
|
||||
m.albedo_color = albedo_color
|
||||
m.albedo_texture = generated_textures.albedo.texture
|
||||
m.metallic = metallic
|
||||
m.metallic_texture = generated_textures.metallic.texture
|
||||
m.roughness = roughness
|
||||
m.roughness_texture = generated_textures.roughness.texture
|
||||
if generated_textures.emission.texture != null:
|
||||
m.emission_enabled = true
|
||||
m.emission_energy = emission_energy
|
||||
m.emission_texture = generated_textures.emission.texture
|
||||
else:
|
||||
m.emission_enabled = false
|
||||
if generated_textures.normal_map.texture != null:
|
||||
m.normal_enabled = true
|
||||
m.normal_texture = generated_textures.normal_map.texture
|
||||
else:
|
||||
m.normal_enabled = false
|
||||
if generated_textures.ambient_occlusion.texture != null:
|
||||
m.ao_enabled = true
|
||||
m.ao_light_affect = ao_light_affect
|
||||
m.ao_texture = generated_textures.ambient_occlusion.texture
|
||||
else:
|
||||
m.ao_enabled = false
|
||||
if generated_textures.depth_map.texture != null:
|
||||
m.depth_enabled = true
|
||||
m.depth_scale = depth_scale
|
||||
m.depth_texture = generated_textures.depth_map.texture
|
||||
else:
|
||||
m.depth_enabled = false
|
||||
update_spatial_material(m)
|
||||
|
||||
func export_textures(prefix, size = null):
|
||||
if size == null:
|
||||
size = int(pow(2, 8+resolution))
|
||||
var texture_list = TEXTURE_LIST
|
||||
if Engine.editor_hint:
|
||||
texture_list = ADDON_TEXTURE_LIST
|
||||
for t in texture_list:
|
||||
if t.has("port"):
|
||||
get_parent().export_texture(get_source(t.port), "%s_%s.png" % [ prefix, t.texture ], size)
|
||||
elif t.has("ports"):
|
||||
get_parent().export_combined_texture(get_source(t.port[0]), get_source(t.port[1]), get_source(t.port[2]), "%s_%s.png" % [ prefix, t.texture ], size)
|
||||
var texture = generated_textures[t.texture].texture
|
||||
if texture != null:
|
||||
var image = texture.get_data()
|
||||
image.save_png("%s_%s.png" % [ prefix, t.texture ])
|
||||
if Engine.editor_hint:
|
||||
print("Exporting material")
|
||||
var resource_filesystem = get_parent().editor_interface.get_resource_filesystem()
|
||||
resource_filesystem.scan()
|
||||
yield(resource_filesystem, "filesystem_changed")
|
||||
var new_material = SpatialMaterial.new()
|
||||
update_spatial_material(new_material, prefix)
|
||||
ResourceSaver.save("%s.tres" % [ prefix ], new_material)
|
||||
resource_filesystem.scan()
|
||||
|
@ -35,13 +35,13 @@ func _ready():
|
||||
initialize_properties([ $HBoxContainer1/size, $HBoxContainer2/amount ])
|
||||
|
||||
func _rerender():
|
||||
get_parent().precalculate_shader(input_shader, get_source().get_textures(), int(pow(2, 5+size)), input_texture, self, "pass_1", [])
|
||||
get_parent().renderer.precalculate_shader(input_shader, get_source().get_textures(), int(pow(2, 5+size)), input_texture, self, "pass_1", [])
|
||||
|
||||
func pass_1():
|
||||
var convolution = CONVOLUTION
|
||||
convolution.epsilon=1.0/pow(2, 5+size)
|
||||
convolution.scale_before_normalize = pow(2, size-1)*amount
|
||||
get_parent().precalculate_shader(get_convolution_shader(convolution), { input=input_texture}, int(pow(2, 5+size)), final_texture, self, "rerender_targets", [])
|
||||
get_parent().renderer.precalculate_shader(get_convolution_shader(convolution), { input=input_texture}, int(pow(2, 5+size)), final_texture, self, "rerender_targets", [])
|
||||
|
||||
func get_textures():
|
||||
var list = {}
|
||||
@ -53,7 +53,7 @@ func _get_shader_code(uv):
|
||||
var src = get_source()
|
||||
if src == null:
|
||||
return rv
|
||||
input_shader = do_generate_shader(src.get_shader_code("UV"))
|
||||
input_shader = get_parent().renderer.generate_shader(src.get_shader_code("UV"))
|
||||
_rerender()
|
||||
if generated_variants.empty():
|
||||
rv.defs = "uniform sampler2D %s_tex;\n" % [ name ]
|
||||
|
@ -5,17 +5,12 @@ var mm_button = null
|
||||
var material_maker = null
|
||||
|
||||
func _enter_tree():
|
||||
#material_maker = preload("res://addons/material_maker/pm_material_maker.tscn").instance()
|
||||
#add_control_to_bottom_panel(material_maker, "ProceduralMaterial")
|
||||
print("Adding menu item")
|
||||
mm_button = Button.new()
|
||||
mm_button.connect("pressed", self, "open_material_maker")
|
||||
mm_button.text = "Material Maker"
|
||||
add_control_to_container(CONTAINER_TOOLBAR, mm_button)
|
||||
print("done")
|
||||
|
||||
func _exit_tree():
|
||||
#remove_control_from_bottom_panel(material_maker)
|
||||
if mm_button != null:
|
||||
remove_control_from_container(CONTAINER_TOOLBAR, mm_button)
|
||||
mm_button.queue_free()
|
||||
@ -34,6 +29,15 @@ func _set_state(s):
|
||||
|
||||
func open_material_maker():
|
||||
if material_maker == null:
|
||||
material_maker = load("res://addons/material_maker/window_dialog.tscn").instance()
|
||||
material_maker = preload("res://addons/material_maker/window_dialog.tscn").instance()
|
||||
var panel = material_maker.get_node("MainWindow")
|
||||
panel.editor_interface = get_editor_interface()
|
||||
panel.connect("quit", self, "close_material_maker")
|
||||
add_child(material_maker)
|
||||
material_maker.popup_centered()
|
||||
|
||||
func close_material_maker():
|
||||
if material_maker != null:
|
||||
material_maker.hide()
|
||||
material_maker.queue_free()
|
||||
material_maker = null
|
||||
|
98
addons/material_maker/renderer.gd
Normal file
98
addons/material_maker/renderer.gd
Normal file
@ -0,0 +1,98 @@
|
||||
tool
|
||||
extends Viewport
|
||||
|
||||
var render_queue = []
|
||||
|
||||
func _ready():
|
||||
$ColorRect.material = $ColorRect.material.duplicate(true)
|
||||
|
||||
# Save shader to image, create image texture
|
||||
|
||||
static func generate_shader(src_code):
|
||||
var code
|
||||
code = "shader_type canvas_item;\n"
|
||||
var file = File.new()
|
||||
file.open("res://addons/material_maker/common.shader", File.READ)
|
||||
code += file.get_as_text()
|
||||
code += "\n"
|
||||
var shader_code = src_code.defs
|
||||
shader_code += "void fragment() {\n"
|
||||
shader_code += src_code.code
|
||||
shader_code += "COLOR = vec4("+src_code.rgb+", 1.0);\n"
|
||||
shader_code += "}\n"
|
||||
#print("GENERATED SHADER:\n"+shader_code)
|
||||
code += shader_code
|
||||
return code
|
||||
|
||||
static func generate_combined_shader(red_code, green_code, blue_code):
|
||||
var code
|
||||
code = "shader_type canvas_item;\n"
|
||||
var file = File.new()
|
||||
file.open("res://addons/material_maker/common.shader", File.READ)
|
||||
code += file.get_as_text()
|
||||
code += "\n"
|
||||
var shader_code = ""
|
||||
shader_code += red_code.defs
|
||||
shader_code += green_code.defs
|
||||
shader_code += blue_code.defs
|
||||
shader_code += "void fragment() {\n"
|
||||
shader_code += red_code.code
|
||||
shader_code += green_code.code
|
||||
shader_code += blue_code.code
|
||||
shader_code += "COLOR = vec4("+red_code.f+", "+green_code.f+", "+blue_code.f+", 1.0);\n"
|
||||
shader_code += "}\n"
|
||||
#print("GENERATED COMBINED SHADER:\n"+shader_code)
|
||||
code += shader_code
|
||||
return code
|
||||
|
||||
func setup_material(shader_material, textures, shader_code):
|
||||
for k in textures.keys():
|
||||
shader_material.set_shader_param(k+"_tex", textures[k])
|
||||
shader_material.shader.code = shader_code
|
||||
|
||||
func render_shader_to_viewport(shader, textures, render_size, method, args):
|
||||
render_queue.append( { shader=shader, textures=textures, size=render_size, method=method, args=args } )
|
||||
if render_queue.size() == 1:
|
||||
while !render_queue.empty():
|
||||
var job = render_queue.front()
|
||||
size = Vector2(job.size, job.size)
|
||||
$ColorRect.rect_position = Vector2(0, 0)
|
||||
$ColorRect.rect_size = Vector2(job.size, job.size)
|
||||
var shader_material = $ColorRect.material
|
||||
shader_material.shader.code = job.shader
|
||||
if job.textures != null:
|
||||
for k in job.textures.keys():
|
||||
shader_material.set_shader_param(k+"_tex", job.textures[k])
|
||||
render_target_update_mode = Viewport.UPDATE_ALWAYS
|
||||
update_worlds()
|
||||
yield(get_tree(), "idle_frame")
|
||||
yield(get_tree(), "idle_frame")
|
||||
render_target_update_mode = Viewport.UPDATE_DISABLED
|
||||
callv(job.method, job.args)
|
||||
render_queue.pop_front()
|
||||
|
||||
func render_to_viewport(node, render_size, method, args):
|
||||
render_shader_to_viewport(node.generate_shader(), node.get_textures(), render_size, method, args)
|
||||
|
||||
func export_texture(node, filename, render_size = 256):
|
||||
if node == null:
|
||||
return null
|
||||
render_to_viewport(node, render_size, "do_export_texture", [ filename ])
|
||||
|
||||
func do_export_texture(filename):
|
||||
var viewport_texture = get_texture()
|
||||
var viewport_image = viewport_texture.get_data()
|
||||
viewport_image.save_png(filename)
|
||||
|
||||
func precalculate_node(node, render_size, target_texture, object, method, args):
|
||||
if node == null:
|
||||
return null
|
||||
render_to_viewport(node, render_size, "do_precalculate_texture", [ object, method, args ])
|
||||
|
||||
func precalculate_shader(shader, textures, render_size, target_texture, object, method, args):
|
||||
render_shader_to_viewport(shader, textures, render_size, "do_precalculate_texture", [ target_texture, object, method, args ])
|
||||
|
||||
func do_precalculate_texture(target_texture, object, method, args):
|
||||
var viewport_texture = get_texture()
|
||||
target_texture.create_from_image(viewport_texture.get_data())
|
||||
object.callv(method, args)
|
72
addons/material_maker/renderer.tscn
Normal file
72
addons/material_maker/renderer.tscn
Normal file
@ -0,0 +1,72 @@
|
||||
[gd_scene load_steps=4 format=2]
|
||||
|
||||
[ext_resource path="res://addons/material_maker/renderer.gd" type="Script" id=1]
|
||||
|
||||
[sub_resource type="Shader" id=1]
|
||||
|
||||
code = "shader_type canvas_item;
|
||||
|
||||
void fragment() {
|
||||
COLOR = vec4(1.0);
|
||||
}
|
||||
"
|
||||
|
||||
[sub_resource type="ShaderMaterial" id=2]
|
||||
|
||||
render_priority = 0
|
||||
shader = SubResource( 1 )
|
||||
|
||||
[node name="Renderer" type="Viewport"]
|
||||
|
||||
arvr = false
|
||||
size = Vector2( 0, 0 )
|
||||
own_world = true
|
||||
world = null
|
||||
transparent_bg = false
|
||||
msaa = 2
|
||||
hdr = false
|
||||
disable_3d = false
|
||||
usage = 2
|
||||
debug_draw = 0
|
||||
render_target_v_flip = true
|
||||
render_target_clear_mode = 0
|
||||
render_target_update_mode = 1
|
||||
audio_listener_enable_2d = false
|
||||
audio_listener_enable_3d = false
|
||||
physics_object_picking = false
|
||||
gui_disable_input = true
|
||||
gui_snap_controls_to_pixels = true
|
||||
shadow_atlas_size = 0
|
||||
shadow_atlas_quad_0 = 2
|
||||
shadow_atlas_quad_1 = 2
|
||||
shadow_atlas_quad_2 = 3
|
||||
shadow_atlas_quad_3 = 4
|
||||
script = ExtResource( 1 )
|
||||
_sections_unfolded = [ "GUI", "Render Target", "Rendering" ]
|
||||
|
||||
[node name="ColorRect" type="ColorRect" parent="." index="0"]
|
||||
|
||||
material = SubResource( 2 )
|
||||
anchor_left = 0.0
|
||||
anchor_top = 0.0
|
||||
anchor_right = 0.0
|
||||
anchor_bottom = 0.0
|
||||
margin_right = 40.0
|
||||
margin_bottom = 40.0
|
||||
rect_pivot_offset = Vector2( 0, 0 )
|
||||
rect_clip_content = false
|
||||
mouse_filter = 0
|
||||
mouse_default_cursor_shape = 0
|
||||
size_flags_horizontal = 1
|
||||
size_flags_vertical = 1
|
||||
color = Color( 1, 1, 1, 1 )
|
||||
_sections_unfolded = [ "Material", "Rect" ]
|
||||
|
||||
[node name="Timer" type="Timer" parent="." index="1"]
|
||||
|
||||
process_mode = 1
|
||||
wait_time = 0.1
|
||||
one_shot = true
|
||||
autostart = false
|
||||
|
||||
|
@ -1 +0,0 @@
|
||||
{"connections":[{"from":"pattern_1","from_port":0,"to":"colorize_0","to_port":0},{"from":"perlin_0","from_port":0,"to":"transform_0","to_port":0},{"from":"colorize_0","from_port":0,"to":"transform_0","to_port":3},{"from":"uniform_0","from_port":0,"to":"Material","to_port":2},{"from":"normal_map_0","from_port":0,"to":"Material","to_port":4},{"from":"transform_0","from_port":0,"to":"normal_map_0","to_port":0},{"from":"transform_0","from_port":0,"to":"colorize_1","to_port":0},{"from":"colorize_1","from_port":0,"to":"Material","to_port":0}],"nodes":[{"iterations":7,"name":"perlin_0","node_position":{"x":-248,"y":-183.5},"persistence":0.5,"scale_x":4,"scale_y":4,"type":"perlin"},{"mix":0,"name":"pattern_1","node_position":{"x":-488,"y":6.5},"type":"pattern","x_scale":1,"x_wave":0,"y_scale":1,"y_wave":0},{"gradient":[{"b":0,"g":0,"pos":0,"r":0},{"b":1,"g":1,"pos":1,"r":1}],"name":"colorize_0","node_position":{"x":-214,"y":9.5},"type":"colorize"},{"albedo_color":{"a":1,"b":1,"g":1,"r":1,"type":"Color"},"ao_light_affect":1,"depth_scale":1,"emission_energy":1,"metallic":1,"name":"Material","node_position":{"x":387,"y":-79},"normal_scale":1,"resolution":1,"roughness":1,"type":"material"},{"color":{"a":1,"b":0,"g":0,"r":0,"type":"Color"},"name":"uniform_0","node_position":{"x":248.577271,"y":-53.178574},"type":"uniform"},{"amount":0.5,"name":"normal_map_0","node_position":{"x":234.577271,"y":52.821411},"type":"normal_map"},{"name":"transform_0","node_position":{"x":-8,"y":-73.5},"repeat":true,"rotate":100,"scale_x":1,"scale_y":1,"translate_x":0,"translate_y":0,"type":"transform"},{"gradient":[{"b":0,"g":0,"pos":0.3,"r":1},{"b":0,"g":1,"pos":0.409091,"r":0.96875},{"b":0,"g":0.801432,"pos":0.518182,"r":0.025045},{"b":1,"g":0.000001,"pos":0.627273,"r":0},{"b":0.708333,"g":0,"pos":0.745455,"r":0.641927}],"name":"colorize_1","node_position":{"x":213,"y":-149.5},"type":"colorize"}]}
|
Loading…
Reference in New Issue
Block a user