Grass shell Texturing
Demo shell texturing for grass using shader and MultiMeshInstance.main
							parent
							
								
									16778381cb
								
							
						
					
					
						commit
						2b4746b77a
					
				@ -0,0 +1,46 @@
 | 
				
			|||||||
 | 
					extends Node3D
 | 
				
			||||||
 | 
					class_name RotateCamera
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@export var camera_rotate_sensitivity: float = 0.005
 | 
				
			||||||
 | 
					@export var camera_zoom_sensititivy: float = 0.01
 | 
				
			||||||
 | 
					@export var zoom_max: float = 10.
 | 
				
			||||||
 | 
					@export var zoom_min: float = 0.1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@onready var camera: Camera3D = $Camera3D
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var camera_rotate_diff: Vector3 = Vector3()
 | 
				
			||||||
 | 
					var camera_zoom_weight: float = .15
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var enable_rotate: bool = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func _process(delta):
 | 
				
			||||||
 | 
						self._update_rotation()
 | 
				
			||||||
 | 
						self._update_zoom()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func _unhandled_input(event):
 | 
				
			||||||
 | 
						if self.enable_rotate and event is InputEventMouseMotion:
 | 
				
			||||||
 | 
							self.camera_rotate_diff.y -= event.relative.x
 | 
				
			||||||
 | 
							self.camera_rotate_diff.x -= event.relative.y
 | 
				
			||||||
 | 
						elif event is InputEventMouseButton:
 | 
				
			||||||
 | 
							if event.is_action_pressed("camera_rotate_button"):
 | 
				
			||||||
 | 
								self.enable_rotate = true
 | 
				
			||||||
 | 
							elif event.is_action_released("camera_rotate_button"):
 | 
				
			||||||
 | 
								self.enable_rotate = false
 | 
				
			||||||
 | 
							elif event.is_action_pressed("camera_zoom_in"):
 | 
				
			||||||
 | 
								self.camera_zoom_weight = clampf(self.camera_zoom_weight - camera_zoom_sensititivy, 0., 1.)
 | 
				
			||||||
 | 
							elif event.is_action_pressed("camera_zoom_out"):
 | 
				
			||||||
 | 
								self.camera_zoom_weight = clampf(self.camera_zoom_weight + camera_zoom_sensititivy, 0., 1.)
 | 
				
			||||||
 | 
								
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func _update_rotation():
 | 
				
			||||||
 | 
						if self.enable_rotate:
 | 
				
			||||||
 | 
							self.rotation += self.camera_rotate_diff * camera_rotate_sensitivity
 | 
				
			||||||
 | 
						self.camera_rotate_diff = Vector3.ZERO
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func _update_zoom():
 | 
				
			||||||
 | 
						var from = self.global_position
 | 
				
			||||||
 | 
						var to = self.global_position + self.camera.get_global_transform().basis.z * zoom_max
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						var new_cam_pos = lerp(from, to, camera_zoom_weight)
 | 
				
			||||||
 | 
						self.camera.global_position = new_cam_pos
 | 
				
			||||||
 | 
						
 | 
				
			||||||
@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					[gd_scene load_steps=2 format=3 uid="uid://bkavbjcx1vhaf"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[ext_resource type="Script" path="res://camera.gd" id="1_bmlh2"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[node name="camera_root" type="Node3D"]
 | 
				
			||||||
 | 
					transform = Transform3D(1, 0, 0, 0, 0.869029, 0.494761, 0, -0.494761, 0.869029, 0, 0, 0)
 | 
				
			||||||
 | 
					script = ExtResource("1_bmlh2")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[node name="Camera3D" type="Camera3D" parent="."]
 | 
				
			||||||
 | 
					transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.306079, 2.53232)
 | 
				
			||||||
@ -0,0 +1,61 @@
 | 
				
			|||||||
 | 
					shader_type spatial;
 | 
				
			||||||
 | 
					render_mode cull_disabled;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//uniform int _ShellIndex;  // index of shell being worked on (0 -> _ShellCount)
 | 
				
			||||||
 | 
					uniform float _NoiseMin;  // minimum strand length
 | 
				
			||||||
 | 
					uniform float _NoiseMax;  // maxmimum strand length
 | 
				
			||||||
 | 
					uniform float _ShellLength;  // amount of distance that the shells cover, 1 means shells will span across 1 world space unit
 | 
				
			||||||
 | 
					uniform int _ShellCount;  // total number of shells (for normalizing the shell index)
 | 
				
			||||||
 | 
					uniform float _ShellDistanceAttenuation;  // exponent how far to push the shell outwards
 | 
				
			||||||
 | 
					uniform float _Thickness;  // how thick a strand shall be
 | 
				
			||||||
 | 
					uniform float _Attenuation;  // AO strength factor
 | 
				
			||||||
 | 
					uniform float _OcclusionBias;  // additive bias for AO
 | 
				
			||||||
 | 
					uniform vec3 _ShellColor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					uniform float _Density; // amout of strands to generate
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					varying flat int _ShellIndex;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					float hash(uint n) {
 | 
				
			||||||
 | 
						n = (n << 13U) ^ n;
 | 
				
			||||||
 | 
						n = n * (n * n * 15731U + 1239221U) + 123376312589U;
 | 
				
			||||||
 | 
						return float(n & uint(0x7fffffffU)) / float(0x7fffffff);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void vertex() {
 | 
				
			||||||
 | 
						_ShellIndex = int(INSTANCE_CUSTOM.x);
 | 
				
			||||||
 | 
						float shell_height = float(_ShellIndex) / float(_ShellCount);
 | 
				
			||||||
 | 
						shell_height = pow(shell_height, _ShellDistanceAttenuation);
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						VERTEX.xyz += NORMAL.xyz * _ShellLength * shell_height;
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						//NORMAL = normalize(NORMAL);
 | 
				
			||||||
 | 
						UV = UV;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void fragment() {
 | 
				
			||||||
 | 
						vec2 newUV = UV * _Density;
 | 
				
			||||||
 | 
						vec2 localUV = fract(newUV) * 2. - 1.;
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						float localDistanceFromCenter = length(localUV);
 | 
				
			||||||
 | 
						float h = float(_ShellIndex) / float(_ShellCount);  // normalized height
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						uint seed = uint(int(newUV.x) + 100 * int(newUV.y) + 100 * 10);
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						float rand = mix(_NoiseMin, _NoiseMax, hash(seed));  // getting random value for strand
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						if (localDistanceFromCenter > _Thickness * (rand - h) && _ShellIndex > 0) discard;  // discarding pixels outside of thickness
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						//float ndotl = dot(NORMAL, WORLD)  // TODO do light later?
 | 
				
			||||||
 | 
						float ambientOcclusion = pow(h, _Attenuation);  // fake Ambient Occlusion
 | 
				
			||||||
 | 
						ambientOcclusion += _OcclusionBias;
 | 
				
			||||||
 | 
						// Called for every pixel the material is visible on.
 | 
				
			||||||
 | 
						ALBEDO.rgb = _ShellColor * ambientOcclusion;
 | 
				
			||||||
 | 
						//ALPHA = 1.0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void light() {
 | 
				
			||||||
 | 
						float ndotl = clamp(dot(NORMAL, LIGHT), 0., 1.) * .5 + .5;
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						DIFFUSE_LIGHT += ndotl * ndotl;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,22 @@
 | 
				
			|||||||
 | 
					[gd_resource type="QuadMesh" load_steps=3 format=3 uid="uid://obd7wi7g27sb"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[ext_resource type="Shader" path="res://grass.gdshader" id="1_5707v"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[sub_resource type="ShaderMaterial" id="ShaderMaterial_hgeuk"]
 | 
				
			||||||
 | 
					render_priority = 0
 | 
				
			||||||
 | 
					shader = ExtResource("1_5707v")
 | 
				
			||||||
 | 
					shader_parameter/_NoiseMin = 0.4
 | 
				
			||||||
 | 
					shader_parameter/_NoiseMax = 1.0
 | 
				
			||||||
 | 
					shader_parameter/_ShellLength = 0.03
 | 
				
			||||||
 | 
					shader_parameter/_ShellCount = 16
 | 
				
			||||||
 | 
					shader_parameter/_ShellDistanceAttenuation = 1.0
 | 
				
			||||||
 | 
					shader_parameter/_Thickness = 3.4
 | 
				
			||||||
 | 
					shader_parameter/_Attenuation = null
 | 
				
			||||||
 | 
					shader_parameter/_OcclusionBias = null
 | 
				
			||||||
 | 
					shader_parameter/_ShellColor = Vector3(0.2, 0.7, 0.2)
 | 
				
			||||||
 | 
					shader_parameter/_Density = 10.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[resource]
 | 
				
			||||||
 | 
					material = SubResource("ShaderMaterial_hgeuk")
 | 
				
			||||||
 | 
					subdivide_width = 32
 | 
				
			||||||
 | 
					subdivide_depth = 32
 | 
				
			||||||
@ -0,0 +1,34 @@
 | 
				
			|||||||
 | 
					[remap]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					importer="texture"
 | 
				
			||||||
 | 
					type="CompressedTexture2D"
 | 
				
			||||||
 | 
					uid="uid://yafv1eyajxv0"
 | 
				
			||||||
 | 
					path="res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.ctex"
 | 
				
			||||||
 | 
					metadata={
 | 
				
			||||||
 | 
					"vram_texture": false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[deps]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					source_file="res://icon.png"
 | 
				
			||||||
 | 
					dest_files=["res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.ctex"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[params]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					compress/mode=0
 | 
				
			||||||
 | 
					compress/high_quality=false
 | 
				
			||||||
 | 
					compress/lossy_quality=0.7
 | 
				
			||||||
 | 
					compress/hdr_compression=1
 | 
				
			||||||
 | 
					compress/normal_map=0
 | 
				
			||||||
 | 
					compress/channel_pack=0
 | 
				
			||||||
 | 
					mipmaps/generate=false
 | 
				
			||||||
 | 
					mipmaps/limit=-1
 | 
				
			||||||
 | 
					roughness/mode=0
 | 
				
			||||||
 | 
					roughness/src_normal=""
 | 
				
			||||||
 | 
					process/fix_alpha_border=true
 | 
				
			||||||
 | 
					process/premult_alpha=false
 | 
				
			||||||
 | 
					process/normal_map_invert_y=false
 | 
				
			||||||
 | 
					process/hdr_as_srgb=false
 | 
				
			||||||
 | 
					process/hdr_clamp_exposure=false
 | 
				
			||||||
 | 
					process/size_limit=0
 | 
				
			||||||
 | 
					detect_3d/compress_to=1
 | 
				
			||||||
@ -0,0 +1,23 @@
 | 
				
			|||||||
 | 
					[gd_scene load_steps=5 format=3 uid="uid://b45p5o8obc4rb"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[ext_resource type="PackedScene" uid="uid://bkavbjcx1vhaf" path="res://camera.tscn" id="1_fql2y"]
 | 
				
			||||||
 | 
					[ext_resource type="PackedScene" uid="uid://gbmju3x1gp7y" path="res://multi_grass.tscn" id="3_lkk6e"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_sivjy"]
 | 
				
			||||||
 | 
					albedo_color = Color(0.2, 0.137255, 0.0431373, 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[sub_resource type="PlaneMesh" id="PlaneMesh_vpqsi"]
 | 
				
			||||||
 | 
					material = SubResource("StandardMaterial3D_sivjy")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[node name="main" type="Node3D"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[node name="camera" parent="." instance=ExtResource("1_fql2y")]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[node name="light" type="DirectionalLight3D" parent="."]
 | 
				
			||||||
 | 
					transform = Transform3D(0.758372, 0.524229, 0.38737, -0.457532, 0.00484201, 0.88918, 0.464258, -0.851564, 0.243524, 1.21403, 2.38572, 0.933982)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[node name="multi_grass" parent="." instance=ExtResource("3_lkk6e")]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[node name="ground" type="MeshInstance3D" parent="."]
 | 
				
			||||||
 | 
					transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.149795, 0)
 | 
				
			||||||
 | 
					mesh = SubResource("PlaneMesh_vpqsi")
 | 
				
			||||||
@ -0,0 +1,72 @@
 | 
				
			|||||||
 | 
					extends MultiMeshInstance3D
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@export_range(1, 126) var shell_count = 64
 | 
				
			||||||
 | 
					@export var noise_min: float = 0.4
 | 
				
			||||||
 | 
					@export var noise_max: float = 1.0
 | 
				
			||||||
 | 
					@export var shell_length: float = .1
 | 
				
			||||||
 | 
					@export var shell_distance_attenuation: float = 1.
 | 
				
			||||||
 | 
					@export var thickness: float = 3.4
 | 
				
			||||||
 | 
					@export var attenuation: float = 1.98
 | 
				
			||||||
 | 
					@export var occlusion_bias: float = .04
 | 
				
			||||||
 | 
					@export var shell_color: Vector3 = Vector3(0.2, 0.7, 0.2)
 | 
				
			||||||
 | 
					@export var density: int = 150
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func _generate_mmi(layers: int, mmi: MultiMeshInstance3D, mesh: Mesh, material: Material, cast_shadow: bool):
 | 
				
			||||||
 | 
						var mdt = MeshDataTool.new()
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						if mmi.multimesh == null:
 | 
				
			||||||
 | 
							mmi.multimesh = MultiMesh.new()
 | 
				
			||||||
 | 
							mmi.multimesh.transform_format = MultiMesh.TRANSFORM_3D
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						var new_mesh: Mesh = mesh.duplicate(true) as Mesh
 | 
				
			||||||
 | 
						#new_mesh = _normals_to_vertex_color(new_mesh, material)  # saves normal data as vertex color to be used by the shader (in the orig code)
 | 
				
			||||||
 | 
						mmi.multimesh.mesh = new_mesh
 | 
				
			||||||
 | 
						mmi.multimesh.instance_count = layers
 | 
				
			||||||
 | 
						mmi.multimesh.visible_instance_count = layers
 | 
				
			||||||
 | 
						for surface in new_mesh.get_surface_count():
 | 
				
			||||||
 | 
							material = ShaderMaterial.new()
 | 
				
			||||||
 | 
							material.set_shader(preload("res://grass.gdshader"))
 | 
				
			||||||
 | 
							mmi.multimesh.mesh.surface_set_material(surface, material.duplicate(true))
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						for i in range(layers):
 | 
				
			||||||
 | 
							#mmi.multimesh.set_instance_transform(i, Transform3D(Basis(), Vector3(0., .1 * i, 0.)))
 | 
				
			||||||
 | 
							mmi.multimesh.set_instance_transform(i, Transform3D(Basis(), Vector3()))
 | 
				
			||||||
 | 
							var grey = float(i) / float(layers)
 | 
				
			||||||
 | 
							#mmi.multimesh.set_instance_color(i, Color(1., 1., 1., 1.))
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							var mat = mmi.multimesh.mesh.surface_get_material(0)
 | 
				
			||||||
 | 
							if mat is ShaderMaterial:
 | 
				
			||||||
 | 
								#print("Setting shader params (", i, ")")
 | 
				
			||||||
 | 
								#mat.set_shader_parameter("_ShellIndex", i)
 | 
				
			||||||
 | 
								mmi.multimesh.set_instance_custom_data(i, Color(i, 0., 0., 0.))  # passes shell index for each instance
 | 
				
			||||||
 | 
								mat.set_shader_parameter("_NoiseMin", noise_min)
 | 
				
			||||||
 | 
								mat.set_shader_parameter("_NoiseMax", noise_max)
 | 
				
			||||||
 | 
								mat.set_shader_parameter("_ShellLength", shell_length)
 | 
				
			||||||
 | 
								mat.set_shader_parameter("_ShellCount", layers)
 | 
				
			||||||
 | 
								mat.set_shader_parameter("_ShellDistanceAttenuation", shell_distance_attenuation)
 | 
				
			||||||
 | 
								mat.set_shader_parameter("_Thickness", thickness)
 | 
				
			||||||
 | 
								mat.set_shader_parameter("_Attenuation", attenuation)
 | 
				
			||||||
 | 
								mat.set_shader_parameter("_OcclusionBias", occlusion_bias)
 | 
				
			||||||
 | 
								mat.set_shader_parameter("_ShellColor", shell_color)
 | 
				
			||||||
 | 
								mat.set_shader_parameter("_Density", density)
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						mmi.cast_shadow = 1 if cast_shadow else 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func _ready():
 | 
				
			||||||
 | 
						var mat = ShaderMaterial.new()
 | 
				
			||||||
 | 
						#var mat2 = StandardMaterial3D.new()
 | 
				
			||||||
 | 
						mat.set_shader(preload("res://grass.gdshader"))
 | 
				
			||||||
 | 
						self._generate_mmi(self.shell_count, self, self.get_multimesh().mesh, mat, false)
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						print("===== Shell Texturing =====")
 | 
				
			||||||
 | 
						print("Layers: ", self.shell_count)
 | 
				
			||||||
 | 
						print("Noise (min): ", self.noise_min)
 | 
				
			||||||
 | 
						print("Noise (max): ", self.noise_max)
 | 
				
			||||||
 | 
						print("Shell length: ", self.shell_length)
 | 
				
			||||||
 | 
						print("Shell count: ", self.shell_count)
 | 
				
			||||||
 | 
						print("Shell distance attenuation: ", self.shell_distance_attenuation)
 | 
				
			||||||
 | 
						print("Thickness: ", self.thickness)
 | 
				
			||||||
 | 
						print("Attenuation: ", self.attenuation)
 | 
				
			||||||
 | 
						print("Occlusion bias: ", self.occlusion_bias)
 | 
				
			||||||
 | 
						print("Shell color: ", self.shell_color)
 | 
				
			||||||
 | 
						print("Density: ", self.density)
 | 
				
			||||||
@ -0,0 +1,15 @@
 | 
				
			|||||||
 | 
					[gd_scene load_steps=4 format=3 uid="uid://gbmju3x1gp7y"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[ext_resource type="Script" path="res://multi_grass.gd" id="1_f7cbg"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[sub_resource type="QuadMesh" id="QuadMesh_h8afx"]
 | 
				
			||||||
 | 
					orientation = 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[sub_resource type="MultiMesh" id="MultiMesh_j6hjp"]
 | 
				
			||||||
 | 
					transform_format = 1
 | 
				
			||||||
 | 
					use_custom_data = true
 | 
				
			||||||
 | 
					mesh = SubResource("QuadMesh_h8afx")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[node name="multi_grass" type="MultiMeshInstance3D"]
 | 
				
			||||||
 | 
					multimesh = SubResource("MultiMesh_j6hjp")
 | 
				
			||||||
 | 
					script = ExtResource("1_f7cbg")
 | 
				
			||||||
@ -0,0 +1,34 @@
 | 
				
			|||||||
 | 
					; Engine configuration file.
 | 
				
			||||||
 | 
					; It's best edited using the editor UI and not directly,
 | 
				
			||||||
 | 
					; since the parameters that go here are not all obvious.
 | 
				
			||||||
 | 
					;
 | 
				
			||||||
 | 
					; Format:
 | 
				
			||||||
 | 
					;   [section] ; section goes between []
 | 
				
			||||||
 | 
					;   param=value ; assign values to parameters
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					config_version=5
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[application]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					config/name="Shell Texturing"
 | 
				
			||||||
 | 
					run/main_scene="res://main.tscn"
 | 
				
			||||||
 | 
					config/features=PackedStringArray("4.2", "Forward Plus")
 | 
				
			||||||
 | 
					config/icon="res://icon.png"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[input]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					camera_rotate_button={
 | 
				
			||||||
 | 
					"deadzone": 0.5,
 | 
				
			||||||
 | 
					"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":0,"position":Vector2(0, 0),"global_position":Vector2(0, 0),"factor":1.0,"button_index":3,"canceled":false,"pressed":false,"double_click":false,"script":null)
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					camera_zoom_in={
 | 
				
			||||||
 | 
					"deadzone": 0.5,
 | 
				
			||||||
 | 
					"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":0,"position":Vector2(0, 0),"global_position":Vector2(0, 0),"factor":1.0,"button_index":4,"canceled":false,"pressed":false,"double_click":false,"script":null)
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					camera_zoom_out={
 | 
				
			||||||
 | 
					"deadzone": 0.5,
 | 
				
			||||||
 | 
					"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":0,"position":Vector2(0, 0),"global_position":Vector2(0, 0),"factor":1.0,"button_index":5,"canceled":false,"pressed":false,"double_click":false,"script":null)
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
					Loading…
					
					
				
		Reference in New Issue