Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions seto_tools/decal_tool/geometry.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,11 +240,9 @@ def surface_basis(normal, tangent, along=None):
does it fall back to the face's own edge tangent.

`along` overrides all of that with a direction the decal's local X must
follow, whatever the face is doing. Nothing passes it today - it was built
for Contact Decals, which laid decals along the line where two objects meet,
and that feature was removed. Kept because it is the answer for any caller
that has a line to follow: "upright" is the wrong idea when the decal has to
line up with something.
follow, whatever the face is doing. Contact decals use it to run their width
along the line where two objects meet - "upright" is the wrong idea there,
because the line is the thing the decal has to line up with.

This is the *base* frame, with no Rotation applied. Rotation is applied in
compose_matrix() instead, so that Offset U/V stay measured in a frame that
Expand Down Expand Up @@ -702,7 +700,7 @@ def decal_loop_color(width, height, edge_fade, alphas=UNIFORM_ALPHA,


def set_decal_alpha(mesh, width, height, edge_fade, alphas, color_attr_name,
border_alphas=DEFAULT_BORDER_ALPHA):
border_alphas=DEFAULT_BORDER_ALPHA, color_rgb=None):
"""Rewrite a decal's per-vertex alpha in place.

The inner rectangle takes the four corner values and the border ring takes the
Expand All @@ -722,6 +720,8 @@ def set_decal_alpha(mesh, width, height, edge_fade, alphas, color_attr_name,

for element, alpha in zip(attr.data, loop_alphas):
colour = list(element.color_srgb)
if color_rgb is not None:
colour[:3] = color_rgb
colour[3] = alpha
element.color_srgb = colour
mesh.update()
Expand Down
24 changes: 22 additions & 2 deletions seto_tools/decal_tool/object_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@

from . import geometry
from ..shared import sollumz_integration as szi
from ..shared import vertex_color

# Guards against a rebuild triggering another rebuild through the same
# property callbacks.
Expand Down Expand Up @@ -132,14 +133,16 @@ def rebuild(obj):

_rebuilding = True
try:
vertex_color.apply_preset(data, data.color_preset)
if not geometry.set_decal_size(obj.data, data.width, data.height,
data.edge_fade, szi.get_uv_map_name(0)):
return "Mesh is no longer a Seto decal grid - it cannot be resized."

if not geometry.set_decal_alpha(obj.data, data.width, data.height,
data.edge_fade, tuple(data.corner_alpha),
szi.get_color_attr_name(0),
border_alphas=tuple(data.border_alpha)):
border_alphas=tuple(data.border_alpha),
color_rgb=tuple(data.color_rgb)):
return "Mesh has no usable 'Color 1' attribute - corner alpha cannot be applied."

# Offset U/V are folded into `position` here, and into the walk below, so
Expand Down Expand Up @@ -179,7 +182,7 @@ def _on_setting_changed(self, context):

def store_placement(obj, placement, source_object, texture_stem,
corner_alpha=geometry.UNIFORM_ALPHA, texture_path="",
edge_fade=0.1):
edge_fade=0.1, color_preset='GREEN', color_rgb=vertex_color.DEFAULT_RGB):
"""Stamp a freshly created decal with everything it needs to edit itself."""
with suppress_rebuild():
data = obj.seto_decal_data
Expand All @@ -189,6 +192,8 @@ def store_placement(obj, placement, source_object, texture_stem,
data.texture_path = texture_path
data.corner_alpha = corner_alpha
data.edge_fade = edge_fade
data.color_preset = color_preset
data.color_rgb = color_rgb

data.frame_center = placement.center
data.frame_normal = placement.normal
Expand Down Expand Up @@ -241,6 +246,21 @@ class SETO_PG_decal_object(bpy.types.PropertyGroup):
default="",
)

color_preset: bpy.props.EnumProperty(
name="Color Preset",
description="Quick-pick a named vertex colour, or choose Custom to set your own",
items=vertex_color.enum_items(),
default='GREEN',
update=_on_setting_changed,
)
color_rgb: bpy.props.FloatVectorProperty(
name="Color 1 RGB",
description="RGB written to Color 1 for every vertex",
subtype='COLOR', size=3, min=0.0, max=1.0,
default=vertex_color.DEFAULT_RGB,
update=_on_setting_changed,
)

# The surface frame the decal was placed on, in world space. Stored as plain
# vectors so the decal is fully self-contained: it never has to look at the
# source mesh again, and editing that mesh cannot invalidate it.
Expand Down
8 changes: 6 additions & 2 deletions seto_tools/decal_tool/operators.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from . import properties
from . import preferences
from ..shared import sollumz_integration as szi
from ..shared import vertex_color

# Generated decals go into "decals", and inside that into one child collection
# per library category, so a scene full of decals stays sorted the same way the
Expand Down Expand Up @@ -200,7 +201,7 @@ def _create_one(self, placement, material, collections, drawable_root,
szi.write_uv_and_color(
mesh,
geometry.decal_loop_uv(placement.width, placement.height, fade),
geometry.decal_loop_color(placement.width, placement.height, fade),
geometry.decal_loop_color(placement.width, placement.height, fade, rgb=tuple(settings.color_rgb)),
)

obj = bpy.data.objects.new(name, mesh)
Expand All @@ -215,7 +216,9 @@ def _create_one(self, placement, material, collections, drawable_root,
object_settings.store_placement(obj, placement, source_obj,
placement.texture_stem,
texture_path=placement.texture_path,
edge_fade=fade)
edge_fade=fade,
color_preset=settings.color_preset,
color_rgb=tuple(settings.color_rgb))
except _PLACEMENT_ERRORS as e:
_rollback(obj, mesh)
failures.append((os.path.basename(placement.texture_path), str(e)))
Expand Down Expand Up @@ -248,6 +251,7 @@ class SETO_OT_create_decal(_DecalBuilder, bpy.types.Operator):

def execute(self, context):
settings = context.scene.seto_decal
vertex_color.apply_preset(settings, settings.color_preset)

available, status_msg = szi.get_status_message()
if not available:
Expand Down
14 changes: 14 additions & 0 deletions seto_tools/decal_tool/properties.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from . import library
from . import preferences
from ..shared import sollumz_integration as szi
from ..shared import vertex_color


def rebuild_browser(settings):
Expand Down Expand Up @@ -103,6 +104,19 @@ class SETO_PG_decal_settings(bpy.types.PropertyGroup):
default=True,
)

color_preset: bpy.props.EnumProperty(
name="Color Preset",
description="Quick-pick a named vertex colour, or choose Custom to set your own",
items=vertex_color.enum_items(),
default='GREEN',
)
color_rgb: bpy.props.FloatVectorProperty(
name="Color 1 RGB",
description="RGB written to Color 1 for every vertex",
subtype='COLOR', size=3, min=0.0, max=1.0,
default=vertex_color.DEFAULT_RGB,
)

width: bpy.props.FloatProperty(
name="Width",
description="Size of the generated decal along its local X axis",
Expand Down
12 changes: 12 additions & 0 deletions seto_tools/decal_tool/ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,12 @@ def draw(self, context):
row.label(text=f"Shader: {szi.DECAL_SHADER_FILENAME}")
layout.prop(settings, "material_mode", text="")

col = pl.section(layout, "Vertex Colour", 'COLOR')
col.prop(settings, "color_preset", text="")
row = col.row()
row.enabled = settings.color_preset == 'CUSTOM'
row.prop(settings, "color_rgb", text="")

# How tall the texture browser is. It lives here rather than beside the
# list itself: it is set once to taste, and a row of its own above the
# list would push the decal you are choosing further down the panel.
Expand Down Expand Up @@ -270,6 +276,12 @@ def draw(self, context):
col.prop(data, "offset_u")
col.prop(data, "offset_v")

col = pl.section(layout, "Vertex Colour", 'COLOR')
col.prop(data, "color_preset", text="")
row = col.row()
row.enabled = data.color_preset == 'CUSTOM'
row.prop(data, "color_rgb", text="")

col = pl.section(layout, "Corner Alpha (Color 1)", 'IMAGE_ALPHA')
# Laid out as the decal actually sits: top row above bottom row, so the
# sliders map onto the corners you can see in the viewport rather than
Expand Down
2 changes: 2 additions & 0 deletions seto_tools/edge_dirt/operators.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from ..fake_ao import source_bevel
from ..shared import manual_offset
from ..shared import sollumz_integration as szi
from ..shared import vertex_color

_NAME_PATTERN = re.compile(r"^edge_dirt_(\d{3,})$")
# Span of the UV island's longer axis once fitted into the 0..1 square.
Expand Down Expand Up @@ -271,6 +272,7 @@ def execute(self, context):

new_name = _next_edge_dirt_name()
new_mesh = geometry.create_mesh_from_strip_data(new_name, strip_data)
vertex_color.apply_preset(settings, settings.color_preset)
loop_uv, loop_rgba = geometry.compute_loop_uv_and_alpha(
new_mesh, strip_data, color_rgb=tuple(settings.color_rgb)
)
Expand Down
6 changes: 6 additions & 0 deletions seto_tools/edge_dirt/ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,12 @@ def draw(self, context):
col.prop(data, "alpha_bottom")
col.prop(data, "alpha_top")

col = pl.section(layout, "Vertex Colour", 'COLOR')
col.prop(data, "color_preset", text="")
row = col.row()
row.enabled = data.color_preset == 'CUSTOM'
row.prop(data, "color_rgb", text="")

manual_offset.draw(layout, data, "seto_edge_dirt_data")

if data.edge_keys:
Expand Down
2 changes: 2 additions & 0 deletions seto_tools/fake_ao/object_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
from . import source_bevel
from ..shared import manual_offset
from ..shared import sollumz_integration as szi
from ..shared import vertex_color

# Span of the UV island's longer axis once fitted into the 0..1 square. Kept
# here rather than imported from operators.py, which imports this module.
Expand Down Expand Up @@ -367,6 +368,7 @@ def rebuild(obj, data=None):
materials = list(old_mesh.materials)

new_mesh = geometry.create_mesh_from_strip_data(old_mesh.name, strip_data)
vertex_color.apply_preset(data, data.color_preset)
loop_uv, loop_rgba = geometry.compute_loop_uv_and_alpha(
new_mesh, strip_data, color_rgb=tuple(data.color_rgb)
)
Expand Down
2 changes: 2 additions & 0 deletions seto_tools/fake_ao/operators.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from . import textures
from ..shared import manual_offset
from ..shared import sollumz_integration as szi
from ..shared import vertex_color

# What a generated strip is called. The tool is Ambient Occlusion, so that is
# what its output says - "fake_ao_003" in the outliner named a tool that no
Expand Down Expand Up @@ -285,6 +286,7 @@ def execute(self, context):

new_name = _next_fake_ao_name()
new_mesh = geometry.create_mesh_from_strip_data(new_name, strip_data)
vertex_color.apply_preset(settings, settings.color_preset)
loop_uv, loop_rgba = geometry.compute_loop_uv_and_alpha(
new_mesh, strip_data, color_rgb=tuple(settings.color_rgb)
)
Expand Down
7 changes: 7 additions & 0 deletions seto_tools/fake_ao/properties.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,13 @@ def settings_annotations(update=None):
default=False,
update=update,
),
"color_preset": bpy.props.EnumProperty(
name="Color Preset",
description="Quick-pick a named vertex colour, or choose Custom to set your own",
items=vertex_color.enum_items(),
default='GREEN',
update=update,
),
"color_rgb": bpy.props.FloatVectorProperty(
name="Color 1 RGB",
description="RGB written to Color 1 for every vertex (center and outer alike); only alpha differs between them",
Expand Down
6 changes: 6 additions & 0 deletions seto_tools/fake_ao/ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,12 @@ def draw(self, context):
col.prop(data, "alpha_bottom")
col.prop(data, "alpha_top")

col = pl.section(layout, "Vertex Colour", 'COLOR')
col.prop(data, "color_preset", text="")
row = col.row()
row.enabled = data.color_preset == 'CUSTOM'
row.prop(data, "color_rgb", text="")

manual_offset.draw(layout, data, "seto_fake_ao_data")

if data.edge_keys:
Expand Down
2 changes: 2 additions & 0 deletions seto_tools/fake_damage/object_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
from ..shared import run_fade
from . import properties
from ..shared import sollumz_integration as szi
from ..shared import vertex_color

# Span of the UV island's longer axis once fitted into the 0..1 square. Kept
# here rather than imported from operators.py, which imports this module.
Expand Down Expand Up @@ -227,6 +228,7 @@ def rebuild(obj):
materials = list(old_mesh.materials)

new_mesh = geometry.create_mesh_from_strip_data(old_mesh.name, strip_data)
vertex_color.apply_preset(data, data.color_preset)
loop_uv, loop_rgba = geometry.compute_loop_uv_and_alpha(
new_mesh, strip_data, color_rgb=tuple(data.color_rgb)
)
Expand Down
2 changes: 2 additions & 0 deletions seto_tools/fake_damage/operators.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from ..shared import run_fade
from ..shared import sollumz_integration as szi
from ..shared import strip_settings
from ..shared import vertex_color

# What a generated strip is called. The tool is Edge Wear, so that is what its
# output says - "fake_dmg_003" in the outliner named a tool that no longer
Expand Down Expand Up @@ -208,6 +209,7 @@ def execute(self, context):

new_name = _next_fake_damage_name()
new_mesh = geometry.create_mesh_from_strip_data(new_name, strip_data)
vertex_color.apply_preset(settings, settings.color_preset)
loop_uv, loop_rgba = geometry.compute_loop_uv_and_alpha(
new_mesh, strip_data, color_rgb=tuple(settings.color_rgb)
)
Expand Down
7 changes: 7 additions & 0 deletions seto_tools/fake_damage/properties.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,13 @@ def settings_annotations(update=None):
default=False,
update=update,
),
"color_preset": bpy.props.EnumProperty(
name="Color Preset",
description="Quick-pick a named vertex colour, or choose Custom to set your own",
items=vertex_color.enum_items(),
default='GREEN',
update=update,
),
"color_rgb": bpy.props.FloatVectorProperty(
name="Color 1 RGB",
description="RGB written to Color 1 for every vertex; only alpha differs between corner and outer edge",
Expand Down
6 changes: 6 additions & 0 deletions seto_tools/fake_damage/ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,12 @@ def draw(self, context):
col.prop(data, "alpha_bottom")
col.prop(data, "alpha_top")

col = pl.section(layout, "Vertex Colour", 'COLOR')
col.prop(data, "color_preset", text="")
row = col.row()
row.enabled = data.color_preset == 'CUSTOM'
row.prop(data, "color_rgb", text="")

col = pl.section(layout, "Texture Placement", 'UV')
col.prop(data, "uv_scale")
col.prop(data, "uv_offset")
Expand Down
8 changes: 8 additions & 0 deletions seto_tools/shared/strip_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"alpha_bottom",
"alpha_top",
"invert_fade",
"color_preset",
"color_rgb",
"flip_direction",
"material_mode",
Expand Down Expand Up @@ -145,6 +146,13 @@ def annotations(update=None):
default=False,
update=update,
),
"color_preset": bpy.props.EnumProperty(
name="Color Preset",
description="Quick-pick a named vertex colour, or choose Custom to set your own",
items=vertex_color.enum_items(),
default='GREEN',
update=update,
),
"color_rgb": bpy.props.FloatVectorProperty(
name="Color 1 RGB",
description=(
Expand Down
35 changes: 35 additions & 0 deletions seto_tools/shared/vertex_color.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,41 @@
# (R, G, B) written to Color 1 on every generated vertex.
DEFAULT_RGB = (0.0, 0.7, 0.0)

# Named presets for quick selection. Each is (enum_id, label, description, rgb).
# 'CUSTOM' lets the user pick any colour via the Color 1 RGB swatch.
COLOR_PRESETS = (
('GREEN', "Green", "Default green (0, 0.7, 0)", (0.0, 0.7, 0.0)),
('RED', "Red", "Red (1, 0, 0)", (1.0, 0.0, 0.0)),
('WHITE', "White", "White (1, 1, 1)", (1.0, 1.0, 1.0)),
('BLUE', "Blue", "Blue (0, 0, 0.1)", (0.0, 0.0, 0.1)),
('YELLOW', "Yellow", "Yellow (0.8, 0.7, 0)", (0.8, 0.7, 0.0)),
('CUSTOM', "Custom", "Pick any colour with the swatch", None),
)


def enum_items():
"""EnumProperty items list built from COLOR_PRESETS."""
return [(pid, label, desc) for pid, label, desc, _rgb in COLOR_PRESETS]


def rgb_for_preset(preset_id):
"""Return the (R, G, B) tuple for a preset, or None for CUSTOM."""
for pid, _label, _desc, rgb in COLOR_PRESETS:
if pid == preset_id:
return rgb
return None

# Alpha at the corner/centre of a strip, and at its outer edge.
DEFAULT_ALPHA_CENTER = 1.0
DEFAULT_ALPHA_OUTER = 0.0


def apply_preset(data, preset_id):
"""Set data.color_rgb to match the named preset, if it is not CUSTOM.

Called from property update callbacks so the swatch stays in sync with the
dropdown. Does nothing for CUSTOM - the user is in control.
"""
rgb = rgb_for_preset(preset_id)
if rgb is not None:
data.color_rgb = rgb
Loading