Expand per-part fur customization
Load authored pattern masks by body component, add custom fur colors and the themed picker UI, and advance the signed profile protocol to version 6.
This commit is contained in:
parent
64c2acb773
commit
84695c473b
27 changed files with 1337 additions and 327 deletions
|
|
@ -6,19 +6,23 @@ func _initialize() -> void:
|
|||
|
||||
|
||||
func _run() -> void:
|
||||
CharacterCustomizationCatalog.refresh_fur_pattern_assets()
|
||||
var legacy_snapshot := CharacterCustomizationCatalog.default_snapshot()
|
||||
legacy_snapshot[CharacterCustomizationCatalog.FUR_STYLE_ID] = "spots_bengal"
|
||||
for added_id: String in [
|
||||
"fur_style",
|
||||
"fur_color_2",
|
||||
"fur_color_3",
|
||||
"fur_color_4",
|
||||
CharacterCustomizationCatalog.FUR_STYLE_ARMS_ID,
|
||||
CharacterCustomizationCatalog.FUR_STYLE_HEAD_ID,
|
||||
CharacterCustomizationCatalog.FUR_STYLE_EARS_ID,
|
||||
CharacterCustomizationCatalog.FUR_STYLE_TAIL_ID,
|
||||
]:
|
||||
legacy_snapshot.erase(added_id)
|
||||
var migrated := CharacterCustomizationCatalog.sanitized_snapshot(
|
||||
legacy_snapshot
|
||||
)
|
||||
assert(CharacterCustomizationCatalog.validate_snapshot(migrated))
|
||||
assert(migrated["fur_style"] == "solid")
|
||||
for style_field: String in CharacterCustomizationCatalog.FUR_STYLE_IDS:
|
||||
assert(migrated[style_field] == "bengal")
|
||||
|
||||
var fur_colors := CharacterCustomizationCatalog.options_for("fur_pattern")
|
||||
assert(fur_colors.size() == 30)
|
||||
for natural_color_id: String in [
|
||||
|
|
@ -38,72 +42,128 @@ func _run() -> void:
|
|||
"fur_pattern",
|
||||
natural_color_id,
|
||||
))
|
||||
var custom_color := Color("2b91c7")
|
||||
var custom_color_id := CharacterCustomizationCatalog.custom_fur_color_id(
|
||||
custom_color
|
||||
)
|
||||
assert(custom_color_id == "custom_2b91c7")
|
||||
assert(
|
||||
CharacterCustomizationCatalog.custom_fur_color_id(
|
||||
Color(custom_color.r, custom_color.g, custom_color.b, 0.2)
|
||||
)
|
||||
== custom_color_id
|
||||
)
|
||||
for color_field: String in CharacterCustomizationCatalog.FUR_COLOR_IDS:
|
||||
assert(CharacterCustomizationCatalog.is_valid_option(
|
||||
color_field,
|
||||
custom_color_id,
|
||||
))
|
||||
assert(
|
||||
CharacterCustomizationCatalog.option_color(
|
||||
color_field, custom_color_id
|
||||
).is_equal_approx(custom_color)
|
||||
)
|
||||
assert(not CharacterCustomizationCatalog.is_valid_option(
|
||||
"fur_pattern", "custom_2b91cg"
|
||||
))
|
||||
assert(not CharacterCustomizationCatalog.is_valid_option(
|
||||
"fur_pattern", "custom_2b91c7ff"
|
||||
))
|
||||
|
||||
var pattern_texture := CharacterCustomizationCatalog.fur_pattern_texture(
|
||||
"spots_bengal"
|
||||
)
|
||||
assert(pattern_texture != null)
|
||||
assert(pattern_texture.get_width() == 1024)
|
||||
assert(pattern_texture.get_height() == 1024)
|
||||
var arm_pattern_texture := CharacterCustomizationCatalog.fur_pattern_texture(
|
||||
"spots_bengal",
|
||||
"body_arms",
|
||||
)
|
||||
assert(arm_pattern_texture != null)
|
||||
assert(arm_pattern_texture.get_width() == 1024)
|
||||
assert(arm_pattern_texture.get_height() == 1024)
|
||||
var fox_pattern_texture := CharacterCustomizationCatalog.fur_pattern_texture(
|
||||
var pattern_ids: Array[String] = []
|
||||
for option: Dictionary in CharacterCustomizationCatalog.options_for(
|
||||
CharacterCustomizationCatalog.FUR_STYLE_ID
|
||||
):
|
||||
pattern_ids.append(str(option.get("id", "")))
|
||||
assert(pattern_ids == [
|
||||
"solid",
|
||||
"bengal",
|
||||
"calico",
|
||||
"fox",
|
||||
"body_main",
|
||||
)
|
||||
assert(fox_pattern_texture != null)
|
||||
assert(fox_pattern_texture.get_width() == 1024)
|
||||
assert(fox_pattern_texture.get_height() == 1024)
|
||||
var fox_arm_pattern_texture := (
|
||||
CharacterCustomizationCatalog.fur_pattern_texture(
|
||||
"fox", "body_arms"
|
||||
)
|
||||
)
|
||||
assert(fox_arm_pattern_texture != null)
|
||||
assert(fox_arm_pattern_texture.get_width() == 1024)
|
||||
assert(fox_arm_pattern_texture.get_height() == 1024)
|
||||
var bengal_head_texture := (
|
||||
CharacterCustomizationCatalog.fur_pattern_texture(
|
||||
"spots_bengal", "head_round"
|
||||
)
|
||||
)
|
||||
assert(bengal_head_texture != null)
|
||||
assert(bengal_head_texture.get_width() == 1024)
|
||||
assert(bengal_head_texture.get_height() == 1024)
|
||||
var fox_head_texture := CharacterCustomizationCatalog.fur_pattern_texture(
|
||||
"fox", "head_pointy"
|
||||
)
|
||||
assert(fox_head_texture != null)
|
||||
assert(fox_head_texture.get_width() == 1024)
|
||||
assert(fox_head_texture.get_height() == 1024)
|
||||
var fox_round_head_texture := (
|
||||
CharacterCustomizationCatalog.fur_pattern_texture(
|
||||
"fox", "head_round"
|
||||
)
|
||||
)
|
||||
assert(fox_round_head_texture != null)
|
||||
assert(fox_round_head_texture.get_width() == 1024)
|
||||
assert(fox_round_head_texture.get_height() == 1024)
|
||||
var fox_tail_texture := CharacterCustomizationCatalog.fur_pattern_texture(
|
||||
"fox", "tails_fox"
|
||||
)
|
||||
assert(fox_tail_texture != null)
|
||||
assert(fox_tail_texture.get_width() == 1024)
|
||||
assert(fox_tail_texture.get_height() == 1024)
|
||||
"paws",
|
||||
"stripes",
|
||||
"tiger",
|
||||
"tummy",
|
||||
])
|
||||
|
||||
var patterned := migrated.duplicate(true)
|
||||
var patterned := CharacterCustomizationCatalog.default_snapshot()
|
||||
patterned["species"] = "round"
|
||||
patterned["ears"] = "pointy_short"
|
||||
patterned["tail"] = "fox"
|
||||
patterned["fur_pattern"] = "orange"
|
||||
patterned["fur_style"] = "spots_bengal"
|
||||
patterned[CharacterCustomizationCatalog.FUR_STYLE_ID] = "calico"
|
||||
patterned[CharacterCustomizationCatalog.FUR_STYLE_ARMS_ID] = "paws"
|
||||
patterned[CharacterCustomizationCatalog.FUR_STYLE_HEAD_ID] = "tiger"
|
||||
patterned[CharacterCustomizationCatalog.FUR_STYLE_EARS_ID] = "fox"
|
||||
patterned[CharacterCustomizationCatalog.FUR_STYLE_TAIL_ID] = "fox"
|
||||
patterned["fur_color_2"] = "cream"
|
||||
patterned["fur_color_3"] = "brown"
|
||||
patterned["fur_color_4"] = "red"
|
||||
assert(CharacterCustomizationCatalog.validate_snapshot(patterned))
|
||||
|
||||
_assert_option_ids(
|
||||
CharacterCustomizationCatalog.fur_pattern_options_for_field(
|
||||
CharacterCustomizationCatalog.FUR_STYLE_ID, patterned
|
||||
),
|
||||
["solid", "bengal", "calico", "fox", "paws", "stripes", "tiger", "tummy"],
|
||||
)
|
||||
_assert_option_ids(
|
||||
CharacterCustomizationCatalog.fur_pattern_options_for_field(
|
||||
CharacterCustomizationCatalog.FUR_STYLE_ARMS_ID, patterned
|
||||
),
|
||||
["solid", "bengal", "fox", "paws"],
|
||||
)
|
||||
_assert_option_ids(
|
||||
CharacterCustomizationCatalog.fur_pattern_options_for_field(
|
||||
CharacterCustomizationCatalog.FUR_STYLE_HEAD_ID, patterned
|
||||
),
|
||||
["solid", "bengal", "fox", "tiger"],
|
||||
)
|
||||
_assert_option_ids(
|
||||
CharacterCustomizationCatalog.fur_pattern_options_for_field(
|
||||
CharacterCustomizationCatalog.FUR_STYLE_EARS_ID, patterned
|
||||
),
|
||||
["solid", "fox"],
|
||||
)
|
||||
_assert_option_ids(
|
||||
CharacterCustomizationCatalog.fur_pattern_options_for_field(
|
||||
CharacterCustomizationCatalog.FUR_STYLE_TAIL_ID, patterned
|
||||
),
|
||||
["solid", "fox"],
|
||||
)
|
||||
var pointy_snapshot := patterned.duplicate(true)
|
||||
pointy_snapshot["species"] = "pointy"
|
||||
_assert_option_ids(
|
||||
CharacterCustomizationCatalog.fur_pattern_options_for_field(
|
||||
CharacterCustomizationCatalog.FUR_STYLE_HEAD_ID, pointy_snapshot
|
||||
),
|
||||
["solid", "fox"],
|
||||
)
|
||||
var no_accessories := patterned.duplicate(true)
|
||||
no_accessories["ears"] = "none"
|
||||
no_accessories["tail"] = "none"
|
||||
_assert_option_ids(
|
||||
CharacterCustomizationCatalog.fur_pattern_options_for_field(
|
||||
CharacterCustomizationCatalog.FUR_STYLE_EARS_ID, no_accessories
|
||||
),
|
||||
["solid"],
|
||||
)
|
||||
_assert_option_ids(
|
||||
CharacterCustomizationCatalog.fur_pattern_options_for_field(
|
||||
CharacterCustomizationCatalog.FUR_STYLE_TAIL_ID, no_accessories
|
||||
),
|
||||
["solid"],
|
||||
)
|
||||
|
||||
var body_texture := _assert_pattern_texture("calico", "body_main")
|
||||
var arms_texture := _assert_pattern_texture("paws", "body_arms")
|
||||
var head_texture := _assert_pattern_texture("tiger", "head_round")
|
||||
var ears_texture := _assert_pattern_texture("fox", "ears_pointy_short")
|
||||
var tail_texture := _assert_pattern_texture("fox", "tails_fox")
|
||||
_assert_pattern_texture("tummy", "body_main")
|
||||
_assert_pattern_texture("stripes", "body_main")
|
||||
_assert_pattern_texture("fox", "head_pointy")
|
||||
|
||||
var visuals := PlayerVisualPresenter.instantiate_visuals()
|
||||
root.add_child(visuals)
|
||||
PlayerVisualPresenter.apply_appearance(visuals, patterned)
|
||||
|
|
@ -121,72 +181,224 @@ func _run() -> void:
|
|||
uv_max = uv_max.max(uv)
|
||||
assert(uv_min.x < 0.05 and uv_min.y < 0.05)
|
||||
assert(uv_max.x > 0.95 and uv_max.y > 0.95)
|
||||
var material := body.material_override as ShaderMaterial
|
||||
assert(material != null)
|
||||
assert(material.shader == preload("res://player/fur_pattern.gdshader"))
|
||||
assert(material.get_shader_parameter("pattern_texture") == pattern_texture)
|
||||
assert(material.get_shader_parameter("base_color") == Color("c86c36"))
|
||||
assert(material.get_shader_parameter("red_zone_color") == Color("e6d39b"))
|
||||
assert(material.get_shader_parameter("green_zone_color") == Color("7b4a32"))
|
||||
assert(material.get_shader_parameter("blue_zone_color") == Color("a9433f"))
|
||||
var arms := skeleton.get_node_or_null("body_arms") as MeshInstance3D
|
||||
assert(arms != null)
|
||||
var arm_material := arms.material_override as ShaderMaterial
|
||||
assert(arm_material != null)
|
||||
assert(
|
||||
arm_material.get_shader_parameter("pattern_texture")
|
||||
== arm_pattern_texture
|
||||
)
|
||||
var round_head := skeleton.get_node_or_null("head_round") as MeshInstance3D
|
||||
assert(round_head != null)
|
||||
var round_head_material := round_head.material_override as ShaderMaterial
|
||||
assert(round_head_material != null)
|
||||
assert(
|
||||
round_head_material.get_shader_parameter("pattern_texture")
|
||||
== bengal_head_texture
|
||||
)
|
||||
|
||||
var fox_snapshot := patterned.duplicate(true)
|
||||
fox_snapshot["species"] = "pointy"
|
||||
fox_snapshot["fur_style"] = "fox"
|
||||
fox_snapshot["tail"] = "fox"
|
||||
PlayerVisualPresenter.apply_appearance(visuals, fox_snapshot)
|
||||
var pointy_head := (
|
||||
skeleton.get_node_or_null("head_pointy") as MeshInstance3D
|
||||
var body_material := _assert_mesh_pattern(
|
||||
skeleton, "body_main", body_texture
|
||||
)
|
||||
assert(pointy_head != null)
|
||||
var pointy_head_material := pointy_head.material_override as ShaderMaterial
|
||||
assert(pointy_head_material != null)
|
||||
assert(
|
||||
pointy_head_material.get_shader_parameter("pattern_texture")
|
||||
== fox_head_texture
|
||||
var arms_material := _assert_mesh_pattern(
|
||||
skeleton, "body_arms", arms_texture
|
||||
)
|
||||
fox_snapshot["species"] = "round"
|
||||
PlayerVisualPresenter.apply_appearance(visuals, fox_snapshot)
|
||||
assert(
|
||||
round_head_material.get_shader_parameter("pattern_texture")
|
||||
== fox_round_head_texture
|
||||
var head_material := _assert_mesh_pattern(
|
||||
skeleton, "head_round", head_texture
|
||||
)
|
||||
var fox_tail := skeleton.get_node_or_null("tails_fox") as MeshInstance3D
|
||||
assert(fox_tail != null)
|
||||
var fox_tail_material := fox_tail.material_override as ShaderMaterial
|
||||
assert(fox_tail_material != null)
|
||||
assert(
|
||||
fox_tail_material.get_shader_parameter("pattern_texture")
|
||||
== fox_tail_texture
|
||||
var ears_material := _assert_mesh_pattern(
|
||||
skeleton, "ears_pointy_short", ears_texture
|
||||
)
|
||||
var tail_material := _assert_mesh_pattern(
|
||||
skeleton, "tails_fox", tail_texture
|
||||
)
|
||||
for material: ShaderMaterial in [
|
||||
body_material,
|
||||
arms_material,
|
||||
head_material,
|
||||
ears_material,
|
||||
tail_material,
|
||||
]:
|
||||
assert(material.shader == preload("res://player/fur_pattern.gdshader"))
|
||||
assert(material.get_shader_parameter("base_color") == Color("c86c36"))
|
||||
assert(material.get_shader_parameter("red_zone_color") == Color("e6d39b"))
|
||||
assert(material.get_shader_parameter("green_zone_color") == Color("7b4a32"))
|
||||
assert(material.get_shader_parameter("blue_zone_color") == Color("a9433f"))
|
||||
|
||||
var recolored := patterned.duplicate(true)
|
||||
recolored["fur_color_3"] = "teal"
|
||||
PlayerVisualPresenter.apply_appearance(visuals, recolored)
|
||||
for material: ShaderMaterial in [
|
||||
body_material,
|
||||
arms_material,
|
||||
head_material,
|
||||
ears_material,
|
||||
tail_material,
|
||||
]:
|
||||
assert(material.get_shader_parameter("green_zone_color") == Color("3a8790"))
|
||||
assert(
|
||||
NetworkProfileProtocol.signature_fields({"appearance": patterned})
|
||||
!= NetworkProfileProtocol.signature_fields({"appearance": recolored})
|
||||
)
|
||||
var other_arms := patterned.duplicate(true)
|
||||
other_arms[CharacterCustomizationCatalog.FUR_STYLE_ARMS_ID] = "fox"
|
||||
assert(
|
||||
NetworkProfileProtocol.signature_fields({"appearance": patterned})
|
||||
!= NetworkProfileProtocol.signature_fields({"appearance": other_arms})
|
||||
)
|
||||
var custom_snapshot := patterned.duplicate(true)
|
||||
custom_snapshot["fur_pattern"] = custom_color_id
|
||||
assert(CharacterCustomizationCatalog.validate_snapshot(custom_snapshot))
|
||||
PlayerVisualPresenter.apply_appearance(visuals, custom_snapshot)
|
||||
for material: ShaderMaterial in [
|
||||
body_material,
|
||||
arms_material,
|
||||
head_material,
|
||||
ears_material,
|
||||
tail_material,
|
||||
]:
|
||||
assert(
|
||||
(material.get_shader_parameter("base_color") as Color).is_equal_approx(
|
||||
custom_color
|
||||
)
|
||||
)
|
||||
assert(
|
||||
NetworkProfileProtocol.signature_fields({"appearance": patterned})
|
||||
!= NetworkProfileProtocol.signature_fields({"appearance": custom_snapshot})
|
||||
)
|
||||
|
||||
PlayerVisualPresenter.apply_appearance(visuals, migrated)
|
||||
var solid := CharacterCustomizationCatalog.default_snapshot()
|
||||
PlayerVisualPresenter.apply_appearance(visuals, solid)
|
||||
assert(body.material_override is StandardMaterial3D)
|
||||
await _validate_fur_color_ui(patterned)
|
||||
|
||||
print("Fur pattern validation: PASS")
|
||||
visuals.queue_free()
|
||||
await process_frame
|
||||
quit()
|
||||
|
||||
|
||||
func _validate_fur_color_ui(snapshot: Dictionary) -> void:
|
||||
var profile_page := ProfilePage.new()
|
||||
profile_page.size = Vector2(1280.0, 720.0)
|
||||
root.add_child(profile_page)
|
||||
await process_frame
|
||||
profile_page.set("_draft_appearance", snapshot.duplicate(true))
|
||||
profile_page.call("_select_category", "fur_pattern")
|
||||
profile_page.call("_select_fur_section", "colors")
|
||||
await process_frame
|
||||
await process_frame
|
||||
var color_panel := profile_page.find_child(
|
||||
"FurColorOptionsPanel", true, false
|
||||
) as PanelContainer
|
||||
var channel_grid := profile_page.find_child(
|
||||
"FurColorChannelGrid", true, false
|
||||
) as GridContainer
|
||||
var palette_grid := profile_page.find_child(
|
||||
"FurPaletteGrid", true, false
|
||||
) as GridContainer
|
||||
var picker := profile_page.find_child(
|
||||
"FurCustomColorPicker", true, false
|
||||
) as ColorPickerButton
|
||||
var picker_popup := picker.get_popup()
|
||||
var picker_control := picker.get_picker()
|
||||
assert(color_panel != null and color_panel.size.x > 360.0)
|
||||
assert(channel_grid != null and channel_grid.columns == 4)
|
||||
assert(channel_grid.get_child_count() == 4)
|
||||
assert(palette_grid != null and palette_grid.columns == 10)
|
||||
assert(palette_grid.get_child_count() == 30)
|
||||
for option_button: Button in palette_grid.get_children():
|
||||
assert(
|
||||
option_button.custom_minimum_size
|
||||
== Vector2.ONE * ProfilePage.FUR_PALETTE_SWATCH_SIZE
|
||||
)
|
||||
for color_index: int in CharacterCustomizationCatalog.FUR_COLOR_IDS.size():
|
||||
var category_id: String = (
|
||||
CharacterCustomizationCatalog.FUR_COLOR_IDS[color_index]
|
||||
)
|
||||
var channel_button := profile_page.find_child(
|
||||
"FurColorSlot%d" % (color_index + 1), true, false
|
||||
) as Button
|
||||
assert(channel_button != null)
|
||||
var swatch := channel_button.get_node(
|
||||
"ChannelContents/ActiveColorSwatch"
|
||||
) as Panel
|
||||
var channel_label := channel_button.get_node(
|
||||
"ChannelContents/ChannelNumber"
|
||||
) as Label
|
||||
assert(swatch.size.is_equal_approx(
|
||||
Vector2.ONE * ProfilePage.FUR_CHANNEL_SWATCH_SIZE
|
||||
))
|
||||
assert(swatch.size.y <= channel_label.size.y)
|
||||
var swatch_style := swatch.get_theme_stylebox(
|
||||
"panel"
|
||||
) as StyleBoxFlat
|
||||
assert(swatch_style != null)
|
||||
assert(
|
||||
swatch_style.corner_radius_top_left
|
||||
== roundi(ProfilePage.FUR_CHANNEL_SWATCH_SIZE * 0.5)
|
||||
)
|
||||
assert(swatch_style.bg_color.is_equal_approx(
|
||||
CharacterCustomizationCatalog.option_color(
|
||||
category_id,
|
||||
str(snapshot[category_id]),
|
||||
)
|
||||
))
|
||||
var picker_style := picker.get_theme_stylebox("normal") as StyleBoxFlat
|
||||
assert(picker_style != null and picker_style.corner_radius_top_left == 14)
|
||||
assert(picker_popup.min_size == ProfilePage.FUR_COLOR_PICKER_POPUP_SIZE)
|
||||
assert(picker_popup.theme.default_font == UtilityPageStyle.TuffyFont)
|
||||
var popup_style := picker_popup.get_theme_stylebox(
|
||||
"panel"
|
||||
) as StyleBoxFlat
|
||||
assert(popup_style != null)
|
||||
assert(popup_style.bg_color.a == 1.0)
|
||||
assert(popup_style.corner_radius_top_left == 20)
|
||||
assert(
|
||||
picker_control.get_theme_constant("sv_width")
|
||||
== ProfilePage.FUR_COLOR_PICKER_SV_SIZE.x
|
||||
)
|
||||
assert(
|
||||
picker_control.get_theme_constant("sv_height")
|
||||
== ProfilePage.FUR_COLOR_PICKER_SV_SIZE.y
|
||||
)
|
||||
assert(not picker_control.edit_alpha)
|
||||
assert(not picker_control.edit_intensity)
|
||||
var picker_display := picker.get_node("FurCustomColorDisplay") as Panel
|
||||
var display_style := picker_display.get_theme_stylebox(
|
||||
"panel"
|
||||
) as StyleBoxFlat
|
||||
assert(display_style != null and display_style.corner_radius_top_left == 11)
|
||||
var live_custom_color := Color("2b91c7")
|
||||
profile_page.call(
|
||||
"_select_custom_fur_color",
|
||||
live_custom_color,
|
||||
CharacterCustomizationCatalog.FUR_COLOR_IDS[0],
|
||||
)
|
||||
var first_swatch := profile_page.find_child(
|
||||
"FurColorSlot1", true, false
|
||||
).get_node("ChannelContents/ActiveColorSwatch") as Panel
|
||||
var first_swatch_style := first_swatch.get_theme_stylebox(
|
||||
"panel"
|
||||
) as StyleBoxFlat
|
||||
assert(first_swatch_style.bg_color.is_equal_approx(live_custom_color))
|
||||
profile_page.free()
|
||||
await process_frame
|
||||
|
||||
|
||||
func _assert_option_ids(options: Array, expected: Array[String]) -> void:
|
||||
var actual: Array[String] = []
|
||||
for option: Dictionary in options:
|
||||
actual.append(str(option.get("id", "")))
|
||||
assert(actual == expected)
|
||||
|
||||
|
||||
func _assert_pattern_texture(
|
||||
pattern_id: String,
|
||||
component_id: String,
|
||||
) -> Texture2D:
|
||||
var texture := CharacterCustomizationCatalog.fur_pattern_texture(
|
||||
pattern_id, component_id
|
||||
)
|
||||
assert(texture != null)
|
||||
assert(texture.get_width() == 1024)
|
||||
assert(texture.get_height() == 1024)
|
||||
return texture
|
||||
|
||||
|
||||
func _assert_mesh_pattern(
|
||||
skeleton: Skeleton3D,
|
||||
mesh_name: String,
|
||||
expected_texture: Texture2D,
|
||||
) -> ShaderMaterial:
|
||||
var mesh := skeleton.get_node_or_null(mesh_name) as MeshInstance3D
|
||||
assert(mesh != null)
|
||||
var material := mesh.material_override as ShaderMaterial
|
||||
assert(material != null)
|
||||
assert(material.get_shader_parameter("pattern_texture") == expected_texture)
|
||||
return material
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue