nx_char_layers.nx source
↩ module page · 131 lines · 7979 B
1// nx_char_layers.nx -- the LAYER STACK (operator: "a nude base + add layers of armor/clothing/makeup/other stuff").
2// A character = a licensed nude BASE (variant + figure) + an ORDERED stack of layers, each applied in turn so
3// outer layers sit over inner. Layer types: OUTFIT (nx_outfit clothing/armor), ACCESSORY (belt/hat/necklace = extra
4// worn geometry), MAKEUP (a face-region cosmetic -- rendered in the charlab detailed-face pipeline, modeled here so
5// the stack + consent tier know about it). Generalizes the single nx_outfit slot into a composable stack; the
6// delivered nudity tier (nx_char_asset) reads the stack for a covering outfit. license_tier: ORIGINAL
7import "nx_syscalls.nx"
8import "nx_sdfrender.nx"
9import "nx_outfit.nx"
10const LT_MAGIC_1405: i64 = 1405
11const LT_MAGIC_1330: i64 = 1330
12const LT_MAGIC_1350: i64 = 1350
13const LT_MAGIC_1180: i64 = 1180
14
15// layer types (operator: "really layer and change things" -- clothing/armor/makeup/jewelry/piercings/body-paint/
16// body-hair grooming/hairstyles, all as data on the stack, on synthetic OR licensed-real characters)
17const LT_OUTFIT: i64 = 1 // clothing/armor via nx_outfit (tunic/robe/cloak/armor)
18const LT_ACCESSORY: i64 = 2 // worn geometry: belt / hat / necklace
19const LT_MAKEUP: i64 = 3 // face cosmetic (charlab face pipeline; nx_makeup)
20const LT_PIERCING: i64 = 4 // body jewelry / piercings (small metal studs at anatomical points)
21const LT_BODYHAIR: i64 = 5 // body-hair grooming STATE (id: 0 natural / 1 shaved / 2 trimmed) -- a render nuance
22const LT_BODYPAINT: i64 = 6 // body paint (a colored material on a body region)
23const LT_HAIRSTYLE: i64 = 7 // head hairstyle variant
24// accessories
25const ACC_BELT: i64 = 1
26const ACC_HAT: i64 = 2
27const ACC_NECKLACE: i64 = 3
28// piercings (small metal studs; tiny geometry at anatomical points)
29const PIERCE_NAVEL: i64 = 1
30const PIERCE_COLLAR: i64 = 2
31
32// add an accessory's geometry on the humanoid body (reuses nx_outfit's of_part; existing cloth/metal materials).
33func acc_apply(base: i64, n0: i64, id: i64) -> i64 {
34 var n: i64 = n0
35 if id == ACC_BELT { n = of_part(base, n, 0, 480, 30, 272, 62, 226, 8) } // metal-buckled waist belt (mat 8)
36 if id == ACC_HAT { n = of_part(base, n, 0, LT_MAGIC_1405, 0, 258, 150, 258, 5) } // crimson cap over the crown (mat 5)
37 if id == ACC_NECKLACE { n = of_part(base, n, 0, 950, 70, 168, 52, 150, 8) } // metal choker at the throat (mat 8)
38 let np: *i64 = (base + O_NPART) as *i64; np[0] = n
39 return n
40}
41// add piercing geometry: small metal studs at anatomical points (reuses of_part, mat 8 = metal).
42func pierce_apply(base: i64, n0: i64, id: i64) -> i64 {
43 var n: i64 = n0
44 if id == PIERCE_NAVEL { n = of_part(base, n, 0, 130, 285, 34, 34, 46, 8) } // navel stud
45 if id == PIERCE_COLLAR { n = of_part(base, n, 0-140, 900, 225, 30, 30, 30, 8); n = of_part(base, n, 140, 900, 225, 30, 30, 30, 8) } // collarbone studs
46 let np: *i64 = (base + O_NPART) as *i64; np[0] = n
47 return n
48}
49// body paint: recolor a body REGION by swapping its part material (like makeup but on the body). id selects a
50// preset; reuses cloth/paint materials. Applies to av_base_human part indices (2=chest,5-8=arms,9-10=legs).
51func bodypaint_apply(base: i64, id: i64) -> i64 {
52 if id == 1 { sdf_set_mat(base, 5, 7); sdf_set_mat(base, 8, 7) } // green paint on both upper arms (tribal)
53 if id == 2 { sdf_set_mat(base, 2, 5) } // crimson paint across the chest
54 return 0
55}
56// ★body-hair GROOMING (operator "body-hair grooming e.g. shave pubic/arm" -- the render half landed in
57// nx_sdfrender mats 14 natural / 15 trimmed = HAIRY SKIN, full skin pipeline + stubble flecks 38%/15%; this is
58// the layer half, landed 2026-07-07). PER-REGION as data: stack id = region*4 + state.
59// region: 0=ALL 1=ARMS (av parts 5-8) 2=LEGS (9-10) 3=CHEST (2) 4=PELVIC (4)
60// state: 1=NATURAL (mat 14) 2=TRIMMED (mat 15) 3=SHAVED (mat 0 = plain skin, explicit bare)
61// Stacks compose in order: [groom_id(0,1), groom_id(4,3), groom_id(1,3)] = natural all over, then pubic +
62// arms SHAVED -- the operator's exact ask. Shaved-after-natural returns the region byte-identical to bare.
63const GROOM_NATURAL: i64 = 1
64const GROOM_TRIMMED: i64 = 2
65const GROOM_SHAVED: i64 = 3
66const HREG_ALL: i64 = 0
67const HREG_ARMS: i64 = 1
68const HREG_LEGS: i64 = 2
69const HREG_CHEST: i64 = 3
70const HREG_PELVIC: i64 = 4
71func groom_id(region: i64, state: i64) -> i64 { return region * 4 + state }
72func bh_region(base: i64, region: i64, mat: i64) -> i64 {
73 if region == HREG_ARMS { sdf_set_mat(base, 5, mat); sdf_set_mat(base, 6, mat); sdf_set_mat(base, 7, mat); sdf_set_mat(base, 8, mat) }
74 if region == HREG_LEGS { sdf_set_mat(base, 9, mat); sdf_set_mat(base, 10, mat) }
75 if region == HREG_CHEST { sdf_set_mat(base, 2, mat) }
76 if region == HREG_PELVIC { sdf_set_mat(base, 4, mat) }
77 return 0
78}
79func bodyhair_apply(base: i64, id: i64) -> i64 {
80 let region: i64 = id / 4
81 let state: i64 = id % 4
82 if state == 0 { return 0 } // malformed id -> no-op (fail-closed)
83 var mat: i64 = 0 // SHAVED = back to plain skin
84 if state == GROOM_NATURAL { mat = 14 }
85 if state == GROOM_TRIMMED { mat = 15 }
86 if region == HREG_ALL {
87 bh_region(base, HREG_ARMS, mat)
88 bh_region(base, HREG_LEGS, mat)
89 bh_region(base, HREG_CHEST, mat)
90 bh_region(base, HREG_PELVIC, mat)
91 } else { bh_region(base, region, mat) }
92 return 0
93}
94// ★HAIRSTYLE on the VARIANT body head (av_base_human head part 0 at y=1250; landed 2026-07-07): appended
95// mat-2 HAIR volume, so the body render (synth bestiary scale) wears hair too. ids: 1 bob cap · 2 long
96// (cap + back-fall down the back, +z) · 3 pixie (tight cap) · 4 ponytail (cap + high tail) · 5 bald (no
97// parts -- the bare av head IS bald). The DETAILED close-up face has its own variants: sdf_face_hairstyle.
98func hairstyle_apply(base: i64, n0: i64, id: i64) -> i64 {
99 var n: i64 = n0
100 if id == 1 { n = of_part(base, n, 0, LT_MAGIC_1330, 40, 245, 240, 250, 2) }
101 if id == 2 { n = of_part(base, n, 0, LT_MAGIC_1330, 40, 245, 240, 250, 2); n = of_part(base, n, 0, 1020, 250, 210, 430, 130, 2) }
102 if id == 3 { n = of_part(base, n, 0, LT_MAGIC_1350, 25, 232, 215, 240, 2) }
103 if id == 4 { n = of_part(base, n, 0, LT_MAGIC_1330, 40, 245, 240, 250, 2); n = of_part(base, n, 0, LT_MAGIC_1180, 340, 90, 320, 90, 2) }
104 let np: *i64 = (base + O_NPART) as *i64; np[0] = n
105 return n
106}
107// apply an ordered layer stack. stack = i64[2*nl]: [type,id] pairs, applied in order. Returns the new part count.
108func char_layers_apply(base: i64, n0: i64, stack: *i64, nl: i64) -> i64 {
109 var n: i64 = n0
110 var i: i64 = 0
111 while i < nl {
112 let ty: i64 = stack[i*2]
113 let id: i64 = stack[i*2+1]
114 if ty == LT_OUTFIT { if id > 0 { n = outfit_apply(base, n, id) } }
115 if ty == LT_ACCESSORY { if id > 0 { n = acc_apply(base, n, id) } }
116 if ty == LT_PIERCING { if id > 0 { n = pierce_apply(base, n, id) } }
117 if ty == LT_BODYPAINT { if id > 0 { bodypaint_apply(base, id) } }
118 if ty == LT_BODYHAIR { if id > 0 { bodyhair_apply(base, id) } }
119 if ty == LT_HAIRSTYLE { if id > 0 { if id < 5 { n = hairstyle_apply(base, n, id) } } } // 5 = bald = the bare head
120 // LT_MAKEUP: face cosmetic -- executes on the DETAILED face pipeline (nx_makeup makeup_apply2 +
121 // nx_sdfrender sdf_face_hairstyle for the close-up head); recorded here for stack + delivered-tier logic.
122 i = i + 1
123 }
124 return n
125}
126// does the stack put a covering outfit on the base? (drives the delivered nudity tier in nx_char_asset)
127func stack_has_outfit(stack: *i64, nl: i64) -> i64 {
128 var i: i64 = 0
129 while i < nl { if stack[i*2] == LT_OUTFIT { if stack[i*2+1] > 0 { return 1 } } i = i + 1 }
130 return 0
131}