Add gender-specific character slot nodes

This commit is contained in:
2026-06-24 15:30:48 +02:00
parent a7743cfd4b
commit cad2f4b4e4
3 changed files with 196 additions and 11 deletions
+20 -11
View File
@@ -14,6 +14,8 @@ The node is registered as:
- `prompt_builder / SxCP Generation Profile`
- `prompt_builder / SxCP Advanced Filters`
- `prompt_builder / SxCP Prompt Builder From Configs`
- `prompt_builder / SxCP Woman Slot`
- `prompt_builder / SxCP Man Slot`
- `prompt_builder / SxCP Character Slot`
- `prompt_builder / SxCP Character Profile Save`
- `prompt_builder / SxCP Character Profile Load`
@@ -53,7 +55,7 @@ The practical compact workflow is:
`Category Preset` + `Cast Control` + `Generation Profile` + optional
`Advanced Filters`, `Seed Locker` or `Seed Control`, `Camera Control`,
`Character Slot`, and `Character Profile`
`Woman Slot` / `Man Slot`, and `Character Profile`
into `Prompt Builder From Configs`.
An importable default workflow is included at
@@ -70,11 +72,18 @@ as one long chain:
## Character Profiles
`SxCP Character Slot` is the scalable per-participant control node. Each slot
defines one woman or man with optional overrides for age, ethnicity, figure,
body/body phrase, skin, hair, and eyes. Leave any field on `random` or blank to
let the generator fill that part from the normal pools; set exact values only
where you want control.
`SxCP Woman Slot` and `SxCP Man Slot` are the scalable per-participant control
nodes. `Cast Control` still decides how many women and men are generated; slot
nodes decide who those people are. Each slot defines one participant with
optional overrides for age, ethnicity, body/body phrase, skin, hair, and eyes.
Leave any field on `random` or blank to let the generator fill that part from
the normal pools; set exact values only where you want control.
Use `Woman Slot` for women because it exposes woman-focused body choices and a
`figure_bias` selector. Use `Man Slot` for men because it exposes man-focused
body choices and omits figure bias. The older generic `SxCP Character Slot`
remains available for compatibility and manual mixed use, but the gendered
slots are the cleaner default.
Slots are chainable through the `character_cast` input/output. In automatic
label mode, the slot closest to the final generator becomes `A` for its gender,
@@ -225,11 +234,11 @@ and location. The generated positive prompts are still standalone: each output
lists the relevant cast descriptors directly and does not depend on the image
model carrying context from another prompt.
For per-character control, chain `SxCP Character Slot` nodes into the pair
node's `character_cast` input. The nearest woman slot controls the shared
primary creator (`Woman A`) in both softcore and hardcore outputs; additional
woman/man slots fill partner descriptors before random fallback descriptors are
used.
For per-character control, chain `SxCP Woman Slot` and `SxCP Man Slot` nodes
into the pair node's `character_cast` input. The nearest woman slot controls
the shared primary creator (`Woman A`) in both softcore and hardcore outputs;
additional woman/man slots fill partner descriptors before random fallback
descriptors are used.
It outputs:
+132
View File
@@ -34,7 +34,9 @@ try:
character_ethnicity_choices,
character_figure_choices,
character_label_choices,
character_man_body_choices,
character_profile_choices,
character_woman_body_choices,
ethnicity_choices,
generation_profile_choices,
load_character_profile_json,
@@ -74,7 +76,9 @@ except ImportError:
character_ethnicity_choices,
character_figure_choices,
character_label_choices,
character_man_body_choices,
character_profile_choices,
character_woman_body_choices,
ethnicity_choices,
generation_profile_choices,
load_character_profile_json,
@@ -606,6 +610,130 @@ class SxCPCharacterSlot:
return result["character_cast"], result["character_slot"], result["summary"], result["status"]
class SxCPWomanSlot:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"enabled": ("BOOLEAN", {"default": True}),
"label": (character_label_choices(), {"default": "auto_chain"}),
"age": (character_age_choices(), {"default": "random"}),
"manual_age": ("STRING", {"default": ""}),
"ethnicity": (character_ethnicity_choices(), {"default": "random"}),
"figure_bias": (character_figure_choices(), {"default": "random"}),
"body": (character_woman_body_choices(), {"default": "random"}),
"manual_body": ("STRING", {"default": ""}),
"body_phrase": ("STRING", {"default": ""}),
"skin": ("STRING", {"default": ""}),
"hair": ("STRING", {"default": ""}),
"eyes": ("STRING", {"default": ""}),
},
"optional": {
"character_cast": ("STRING", {"default": "", "multiline": True}),
},
}
RETURN_TYPES = ("STRING", "STRING", "STRING", "STRING")
RETURN_NAMES = ("character_cast", "character_slot", "summary", "status")
FUNCTION = "build"
CATEGORY = "prompt_builder"
def build(
self,
enabled,
label,
age,
manual_age,
ethnicity,
figure_bias,
body,
manual_body,
body_phrase,
skin,
hair,
eyes,
character_cast="",
):
result = build_character_slot_json(
subject_type="woman",
label=label,
age=age,
manual_age=manual_age,
ethnicity=ethnicity,
figure=figure_bias,
body=body,
manual_body=manual_body,
body_phrase=body_phrase,
skin=skin,
hair=hair,
eyes=eyes,
enabled=enabled,
character_cast=character_cast or "",
)
return result["character_cast"], result["character_slot"], result["summary"], result["status"]
class SxCPManSlot:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"enabled": ("BOOLEAN", {"default": True}),
"label": (character_label_choices(), {"default": "auto_chain"}),
"age": (character_age_choices(), {"default": "random"}),
"manual_age": ("STRING", {"default": ""}),
"ethnicity": (character_ethnicity_choices(), {"default": "random"}),
"body": (character_man_body_choices(), {"default": "random"}),
"manual_body": ("STRING", {"default": ""}),
"body_phrase": ("STRING", {"default": ""}),
"skin": ("STRING", {"default": ""}),
"hair": ("STRING", {"default": ""}),
"eyes": ("STRING", {"default": ""}),
},
"optional": {
"character_cast": ("STRING", {"default": "", "multiline": True}),
},
}
RETURN_TYPES = ("STRING", "STRING", "STRING", "STRING")
RETURN_NAMES = ("character_cast", "character_slot", "summary", "status")
FUNCTION = "build"
CATEGORY = "prompt_builder"
def build(
self,
enabled,
label,
age,
manual_age,
ethnicity,
body,
manual_body,
body_phrase,
skin,
hair,
eyes,
character_cast="",
):
result = build_character_slot_json(
subject_type="man",
label=label,
age=age,
manual_age=manual_age,
ethnicity=ethnicity,
figure="random",
body=body,
manual_body=manual_body,
body_phrase=body_phrase,
skin=skin,
hair=hair,
eyes=eyes,
enabled=enabled,
character_cast=character_cast or "",
)
return result["character_cast"], result["character_slot"], result["summary"], result["status"]
class SxCPCharacterProfileSave:
@classmethod
def INPUT_TYPES(cls):
@@ -985,6 +1113,8 @@ NODE_CLASS_MAPPINGS = {
"SxCPGenerationProfile": SxCPGenerationProfile,
"SxCPAdvancedFilters": SxCPAdvancedFilters,
"SxCPPromptBuilderFromConfigs": SxCPPromptBuilderFromConfigs,
"SxCPWomanSlot": SxCPWomanSlot,
"SxCPManSlot": SxCPManSlot,
"SxCPCharacterSlot": SxCPCharacterSlot,
"SxCPCharacterProfileSave": SxCPCharacterProfileSave,
"SxCPCharacterProfileLoad": SxCPCharacterProfileLoad,
@@ -1004,6 +1134,8 @@ NODE_DISPLAY_NAME_MAPPINGS = {
"SxCPGenerationProfile": "SxCP Generation Profile",
"SxCPAdvancedFilters": "SxCP Advanced Filters",
"SxCPPromptBuilderFromConfigs": "SxCP Prompt Builder From Configs",
"SxCPWomanSlot": "SxCP Woman Slot",
"SxCPManSlot": "SxCP Man Slot",
"SxCPCharacterSlot": "SxCP Character Slot",
"SxCPCharacterProfileSave": "SxCP Character Profile Save",
"SxCPCharacterProfileLoad": "SxCP Character Profile Load",
+44
View File
@@ -137,6 +137,42 @@ CHARACTER_BODY_CHOICES = [
"broad",
"muscular",
]
CHARACTER_WOMAN_BODY_CHOICES = [
"random",
"manual",
"slim",
"petite adult",
"toned",
"athletic",
"average",
"curvy",
"soft curvy",
"curvy athletic",
"hourglass",
"slim busty",
"busty",
"busty curvy",
"voluptuous",
"plus-size",
"heavyset",
"fat",
]
CHARACTER_MAN_BODY_CHOICES = [
"random",
"manual",
"slim",
"lean",
"lean athletic",
"toned",
"average",
"athletic",
"muscular",
"broad",
"broad-shouldered",
"stocky",
"heavyset",
"fat",
]
CHARACTER_RANDOM_TOKENS = {"", "random", "auto", "global", "from_global", "default"}
CAMERA_DETAIL_CHOICES = ["off", "compact", "full"]
@@ -1296,6 +1332,14 @@ def character_body_choices() -> list[str]:
return list(CHARACTER_BODY_CHOICES)
def character_woman_body_choices() -> list[str]:
return list(CHARACTER_WOMAN_BODY_CHOICES)
def character_man_body_choices() -> list[str]:
return list(CHARACTER_MAN_BODY_CHOICES)
def character_ethnicity_choices() -> list[str]:
return ["random"] + list(ETHNICITY_FILTER_CHOICES)