Эх сурвалжийг харах

[godot] Support per-slot materials via SpineSlotNode.

badlogic 3 жил өмнө
parent
commit
4906d71856

+ 2 - 2
spine-godot/example/examples/07-slot-node/slot-node.gd

@@ -1,8 +1,8 @@
 extends Node2D
 
 onready var spineboy: SpineSprite = $Spineboy
-onready var raptor: SpineSprite = $Spineboy/SlotNodeGun/Raptor
-onready var tiny_spineboy: SpineSprite = $Spineboy/SlotNodeFrontFist/TinySpineboy
+onready var raptor: SpineSprite = $Spineboy/GunSlot/Raptor
+onready var tiny_spineboy: SpineSprite = $Spineboy/FrontFistSlot/TinySpineboy
 
 func _ready():	
 	var entry = spineboy.get_animation_state().set_animation("run", true, 0)

+ 19 - 7
spine-godot/example/examples/07-slot-node/slot-node.tscn

@@ -1,35 +1,47 @@
-[gd_scene load_steps=4 format=2]
+[gd_scene load_steps=5 format=2]
 
 [ext_resource path="res://examples/07-slot-node/slot-node.gd" type="Script" id=1]
 [ext_resource path="res://assets/spineboy/spinebody-data-res.tres" type="SpineSkeletonDataResource" id=2]
 [ext_resource path="res://assets/raptor/raprot-data.tres" type="SpineSkeletonDataResource" id=3]
+[ext_resource path="res://icon.png" type="Texture" id=4]
 
 [node name="Node2D" type="Node2D"]
 script = ExtResource( 1 )
 
 [node name="Spineboy" type="SpineSprite" parent="."]
-position = Vector2( 506, 480 )
+position = Vector2( 474, 506 )
 scale = Vector2( 0.560712, 0.560712 )
 skeleton_data_res = ExtResource( 2 )
 
-[node name="SlotNodeGun" type="SpineSlotNode" parent="Spineboy"]
-position = Vector2( 40.8752, -276.036 )
+[node name="GunSlot" type="SpineSlotNode" parent="Spineboy"]
+position = Vector2( 40.8753, -276.036 )
 rotation = 0.837234
 scale = Vector2( 1, 1 )
 slot_name = "gun"
 
-[node name="Raptor" type="SpineSprite" parent="Spineboy/SlotNodeGun"]
+[node name="Raptor" type="SpineSprite" parent="Spineboy/GunSlot"]
 position = Vector2( 84.6909, -67.9174 )
 scale = Vector2( 0.193472, 0.193472 )
 skeleton_data_res = ExtResource( 3 )
 
-[node name="SlotNodeFrontFist" type="SpineSlotNode" parent="Spineboy"]
+[node name="EyeSlot" type="SpineSlotNode" parent="Spineboy"]
+position = Vector2( -23.4598, -402.301 )
+rotation = -1.71793
+scale = Vector2( 1, 1 )
+slot_name = "eye"
+
+[node name="Sprite" type="Sprite" parent="Spineboy/EyeSlot"]
+position = Vector2( 84.4734, 43.4469 )
+rotation = 1.66344
+texture = ExtResource( 4 )
+
+[node name="FrontFistSlot" type="SpineSlotNode" parent="Spineboy"]
 position = Vector2( -29.0298, -241.577 )
 rotation = 0.995187
 scale = Vector2( 1, 1 )
 slot_name = "front-fist"
 
-[node name="TinySpineboy" type="SpineSprite" parent="Spineboy/SlotNodeFrontFist"]
+[node name="TinySpineboy" type="SpineSprite" parent="Spineboy/FrontFistSlot"]
 position = Vector2( -2.64624, -10.8111 )
 scale = Vector2( 0.193389, 0.193389 )
 skeleton_data_res = ExtResource( 2 )

+ 27 - 27
spine-godot/example/examples/08-animation-player/animation-player.tscn

@@ -36,7 +36,6 @@ tracks/1/keys = {
 [sub_resource type="Animation" id=85]
 resource_name = "aim"
 length = 0.5
-loop = true
 tracks/0/type = "value"
 tracks/0/path = NodePath(".:animation_name")
 tracks/0/interp = 1
@@ -65,6 +64,7 @@ tracks/1/keys = {
 [sub_resource type="Animation" id=86]
 resource_name = "aim_looped"
 length = 0.5
+loop = true
 tracks/0/type = "value"
 tracks/0/path = NodePath(".:animation_name")
 tracks/0/interp = 1
@@ -93,7 +93,6 @@ tracks/1/keys = {
 [sub_resource type="Animation" id=87]
 resource_name = "death"
 length = 4.9333
-loop = true
 tracks/0/type = "value"
 tracks/0/path = NodePath(".:animation_name")
 tracks/0/interp = 1
@@ -122,6 +121,7 @@ tracks/1/keys = {
 [sub_resource type="Animation" id=88]
 resource_name = "death_looped"
 length = 4.9333
+loop = true
 tracks/0/type = "value"
 tracks/0/path = NodePath(".:animation_name")
 tracks/0/interp = 1
@@ -149,7 +149,6 @@ tracks/1/keys = {
 
 [sub_resource type="Animation" id=89]
 resource_name = "hoverboard"
-loop = true
 tracks/0/type = "value"
 tracks/0/path = NodePath(".:animation_name")
 tracks/0/interp = 1
@@ -177,6 +176,7 @@ tracks/1/keys = {
 
 [sub_resource type="Animation" id=90]
 resource_name = "hoverboard_looped"
+loop = true
 tracks/0/type = "value"
 tracks/0/path = NodePath(".:animation_name")
 tracks/0/interp = 1
@@ -205,7 +205,6 @@ tracks/1/keys = {
 [sub_resource type="Animation" id=91]
 resource_name = "idle"
 length = 1.6667
-loop = true
 tracks/0/type = "value"
 tracks/0/path = NodePath(".:animation_name")
 tracks/0/interp = 1
@@ -234,7 +233,6 @@ tracks/1/keys = {
 [sub_resource type="Animation" id=92]
 resource_name = "idle-turn"
 length = 0.2667
-loop = true
 tracks/0/type = "value"
 tracks/0/path = NodePath(".:animation_name")
 tracks/0/interp = 1
@@ -263,6 +261,7 @@ tracks/1/keys = {
 [sub_resource type="Animation" id=93]
 resource_name = "idle-turn_looped"
 length = 0.2667
+loop = true
 tracks/0/type = "value"
 tracks/0/path = NodePath(".:animation_name")
 tracks/0/interp = 1
@@ -291,6 +290,7 @@ tracks/1/keys = {
 [sub_resource type="Animation" id=94]
 resource_name = "idle_looped"
 length = 1.6667
+loop = true
 tracks/0/type = "value"
 tracks/0/path = NodePath(".:animation_name")
 tracks/0/interp = 1
@@ -319,7 +319,6 @@ tracks/1/keys = {
 [sub_resource type="Animation" id=95]
 resource_name = "jump"
 length = 1.3333
-loop = true
 tracks/0/type = "value"
 tracks/0/path = NodePath(".:animation_name")
 tracks/0/interp = 1
@@ -348,6 +347,7 @@ tracks/1/keys = {
 [sub_resource type="Animation" id=96]
 resource_name = "jump_looped"
 length = 1.3333
+loop = true
 tracks/0/type = "value"
 tracks/0/path = NodePath(".:animation_name")
 tracks/0/interp = 1
@@ -376,7 +376,6 @@ tracks/1/keys = {
 [sub_resource type="Animation" id=97]
 resource_name = "portal"
 length = 3.1667
-loop = true
 tracks/0/type = "value"
 tracks/0/path = NodePath(".:animation_name")
 tracks/0/interp = 1
@@ -405,6 +404,7 @@ tracks/1/keys = {
 [sub_resource type="Animation" id=98]
 resource_name = "portal_looped"
 length = 3.1667
+loop = true
 tracks/0/type = "value"
 tracks/0/path = NodePath(".:animation_name")
 tracks/0/interp = 1
@@ -433,7 +433,6 @@ tracks/1/keys = {
 [sub_resource type="Animation" id=99]
 resource_name = "run"
 length = 0.6667
-loop = true
 tracks/0/type = "value"
 tracks/0/path = NodePath(".:animation_name")
 tracks/0/interp = 1
@@ -462,7 +461,6 @@ tracks/1/keys = {
 [sub_resource type="Animation" id=100]
 resource_name = "run-to-idle"
 length = 0.2667
-loop = true
 tracks/0/type = "value"
 tracks/0/path = NodePath(".:animation_name")
 tracks/0/interp = 1
@@ -491,6 +489,7 @@ tracks/1/keys = {
 [sub_resource type="Animation" id=101]
 resource_name = "run-to-idle_looped"
 length = 0.2667
+loop = true
 tracks/0/type = "value"
 tracks/0/path = NodePath(".:animation_name")
 tracks/0/interp = 1
@@ -519,6 +518,7 @@ tracks/1/keys = {
 [sub_resource type="Animation" id=102]
 resource_name = "run_looped"
 length = 0.6667
+loop = true
 tracks/0/type = "value"
 tracks/0/path = NodePath(".:animation_name")
 tracks/0/interp = 1
@@ -547,7 +547,6 @@ tracks/1/keys = {
 [sub_resource type="Animation" id=103]
 resource_name = "shoot"
 length = 0.6333
-loop = true
 tracks/0/type = "value"
 tracks/0/path = NodePath(".:animation_name")
 tracks/0/interp = 1
@@ -576,6 +575,7 @@ tracks/1/keys = {
 [sub_resource type="Animation" id=104]
 resource_name = "shoot_looped"
 length = 0.6333
+loop = true
 tracks/0/type = "value"
 tracks/0/path = NodePath(".:animation_name")
 tracks/0/interp = 1
@@ -603,7 +603,6 @@ tracks/1/keys = {
 
 [sub_resource type="Animation" id=105]
 resource_name = "walk"
-loop = true
 tracks/0/type = "value"
 tracks/0/path = NodePath(".:animation_name")
 tracks/0/interp = 1
@@ -631,6 +630,7 @@ tracks/1/keys = {
 
 [sub_resource type="Animation" id=106]
 resource_name = "walk_looped"
+loop = true
 tracks/0/type = "value"
 tracks/0/path = NodePath(".:animation_name")
 tracks/0/interp = 1
@@ -688,7 +688,6 @@ tracks/1/keys = {
 [sub_resource type="Animation" id=108]
 resource_name = "aim"
 length = 0.5
-loop = true
 tracks/0/type = "value"
 tracks/0/path = NodePath(".:animation_name")
 tracks/0/interp = 1
@@ -717,6 +716,7 @@ tracks/1/keys = {
 [sub_resource type="Animation" id=109]
 resource_name = "aim_looped"
 length = 0.5
+loop = true
 tracks/0/type = "value"
 tracks/0/path = NodePath(".:animation_name")
 tracks/0/interp = 1
@@ -745,7 +745,6 @@ tracks/1/keys = {
 [sub_resource type="Animation" id=110]
 resource_name = "death"
 length = 4.9333
-loop = true
 tracks/0/type = "value"
 tracks/0/path = NodePath(".:animation_name")
 tracks/0/interp = 1
@@ -774,6 +773,7 @@ tracks/1/keys = {
 [sub_resource type="Animation" id=111]
 resource_name = "death_looped"
 length = 4.9333
+loop = true
 tracks/0/type = "value"
 tracks/0/path = NodePath(".:animation_name")
 tracks/0/interp = 1
@@ -801,7 +801,6 @@ tracks/1/keys = {
 
 [sub_resource type="Animation" id=112]
 resource_name = "hoverboard"
-loop = true
 tracks/0/type = "value"
 tracks/0/path = NodePath(".:animation_name")
 tracks/0/interp = 1
@@ -829,6 +828,7 @@ tracks/1/keys = {
 
 [sub_resource type="Animation" id=113]
 resource_name = "hoverboard_looped"
+loop = true
 tracks/0/type = "value"
 tracks/0/path = NodePath(".:animation_name")
 tracks/0/interp = 1
@@ -857,7 +857,6 @@ tracks/1/keys = {
 [sub_resource type="Animation" id=114]
 resource_name = "idle"
 length = 1.6667
-loop = true
 tracks/0/type = "value"
 tracks/0/path = NodePath(".:animation_name")
 tracks/0/interp = 1
@@ -886,7 +885,6 @@ tracks/1/keys = {
 [sub_resource type="Animation" id=115]
 resource_name = "idle-turn"
 length = 0.2667
-loop = true
 tracks/0/type = "value"
 tracks/0/path = NodePath(".:animation_name")
 tracks/0/interp = 1
@@ -915,6 +913,7 @@ tracks/1/keys = {
 [sub_resource type="Animation" id=116]
 resource_name = "idle-turn_looped"
 length = 0.2667
+loop = true
 tracks/0/type = "value"
 tracks/0/path = NodePath(".:animation_name")
 tracks/0/interp = 1
@@ -943,6 +942,7 @@ tracks/1/keys = {
 [sub_resource type="Animation" id=117]
 resource_name = "idle_looped"
 length = 1.6667
+loop = true
 tracks/0/type = "value"
 tracks/0/path = NodePath(".:animation_name")
 tracks/0/interp = 1
@@ -971,7 +971,6 @@ tracks/1/keys = {
 [sub_resource type="Animation" id=118]
 resource_name = "jump"
 length = 1.3333
-loop = true
 tracks/0/type = "value"
 tracks/0/path = NodePath(".:animation_name")
 tracks/0/interp = 1
@@ -1000,6 +999,7 @@ tracks/1/keys = {
 [sub_resource type="Animation" id=119]
 resource_name = "jump_looped"
 length = 1.3333
+loop = true
 tracks/0/type = "value"
 tracks/0/path = NodePath(".:animation_name")
 tracks/0/interp = 1
@@ -1028,7 +1028,6 @@ tracks/1/keys = {
 [sub_resource type="Animation" id=120]
 resource_name = "portal"
 length = 3.1667
-loop = true
 tracks/0/type = "value"
 tracks/0/path = NodePath(".:animation_name")
 tracks/0/interp = 1
@@ -1057,6 +1056,7 @@ tracks/1/keys = {
 [sub_resource type="Animation" id=121]
 resource_name = "portal_looped"
 length = 3.1667
+loop = true
 tracks/0/type = "value"
 tracks/0/path = NodePath(".:animation_name")
 tracks/0/interp = 1
@@ -1085,7 +1085,6 @@ tracks/1/keys = {
 [sub_resource type="Animation" id=122]
 resource_name = "run"
 length = 0.6667
-loop = true
 tracks/0/type = "value"
 tracks/0/path = NodePath(".:animation_name")
 tracks/0/interp = 1
@@ -1114,7 +1113,6 @@ tracks/1/keys = {
 [sub_resource type="Animation" id=123]
 resource_name = "run-to-idle"
 length = 0.2667
-loop = true
 tracks/0/type = "value"
 tracks/0/path = NodePath(".:animation_name")
 tracks/0/interp = 1
@@ -1143,6 +1141,7 @@ tracks/1/keys = {
 [sub_resource type="Animation" id=124]
 resource_name = "run-to-idle_looped"
 length = 0.2667
+loop = true
 tracks/0/type = "value"
 tracks/0/path = NodePath(".:animation_name")
 tracks/0/interp = 1
@@ -1171,6 +1170,7 @@ tracks/1/keys = {
 [sub_resource type="Animation" id=125]
 resource_name = "run_looped"
 length = 0.6667
+loop = true
 tracks/0/type = "value"
 tracks/0/path = NodePath(".:animation_name")
 tracks/0/interp = 1
@@ -1199,7 +1199,6 @@ tracks/1/keys = {
 [sub_resource type="Animation" id=126]
 resource_name = "shoot"
 length = 0.6333
-loop = true
 tracks/0/type = "value"
 tracks/0/path = NodePath(".:animation_name")
 tracks/0/interp = 1
@@ -1228,6 +1227,7 @@ tracks/1/keys = {
 [sub_resource type="Animation" id=127]
 resource_name = "shoot_looped"
 length = 0.6333
+loop = true
 tracks/0/type = "value"
 tracks/0/path = NodePath(".:animation_name")
 tracks/0/interp = 1
@@ -1255,7 +1255,6 @@ tracks/1/keys = {
 
 [sub_resource type="Animation" id=128]
 resource_name = "walk"
-loop = true
 tracks/0/type = "value"
 tracks/0/path = NodePath(".:animation_name")
 tracks/0/interp = 1
@@ -1283,6 +1282,7 @@ tracks/1/keys = {
 
 [sub_resource type="Animation" id=129]
 resource_name = "walk_looped"
+loop = true
 tracks/0/type = "value"
 tracks/0/path = NodePath(".:animation_name")
 tracks/0/interp = 1
@@ -1340,7 +1340,6 @@ tracks/1/keys = {
 [sub_resource type="Animation" id=131]
 resource_name = "gun-grab"
 length = 0.666667
-loop = true
 tracks/0/type = "value"
 tracks/0/path = NodePath(".:animation_name")
 tracks/0/interp = 1
@@ -1369,6 +1368,7 @@ tracks/1/keys = {
 [sub_resource type="Animation" id=132]
 resource_name = "gun-grab_looped"
 length = 0.666667
+loop = true
 tracks/0/type = "value"
 tracks/0/path = NodePath(".:animation_name")
 tracks/0/interp = 1
@@ -1397,7 +1397,6 @@ tracks/1/keys = {
 [sub_resource type="Animation" id=133]
 resource_name = "gun-holster"
 length = 0.666667
-loop = true
 tracks/0/type = "value"
 tracks/0/path = NodePath(".:animation_name")
 tracks/0/interp = 1
@@ -1426,6 +1425,7 @@ tracks/1/keys = {
 [sub_resource type="Animation" id=134]
 resource_name = "gun-holster_looped"
 length = 0.666667
+loop = true
 tracks/0/type = "value"
 tracks/0/path = NodePath(".:animation_name")
 tracks/0/interp = 1
@@ -1454,7 +1454,6 @@ tracks/1/keys = {
 [sub_resource type="Animation" id=135]
 resource_name = "jump"
 length = 1.53333
-loop = true
 tracks/0/type = "value"
 tracks/0/path = NodePath(".:animation_name")
 tracks/0/interp = 1
@@ -1483,6 +1482,7 @@ tracks/1/keys = {
 [sub_resource type="Animation" id=136]
 resource_name = "jump_looped"
 length = 1.53333
+loop = true
 tracks/0/type = "value"
 tracks/0/path = NodePath(".:animation_name")
 tracks/0/interp = 1
@@ -1511,7 +1511,6 @@ tracks/1/keys = {
 [sub_resource type="Animation" id=137]
 resource_name = "roar"
 length = 2.13333
-loop = true
 tracks/0/type = "value"
 tracks/0/path = NodePath(".:animation_name")
 tracks/0/interp = 1
@@ -1540,6 +1539,7 @@ tracks/1/keys = {
 [sub_resource type="Animation" id=138]
 resource_name = "roar_looped"
 length = 2.13333
+loop = true
 tracks/0/type = "value"
 tracks/0/path = NodePath(".:animation_name")
 tracks/0/interp = 1
@@ -1568,7 +1568,6 @@ tracks/1/keys = {
 [sub_resource type="Animation" id=139]
 resource_name = "walk"
 length = 1.26667
-loop = true
 tracks/0/type = "value"
 tracks/0/path = NodePath(".:animation_name")
 tracks/0/interp = 1
@@ -1597,6 +1596,7 @@ tracks/1/keys = {
 [sub_resource type="Animation" id=140]
 resource_name = "walk_looped"
 length = 1.26667
+loop = true
 tracks/0/type = "value"
 tracks/0/path = NodePath(".:animation_name")
 tracks/0/interp = 1

+ 22 - 3
spine-godot/example/examples/09-custom-material/custom-material.tscn

@@ -1,4 +1,4 @@
-[gd_scene load_steps=4 format=2]
+[gd_scene load_steps=6 format=2]
 
 [ext_resource path="res://assets/spineboy/spinebody-data-res.tres" type="SpineSkeletonDataResource" id=1]
 
@@ -6,16 +6,35 @@
 code = "shader_type canvas_item;
 
 void fragment() {
-  COLOR = vec4(0.4, 0.6, 0.9, 1.0);
+	COLOR = texture(TEXTURE, UV);
+	COLOR.r = 0.0;
 }"
 
 [sub_resource type="ShaderMaterial" id=2]
 shader = SubResource( 1 )
 
+[sub_resource type="Shader" id=3]
+code = "shader_type canvas_item;
+
+void fragment() {
+	COLOR = texture(TEXTURE, UV);
+	COLOR.b = 0.0;
+}"
+
+[sub_resource type="ShaderMaterial" id=4]
+shader = SubResource( 3 )
+
 [node name="Node2D" type="Node2D"]
 
 [node name="SpineSprite" type="SpineSprite" parent="."]
-position = Vector2( 471, 482 )
+position = Vector2( 501, 507 )
 scale = Vector2( 0.546374, 0.546373 )
 skeleton_data_res = ExtResource( 1 )
 normal_material = SubResource( 2 )
+
+[node name="GunSlot" type="SpineSlotNode" parent="SpineSprite"]
+position = Vector2( 40.8752, -276.036 )
+rotation = 0.837234
+scale = Vector2( 1, 1 )
+slot_name = "gun"
+normal_material = SubResource( 4 )

+ 55 - 2
spine-godot/spine_godot/SpineSlotNode.cpp

@@ -7,6 +7,21 @@ void SpineSlotNode::_bind_methods() {
     ClassDB::bind_method(D_METHOD("set_slot_name"), &SpineSlotNode::set_slot_name);
     ClassDB::bind_method(D_METHOD("get_slot_name"), &SpineSlotNode::get_slot_name);
     ClassDB::bind_method(D_METHOD("_on_world_transforms_changed", "spine_sprite"), &SpineSlotNode::on_world_transforms_changed);
+
+    ClassDB::bind_method(D_METHOD("set_normal_material", "material"), &SpineSlotNode::set_normal_material);
+    ClassDB::bind_method(D_METHOD("get_normal_material"), &SpineSlotNode::get_normal_material);
+    ClassDB::bind_method(D_METHOD("set_additive_material", "material"), &SpineSlotNode::set_additive_material);
+    ClassDB::bind_method(D_METHOD("get_additive_material"), &SpineSlotNode::get_additive_material);
+    ClassDB::bind_method(D_METHOD("set_multiply_material", "material"), &SpineSlotNode::set_multiply_material);
+    ClassDB::bind_method(D_METHOD("get_multiply_material"), &SpineSlotNode::get_multiply_material);
+    ClassDB::bind_method(D_METHOD("set_screen_material", "material"), &SpineSlotNode::set_screen_material);
+    ClassDB::bind_method(D_METHOD("get_screen_material"), &SpineSlotNode::get_screen_material);
+
+    ADD_GROUP("Materials", "");
+    ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "normal_material", PROPERTY_HINT_RESOURCE_TYPE, "Material"), "set_normal_material", "get_normal_material");
+    ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "additive_material", PROPERTY_HINT_RESOURCE_TYPE, "Material"), "set_additive_material", "get_additive_material");
+    ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "multiply_material", PROPERTY_HINT_RESOURCE_TYPE, "Material"), "set_multiply_material", "get_multiply_material");
+    ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "screen_material", PROPERTY_HINT_RESOURCE_TYPE, "Material"), "set_screen_material", "get_screen_material");
 }
 
 SpineSlotNode::SpineSlotNode(): slot_index(-1), sprite(nullptr) {
@@ -56,13 +71,19 @@ void SpineSlotNode::_get_property_list(List<PropertyInfo>* list) const {
     Vector<String> slot_names;
     if (sprite) sprite->get_skeleton_data_res()->get_slot_names(slot_names);
     else slot_names.push_back(slot_name);
+    auto element = list->front();
+    while (element) {
+        auto property_info = element->get();
+        if (property_info.name == "SpineSlotNode") break;
+        element = element->next();
+    }
     PropertyInfo slot_name_property;
     slot_name_property.name = "slot_name";
     slot_name_property.type = Variant::STRING;
     slot_name_property.hint_string = String(",").join(slot_names);
     slot_name_property.hint = PROPERTY_HINT_ENUM;
     slot_name_property.usage = PROPERTY_USAGE_DEFAULT;
-    list->push_back(slot_name_property);
+    list->insert_after(element, slot_name_property);
 }
 
 bool SpineSlotNode::_get(const StringName& property, Variant& value) const {
@@ -117,4 +138,36 @@ void SpineSlotNode::set_slot_name(const String& _slot_name) {
 
 String SpineSlotNode::get_slot_name() {
     return slot_name;
-}
+}
+
+Ref<Material> SpineSlotNode::get_normal_material() {
+    return normal_material;
+}
+
+void SpineSlotNode::set_normal_material(Ref<Material> material) {
+    normal_material = material;
+}
+
+Ref<Material> SpineSlotNode::get_additive_material() {
+    return additive_material;
+}
+
+void SpineSlotNode::set_additive_material(Ref<Material> material) {
+    additive_material = material;
+}
+
+Ref<Material> SpineSlotNode::get_multiply_material() {
+    return multiply_material;
+}
+
+void SpineSlotNode::set_multiply_material(Ref<Material> material) {
+    multiply_material = material;
+}
+
+Ref<Material> SpineSlotNode::get_screen_material() {
+    return screen_material;
+}
+
+void SpineSlotNode::set_screen_material(Ref<Material> material) {
+    screen_material = material;
+}

+ 20 - 0
spine-godot/spine_godot/SpineSlotNode.h

@@ -11,6 +11,10 @@ class SpineSlotNode: public Node2D {
 protected:
     String slot_name;
     int slot_index;
+    Ref<Material> normal_material;
+    Ref<Material> additive_material;
+    Ref<Material> multiply_material;
+    Ref<Material> screen_material;
     SpineSprite *sprite;
 
     static void _bind_methods();
@@ -28,6 +32,22 @@ public:
     String get_slot_name();
 
     int get_slot_index() { return slot_index; }
+
+    Ref<Material> get_normal_material();
+
+    void set_normal_material(Ref<Material> material);
+
+    Ref<Material> get_additive_material();
+
+    void set_additive_material(Ref<Material> material);
+
+    Ref<Material> get_multiply_material();
+
+    void set_multiply_material(Ref<Material> material);
+
+    Ref<Material> get_screen_material();
+
+    void set_screen_material(Ref<Material> material);
 };
 
 #endif

+ 21 - 1
spine-godot/spine_godot/SpineSprite.cpp

@@ -419,12 +419,32 @@ void SpineSprite::update_meshes(Ref<SpineSkeleton> skeleton_ref) {
 
 			spine::BlendMode blend_mode = slot->getData().getBlendMode();
 			Ref<Material> custom_material;
-			switch (blend_mode) {
+
+			// See if we have a slot node for this slot with a custom material
+			auto &nodes = slot_nodes[i];
+			if (nodes.size() > 0) {
+				auto slot_node = nodes[0];
+				if (slot_node) {
+					switch (blend_mode) {
+					case spine::BlendMode_Normal: custom_material = slot_node->get_normal_material(); break;
+					case spine::BlendMode_Additive: custom_material = slot_node->get_additive_material(); break;
+					case spine::BlendMode_Multiply: custom_material = slot_node->get_multiply_material(); break;
+					case spine::BlendMode_Screen: custom_material = slot_node->get_screen_material(); break;
+					}
+				}
+			}
+
+			// Else, check if we have a material on the sprite itself
+			if (!custom_material.is_valid()) {
+				switch (blend_mode) {
 				case spine::BlendMode_Normal: custom_material = normal_material; break;
 				case spine::BlendMode_Additive: custom_material = additive_material; break;
 				case spine::BlendMode_Multiply: custom_material = multiply_material; break;
 				case spine::BlendMode_Screen: custom_material = screen_material; break;
+				}
 			}
+
+			// Set the custom material, or the default material
 			if (custom_material.is_valid()) mesh_instance->set_material(custom_material);
 			else mesh_instance->set_material(default_materials[slot->getData().getBlendMode()]);
 		}