Add files via upload

This commit is contained in:
Fictiverse
2026-02-12 07:05:57 +01:00
committed by GitHub
parent 2be2803480
commit 3cc04b022c
2 changed files with 388 additions and 0 deletions
+54
View File
@@ -0,0 +1,54 @@
# ---------------------------
# Node: Camera Settings
# ---------------------------
class FV_Camera_Settings:
CAMERA_DESCRIPTIONS = {
"None": "",
"Orthographic 3-view": "Orthographic character sheet with 3 views, one side view, one front view, and one back view, neutral stance, consistent proportions across all views.",
"Medium shot": "Three-quarter length medium shot, focusing on the character from waist up, neutral pose, balanced composition.",
"Close-up": "Close-up shot of the character's face, highlighting expressions and facial details, neutral lighting.",
"Long shot": "Full-body long shot showing the character in their environment, maintaining correct proportions and perspective.",
"Bird's eye view": "Top-down bird's eye view of the character, showing layout and surroundings from above.",
"Worm's eye view": "Low-angle worm's eye view looking up at the character, emphasizing height and power.",
"Over-the-shoulder": "Over-the-shoulder shot showing the character from behind another character, perspective focused on interaction.",
"First-person": "First-person perspective from the character's viewpoint, immersive and realistic.",
"Panoramic": "Wide panoramic view capturing the character and surroundings in a broad context.",
"Wide-angle": "Wide-angle shot exaggerating perspective and depth, character remains central focus.",
"Telephoto": "Telephoto shot compressing distance, focusing tightly on the character from afar.",
"Portrait": "Portrait-style framing with character centered, neutral background, emphasizing face and upper body.",
"Landscape": "Landscape-style framing showing character in wide environment, emphasizing scenery and context."
}
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"camera_type": (tuple(cls.CAMERA_DESCRIPTIONS.keys()),),
},
"optional": {
"prefix": ("*", {}),
}
}
@classmethod
def VALIDATE_INPUTS(cls, input_types):
return True
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("camera_text",)
FUNCTION = "apply"
CATEGORY = "Fictiverse/Camera"
def apply(self, camera_type, prefix=""):
description = self.CAMERA_DESCRIPTIONS.get(camera_type, "")
if not description:
return (prefix,)
return (f"{prefix}{description}",)
# ---------------------------
# Node registration
# ---------------------------
NODE_CLASS_MAPPINGS = {
"Camera Settings": FV_Camera_Settings,
}
+334
View File
@@ -0,0 +1,334 @@
import nodes
# ---------------------------
# Node: Character Appearance
# ---------------------------
class FV_Character_Appearance:
GENDERS = ["None", "Male", "Female"]
BODY_TYPES = ["None", "Skinny", "Slim", "Athletic", "Muscular", "Curvy", "Heavy", "Very fat"]
HEIGHTS = ["None", "Small", "Tall", "Gigantic"]
SKIN_TONES = ["None", "Pale", "Fair", "Olive", "Dark"]
HAIR_STYLES = ["None", "Short", "Long", "Curly", "Bald"]
HAIR_COLORS = ["None", "Black", "Brown", "Blonde", "Red", "Gray", "Blue", "Green", "Purple", "White"]
EYE_COLORS = ["None", "Blue", "Green", "Brown", "Hazel"]
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"gender": (cls.GENDERS,),
"body_type": (cls.BODY_TYPES,),
"height": (cls.HEIGHTS,),
"skin_tone": (cls.SKIN_TONES,),
"hair_style": (cls.HAIR_STYLES,),
"hair_color": (cls.HAIR_COLORS,),
"eye_color": (cls.EYE_COLORS,),
},
"optional": {
"prefix": ("*", {}),
}
}
@classmethod
def VALIDATE_INPUTS(cls, input_types):
return True # Permet de bypasser la validation du type
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("appearance_text",)
FUNCTION = "apply"
CATEGORY = "Fictiverse/Character"
def apply(self, gender, body_type, height, skin_tone, hair_style, hair_color, eye_color, prefix=""):
gender_txt = "person" if gender == "None" else gender.lower()
parts = []
if body_type != "None":
parts.append(body_type.lower())
if height != "None":
parts.append(height.lower())
if skin_tone != "None":
parts.append(skin_tone.lower() + " skin")
parts.append(gender_txt)
text = "A " + " ".join(parts)
eye_text = None
if eye_color != "None":
eye_text = f"{eye_color.lower()} eyes"
hair_parts = []
if hair_color != "None":
hair_parts.append(hair_color.lower())
if hair_style != "None":
hair_parts.append(hair_style.lower())
hair_text = None
if hair_parts:
if len(hair_parts) == 1:
hair_text = f"{hair_parts[0]} hair"
else:
hair_text = f"{' '.join(hair_parts)} haircut"
extras = []
if eye_text:
extras.append(eye_text)
if hair_text:
extras.append(hair_text)
if extras:
text += " with " + " and ".join(extras)
text += "."
return (f"{prefix}{text}",)
# ---------------------------
# Node: Character Makeup
# ---------------------------
class FV_Character_Makeup:
MAKEUP_STYLES = ["None", "Natural", "Glam", "Theatrical"]
FOUNDATIONS = ["None", "Light", "Medium", "Dark"]
BLUSHES = ["None", "Pink", "Peach", "Bronze"]
EYESHADOWS = ["None", "Neutral", "Smoky", "Colorful"]
EYELINERS = ["None", "Winged", "Smudged", "Cat-eye"]
MASCARA = ["None", "True", "False"]
LIPSTICKS = ["None", "Red", "Pink", "Nude", "Dark"]
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"makeup_style": (cls.MAKEUP_STYLES,),
"foundation": (cls.FOUNDATIONS,),
"blush": (cls.BLUSHES,),
"eyeshadow": (cls.EYESHADOWS,),
"eyeliner": (cls.EYELINERS,),
"mascara": (cls.MASCARA,),
"lipstick": (cls.LIPSTICKS,),
},
"optional": {
"prefix": ("*", {}),
}
}
@classmethod
def VALIDATE_INPUTS(cls, input_types):
return True # Permet de bypasser la validation du type
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("makeup_text",)
FUNCTION = "apply"
CATEGORY = "Fictiverse/Character"
def apply(self, makeup_style, foundation, blush, eyeshadow, eyeliner, mascara, lipstick, prefix=""):
parts = []
if makeup_style != "None":
parts.append(makeup_style.lower())
if foundation != "None":
parts.append(f"{foundation.lower()} foundation")
if blush != "None":
parts.append(f"{blush.lower()} blush")
if eyeshadow != "None":
parts.append(f"{eyeshadow.lower()} eyeshadow")
if eyeliner != "None":
parts.append(f"{eyeliner.lower()} eyeliner")
if mascara == "True":
parts.append("mascara")
elif mascara == "False":
parts.append("no mascara")
if lipstick != "None":
parts.append(f"{lipstick.lower()} lipstick")
if not parts:
return (prefix,)
if len(parts) == 1:
text = f"makeup with {parts[0]}"
else:
text = "makeup with " + ", ".join(parts[:-1]) + " and " + parts[-1]
text += "."
return (f"{prefix}{text}",)
# ---------------------------
# Node: Character Clothing
# ---------------------------
class FV_Character_Clothing:
TOPS = ["None", "No", "Shirt", "Blouse", "Jacket", "Coat", "Sweater", "Hoodie"]
BOTTOMS = ["None", "No", "Pants", "Skirt", "Shorts", "Leggings"]
FULL_BODY = ["None", "No", "Dress", "Suit", "Armor"]
COLORS = ["None", "Red", "Blue", "Green", "Black", "White"]
ACCESSORIES = ["None", "Belt", "Necklace", "Ring", "Hat"]
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"top": (cls.TOPS,),
"top_color": (cls.COLORS,),
"bottom": (cls.BOTTOMS,),
"bottom_color": (cls.COLORS,),
"full_body": (cls.FULL_BODY,),
"full_body_color": (cls.COLORS,),
"accessories": (cls.ACCESSORIES,),
},
"optional": {
"prefix": ("*", {}),
}
}
@classmethod
def VALIDATE_INPUTS(cls, input_types):
return True # Permet de bypasser la validation du type
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("clothing_text",)
FUNCTION = "apply"
CATEGORY = "Fictiverse/Character"
def apply(self, top, top_color, bottom, bottom_color, full_body, full_body_color, accessories, prefix=""):
parts = []
if full_body != "None":
if full_body == "No":
parts.append("nothing and is naked")
else:
fb_part = full_body.lower()
if full_body_color != "None":
fb_part += f" in {full_body_color.lower()}"
parts.append(fb_part)
else:
if top != "None":
if top == "No":
parts.append("nothing on top")
else:
t_part = top.lower()
if top_color != "None":
t_part += f" in {top_color.lower()}"
parts.append(t_part)
if bottom != "None":
if bottom == "No":
parts.append("nothing on bottom")
else:
b_part = bottom.lower()
if bottom_color != "None":
b_part += f" in {bottom_color.lower()}"
parts.append(b_part)
if accessories != "None":
parts.append(f"with {accessories.lower()}")
if not parts:
return (prefix,)
if len(parts) == 1:
text = f"wearing {parts[0]}"
else:
text = "wearing " + ", ".join(parts[:-1]) + " and " + parts[-1]
text += "."
return (f"{prefix}{text}",)
# ---------------------------
# Node: Character Expressions
# ---------------------------
class FV_Character_Expressions:
EXPRESSIONS = [
"happy",
"sad",
"angry",
"surprised",
"confused",
"tired",
"smiling",
"frowning",
"winking",
"terrified",
"shocked",
"panicked",
"nervous",
"excited",
"bored",
"disgusted",
"anxious",
"relieved"
]
@classmethod
def INPUT_TYPES(cls):
# Chaque expression est un booléen, plus un prefix wildcard
required = {expr: ("BOOLEAN",) for expr in cls.EXPRESSIONS}
optional = {
"prefix": ("*", {}) # Champ texte libre pour préfix
}
return {
"required": required,
"optional": optional,
}
@classmethod
def VALIDATE_INPUTS(cls, input_types):
return True
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("expression_text",)
FUNCTION = "apply"
CATEGORY = "Fictiverse/Character"
def apply(
self,
happy=False,
sad=False,
angry=False,
surprised=False,
confused=False,
tired=False,
smiling=False,
frowning=False,
winking=False,
terrified=False,
shocked=False,
panicked=False,
nervous=False,
excited=False,
bored=False,
disgusted=False,
anxious=False,
relieved=False,
prefix=None,
):
if prefix is None:
prefix = ""
# Accumuler les expressions activées
active_expressions = []
for expr_name in self.EXPRESSIONS:
if locals()[expr_name]:
active_expressions.append(expr_name)
if not active_expressions:
return (prefix,)
# Construire la phrase finale
if len(active_expressions) == 1:
text = f"{active_expressions[0]} expression"
else:
text = f"{', '.join(active_expressions[:-1])} and {active_expressions[-1]} expressions"
text += "."
return (f"{prefix}{text}",)
# ---------------------------
# Node registration
# ---------------------------
NODE_CLASS_MAPPINGS = {
"Character Appearance": FV_Character_Appearance,
"Character Makeup": FV_Character_Makeup,
"Character Clothing": FV_Character_Clothing,
"Character Expressions": FV_Character_Expressions,
}