day 2
This commit is contained in:
		
							parent
							
								
									9c30937a6c
								
							
						
					
					
						commit
						00c29e66fd
					
				
					 44 changed files with 9546 additions and 0 deletions
				
			
		
							
								
								
									
										8
									
								
								Base.tres
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								Base.tres
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,8 @@
 | 
			
		|||
[gd_resource type="StandardMaterial3D" format=3 uid="uid://c3a4yhqx17svd"]
 | 
			
		||||
 | 
			
		||||
[resource]
 | 
			
		||||
resource_name = "Base"
 | 
			
		||||
cull_mode = 2
 | 
			
		||||
vertex_color_use_as_albedo = true
 | 
			
		||||
albedo_color = Color(0.906332, 0.906332, 0.906332, 1)
 | 
			
		||||
roughness = 0.5
 | 
			
		||||
							
								
								
									
										8
									
								
								Eyes.tres
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								Eyes.tres
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,8 @@
 | 
			
		|||
[gd_resource type="StandardMaterial3D" format=3 uid="uid://bmbuvvtluxtqa"]
 | 
			
		||||
 | 
			
		||||
[resource]
 | 
			
		||||
resource_name = "Eyes"
 | 
			
		||||
cull_mode = 2
 | 
			
		||||
vertex_color_use_as_albedo = true
 | 
			
		||||
albedo_color = Color(0, 0, 0, 1)
 | 
			
		||||
roughness = 0.5
 | 
			
		||||
							
								
								
									
										51
									
								
								Fur.tres
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								Fur.tres
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,51 @@
 | 
			
		|||
[gd_resource type="ShaderMaterial" load_steps=12 format=3 uid="uid://bf7f3xu32tv26"]
 | 
			
		||||
 | 
			
		||||
[ext_resource type="Shader" uid="uid://bgf62oe1pyffc" path="res://shell_fur.gdshader" id="1_4lo6c"]
 | 
			
		||||
 | 
			
		||||
[sub_resource type="Curve" id="Curve_4lo6c"]
 | 
			
		||||
_data = [Vector2(0, 0.460674), 0.0, 0.0, 0, 0, Vector2(0.706897, 0.47191), 0.0, 0.0, 0, 0, Vector2(0.894089, 0.494382), 0.0, 0.0, 0, 0]
 | 
			
		||||
point_count = 3
 | 
			
		||||
 | 
			
		||||
[sub_resource type="CurveTexture" id="CurveTexture_fxxdy"]
 | 
			
		||||
curve = SubResource("Curve_4lo6c")
 | 
			
		||||
 | 
			
		||||
[sub_resource type="Curve" id="Curve_38t5h"]
 | 
			
		||||
_data = [Vector2(0, 0), 0.0, 0.0, 0, 0, Vector2(0.684211, 0.438202), 0.0, 0.0, 0, 0, Vector2(1, 0.988764), 0.0, 0.0, 0, 0]
 | 
			
		||||
point_count = 3
 | 
			
		||||
 | 
			
		||||
[sub_resource type="CurveTexture" id="CurveTexture_di2cg"]
 | 
			
		||||
curve = SubResource("Curve_38t5h")
 | 
			
		||||
 | 
			
		||||
[sub_resource type="Curve" id="Curve_kh7y5"]
 | 
			
		||||
_data = [Vector2(0, 0), 0.0, 0.0, 0, 0, Vector2(1, 1), 0.0, 0.0, 0, 0]
 | 
			
		||||
point_count = 2
 | 
			
		||||
 | 
			
		||||
[sub_resource type="CurveTexture" id="CurveTexture_b6cys"]
 | 
			
		||||
curve = SubResource("Curve_kh7y5")
 | 
			
		||||
 | 
			
		||||
[sub_resource type="Curve" id="Curve_frk85"]
 | 
			
		||||
_data = [Vector2(0, 0.58427), 0.0, 0.0, 0, 0, Vector2(0.552632, 1), 0.0, 0.0, 0, 0, Vector2(1, 1), 0.0, 0.0, 0, 0]
 | 
			
		||||
point_count = 3
 | 
			
		||||
 | 
			
		||||
[sub_resource type="CurveTexture" id="CurveTexture_rdnk8"]
 | 
			
		||||
curve = SubResource("Curve_frk85")
 | 
			
		||||
 | 
			
		||||
[sub_resource type="Curve" id="Curve_tjf75"]
 | 
			
		||||
_data = [Vector2(0, 1), 0.0, 0.0, 0, 0, Vector2(0.473684, 0.820225), -0.288202, -0.288202, 0, 0, Vector2(0.719298, 0.550562), -0.669561, -0.669561, 0, 0, Vector2(1, 0.202247), 0.0, 0.0, 0, 0]
 | 
			
		||||
point_count = 4
 | 
			
		||||
 | 
			
		||||
[sub_resource type="CurveTexture" id="CurveTexture_hawuf"]
 | 
			
		||||
curve = SubResource("Curve_tjf75")
 | 
			
		||||
 | 
			
		||||
[resource]
 | 
			
		||||
resource_name = "Fur"
 | 
			
		||||
render_priority = 0
 | 
			
		||||
shader = ExtResource("1_4lo6c")
 | 
			
		||||
shader_parameter/scale = 500.0
 | 
			
		||||
shader_parameter/clump_scale = 91.075
 | 
			
		||||
shader_parameter/grooming_factor = 2.033
 | 
			
		||||
shader_parameter/hair_curve_shape = SubResource("CurveTexture_b6cys")
 | 
			
		||||
shader_parameter/hair_stem_shape = SubResource("CurveTexture_hawuf")
 | 
			
		||||
shader_parameter/hair_clump_radius = SubResource("CurveTexture_di2cg")
 | 
			
		||||
shader_parameter/hair_clump_height = SubResource("CurveTexture_fxxdy")
 | 
			
		||||
shader_parameter/hair_depth_factor = SubResource("CurveTexture_rdnk8")
 | 
			
		||||
							
								
								
									
										7
									
								
								Mouth.tres
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								Mouth.tres
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,7 @@
 | 
			
		|||
[gd_resource type="StandardMaterial3D" format=3 uid="uid://bbdd5508jhw37"]
 | 
			
		||||
 | 
			
		||||
[resource]
 | 
			
		||||
resource_name = "Mouth"
 | 
			
		||||
cull_mode = 2
 | 
			
		||||
albedo_color = Color(1, 0.62, 0.62, 1)
 | 
			
		||||
roughness = 0.5
 | 
			
		||||
							
								
								
									
										129
									
								
								PlayerInput.gd
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										129
									
								
								PlayerInput.gd
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,129 @@
 | 
			
		|||
extends Node
 | 
			
		||||
 | 
			
		||||
var allow_joins = true
 | 
			
		||||
var players: Dictionary[int, PlayerInputData]
 | 
			
		||||
 | 
			
		||||
const DEADZONE = 0.2
 | 
			
		||||
 | 
			
		||||
signal player_join(data: PlayerInputData)
 | 
			
		||||
 | 
			
		||||
func _ready() -> void:
 | 
			
		||||
	Input.joy_connection_changed.connect(joy_connection_changed)
 | 
			
		||||
 | 
			
		||||
@warning_ignore("unused_parameter")
 | 
			
		||||
func joy_connection_changed(device: int, connected: bool):
 | 
			
		||||
	print("Connected ", device)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
func move_input(player: PlayerInputData, event: InputEvent, action: String):
 | 
			
		||||
	var strength := 0.0
 | 
			
		||||
	if event is InputEventJoypadMotion:
 | 
			
		||||
		strength = abs(event.axis_value)
 | 
			
		||||
		if strength <= DEADZONE:
 | 
			
		||||
			strength = 0.0
 | 
			
		||||
		if event.axis_value < 0.0:
 | 
			
		||||
			action = action.replace("down", "up").replace("right", "left")
 | 
			
		||||
		if action == "move_up" or action == "move_down":
 | 
			
		||||
			if event.axis_value < 0.0:
 | 
			
		||||
				player.raw_up = strength
 | 
			
		||||
				player.raw_down = 0.0
 | 
			
		||||
			else:
 | 
			
		||||
				player.raw_down = strength
 | 
			
		||||
				player.raw_up = 0.0
 | 
			
		||||
		elif action == "move_right" or action == "move_left":
 | 
			
		||||
			if event.axis_value < 0.0:
 | 
			
		||||
				player.raw_left = strength
 | 
			
		||||
				player.raw_right = 0.0
 | 
			
		||||
			else:
 | 
			
		||||
				player.raw_right = strength
 | 
			
		||||
				player.raw_left = 0.0
 | 
			
		||||
	elif event is InputEventKey:
 | 
			
		||||
		strength = float(event.pressed)
 | 
			
		||||
		if action == "move_up":
 | 
			
		||||
			player.raw_up = strength
 | 
			
		||||
		elif action == "move_right":
 | 
			
		||||
			player.raw_right = strength
 | 
			
		||||
		elif action == "move_down":
 | 
			
		||||
			player.raw_down = strength
 | 
			
		||||
		elif action == "move_left":
 | 
			
		||||
			player.raw_left = strength
 | 
			
		||||
	
 | 
			
		||||
	player.walk_dir = Vector3(
 | 
			
		||||
		player.raw_right - player.raw_left,
 | 
			
		||||
		0.0,
 | 
			
		||||
		player.raw_down - player.raw_up,
 | 
			
		||||
	)
 | 
			
		||||
	
 | 
			
		||||
func game_input(player: PlayerInputData, event: InputEvent):
 | 
			
		||||
	if not event.is_action_type():
 | 
			
		||||
		return
 | 
			
		||||
	var action := get_action_name_all(event)
 | 
			
		||||
	if action.contains("move"):
 | 
			
		||||
		move_input(player, event, action)
 | 
			
		||||
	if action == "jump":
 | 
			
		||||
		player.raw_jump = event.is_pressed()
 | 
			
		||||
	if action == "dig":
 | 
			
		||||
		player.raw_dig = event.is_pressed()
 | 
			
		||||
	
 | 
			
		||||
	player.jumping = player.raw_jump
 | 
			
		||||
	player.digging = player.raw_dig
 | 
			
		||||
	
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
func join(who: int):
 | 
			
		||||
	var new_player := PlayerInputData.new()
 | 
			
		||||
	players[who] = new_player
 | 
			
		||||
	player_join.emit(new_player)
 | 
			
		||||
 | 
			
		||||
func drop(who: int):
 | 
			
		||||
	players[who].drop.emit()
 | 
			
		||||
	players.erase(who)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
func get_action_name_all(event: InputEvent) -> String:
 | 
			
		||||
	return get_action_name(event).replace("_alt", "")
 | 
			
		||||
 | 
			
		||||
func get_action_name(event: InputEvent) -> String:
 | 
			
		||||
	if event.is_echo():
 | 
			
		||||
		return ""
 | 
			
		||||
	if event.is_action("dig"):
 | 
			
		||||
		return "dig"
 | 
			
		||||
	if event.is_action("jump"):
 | 
			
		||||
		return "jump"
 | 
			
		||||
	if event.is_action("dig_alt"):
 | 
			
		||||
		return "dig_alt"
 | 
			
		||||
	if event.is_action("jump_alt"):
 | 
			
		||||
		return "jump_alt"
 | 
			
		||||
	if event.is_action("move_up"):
 | 
			
		||||
		return "move_up"
 | 
			
		||||
	if event.is_action("move_right"):
 | 
			
		||||
		return "move_right"
 | 
			
		||||
	if event.is_action("move_down"):
 | 
			
		||||
		return "move_down"
 | 
			
		||||
	if event.is_action("move_left"):
 | 
			
		||||
		return "move_left"
 | 
			
		||||
	if event.is_action("move_up_alt"):
 | 
			
		||||
		return "move_up_alt"
 | 
			
		||||
	if event.is_action("move_right_alt"):
 | 
			
		||||
		return "move_right_alt"
 | 
			
		||||
	if event.is_action("move_down_alt"):
 | 
			
		||||
		return "move_down_alt"
 | 
			
		||||
	if event.is_action("move_left_alt"):
 | 
			
		||||
		return "move_left_alt"
 | 
			
		||||
	return ""
 | 
			
		||||
 | 
			
		||||
func _input(event: InputEvent) -> void:
 | 
			
		||||
	if not event.is_action_type():
 | 
			
		||||
		return
 | 
			
		||||
	var who = event.device
 | 
			
		||||
	if get_action_name(event).contains("_alt"):
 | 
			
		||||
		who = -2
 | 
			
		||||
	elif event is InputEventKey:
 | 
			
		||||
		who = -3
 | 
			
		||||
		
 | 
			
		||||
			
 | 
			
		||||
	if players.has(who):
 | 
			
		||||
		game_input(players[who], event)
 | 
			
		||||
	elif allow_joins and get_action_name(event):
 | 
			
		||||
		join(who)
 | 
			
		||||
	
 | 
			
		||||
							
								
								
									
										1
									
								
								PlayerInput.gd.uid
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								PlayerInput.gd.uid
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1 @@
 | 
			
		|||
uid://1xgx6a7b0tsx
 | 
			
		||||
							
								
								
									
										16
									
								
								PlayerInputData.gd
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								PlayerInputData.gd
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,16 @@
 | 
			
		|||
extends Resource
 | 
			
		||||
class_name PlayerInputData
 | 
			
		||||
 | 
			
		||||
@warning_ignore("unused_signal")
 | 
			
		||||
signal drop
 | 
			
		||||
 | 
			
		||||
var walk_dir: Vector3 = Vector3.ZERO
 | 
			
		||||
var digging: bool = false
 | 
			
		||||
var jumping: bool = false
 | 
			
		||||
 | 
			
		||||
var raw_dig := false
 | 
			
		||||
var raw_jump := false
 | 
			
		||||
var raw_up := 0.0
 | 
			
		||||
var raw_right := 0.0
 | 
			
		||||
var raw_down := 0.0
 | 
			
		||||
var raw_left := 0.0
 | 
			
		||||
							
								
								
									
										1
									
								
								PlayerInputData.gd.uid
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								PlayerInputData.gd.uid
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1 @@
 | 
			
		|||
uid://dsqdjritmyalk
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								Pounce.res
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								Pounce.res
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										0
									
								
								blends/.gdignore
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								blends/.gdignore
									
										
									
									
									
										Normal file
									
								
							
							
								
								
									
										
											BIN
										
									
								
								blends/__pycache__/fox.cpython-311.pyc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								blends/__pycache__/fox.cpython-311.pyc
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								blends/__pycache__/shared_export.cpython-311.pyc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								blends/__pycache__/shared_export.cpython-311.pyc
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								blends/__pycache__/vole.cpython-311.pyc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								blends/__pycache__/vole.cpython-311.pyc
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								blends/fox.blend
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								blends/fox.blend
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								blends/fox.blend1
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								blends/fox.blend1
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										102
									
								
								blends/fox.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								blends/fox.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,102 @@
 | 
			
		|||
 | 
			
		||||
import bpy
 | 
			
		||||
from bpy.types import Object
 | 
			
		||||
from pathlib import Path
 | 
			
		||||
from contextlib import contextmanager
 | 
			
		||||
 | 
			
		||||
if True:
 | 
			
		||||
    blend_folder = Path(bpy.path.abspath("//"))
 | 
			
		||||
    while blend_folder.resolve().stem not in ("", "blends"):
 | 
			
		||||
        blend_folder /= ".."
 | 
			
		||||
    EXPORT_PATH = str((blend_folder / "../fox.glb").resolve())
 | 
			
		||||
    EXPORT_ANIMATIONS_PATH = str(
 | 
			
		||||
        (blend_folder / "../fox_animations.glb").resolve())
 | 
			
		||||
    import sys
 | 
			
		||||
    sys.path.append(str(blend_folder.resolve()))
 | 
			
		||||
    from shared_export import clear_selection, apply_modifier, sort_uv_layers, apply_bone_groups, select_collection, reverted, delete_uv_layer, apply_gn_attr
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def prepare_modifiers(fox: Object, foxrig: Object):
 | 
			
		||||
    geo = fox.modifiers["Fur"]
 | 
			
		||||
    geo.show_viewport = True
 | 
			
		||||
    apply_modifier(fox, geo)
 | 
			
		||||
    apply_bone_groups(foxrig.data, fox)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def gltf_export(filename: str):
 | 
			
		||||
    bpy.context.view_layer.update()
 | 
			
		||||
    bpy.ops.export_scene.gltf(
 | 
			
		||||
        filepath=filename,
 | 
			
		||||
        export_format="GLB",
 | 
			
		||||
        use_selection=True,
 | 
			
		||||
        export_vertex_color="ACTIVE",
 | 
			
		||||
        export_apply=False,
 | 
			
		||||
        export_animation_mode="ACTIONS",
 | 
			
		||||
        export_anim_slide_to_zero=True,
 | 
			
		||||
        export_optimize_animation_size=False,
 | 
			
		||||
        export_optimize_animation_keep_anim_armature=False,
 | 
			
		||||
        export_def_bones=True,
 | 
			
		||||
        export_animations=True,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@reverted
 | 
			
		||||
def export():
 | 
			
		||||
    fox = bpy.data.objects["Fox"]
 | 
			
		||||
    foxrig = bpy.data.objects["FoxRig"]
 | 
			
		||||
    collection = bpy.data.collections["export"]
 | 
			
		||||
    bpy.ops.object.mode_set(mode="OBJECT")
 | 
			
		||||
    bpy.context.view_layer.update()
 | 
			
		||||
    clear_selection(bpy.context)
 | 
			
		||||
    select_collection(bpy.context, collection)
 | 
			
		||||
    prepare_modifiers(fox, foxrig)
 | 
			
		||||
    sort_uv_layers(fox.data)
 | 
			
		||||
    # write_vertex_id_to_uv(fox)
 | 
			
		||||
    gltf_export(EXPORT_PATH)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def vertex_id_mesh_info(helper):
 | 
			
		||||
    return helper.VertexIDMeshInfo(
 | 
			
		||||
        resource_path="res://fox.tscn",
 | 
			
		||||
        node_path="FoxRig/Skeleton3D/Fox",
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def write_vertex_id_to_uv(obj: Object):
 | 
			
		||||
    import bmesh
 | 
			
		||||
    mesh = obj.data
 | 
			
		||||
    mesh.uv_layers.active = mesh.uv_layers[1]
 | 
			
		||||
 | 
			
		||||
    bm = bmesh.new()
 | 
			
		||||
    bm.from_mesh(mesh)
 | 
			
		||||
    uv_layer = bm.loops.layers.uv.active
 | 
			
		||||
    for face in bm.faces:
 | 
			
		||||
        for loop in face.loops:
 | 
			
		||||
            loop[uv_layer].uv.x = loop.vert.index
 | 
			
		||||
    bm.to_mesh(obj.data)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@reverted
 | 
			
		||||
def export_animations():
 | 
			
		||||
    mesh = bpy.data.objects["Fox"]
 | 
			
		||||
    rig = bpy.data.objects["FoxRig"]
 | 
			
		||||
    bpy.ops.object.mode_set(mode="OBJECT")
 | 
			
		||||
    bpy.context.view_layer.update()
 | 
			
		||||
    clear_selection(bpy.context)
 | 
			
		||||
    rig.select_set(True)
 | 
			
		||||
    mesh.select_set(True)
 | 
			
		||||
    mesh.modifiers["Fur"].show_viewport = False
 | 
			
		||||
 | 
			
		||||
    rig.data.pose_position = "POSE"
 | 
			
		||||
    bpy.context.view_layer.update()
 | 
			
		||||
    bpy.ops.export_scene.gltf(
 | 
			
		||||
        filepath=EXPORT_ANIMATIONS_PATH,
 | 
			
		||||
        export_format="GLB",
 | 
			
		||||
        use_selection=True,
 | 
			
		||||
        export_apply=False,
 | 
			
		||||
        export_animation_mode="ACTIONS",
 | 
			
		||||
        export_anim_slide_to_zero=True,
 | 
			
		||||
        export_optimize_animation_size=False,
 | 
			
		||||
        export_optimize_animation_keep_anim_armature=False,
 | 
			
		||||
        export_def_bones=True,
 | 
			
		||||
    )
 | 
			
		||||
							
								
								
									
										132
									
								
								blends/shared_export.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										132
									
								
								blends/shared_export.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,132 @@
 | 
			
		|||
from pathlib import Path
 | 
			
		||||
from sys import stderr
 | 
			
		||||
import bpy
 | 
			
		||||
from bpy.types import Object, Armature, Modifier, Mesh, Context, Collection
 | 
			
		||||
from functools import reduce, wraps
 | 
			
		||||
from operator import attrgetter, itemgetter, or_
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def reverted(fn):
 | 
			
		||||
    @wraps(fn)
 | 
			
		||||
    def inside(*args, **kwargs):
 | 
			
		||||
        bpy.ops.wm.save_as_mainfile()
 | 
			
		||||
        try:
 | 
			
		||||
            fn(*args, **kwargs)
 | 
			
		||||
        finally:
 | 
			
		||||
            bpy.ops.wm.revert_mainfile()
 | 
			
		||||
    return inside
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def read_export_path(argv) -> str:
 | 
			
		||||
    i = next((i for i, arg in enumerate(argv) if arg == "--export-path"), None)
 | 
			
		||||
    if i is None:
 | 
			
		||||
        raise Exception(
 | 
			
		||||
            "Error getting --export-path argument. This script must be executed from Makefile")
 | 
			
		||||
    return str(Path(argv[i+1]).resolve())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def clear_selection(ctx: Context):
 | 
			
		||||
    for obj in ctx.selected_objects:
 | 
			
		||||
        obj.select_set(False)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def select_collection(ctx: Context, c: Collection):
 | 
			
		||||
    for obj in c.objects:
 | 
			
		||||
        obj.select_set(True)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_ik_limits(armature: Object) -> dict:
 | 
			
		||||
    assert type(armature.data) is Armature
 | 
			
		||||
    return reduce(or_, ({pbone.name: [
 | 
			
		||||
        [pbone.ik_min_x, pbone.ik_max_x],
 | 
			
		||||
        [pbone.ik_min_y, pbone.ik_max_y],
 | 
			
		||||
        [pbone.ik_min_z, pbone.ik_max_z],
 | 
			
		||||
    ]} for pbone in armature.pose.bones
 | 
			
		||||
        if pbone.use_ik_limit_x or pbone.use_ik_limit_y or pbone.use_ik_limit_z
 | 
			
		||||
    ), dict())
 | 
			
		||||
 | 
			
		||||
# https://blenderartists.org/t/how-to-apply-all-the-modifiers-with-python/1314483/2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def apply_modifier(obj: Object, modifier: Modifier):
 | 
			
		||||
    with bpy.context.temp_override(object=obj, modifier=modifier):
 | 
			
		||||
        bpy.context.view_layer.update()
 | 
			
		||||
        bpy.ops.object.modifier_apply(modifier=modifier.name)
 | 
			
		||||
        bpy.context.view_layer.update()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def sort_uv_layers(mesh: Mesh):
 | 
			
		||||
    # UV layers use non-pythonic reference semantics, safest to always reference by their indices
 | 
			
		||||
    assert len(
 | 
			
		||||
        mesh.uv_layers) <= 7, "There must be fewer than 8 UV maps to sort (one less than the Blender maximum of 8)"
 | 
			
		||||
    initial_layers = [[i, layer.name]
 | 
			
		||||
                      for i, layer in enumerate(mesh.uv_layers)]
 | 
			
		||||
    initial_layers.sort(key=itemgetter(1))
 | 
			
		||||
    for i, layer_name in tuple(initial_layers):
 | 
			
		||||
        assert mesh.uv_layers[i].name == layer_name, mesh.uv_layers[i].name + \
 | 
			
		||||
            " " + layer_name
 | 
			
		||||
        mesh.uv_layers.active = mesh.uv_layers[i]
 | 
			
		||||
        new_layer = mesh.uv_layers.new(name=f"new_{mesh.uv_layers[i].name}")
 | 
			
		||||
        assert new_layer == mesh.uv_layers[-1]
 | 
			
		||||
        mesh.uv_layers.active = mesh.uv_layers[0]
 | 
			
		||||
        mesh.uv_layers.remove(mesh.uv_layers[i])
 | 
			
		||||
        for pair in initial_layers:
 | 
			
		||||
            if pair[0] > i:
 | 
			
		||||
                pair[0] -= 1
 | 
			
		||||
    for layer in mesh.uv_layers:
 | 
			
		||||
        layer.name = layer.name.replace("new_", "")
 | 
			
		||||
    mesh.uv_layers.active = mesh.uv_layers[0]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def delete_uv_layer(mesh: Mesh, name: str):
 | 
			
		||||
    for layer in mesh.uv_layers:
 | 
			
		||||
        if layer.name == name:
 | 
			
		||||
            mesh.uv_layers.remove(layer)
 | 
			
		||||
            return
 | 
			
		||||
    raise Exception(f"failed to remove UV layer with name {name}")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def convert_attribute(obj, attribute_name: str):
 | 
			
		||||
    "Don't convert this into a function that does multiple at once, indices are too fucky for that"
 | 
			
		||||
    for i, attr in enumerate(obj.data.attributes):
 | 
			
		||||
        if attr.name == attribute_name:
 | 
			
		||||
            obj.data.attributes.active_index = i
 | 
			
		||||
            bpy.context.view_layer.update()
 | 
			
		||||
            bpy.ops.geometry.attribute_convert(
 | 
			
		||||
                mode='VERTEX_GROUP', domain='POINT', data_type='FLOAT')
 | 
			
		||||
            bpy.context.view_layer.update()
 | 
			
		||||
            return
 | 
			
		||||
    raise Exception("attribute not found", attribute_name)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def apply_bone_groups(rig: Armature, skinned: Object):
 | 
			
		||||
    """May not be needed in future versions of Blender
 | 
			
		||||
    Converts geometry nodes attributes for bone groups back to vertex groups
 | 
			
		||||
    Something fishy with indices and attributes, so algorithm is not efficient just to be safe
 | 
			
		||||
    """
 | 
			
		||||
    with bpy.context.temp_override(object=skinned):
 | 
			
		||||
        for bone_name in map(attrgetter("name"), rig.bones):
 | 
			
		||||
            for i, attribute in enumerate(skinned.data.attributes):
 | 
			
		||||
                if attribute.name == bone_name:
 | 
			
		||||
                    skinned.data.attributes.active_index = i
 | 
			
		||||
                    bpy.context.view_layer.update()
 | 
			
		||||
                    bpy.ops.geometry.attribute_convert(
 | 
			
		||||
                        mode='VERTEX_GROUP', domain='POINT', data_type='FLOAT')
 | 
			
		||||
                    bpy.context.view_layer.update()
 | 
			
		||||
                    break
 | 
			
		||||
            else:
 | 
			
		||||
                print("Failed to find attribute for", bone_name, file=stderr)
 | 
			
		||||
 | 
			
		||||
def apply_gn_attr(obj: Object, name: str):
 | 
			
		||||
    "See above"
 | 
			
		||||
    with bpy.context.temp_override(object=obj):
 | 
			
		||||
        for i, attribute in enumerate(obj.data.attributes):
 | 
			
		||||
            if attribute.name == name:
 | 
			
		||||
                obj.data.attributes.active_index = i
 | 
			
		||||
                bpy.context.view_layer.update()
 | 
			
		||||
                bpy.ops.geometry.attribute_convert(
 | 
			
		||||
                    mode='VERTEX_GROUP', domain='POINT', data_type='FLOAT')
 | 
			
		||||
                bpy.context.view_layer.update()
 | 
			
		||||
                return
 | 
			
		||||
    raise Exception(f"Failed to find attribute: {name}")
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								blends/vole.blend
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								blends/vole.blend
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								blends/vole.blend1
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								blends/vole.blend1
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										102
									
								
								blends/vole.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								blends/vole.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,102 @@
 | 
			
		|||
 | 
			
		||||
import bpy
 | 
			
		||||
from bpy.types import Object
 | 
			
		||||
from pathlib import Path
 | 
			
		||||
from contextlib import contextmanager
 | 
			
		||||
 | 
			
		||||
if True:
 | 
			
		||||
    blend_folder = Path(bpy.path.abspath("//"))
 | 
			
		||||
    while blend_folder.resolve().stem not in ("", "blends"):
 | 
			
		||||
        blend_folder /= ".."
 | 
			
		||||
    EXPORT_PATH = str((blend_folder / "../vole.glb").resolve())
 | 
			
		||||
    EXPORT_ANIMATIONS_PATH = str(
 | 
			
		||||
        (blend_folder / "../vole_animations.glb").resolve())
 | 
			
		||||
    import sys
 | 
			
		||||
    sys.path.append(str(blend_folder.resolve()))
 | 
			
		||||
    from shared_export import clear_selection, apply_modifier, sort_uv_layers, apply_bone_groups, select_collection, reverted, delete_uv_layer, apply_gn_attr
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def prepare_modifiers(vole: Object, volerig: Object):
 | 
			
		||||
    geo = vole.modifiers["Fur"]
 | 
			
		||||
    geo.show_viewport = True
 | 
			
		||||
    apply_modifier(vole, geo)
 | 
			
		||||
    apply_bone_groups(volerig.data, vole)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def gltf_export(filename: str):
 | 
			
		||||
    bpy.context.view_layer.update()
 | 
			
		||||
    bpy.ops.export_scene.gltf(
 | 
			
		||||
        filepath=filename,
 | 
			
		||||
        export_format="GLB",
 | 
			
		||||
        use_selection=True,
 | 
			
		||||
        export_vertex_color="ACTIVE",
 | 
			
		||||
        export_apply=False,
 | 
			
		||||
        export_animation_mode="ACTIONS",
 | 
			
		||||
        export_anim_slide_to_zero=True,
 | 
			
		||||
        export_optimize_animation_size=False,
 | 
			
		||||
        export_optimize_animation_keep_anim_armature=False,
 | 
			
		||||
        export_def_bones=True,
 | 
			
		||||
        export_animations=True,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@reverted
 | 
			
		||||
def export():
 | 
			
		||||
    vole = bpy.data.objects["Vole"]
 | 
			
		||||
    volerig = bpy.data.objects["VoleRig"]
 | 
			
		||||
    collection = bpy.data.collections["export"]
 | 
			
		||||
    bpy.ops.object.mode_set(mode="OBJECT")
 | 
			
		||||
    bpy.context.view_layer.update()
 | 
			
		||||
    clear_selection(bpy.context)
 | 
			
		||||
    select_collection(bpy.context, collection)
 | 
			
		||||
    prepare_modifiers(vole, volerig)
 | 
			
		||||
    sort_uv_layers(vole.data)
 | 
			
		||||
    # write_vertex_id_to_uv(vole)
 | 
			
		||||
    gltf_export(EXPORT_PATH)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def vertex_id_mesh_info(helper):
 | 
			
		||||
    return helper.VertexIDMeshInfo(
 | 
			
		||||
        resource_path="res://vole.tscn",
 | 
			
		||||
        node_path="VoleRig/Skeleton3D/Vole",
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def write_vertex_id_to_uv(obj: Object):
 | 
			
		||||
    import bmesh
 | 
			
		||||
    mesh = obj.data
 | 
			
		||||
    mesh.uv_layers.active = mesh.uv_layers[1]
 | 
			
		||||
 | 
			
		||||
    bm = bmesh.new()
 | 
			
		||||
    bm.from_mesh(mesh)
 | 
			
		||||
    uv_layer = bm.loops.layers.uv.active
 | 
			
		||||
    for face in bm.faces:
 | 
			
		||||
        for loop in face.loops:
 | 
			
		||||
            loop[uv_layer].uv.x = loop.vert.index
 | 
			
		||||
    bm.to_mesh(obj.data)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@reverted
 | 
			
		||||
def export_animations():
 | 
			
		||||
    mesh = bpy.data.objects["Vole"]
 | 
			
		||||
    rig = bpy.data.objects["VoleRig"]
 | 
			
		||||
    bpy.ops.object.mode_set(mode="OBJECT")
 | 
			
		||||
    bpy.context.view_layer.update()
 | 
			
		||||
    clear_selection(bpy.context)
 | 
			
		||||
    rig.select_set(True)
 | 
			
		||||
    mesh.select_set(True)
 | 
			
		||||
    mesh.modifiers["Fur"].show_viewport = False
 | 
			
		||||
 | 
			
		||||
    rig.data.pose_position = "POSE"
 | 
			
		||||
    bpy.context.view_layer.update()
 | 
			
		||||
    bpy.ops.export_scene.gltf(
 | 
			
		||||
        filepath=EXPORT_ANIMATIONS_PATH,
 | 
			
		||||
        export_format="GLB",
 | 
			
		||||
        use_selection=True,
 | 
			
		||||
        export_apply=False,
 | 
			
		||||
        export_animation_mode="ACTIONS",
 | 
			
		||||
        export_anim_slide_to_zero=True,
 | 
			
		||||
        export_optimize_animation_size=False,
 | 
			
		||||
        export_optimize_animation_keep_anim_armature=False,
 | 
			
		||||
        export_def_bones=True,
 | 
			
		||||
    )
 | 
			
		||||
							
								
								
									
										18
									
								
								color.gdshaderinc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								color.gdshaderinc
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,18 @@
 | 
			
		|||
// https://godotshaders.com/shader/hsv-adjustment/
 | 
			
		||||
 | 
			
		||||
vec3 rgb2hsv(vec3 c) {
 | 
			
		||||
    vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
 | 
			
		||||
    vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
 | 
			
		||||
    vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));
 | 
			
		||||
 | 
			
		||||
    float d = q.x - min(q.w, q.y);
 | 
			
		||||
    float e = 1.0e-10;
 | 
			
		||||
    return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
vec3 hsv2rgb(vec3 c) {
 | 
			
		||||
    vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
 | 
			
		||||
    vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
 | 
			
		||||
    return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										1
									
								
								color.gdshaderinc.uid
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								color.gdshaderinc.uid
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1 @@
 | 
			
		|||
uid://dltay473a1mjr
 | 
			
		||||
							
								
								
									
										19
									
								
								fox.gd
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								fox.gd
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,19 @@
 | 
			
		|||
extends Node3D
 | 
			
		||||
 | 
			
		||||
@onready var playback: AnimationNodeStateMachinePlayback = %AnimationTree.get("parameters/playback")
 | 
			
		||||
@export var pouncing := false
 | 
			
		||||
 | 
			
		||||
func idle():
 | 
			
		||||
	playback.travel("Idle")
 | 
			
		||||
	
 | 
			
		||||
func walk():
 | 
			
		||||
	playback.travel("Walk")
 | 
			
		||||
	
 | 
			
		||||
func pounce():
 | 
			
		||||
	playback.travel("Pounce")
 | 
			
		||||
 | 
			
		||||
func is_busy():
 | 
			
		||||
	return pouncing
 | 
			
		||||
 | 
			
		||||
func root_motion():
 | 
			
		||||
	return %AnimationTree.get_root_motion_position()
 | 
			
		||||
							
								
								
									
										1
									
								
								fox.gd.uid
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								fox.gd.uid
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1 @@
 | 
			
		|||
uid://8683t0qihiwq
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								fox.glb
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								fox.glb
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										6230
									
								
								fox.glb.import
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										6230
									
								
								fox.glb.import
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										57
									
								
								fox.tscn
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								fox.tscn
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,57 @@
 | 
			
		|||
[gd_scene load_steps=13 format=3 uid="uid://dwm6v58p82b57"]
 | 
			
		||||
 | 
			
		||||
[ext_resource type="PackedScene" uid="uid://bhhevwh2jipt7" path="res://fox.glb" id="1_8x0u3"]
 | 
			
		||||
[ext_resource type="Script" uid="uid://8683t0qihiwq" path="res://fox.gd" id="2_kpck0"]
 | 
			
		||||
 | 
			
		||||
[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_8x0u3"]
 | 
			
		||||
animation = &"Idle"
 | 
			
		||||
 | 
			
		||||
[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_kpck0"]
 | 
			
		||||
animation = &"Pounce"
 | 
			
		||||
 | 
			
		||||
[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_k0xb2"]
 | 
			
		||||
animation = &"Walk"
 | 
			
		||||
 | 
			
		||||
[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_kpck0"]
 | 
			
		||||
advance_mode = 2
 | 
			
		||||
 | 
			
		||||
[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_rxxu0"]
 | 
			
		||||
xfade_time = 0.1
 | 
			
		||||
 | 
			
		||||
[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_wyljk"]
 | 
			
		||||
xfade_time = 0.1
 | 
			
		||||
 | 
			
		||||
[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_somxj"]
 | 
			
		||||
xfade_time = 0.1
 | 
			
		||||
 | 
			
		||||
[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_pcpmy"]
 | 
			
		||||
xfade_time = 0.1
 | 
			
		||||
 | 
			
		||||
[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_k0xb2"]
 | 
			
		||||
xfade_time = 0.1
 | 
			
		||||
switch_mode = 2
 | 
			
		||||
advance_mode = 2
 | 
			
		||||
 | 
			
		||||
[sub_resource type="AnimationNodeStateMachine" id="AnimationNodeStateMachine_k0xb2"]
 | 
			
		||||
states/Idle/node = SubResource("AnimationNodeAnimation_8x0u3")
 | 
			
		||||
states/Idle/position = Vector2(370, 127)
 | 
			
		||||
states/Pounce/node = SubResource("AnimationNodeAnimation_kpck0")
 | 
			
		||||
states/Pounce/position = Vector2(529, 197)
 | 
			
		||||
states/Walk/node = SubResource("AnimationNodeAnimation_k0xb2")
 | 
			
		||||
states/Walk/position = Vector2(514, 58)
 | 
			
		||||
transitions = ["Start", "Idle", SubResource("AnimationNodeStateMachineTransition_kpck0"), "Idle", "Walk", SubResource("AnimationNodeStateMachineTransition_rxxu0"), "Walk", "Pounce", SubResource("AnimationNodeStateMachineTransition_wyljk"), "Walk", "Idle", SubResource("AnimationNodeStateMachineTransition_somxj"), "Idle", "Pounce", SubResource("AnimationNodeStateMachineTransition_pcpmy"), "Pounce", "Idle", SubResource("AnimationNodeStateMachineTransition_k0xb2")]
 | 
			
		||||
 | 
			
		||||
[node name="fox" instance=ExtResource("1_8x0u3")]
 | 
			
		||||
script = ExtResource("2_kpck0")
 | 
			
		||||
 | 
			
		||||
[node name="AnimationPlayer" parent="." index="1"]
 | 
			
		||||
root_motion_track = NodePath("FoxRig/Skeleton3D:root")
 | 
			
		||||
root_motion_local = false
 | 
			
		||||
 | 
			
		||||
[node name="AnimationTree" type="AnimationTree" parent="." index="2"]
 | 
			
		||||
unique_name_in_owner = true
 | 
			
		||||
root_node = NodePath("%AnimationTree/..")
 | 
			
		||||
root_motion_track = NodePath("FoxRig/Skeleton3D:root")
 | 
			
		||||
root_motion_local = false
 | 
			
		||||
tree_root = SubResource("AnimationNodeStateMachine_k0xb2")
 | 
			
		||||
anim_player = NodePath("../AnimationPlayer")
 | 
			
		||||
							
								
								
									
										40
									
								
								main.tscn
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								main.tscn
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,40 @@
 | 
			
		|||
[gd_scene load_steps=6 format=3 uid="uid://cgpuhiledvecy"]
 | 
			
		||||
 | 
			
		||||
[ext_resource type="PackedScene" uid="uid://bmt76eb0ke5fw" path="res://player_spawn.tscn" id="1_ig7tw"]
 | 
			
		||||
 | 
			
		||||
[sub_resource type="Environment" id="Environment_ig7tw"]
 | 
			
		||||
 | 
			
		||||
[sub_resource type="BoxMesh" id="BoxMesh_7dm0k"]
 | 
			
		||||
size = Vector3(50, 0.5, 50)
 | 
			
		||||
 | 
			
		||||
[sub_resource type="ConvexPolygonShape3D" id="ConvexPolygonShape3D_ig7tw"]
 | 
			
		||||
points = PackedVector3Array(25.3919, 0.940476, 25.3919, -25.3919, -0.646825, -25.3919, -25.3919, 0.940476, -25.3919, 25.3919, -0.646825, -25.3919, -25.3919, -0.646825, 25.3919, -25.3919, 0.940476, 25.3919, 25.3919, 0.940476, -25.3919, 25.3919, -0.646825, 25.3919)
 | 
			
		||||
 | 
			
		||||
[sub_resource type="BoxMesh" id="BoxMesh_ig7tw"]
 | 
			
		||||
 | 
			
		||||
[node name="Main" type="Node3D"]
 | 
			
		||||
 | 
			
		||||
[node name="WorldEnvironment" type="WorldEnvironment" parent="."]
 | 
			
		||||
environment = SubResource("Environment_ig7tw")
 | 
			
		||||
 | 
			
		||||
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="WorldEnvironment"]
 | 
			
		||||
transform = Transform3D(1, 0, 0, 0, 0.473074, 0.881023, 0, -0.881023, 0.473074, 0, 0, 0)
 | 
			
		||||
 | 
			
		||||
[node name="MeshInstance3D" type="MeshInstance3D" parent="."]
 | 
			
		||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.694463, 0)
 | 
			
		||||
mesh = SubResource("BoxMesh_7dm0k")
 | 
			
		||||
 | 
			
		||||
[node name="PlayerSpawn" parent="MeshInstance3D" instance=ExtResource("1_ig7tw")]
 | 
			
		||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.494699, 8.50994, 0.238789)
 | 
			
		||||
 | 
			
		||||
[node name="StaticBody3D2" type="StaticBody3D" parent="MeshInstance3D"]
 | 
			
		||||
 | 
			
		||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="MeshInstance3D/StaticBody3D2"]
 | 
			
		||||
shape = SubResource("ConvexPolygonShape3D_ig7tw")
 | 
			
		||||
 | 
			
		||||
[node name="Camera3D" type="Camera3D" parent="."]
 | 
			
		||||
transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0, 31.348, 0)
 | 
			
		||||
fov = 50.0
 | 
			
		||||
 | 
			
		||||
[node name="MeshInstance3D2" type="MeshInstance3D" parent="."]
 | 
			
		||||
mesh = SubResource("BoxMesh_ig7tw")
 | 
			
		||||
							
								
								
									
										249
									
								
								noise.gdshaderinc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										249
									
								
								noise.gdshaderinc
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,249 @@
 | 
			
		|||
/* https://www.shadertoy.com/view/XsX3zB
 | 
			
		||||
 *
 | 
			
		||||
 * The MIT License
 | 
			
		||||
 * Copyright © 2013 Nikita Miropolskiy
 | 
			
		||||
 *
 | 
			
		||||
 * ( license has been changed from CCA-NC-SA 3.0 to MIT
 | 
			
		||||
 *
 | 
			
		||||
 *   but thanks for attributing your source code when deriving from this sample
 | 
			
		||||
 *   with a following link: https://www.shadertoy.com/view/XsX3zB )
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/* discontinuous pseudorandom uniformly distributed in [-0.5, +0.5]^3 */
 | 
			
		||||
vec3 random3(vec3 c) {
 | 
			
		||||
	float j = 4096.0*sin(dot(c,vec3(17.0, 59.4, 15.0)));
 | 
			
		||||
	vec3 r;
 | 
			
		||||
	r.z = fract(512.0*j);
 | 
			
		||||
	j *= .125;
 | 
			
		||||
	r.x = fract(512.0*j);
 | 
			
		||||
	j *= .125;
 | 
			
		||||
	r.y = fract(512.0*j);
 | 
			
		||||
	return r-0.5;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
vec3 hash33(vec3 p){
 | 
			
		||||
    float n = sin(dot(p, vec3(7, 157, 113)));
 | 
			
		||||
    return fract(vec3(2097152, 262144, 32768)*n);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
float worley(vec3 uv, float local_scale) {
 | 
			
		||||
	vec3 prod = uv * local_scale;
 | 
			
		||||
	vec3 index_uv = floor(prod);
 | 
			
		||||
	vec3 fract_uv = fract(prod);
 | 
			
		||||
 | 
			
		||||
	float minimum_dist = 0.6;
 | 
			
		||||
 | 
			
		||||
	for (int y = -1; y <= 1; y++) {
 | 
			
		||||
		for (int x = -1; x <= 1; x++) {
 | 
			
		||||
			for (int z = -1; z <= 1; z++) {
 | 
			
		||||
				vec3 neighbor = vec3(float(x),float(y), float(z));
 | 
			
		||||
				vec3 point = hash33(index_uv + neighbor);
 | 
			
		||||
 | 
			
		||||
				vec3 diff = neighbor + point - fract_uv;
 | 
			
		||||
				float dist = length(diff);
 | 
			
		||||
				minimum_dist = min(minimum_dist, dist);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return minimum_dist;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FROM: https://www.shadertoy.com/view/XtBGDG
 | 
			
		||||
//http://webstaff.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf
 | 
			
		||||
//simplex pretty much 99% copied from there
 | 
			
		||||
//adjusted by getting "completely random" gradients instead of randoming from 12 preset ones
 | 
			
		||||
//and normalizing the gradient vector
 | 
			
		||||
float noise3D(vec3 p)
 | 
			
		||||
{
 | 
			
		||||
	return fract(sin(dot(p ,vec3(12.9898,78.233,128.852))) * 43758.5453)*2.0-1.0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
float simplex3D(vec3 p)
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
	float f3 = 1.0/3.0;
 | 
			
		||||
	float s = (p.x+p.y+p.z)*f3;
 | 
			
		||||
	int i = int(floor(p.x+s));
 | 
			
		||||
	int j = int(floor(p.y+s));
 | 
			
		||||
	int k = int(floor(p.z+s));
 | 
			
		||||
 | 
			
		||||
	float g3 = 1.0/6.0;
 | 
			
		||||
	float t = float((i+j+k))*g3;
 | 
			
		||||
	float x0 = float(i)-t;
 | 
			
		||||
	float y0 = float(j)-t;
 | 
			
		||||
	float z0 = float(k)-t;
 | 
			
		||||
	x0 = p.x-x0;
 | 
			
		||||
	y0 = p.y-y0;
 | 
			
		||||
	z0 = p.z-z0;
 | 
			
		||||
 | 
			
		||||
	int i1,j1,k1;
 | 
			
		||||
	int i2,j2,k2;
 | 
			
		||||
 | 
			
		||||
	if(x0>=y0)
 | 
			
		||||
	{
 | 
			
		||||
		if(y0>=z0){ i1=1; j1=0; k1=0; i2=1; j2=1; k2=0; } // X Y Z order
 | 
			
		||||
		else if(x0>=z0){ i1=1; j1=0; k1=0; i2=1; j2=0; k2=1; } // X Z Y order
 | 
			
		||||
		else { i1=0; j1=0; k1=1; i2=1; j2=0; k2=1; }  // Z X Z order
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
	{
 | 
			
		||||
		if(y0<z0) { i1=0; j1=0; k1=1; i2=0; j2=1; k2=1; } // Z Y X order
 | 
			
		||||
		else if(x0<z0) { i1=0; j1=1; k1=0; i2=0; j2=1; k2=1; } // Y Z X order
 | 
			
		||||
		else { i1=0; j1=1; k1=0; i2=1; j2=1; k2=0; } // Y X Z order
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	float x1 = x0 - float(i1) + g3;
 | 
			
		||||
	float y1 = y0 - float(j1) + g3;
 | 
			
		||||
	float z1 = z0 - float(k1) + g3;
 | 
			
		||||
	float x2 = x0 - float(i2) + 2.0*g3;
 | 
			
		||||
	float y2 = y0 - float(j2) + 2.0*g3;
 | 
			
		||||
	float z2 = z0 - float(k2) + 2.0*g3;
 | 
			
		||||
	float x3 = x0 - 1.0 + 3.0*g3;
 | 
			
		||||
	float y3 = y0 - 1.0 + 3.0*g3;
 | 
			
		||||
	float z3 = z0 - 1.0 + 3.0*g3;
 | 
			
		||||
 | 
			
		||||
	vec3 ijk0 = vec3(float(i),float(j),float(k));
 | 
			
		||||
	vec3 ijk1 = vec3(float(i)+float(i1),float(j)+float(j1),float(k)+float(k1));
 | 
			
		||||
	vec3 ijk2 = vec3(float(i)+float(i2),float(j)+float(j2),float(k)+float(k2));
 | 
			
		||||
	vec3 ijk3 = vec3(float(i)+1.,float(j)+1.,float(k)+1.);
 | 
			
		||||
 | 
			
		||||
	vec3 gr0 = normalize(vec3(noise3D(ijk0),noise3D(ijk0*2.01),noise3D(ijk0*2.02)));
 | 
			
		||||
	vec3 gr1 = normalize(vec3(noise3D(ijk1),noise3D(ijk1*2.01),noise3D(ijk1*2.02)));
 | 
			
		||||
	vec3 gr2 = normalize(vec3(noise3D(ijk2),noise3D(ijk2*2.01),noise3D(ijk2*2.02)));
 | 
			
		||||
	vec3 gr3 = normalize(vec3(noise3D(ijk3),noise3D(ijk3*2.01),noise3D(ijk3*2.02)));
 | 
			
		||||
 | 
			
		||||
	float n0 = 0.0;
 | 
			
		||||
	float n1 = 0.0;
 | 
			
		||||
	float n2 = 0.0;
 | 
			
		||||
	float n3 = 0.0;
 | 
			
		||||
 | 
			
		||||
	float t0 = 0.5 - x0*x0 - y0*y0 - z0*z0;
 | 
			
		||||
	if(t0>=0.0)
 | 
			
		||||
	{
 | 
			
		||||
		t0*=t0;
 | 
			
		||||
		n0 = t0 * t0 * dot(gr0, vec3(x0, y0, z0));
 | 
			
		||||
	}
 | 
			
		||||
	float t1 = 0.5 - x1*x1 - y1*y1 - z1*z1;
 | 
			
		||||
	if(t1>=0.0)
 | 
			
		||||
	{
 | 
			
		||||
		t1*=t1;
 | 
			
		||||
		n1 = t1 * t1 * dot(gr1, vec3(x1, y1, z1));
 | 
			
		||||
	}
 | 
			
		||||
	float t2 = 0.5 - x2*x2 - y2*y2 - z2*z2;
 | 
			
		||||
	if(t2>=0.0)
 | 
			
		||||
	{
 | 
			
		||||
		t2 *= t2;
 | 
			
		||||
		n2 = t2 * t2 * dot(gr2, vec3(x2, y2, z2));
 | 
			
		||||
	}
 | 
			
		||||
	float t3 = 0.5 - x3*x3 - y3*y3 - z3*z3;
 | 
			
		||||
	if(t3>=0.0)
 | 
			
		||||
	{
 | 
			
		||||
		t3 *= t3;
 | 
			
		||||
		n3 = t3 * t3 * dot(gr3, vec3(x3, y3, z3));
 | 
			
		||||
	}
 | 
			
		||||
	return 96.0*(n0+n1+n2+n3);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
float fbm(vec3 p)
 | 
			
		||||
{
 | 
			
		||||
	float f;
 | 
			
		||||
    f  = 0.50000*simplex3D( p ); p = p*2.01;
 | 
			
		||||
    f += 0.25000*simplex3D( p ); p = p*2.02; //from iq
 | 
			
		||||
    f += 0.12500*simplex3D( p ); p = p*2.03;
 | 
			
		||||
    f += 0.06250*simplex3D( p ); p = p*2.04;
 | 
			
		||||
    f += 0.03125*simplex3D( p );
 | 
			
		||||
	return f;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
float noise(vec3 noise_uv, float noise_scale) {
 | 
			
		||||
	return fbm(noise_uv * noise_scale);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* https://www.shadertoy.com/view/XsX3zB
 | 
			
		||||
 *
 | 
			
		||||
 * The MIT License
 | 
			
		||||
 * Copyright © 2013 Nikita Miropolskiy
 | 
			
		||||
 *
 | 
			
		||||
 * ( license has been changed from CCA-NC-SA 3.0 to MIT
 | 
			
		||||
 *
 | 
			
		||||
 *   but thanks for attributing your source code when deriving from this sample
 | 
			
		||||
 *   with a following link: https://www.shadertoy.com/view/XsX3zB )
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/* discontinuous pseudorandom uniformly distributed in [-0.5, +0.5]^3 */
 | 
			
		||||
vec3 shell_random3(vec3 c) {
 | 
			
		||||
	float j = 4096.0*sin(dot(c,vec3(17.0, 59.4, 15.0)));
 | 
			
		||||
	vec3 r;
 | 
			
		||||
	r.z = fract(512.0*j);
 | 
			
		||||
	j *= .125;
 | 
			
		||||
	r.x = fract(512.0*j);
 | 
			
		||||
	j *= .125;
 | 
			
		||||
	r.y = fract(512.0*j);
 | 
			
		||||
	return r-0.5;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
vec3 shell_hash33(vec3 p){
 | 
			
		||||
    float n = sin(dot(p, vec3(7, 157, 113)));
 | 
			
		||||
    return fract(vec3(2097152, 262144, 32768)*n);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
float shell_worley(vec3 uv, float local_scale) {
 | 
			
		||||
	vec3 prod = uv * local_scale;
 | 
			
		||||
	vec3 index_uv = floor(prod);
 | 
			
		||||
	vec3 fract_uv = fract(prod);
 | 
			
		||||
 | 
			
		||||
	float minimum_dist = 1.0;
 | 
			
		||||
 | 
			
		||||
	for (int y = -1; y <= 1; y++) {
 | 
			
		||||
		for (int x = -1; x <= 1; x++) {
 | 
			
		||||
			for (int z = -1; z <= 1; z++) {
 | 
			
		||||
				vec3 neighbor = vec3(float(x),float(y), float(z));
 | 
			
		||||
				vec3 point = shell_random3(index_uv + neighbor);
 | 
			
		||||
 | 
			
		||||
				vec3 diff = neighbor + point - fract_uv;
 | 
			
		||||
				float dist = length(diff);
 | 
			
		||||
				minimum_dist = min(minimum_dist, dist);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return minimum_dist;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
float shell_worley_color(vec3 uv, float local_scale, out vec3 vcolor) {
 | 
			
		||||
	vec3 prod = uv * local_scale;
 | 
			
		||||
	vec3 index_uv = floor(prod);
 | 
			
		||||
	vec3 fract_uv = fract(prod);
 | 
			
		||||
 | 
			
		||||
	float minimum_dist = 1.0;
 | 
			
		||||
	// This is magic, fixed some weird noisy glitches
 | 
			
		||||
	vcolor = vec3(-1.0);
 | 
			
		||||
 | 
			
		||||
	for (int y = -1; y <= 1; y++) {
 | 
			
		||||
		for (int x = -1; x <= 1; x++) {
 | 
			
		||||
			for (int z = -1; z <= 1; z++) {
 | 
			
		||||
				vec3 neighbor = vec3(float(x),float(y), float(z));
 | 
			
		||||
				vec3 point = shell_random3(index_uv + neighbor);
 | 
			
		||||
 | 
			
		||||
				vec3 diff = neighbor + point - fract_uv;
 | 
			
		||||
				float dist = length(diff);
 | 
			
		||||
				if (dist < minimum_dist) {
 | 
			
		||||
					minimum_dist = dist;
 | 
			
		||||
					vcolor = point;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return minimum_dist;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										1
									
								
								noise.gdshaderinc.uid
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								noise.gdshaderinc.uid
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1 @@
 | 
			
		|||
uid://dlwojkhehvmio
 | 
			
		||||
							
								
								
									
										33
									
								
								player.gd
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								player.gd
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,33 @@
 | 
			
		|||
extends CharacterBody3D
 | 
			
		||||
class_name Player
 | 
			
		||||
 | 
			
		||||
var input: PlayerInputData
 | 
			
		||||
 | 
			
		||||
const SPEED = 5.0
 | 
			
		||||
const JUMP_VELOCITY = 4.5
 | 
			
		||||
 | 
			
		||||
@onready var model = %fox
 | 
			
		||||
 | 
			
		||||
func _physics_process(delta: float) -> void:
 | 
			
		||||
	if not model.is_busy():
 | 
			
		||||
		var direction := input.walk_dir
 | 
			
		||||
		if direction:
 | 
			
		||||
			velocity.x = direction.x * SPEED
 | 
			
		||||
			velocity.z = direction.z * SPEED
 | 
			
		||||
		else:
 | 
			
		||||
			velocity.x = move_toward(velocity.x, 0, SPEED)
 | 
			
		||||
			velocity.z = move_toward(velocity.z, 0, SPEED)
 | 
			
		||||
		if direction.length() > 0.01:
 | 
			
		||||
			var target := global_transform.looking_at(global_position + input.walk_dir, Vector3(0.0, 1.0, 0.0)).basis.get_euler().y
 | 
			
		||||
			var current_euler := global_basis.get_euler()
 | 
			
		||||
			current_euler.y = lerp_angle(current_euler.y, target, delta * 20.0)
 | 
			
		||||
			global_basis = Basis.from_euler(current_euler)
 | 
			
		||||
			
 | 
			
		||||
			model.walk()
 | 
			
		||||
		else:
 | 
			
		||||
			model.idle()
 | 
			
		||||
		if input.jumping:
 | 
			
		||||
			model.pounce()
 | 
			
		||||
		
 | 
			
		||||
	position += model.global_transform.basis * model.root_motion()
 | 
			
		||||
	move_and_slide()
 | 
			
		||||
							
								
								
									
										1
									
								
								player.gd.uid
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								player.gd.uid
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1 @@
 | 
			
		|||
uid://ccyxlwjirmyt0
 | 
			
		||||
							
								
								
									
										19
									
								
								player.tscn
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								player.tscn
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,19 @@
 | 
			
		|||
[gd_scene load_steps=4 format=3 uid="uid://54sf77urgp3h"]
 | 
			
		||||
 | 
			
		||||
[ext_resource type="Script" uid="uid://ccyxlwjirmyt0" path="res://player.gd" id="1_4flbx"]
 | 
			
		||||
[ext_resource type="PackedScene" uid="uid://dwm6v58p82b57" path="res://fox.tscn" id="2_onrkg"]
 | 
			
		||||
 | 
			
		||||
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_i3pqv"]
 | 
			
		||||
radius = 1.96912
 | 
			
		||||
height = 5.13133
 | 
			
		||||
 | 
			
		||||
[node name="Player" type="CharacterBody3D"]
 | 
			
		||||
script = ExtResource("1_4flbx")
 | 
			
		||||
 | 
			
		||||
[node name="FoxShape" type="CollisionShape3D" parent="."]
 | 
			
		||||
transform = Transform3D(1, 0, 0, 0, -4.37114e-08, -1, 0, 1, -4.37114e-08, 0, 1.9351, 0.152762)
 | 
			
		||||
shape = SubResource("CapsuleShape3D_i3pqv")
 | 
			
		||||
 | 
			
		||||
[node name="fox" parent="." instance=ExtResource("2_onrkg")]
 | 
			
		||||
unique_name_in_owner = true
 | 
			
		||||
transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 0, 0, 0)
 | 
			
		||||
							
								
								
									
										10
									
								
								player_spawn.gd
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								player_spawn.gd
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,10 @@
 | 
			
		|||
extends Marker3D
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
func _ready() -> void:
 | 
			
		||||
	PlayerInput.player_join.connect(player_join)
 | 
			
		||||
	
 | 
			
		||||
func player_join(data: PlayerInputData):
 | 
			
		||||
	var player: Player = preload("res://player.tscn").instantiate()
 | 
			
		||||
	player.input = data
 | 
			
		||||
	add_child(player)
 | 
			
		||||
							
								
								
									
										1
									
								
								player_spawn.gd.uid
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								player_spawn.gd.uid
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1 @@
 | 
			
		|||
uid://ddce7di45eosf
 | 
			
		||||
							
								
								
									
										6
									
								
								player_spawn.tscn
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								player_spawn.tscn
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,6 @@
 | 
			
		|||
[gd_scene load_steps=2 format=3 uid="uid://bmt76eb0ke5fw"]
 | 
			
		||||
 | 
			
		||||
[ext_resource type="Script" uid="uid://ddce7di45eosf" path="res://player_spawn.gd" id="1_ckl4a"]
 | 
			
		||||
 | 
			
		||||
[node name="PlayerSpawn" type="Marker3D"]
 | 
			
		||||
script = ExtResource("1_ckl4a")
 | 
			
		||||
							
								
								
									
										91
									
								
								project.godot
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								project.godot
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,91 @@
 | 
			
		|||
; 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]
 | 
			
		||||
 | 
			
		||||
run/main_scene="uid://cgpuhiledvecy"
 | 
			
		||||
config/features=PackedStringArray("4.5")
 | 
			
		||||
 | 
			
		||||
[autoload]
 | 
			
		||||
 | 
			
		||||
PlayerInput="*res://PlayerInput.gd"
 | 
			
		||||
 | 
			
		||||
[input]
 | 
			
		||||
 | 
			
		||||
move_up={
 | 
			
		||||
"deadzone": 0.01,
 | 
			
		||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":87,"key_label":0,"unicode":119,"location":0,"echo":false,"script":null)
 | 
			
		||||
, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":1,"axis_value":-1.0,"script":null)
 | 
			
		||||
]
 | 
			
		||||
}
 | 
			
		||||
move_right={
 | 
			
		||||
"deadzone": 0.01,
 | 
			
		||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":68,"key_label":0,"unicode":100,"location":0,"echo":false,"script":null)
 | 
			
		||||
, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":0,"axis_value":1.0,"script":null)
 | 
			
		||||
]
 | 
			
		||||
}
 | 
			
		||||
move_down={
 | 
			
		||||
"deadzone": 0.01,
 | 
			
		||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":83,"key_label":0,"unicode":115,"location":0,"echo":false,"script":null)
 | 
			
		||||
, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":1,"axis_value":1.0,"script":null)
 | 
			
		||||
]
 | 
			
		||||
}
 | 
			
		||||
move_left={
 | 
			
		||||
"deadzone": 0.01,
 | 
			
		||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":65,"key_label":0,"unicode":97,"location":0,"echo":false,"script":null)
 | 
			
		||||
, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":0,"axis_value":-1.0,"script":null)
 | 
			
		||||
]
 | 
			
		||||
}
 | 
			
		||||
jump={
 | 
			
		||||
"deadzone": 0.2,
 | 
			
		||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":32,"key_label":0,"unicode":32,"location":0,"echo":false,"script":null)
 | 
			
		||||
, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":7,"pressure":0.0,"pressed":true,"script":null)
 | 
			
		||||
, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":0,"pressure":0.0,"pressed":true,"script":null)
 | 
			
		||||
, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":1,"pressure":0.0,"pressed":true,"script":null)
 | 
			
		||||
]
 | 
			
		||||
}
 | 
			
		||||
dig={
 | 
			
		||||
"deadzone": 0.2,
 | 
			
		||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":69,"key_label":0,"unicode":101,"location":0,"echo":false,"script":null)
 | 
			
		||||
, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":6,"pressure":0.0,"pressed":true,"script":null)
 | 
			
		||||
, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":3,"pressure":0.0,"pressed":true,"script":null)
 | 
			
		||||
, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":4,"pressure":0.0,"pressed":true,"script":null)
 | 
			
		||||
]
 | 
			
		||||
}
 | 
			
		||||
move_up_alt={
 | 
			
		||||
"deadzone": 0.2,
 | 
			
		||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194320,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
 | 
			
		||||
]
 | 
			
		||||
}
 | 
			
		||||
move_right_alt={
 | 
			
		||||
"deadzone": 0.2,
 | 
			
		||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194321,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
 | 
			
		||||
]
 | 
			
		||||
}
 | 
			
		||||
move_down_alt={
 | 
			
		||||
"deadzone": 0.2,
 | 
			
		||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194322,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
 | 
			
		||||
]
 | 
			
		||||
}
 | 
			
		||||
move_left_alt={
 | 
			
		||||
"deadzone": 0.2,
 | 
			
		||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194319,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
 | 
			
		||||
]
 | 
			
		||||
}
 | 
			
		||||
jump_alt={
 | 
			
		||||
"deadzone": 0.2,
 | 
			
		||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194309,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
 | 
			
		||||
]
 | 
			
		||||
}
 | 
			
		||||
dig_alt={
 | 
			
		||||
"deadzone": 0.2,
 | 
			
		||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194325,"key_label":0,"unicode":0,"location":2,"echo":false,"script":null)
 | 
			
		||||
]
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										68
									
								
								shell_fur.gdshader
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								shell_fur.gdshader
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,68 @@
 | 
			
		|||
shader_type spatial;
 | 
			
		||||
// Can't figure out how to make back faces look good
 | 
			
		||||
// Flipping normals doesn't change their shading
 | 
			
		||||
render_mode cull_back, depth_prepass_alpha;
 | 
			
		||||
 | 
			
		||||
//#include "res://addons/vertexanimations/va_default.gdshaderinc"
 | 
			
		||||
#include "res://color.gdshaderinc"
 | 
			
		||||
#include "res://noise.gdshaderinc"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
varying float fur_length;
 | 
			
		||||
varying float is_shell;
 | 
			
		||||
varying float shell_id_factor;
 | 
			
		||||
varying vec3 ungroomed_3d_uv;
 | 
			
		||||
varying vec3 groomed_normals;
 | 
			
		||||
 | 
			
		||||
uniform float scale = 700.0;
 | 
			
		||||
uniform float clump_scale = 90.1;
 | 
			
		||||
uniform float grooming_factor = 0.008;
 | 
			
		||||
uniform sampler2D hair_curve_shape : repeat_disable;
 | 
			
		||||
uniform sampler2D hair_stem_shape : repeat_disable;
 | 
			
		||||
uniform sampler2D hair_clump_radius : repeat_disable;
 | 
			
		||||
uniform sampler2D hair_clump_height : repeat_disable;
 | 
			
		||||
uniform sampler2D hair_base_color : source_color;
 | 
			
		||||
uniform sampler2D hair_depth_factor : repeat_disable;
 | 
			
		||||
 | 
			
		||||
uniform sampler2D normals : hint_normal;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void vertex() {
 | 
			
		||||
	fur_length = CUSTOM0.w;
 | 
			
		||||
	shell_id_factor = CUSTOM2.r;
 | 
			
		||||
	ungroomed_3d_uv = CUSTOM0.xzy;
 | 
			
		||||
	ungroomed_3d_uv.z *= -1.0;
 | 
			
		||||
	groomed_normals = CUSTOM1.xzy;
 | 
			
		||||
	groomed_normals.z *= -1.0;
 | 
			
		||||
	is_shell = CUSTOM2.y;
 | 
			
		||||
 | 
			
		||||
	//VERTEX = vertex_animation_default(VERTEX_ID, VERTEX);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void fragment() {
 | 
			
		||||
	if (is_shell <= 0.0001) {
 | 
			
		||||
		discard;
 | 
			
		||||
	}
 | 
			
		||||
	float curve_shape = texture(hair_curve_shape, vec2(1.0-shell_id_factor)).r;
 | 
			
		||||
	float groom_weight = (((1.0-fur_length)*grooming_factor) + grooming_factor) * curve_shape;
 | 
			
		||||
	vec3 groomed_3d_uv = ungroomed_3d_uv + (groomed_normals * groom_weight);
 | 
			
		||||
 | 
			
		||||
	float clump_radius = 1.0-texture(hair_clump_radius, vec2(shell_worley(groomed_3d_uv, clump_scale))).r;
 | 
			
		||||
	float clump_height = texture(hair_clump_height, vec2(clump_radius)).r;
 | 
			
		||||
	float clump_shell_id = shell_id_factor / clump_height;
 | 
			
		||||
	float stem_shape = texture(hair_stem_shape, vec2(clump_shell_id)).r;
 | 
			
		||||
	float c = shell_worley(groomed_3d_uv, scale);
 | 
			
		||||
	if (c > stem_shape) {
 | 
			
		||||
		discard;
 | 
			
		||||
	}
 | 
			
		||||
	vec3 sample_hair_base_color = COLOR.rgb;
 | 
			
		||||
	vec3 base = rgb2hsv(sample_hair_base_color);
 | 
			
		||||
	float v = base.z * texture(hair_depth_factor, vec2(shell_id_factor)).r;
 | 
			
		||||
	ALBEDO = hsv2rgb(vec3(base.x, base.y, v));
 | 
			
		||||
	ROUGHNESS = 1.0;
 | 
			
		||||
	SPECULAR = 0.0;
 | 
			
		||||
	NORMAL_MAP = texture(normals, UV).xyz;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										1
									
								
								shell_fur.gdshader.uid
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								shell_fur.gdshader.uid
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1 @@
 | 
			
		|||
uid://bgf62oe1pyffc
 | 
			
		||||
							
								
								
									
										19
									
								
								vole.gd
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								vole.gd
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,19 @@
 | 
			
		|||
extends Node3D
 | 
			
		||||
 | 
			
		||||
@onready var playback: AnimationNodeStateMachinePlayback = %AnimationTree.get("parameters/playback")
 | 
			
		||||
@export var pouncing := false
 | 
			
		||||
 | 
			
		||||
func idle():
 | 
			
		||||
	playback.travel("Idle")
 | 
			
		||||
	
 | 
			
		||||
func walk():
 | 
			
		||||
	playback.travel("Walk")
 | 
			
		||||
	
 | 
			
		||||
func pounce():
 | 
			
		||||
	pass
 | 
			
		||||
 | 
			
		||||
func is_busy():
 | 
			
		||||
	return pouncing
 | 
			
		||||
 | 
			
		||||
func root_motion():
 | 
			
		||||
	return %AnimationTree.get_root_motion_position()
 | 
			
		||||
							
								
								
									
										1
									
								
								vole.gd.uid
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								vole.gd.uid
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1 @@
 | 
			
		|||
uid://dxdgep3hm0i8d
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								vole.glb
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								vole.glb
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										2103
									
								
								vole.glb.import
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										2103
									
								
								vole.glb.import
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										20
									
								
								vole.tscn
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								vole.tscn
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,20 @@
 | 
			
		|||
[gd_scene load_steps=5 format=3 uid="uid://dlpr8asp0m21a"]
 | 
			
		||||
 | 
			
		||||
[ext_resource type="PackedScene" uid="uid://cg7vjpjmg60ti" path="res://vole.glb" id="1_fw737"]
 | 
			
		||||
[ext_resource type="Script" uid="uid://dxdgep3hm0i8d" path="res://vole.gd" id="2_6xove"]
 | 
			
		||||
 | 
			
		||||
[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_fw737"]
 | 
			
		||||
animation = &"IDLE"
 | 
			
		||||
 | 
			
		||||
[sub_resource type="AnimationNodeStateMachine" id="AnimationNodeStateMachine_fw737"]
 | 
			
		||||
states/IDLE/node = SubResource("AnimationNodeAnimation_fw737")
 | 
			
		||||
states/IDLE/position = Vector2(411, 153)
 | 
			
		||||
 | 
			
		||||
[node name="vole" instance=ExtResource("1_fw737")]
 | 
			
		||||
script = ExtResource("2_6xove")
 | 
			
		||||
 | 
			
		||||
[node name="AnimationTree" type="AnimationTree" parent="." index="2"]
 | 
			
		||||
root_motion_track = NodePath("VoleRig/Skeleton3D:root")
 | 
			
		||||
root_motion_local = false
 | 
			
		||||
tree_root = SubResource("AnimationNodeStateMachine_fw737")
 | 
			
		||||
anim_player = NodePath("../AnimationPlayer")
 | 
			
		||||
		Loading…
	
	Add table
		
		Reference in a new issue