| … | … | @@ -9,6 +9,7 @@ from ...ui_framework.master import Master |
| 9 | 9 | from ...ui_framework.utils.mods_list import get_mods_list |
| 10 | 10 | from ...utility import addon, method_handler |
| 11 | 11 | from ...utility.base_modal_controls import Base_Modal_Controls |
| 12 | from ...utility.geometry_nodes import compat | |
| 12 | 13 | from ...utils.cursor_warp import mouse_warp |
| 13 | 14 | from ...utils.modal_frame_drawing import draw_modal_frame |
| 14 | 15 | from ...utils.toggle_view3d_panels import collapse_3D_view_panels |
| … | … | @@ -45,8 +46,10 @@ class HOPS_OT_ToThread(bpy.types.Operator): |
| 45 | 46 | else: |
| 46 | 47 | self.modifier, self.table = self.new_modifier(object) |
| 47 | 48 | self.backup = None |
| 49 | ||
| 50 | self.inputs = compat.ModifierInputs(self.modifier) | |
| 48 | 51 | for key, value in parameters.items(): |
| 49 | self.modifier[self.table[key]] = value | |
| 52 | self.inputs[self.table[key]] = value | |
| 50 | 53 | |
| 51 | 54 | # States |
| 52 | 55 | self.mouse_mode = 'DEPTH' |
| … | … | @@ -135,12 +138,12 @@ class HOPS_OT_ToThread(bpy.types.Operator): |
| 135 | 138 | |
| 136 | 139 | # Direction |
| 137 | 140 | if event.shift: |
| 138 | self.modifier[self.table['Direction']] += self.base_controls.scroll * tau / self.modifier[self.table['Resolution']] | |
| 141 | self.inputs[self.table['Direction']] += self.base_controls.scroll * tau / self.inputs[self.table['Resolution']] | |
| 139 | 142 | self.refresh_modifier(self.modifier) |
| 140 | 143 | |
| 141 | 144 | # Turns |
| 142 | 145 | else: |
| 143 | self.modifier[self.table['Turns']] = clamp(self.modifier[self.table['Turns']] + self.base_controls.scroll, 1, 100) | |
| 146 | self.inputs[self.table['Turns']] = clamp(self.inputs[self.table['Turns']] + self.base_controls.scroll, 1, 100) | |
| 144 | 147 | self.refresh_modifier(self.modifier) |
| 145 | 148 | |
| 146 | 149 | |
| … | … | @@ -153,13 +156,13 @@ class HOPS_OT_ToThread(bpy.types.Operator): |
| 153 | 156 | |
| 154 | 157 | if abs(self.snap_buffer) > self.snap_break: |
| 155 | 158 | increment = 0.01 if event.shift else 0.05 |
| 156 | value = snap(self.modifier[self.table['Depth']] + copysign(increment, self.snap_buffer), increment) | |
| 157 | self.modifier[self.table['Depth']] = clamp(value, -100.0, 100.0) | |
| 159 | value = snap(self.inputs[self.table['Depth']] + copysign(increment, self.snap_buffer), increment) | |
| 160 | self.inputs[self.table['Depth']] = clamp(value, -100.0, 100.0) | |
| 158 | 161 | self.snap_buffer = 0.0 |
| 159 | 162 | |
| 160 | 163 | else: |
| 161 | value = self.modifier[self.table['Depth']] + self.base_controls.mouse | |
| 162 | self.modifier[self.table['Depth']] = clamp(value, -100.0, 100.0) | |
| 164 | value = self.inputs[self.table['Depth']] + self.base_controls.mouse | |
| 165 | self.inputs[self.table['Depth']] = clamp(value, -100.0, 100.0) | |
| 163 | 166 | |
| 164 | 167 | self.refresh_modifier(self.modifier) |
| 165 | 168 | |
| … | … | @@ -170,13 +173,13 @@ class HOPS_OT_ToThread(bpy.types.Operator): |
| 170 | 173 | |
| 171 | 174 | if abs(self.snap_buffer) > self.snap_break: |
| 172 | 175 | increment = 0.01 if event.shift else 0.05 |
| 173 | value = snap(self.modifier[self.table['Root']] + copysign(increment, self.snap_buffer), increment) | |
| 174 | self.modifier[self.table['Root']] = clamp(value, 0.0, 1.0) | |
| 176 | value = snap(self.inputs[self.table['Root']] + copysign(increment, self.snap_buffer), increment) | |
| 177 | self.inputs[self.table['Root']] = clamp(value, 0.0, 1.0) | |
| 175 | 178 | self.snap_buffer = 0.0 |
| 176 | 179 | |
| 177 | 180 | else: |
| 178 | value = self.modifier[self.table['Root']] + self.base_controls.mouse | |
| 179 | self.modifier[self.table['Root']] = clamp(value, 0.0, 1.0) | |
| 181 | value = self.inputs[self.table['Root']] + self.base_controls.mouse | |
| 182 | self.inputs[self.table['Root']] = clamp(value, 0.0, 1.0) | |
| 180 | 183 | |
| 181 | 184 | self.refresh_modifier(self.modifier) |
| 182 | 185 | |
| … | … | @@ -187,13 +190,13 @@ class HOPS_OT_ToThread(bpy.types.Operator): |
| 187 | 190 | |
| 188 | 191 | if abs(self.snap_buffer) > self.snap_break: |
| 189 | 192 | increment = 0.01 if event.shift else 0.05 |
| 190 | value = snap(self.modifier[self.table['Crest']] + copysign(increment, self.snap_buffer), increment) | |
| 191 | self.modifier[self.table['Crest']] = clamp(value, 0.0, 1.0) | |
| 193 | value = snap(self.inputs[self.table['Crest']] + copysign(increment, self.snap_buffer), increment) | |
| 194 | self.inputs[self.table['Crest']] = clamp(value, 0.0, 1.0) | |
| 192 | 195 | self.snap_buffer = 0.0 |
| 193 | 196 | |
| 194 | 197 | else: |
| 195 | value = self.modifier[self.table['Crest']] + self.base_controls.mouse | |
| 196 | self.modifier[self.table['Crest']] = clamp(value, 0.0, 1.0) | |
| 198 | value = self.inputs[self.table['Crest']] + self.base_controls.mouse | |
| 199 | self.inputs[self.table['Crest']] = clamp(value, 0.0, 1.0) | |
| 197 | 200 | |
| 198 | 201 | self.refresh_modifier(self.modifier) |
| 199 | 202 | |
| … | … | @@ -204,13 +207,13 @@ class HOPS_OT_ToThread(bpy.types.Operator): |
| 204 | 207 | |
| 205 | 208 | if abs(self.snap_buffer) > self.snap_break: |
| 206 | 209 | increment = radians(1.0) if event.shift else radians(5.0) |
| 207 | value = snap(self.modifier[self.table['Taper']] + copysign(increment, self.snap_buffer), increment) | |
| 208 | self.modifier[self.table['Taper']] = clamp(value, radians(15.0), radians(180.0)) | |
| 210 | value = snap(self.inputs[self.table['Taper']] + copysign(increment, self.snap_buffer), increment) | |
| 211 | self.inputs[self.table['Taper']] = clamp(value, radians(15.0), radians(180.0)) | |
| 209 | 212 | self.snap_buffer = 0.0 |
| 210 | 213 | |
| 211 | 214 | else: |
| 212 | value = self.modifier[self.table['Taper']] + self.base_controls.mouse | |
| 213 | self.modifier[self.table['Taper']] = clamp(value, radians(15.0), radians(180.0)) | |
| 215 | value = self.inputs[self.table['Taper']] + self.base_controls.mouse | |
| 216 | self.inputs[self.table['Taper']] = clamp(value, radians(15.0), radians(180.0)) | |
| 214 | 217 | |
| 215 | 218 | self.refresh_modifier(self.modifier) |
| 216 | 219 | |
| … | … | @@ -223,20 +226,20 @@ class HOPS_OT_ToThread(bpy.types.Operator): |
| 223 | 226 | # Main |
| 224 | 227 | win_list = [] |
| 225 | 228 | if addon.preference().ui.Hops_modal_fast_ui_loc_options != 1: |
| 226 | win_list.append(f'{self.modifier[self.table["Turns"]]}') | |
| 227 | win_list.append(f'{self.modifier[self.table["Depth"]]:.3f}') | |
| 228 | win_list.append(f'{self.modifier[self.table["Root"]]:.3f}') | |
| 229 | win_list.append(f'{self.modifier[self.table["Crest"]]:.3f}') | |
| 230 | win_list.append(f'{degrees(self.modifier[self.table["Taper"]]):.1f}°') | |
| 231 | win_list.append(f'{degrees(self.modifier[self.table["Direction"]]):.1f}°') | |
| 229 | win_list.append(f'{self.inputs[self.table["Turns"]]}') | |
| 230 | win_list.append(f'{self.inputs[self.table["Depth"]]:.3f}') | |
| 231 | win_list.append(f'{self.inputs[self.table["Root"]]:.3f}') | |
| 232 | win_list.append(f'{self.inputs[self.table["Crest"]]:.3f}') | |
| 233 | win_list.append(f'{degrees(self.inputs[self.table["Taper"]]):.1f}°') | |
| 234 | win_list.append(f'{degrees(self.inputs[self.table["Direction"]]):.1f}°') | |
| 232 | 235 | else: |
| 233 | 236 | win_list.append(self.modifier.name) |
| 234 | win_list.append(f'Turns: {self.modifier[self.table["Turns"]]}') | |
| 235 | win_list.append(f'Depth: {self.modifier[self.table["Depth"]]:.3f}') | |
| 236 | win_list.append(f'Root: {self.modifier[self.table["Root"]]:.3f}') | |
| 237 | win_list.append(f'Crest: {self.modifier[self.table["Crest"]]:.3f}') | |
| 238 | win_list.append(f'Taper: {degrees(self.modifier[self.table["Taper"]]):.1f}°') | |
| 239 | win_list.append(f'Direction: {degrees(self.modifier[self.table["Direction"]]):.1f}°') | |
| 237 | win_list.append(f'Turns: {self.inputs[self.table["Turns"]]}') | |
| 238 | win_list.append(f'Depth: {self.inputs[self.table["Depth"]]:.3f}') | |
| 239 | win_list.append(f'Root: {self.inputs[self.table["Root"]]:.3f}') | |
| 240 | win_list.append(f'Crest: {self.inputs[self.table["Crest"]]:.3f}') | |
| 241 | win_list.append(f'Taper: {degrees(self.inputs[self.table["Taper"]]):.1f}°') | |
| 242 | win_list.append(f'Direction: {degrees(self.inputs[self.table["Direction"]]):.1f}°') | |
| 240 | 243 | |
| 241 | 244 | # Help |
| 242 | 245 | help_items = {} |
| … | … | @@ -457,12 +460,13 @@ class HOPS_OT_ToThread(bpy.types.Operator): |
| 457 | 460 | continue |
| 458 | 461 | |
| 459 | 462 | # Vertex group |
| 460 | group = object.vertex_groups.find(modifier[table['Selection']]) | |
| 463 | inputs = compat.ModifierInputs(modifier) | |
| 464 | group = object.vertex_groups.find(inputs[table['Selection']]) | |
| 461 | 465 | if all(group in groups for groups in groups_per_vertex): |
| 462 | 466 | return modifier, table |
| 463 | 467 | |
| 464 | 468 | # Attribute |
| 465 | layer = bm.verts.layers.float.get(modifier[table['Selection']]) | |
| 469 | layer = bm.verts.layers.float.get(inputs[table['Selection']]) | |
| 466 | 470 | if layer and all(vert[layer] for vert in verts): |
| 467 | 471 | return modifier, table |
| 468 | 472 | |
| … | … | @@ -480,7 +484,7 @@ class HOPS_OT_ToThread(bpy.types.Operator): |
| 480 | 484 | modifier: bpy.types.NodesModifier = object.modifiers.new(group.name, 'NODES') |
| 481 | 485 | object.modifiers.move(len(object.modifiers) - 1, 0) |
| 482 | 486 | modifier.node_group, table = to_thread_nodes() |
| 483 | modifier[table['Selection']] = group.name | |
| 487 | compat.ModifierInputs(modifier)[table['Selection']] = group.name | |
| 484 | 488 | |
| 485 | 489 | try: |
| 486 | 490 | modifier.panels[0].is_open = False |
| … | … | @@ -492,9 +496,10 @@ class HOPS_OT_ToThread(bpy.types.Operator): |
| 492 | 496 | |
| 493 | 497 | def backup_modifier(self, modifier: bpy.types.NodesModifier, table: dict) -> dict: |
| 494 | 498 | backup = {} |
| 499 | inputs = compat.ModifierInputs(modifier) | |
| 495 | 500 | |
| 496 | 501 | for key in REQUIRED_SOCKETS: |
| 497 | value = modifier[table[key]] | |
| 502 | value = inputs[table[key]] | |
| 498 | 503 | |
| 499 | 504 | # Copy vector properties |
| 500 | 505 | if hasattr(value, 'to_list'): |
| … | … | @@ -506,8 +511,10 @@ class HOPS_OT_ToThread(bpy.types.Operator): |
| 506 | 511 | |
| 507 | 512 | |
| 508 | 513 | def restore_modifier(self, modifier: bpy.types.NodesModifier, table: dict, backup: dict): |
| 514 | inputs = compat.ModifierInputs(modifier) | |
| 515 | ||
| 509 | 516 | for key, value in backup.items(): |
| 510 | modifier[table[key]] = value | |
| 517 | inputs[table[key]] = value | |
| 511 | 518 | |
| 512 | 519 | self.refresh_modifier(modifier) |
| 513 | 520 |
| … | … | @@ -170,7 +170,7 @@ def get_node_graph_objects(mod): |
| 170 | 170 | link = obj_input.links[0] |
| 171 | 171 | if link.from_node.type != 'GROUP_INPUT' or link.from_socket.type != 'OBJECT': continue |
| 172 | 172 | |
| 173 | val = mod[link.from_socket.identifier] | |
| 173 | val = geometry_nodes.compat.ModifierInputs(mod)[link.from_socket.identifier] | |
| 174 | 174 | if val and val.type == 'MESH': |
| 175 | 175 | objects.append(val) |
| 176 | 176 |
| … | … | @@ -6,6 +6,7 @@ from ... utility import addon, method_handler, operator_override, context_copy |
| 6 | 6 | from ...ui_framework.master import Master |
| 7 | 7 | from ...ui_framework.utils.mods_list import get_mods_list |
| 8 | 8 | from ...utility.base_modal_controls import Base_Modal_Controls |
| 9 | from ...utility.geometry_nodes import compat | |
| 9 | 10 | |
| 10 | 11 | # Cursor Warp imports |
| 11 | 12 | from ...utils.toggle_view3d_panels import collapse_3D_view_panels |
| … | … | @@ -155,23 +156,23 @@ class RadArray(): |
| 155 | 156 | |
| 156 | 157 | @property |
| 157 | 158 | def count(self): |
| 158 | return self.modifier[self.input_map[1]] | |
| 159 | return self.inputs[self.input_map[1]] | |
| 159 | 160 | |
| 160 | 161 | @count.setter |
| 161 | 162 | def count(self, value): |
| 162 | 163 | if value < 1: |
| 163 | 164 | value = 1 |
| 164 | 165 | |
| 165 | self.modifier[self.input_map[1]] = value | |
| 166 | self.inputs[self.input_map[1]] = value | |
| 166 | 167 | self.update() |
| 167 | 168 | |
| 168 | 169 | @property |
| 169 | 170 | def axis(self): |
| 170 | return self.modifier[self.input_map[2]] | |
| 171 | return self.inputs[self.input_map[2]] | |
| 171 | 172 | |
| 172 | 173 | @axis.setter |
| 173 | 174 | def axis(self, value): |
| 174 | prop = self.modifier[self.input_map[2]] | |
| 175 | prop = self.inputs[self.input_map[2]] | |
| 175 | 176 | for i in range(3): |
| 176 | 177 | val = value[i] |
| 177 | 178 | if val > 1.0: val = 1.0 |
| … | … | @@ -183,7 +184,7 @@ class RadArray(): |
| 183 | 184 | |
| 184 | 185 | @property |
| 185 | 186 | def rotation(self): |
| 186 | return self.modifier[self.input_map[3]] | |
| 187 | return self.inputs[self.input_map[3]] | |
| 187 | 188 | |
| 188 | 189 | @rotation.setter |
| 189 | 190 | def rotation(self, value): |
| … | … | @@ -193,52 +194,52 @@ class RadArray(): |
| 193 | 194 | elif value > 360.0: |
| 194 | 195 | val = 360.0 |
| 195 | 196 | |
| 196 | self.modifier[self.input_map[3]] = val | |
| 197 | self.inputs[self.input_map[3]] = val | |
| 197 | 198 | self.update() |
| 198 | 199 | |
| 199 | 200 | @property |
| 200 | 201 | def offset(self): |
| 201 | return self.modifier[self.input_map[4]] | |
| 202 | return self.inputs[self.input_map[4]] | |
| 202 | 203 | |
| 203 | 204 | @offset.setter |
| 204 | 205 | def offset(self, value): |
| 205 | self.modifier[self.input_map[4]] = value | |
| 206 | self.inputs[self.input_map[4]] = value | |
| 206 | 207 | self.update() |
| 207 | 208 | |
| 208 | 209 | @property |
| 209 | 210 | def apply_scale(self): |
| 210 | return self.modifier[self.input_map[5]] | |
| 211 | return self.inputs[self.input_map[5]] | |
| 211 | 212 | |
| 212 | 213 | @apply_scale.setter |
| 213 | 214 | def apply_scale(self, value): |
| 214 | self.modifier[self.input_map[5]] = value | |
| 215 | self.inputs[self.input_map[5]] = value | |
| 215 | 216 | self.update() |
| 216 | 217 | |
| 217 | 218 | @property |
| 218 | 219 | def center_pivot(self): |
| 219 | return self.modifier[self.input_map[6]] | |
| 220 | return self.inputs[self.input_map[6]] | |
| 220 | 221 | |
| 221 | 222 | @center_pivot.setter |
| 222 | 223 | def center_pivot(self, value): |
| 223 | self.modifier[self.input_map[6]] = value | |
| 224 | self.inputs[self.input_map[6]] = value | |
| 224 | 225 | self.update() |
| 225 | 226 | |
| 226 | 227 | @property |
| 227 | 228 | def full_circle(self): |
| 228 | return self.modifier[self.input_map[7]] | |
| 229 | return self.inputs[self.input_map[7]] | |
| 229 | 230 | |
| 230 | 231 | @full_circle.setter |
| 231 | 232 | def full_circle(self, value): |
| 232 | self.modifier[self.input_map[7]] = value | |
| 233 | self.inputs[self.input_map[7]] = value | |
| 233 | 234 | self.update() |
| 234 | 235 | |
| 235 | 236 | @property |
| 236 | 237 | def pivot_offset(self): |
| 237 | return self.modifier[self.input_map[8]] | |
| 238 | return self.inputs[self.input_map[8]] | |
| 238 | 239 | |
| 239 | 240 | @pivot_offset.setter |
| 240 | 241 | def pivot_offset(self, value): |
| 241 | input = self.modifier[self.input_map[8]] | |
| 242 | input = self.inputs[self.input_map[8]] | |
| 242 | 243 | |
| 243 | 244 | input[0] = value[0] |
| 244 | 245 | input[1] = value[1] |
| … | … | @@ -247,11 +248,11 @@ class RadArray(): |
| 247 | 248 | |
| 248 | 249 | @property |
| 249 | 250 | def rotation_offset(self): |
| 250 | return self.modifier[self.input_map[9]] | |
| 251 | return self.inputs[self.input_map[9]] | |
| 251 | 252 | |
| 252 | 253 | @rotation_offset.setter |
| 253 | 254 | def rotation_offset(self, value): |
| 254 | input = self.modifier[self.input_map[9]] | |
| 255 | input = self.inputs[self.input_map[9]] | |
| 255 | 256 | |
| 256 | 257 | input[0] = value[0] |
| 257 | 258 | input[1] = value[1] |
| … | … | @@ -260,11 +261,11 @@ class RadArray(): |
| 260 | 261 | |
| 261 | 262 | @property |
| 262 | 263 | def self(self): |
| 263 | return self.modifier[self.input_map[10]] | |
| 264 | return self.inputs[self.input_map[10]] | |
| 264 | 265 | |
| 265 | 266 | @self.setter |
| 266 | 267 | def self(self, value): |
| 267 | self.modifier[self.input_map[10]] = value | |
| 268 | self.inputs[self.input_map[10]] = value | |
| 268 | 269 | self.update() |
| 269 | 270 | |
| 270 | 271 | def __init__(self, modifier) -> None: |
| … | … | @@ -275,6 +276,7 @@ class RadArray(): |
| 275 | 276 | self.input_map = [input.identifier for input in modifier.node_group.inputs] |
| 276 | 277 | |
| 277 | 278 | self.modifier = modifier |
| 279 | self.inputs = compat.ModifierInputs(modifier) | |
| 278 | 280 | self.st_offset = self.offset |
| 279 | 281 | self.index = self.start_index = modifier.id_data.modifiers.find(modifier.name) |
| 280 | 282 | |
| … | … | @@ -302,19 +304,19 @@ class RadArray(): |
| 302 | 304 | |
| 303 | 305 | for input in self.modifier.node_group.inputs[1:]: |
| 304 | 306 | if input.type in {'RGBA', 'VECTOR'}: |
| 305 | self.backup.append((input.identifier, self.modifier[input.identifier][:], True)) | |
| 307 | self.backup.append((input.identifier, self.inputs[input.identifier][:], True)) | |
| 306 | 308 | else: |
| 307 | self.backup.append((input.identifier, self.modifier[input.identifier], False)) | |
| 309 | self.backup.append((input.identifier, self.inputs[input.identifier], False)) | |
| 308 | 310 | |
| 309 | 311 | def restore_backup(self): |
| 310 | 312 | for name, value, iterable in self.backup: |
| 311 | 313 | if iterable: |
| 312 | prop = self.modifier[name] | |
| 314 | prop = self.inputs[name] | |
| 313 | 315 | for i, value in enumerate(value): |
| 314 | 316 | prop[i] = value |
| 315 | 317 | |
| 316 | 318 | else: |
| 317 | self.modifier[name] = value | |
| 319 | self.inputs[name] = value | |
| 318 | 320 | |
| 319 | 321 | self.update() |
| 320 | 322 |
| … | … | @@ -108,7 +108,7 @@ class HOPS_OT_bevel_helper(bpy.types.Operator): |
| 108 | 108 | |
| 109 | 109 | row = split.row() |
| 110 | 110 | row.alignment = 'LEFT' |
| 111 | prop_string = geometry_nodes.NodeBoolean.prop_draw_string | |
| 111 | prop_data = geometry_nodes.NodeBoolean.prop_draw_data | |
| 112 | 112 | |
| 113 | 113 | wrapper = geometry_nodes.NodeBoolean.new(mod) |
| 114 | 114 | |
| … | … | @@ -134,15 +134,15 @@ class HOPS_OT_bevel_helper(bpy.types.Operator): |
| 134 | 134 | swapoperator = row.operator('hops.bevel_helper_bool_swap', text='', icon_value=icon, emboss=False) |
| 135 | 135 | swapoperator.obj_name = obj.name |
| 136 | 136 | swapoperator.mod_name = mod.name |
| 137 | row.prop(mod, prop_string(mod, 'name'), text='', emboss=False) | |
| 137 | row.prop(*prop_data(mod, 'name'), text='', emboss=False) | |
| 138 | 138 | row.separator() |
| 139 | row.prop(mod, prop_string(mod, 'show_viewport'), text='', emboss=False) | |
| 139 | row.prop(*prop_data(mod, 'show_viewport'), text='', emboss=False) | |
| 140 | 140 | |
| 141 | 141 | if bpy.app.version > (2, 90, 0): |
| 142 | 142 | split = split.row(align=True).split(factor=0.3) |
| 143 | 143 | row = split.row(align=True) |
| 144 | 144 | row.alignment = 'LEFT' |
| 145 | row.prop(mod, prop_string(mod, 'solver'), text='', emboss=True) | |
| 145 | row.prop(*prop_data(mod, 'solver'), text='', emboss=True) | |
| 146 | 146 | |
| 147 | 147 | row = split.row(align=True) |
| 148 | 148 | row.alignment = 'RIGHT' |
| … | … | @@ -1,6 +1,7 @@ |
| 1 | 1 | import bpy |
| 2 | 2 | from bpy.types import Panel |
| 3 | 3 | from ... utility import addon, version |
| 4 | from ... utility.geometry_nodes import compat | |
| 4 | 5 | |
| 5 | 6 | mods_dic = { |
| 6 | 7 | 'ARMATURE': 'MOD_ARMATURE', |
| … | … | @@ -813,32 +814,35 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel): |
| 813 | 814 | for input in inputs: |
| 814 | 815 | if getattr(input, _attr) == _type: continue |
| 815 | 816 | |
| 816 | id = f'["{input.identifier}"]' | |
| 817 | use_attr_id = f'{input.identifier}_use_attribute' | |
| 817 | value_data, value_prop = compat.value_prop(md, input.identifier) | |
| 818 | if not value_data: continue | |
| 818 | 819 | |
| 819 | 820 | row = layout.row(align=True) |
| 820 | 821 | row.label(text=input.name) |
| 821 | 822 | |
| 822 | if use_attr_id in md: | |
| 823 | if compat.supports_attribute(md, input.identifier): | |
| 823 | 824 | |
| 824 | 825 | op = row.operator("object.geometry_nodes_input_attribute_toggle", text='', icon='SPREADSHEET') |
| 825 | 826 | if not use_interface: |
| 826 | 827 | op.prop_path = f"[\"{input.identifier}_use_attribute\"]" |
| 827 | 828 | else: |
| 828 | op.input_name=input.name | |
| 829 | # the operator resolves the socket by identifier, passing the name | |
| 830 | # is a no-op up to 5.1 and crashes 5.2 | |
| 831 | op.input_name=input.identifier | |
| 829 | 832 | op.modifier_name = md.name |
| 830 | 833 | |
| 831 | if md[use_attr_id]: | |
| 834 | if compat.get_use_attribute(md, input.identifier): | |
| 832 | 835 | HOPS_MT_helper_node_attr_search.__class__.modifier_name = md.name |
| 833 | HOPS_MT_helper_node_attr_search.__class__.attribute_name = f'{input.identifier}_attribute_name' | |
| 836 | HOPS_MT_helper_node_attr_search.__class__.attribute_name = input.identifier | |
| 837 | HOPS_MT_helper_node_attr_search.__class__.is_output = False | |
| 834 | 838 | |
| 835 | 839 | row.menu(HOPS_MT_helper_node_attr_search.bl_idname, text='', icon='DOWNARROW_HLT') |
| 836 | row.prop(md, f'["{input.identifier}_attribute_name"]', text='') | |
| 840 | row.prop(*compat.attribute_name_prop(md, input.identifier), text='') | |
| 837 | 841 | else: |
| 838 | row.prop(md, id, text='') | |
| 842 | row.prop(value_data, value_prop, text='') | |
| 839 | 843 | |
| 840 | 844 | else: |
| 841 | row.prop(md, id, text='') | |
| 845 | row.prop(value_data, value_prop, text='') | |
| 842 | 846 | |
| 843 | 847 | layout.separator() |
| 844 | 848 | |
| … | … | @@ -856,18 +860,19 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel): |
| 856 | 860 | outputs = [s for s in md.node_group.interface.items_tree if s.item_type == 'SOCKET' and s.in_out == 'OUTPUT'] |
| 857 | 861 | |
| 858 | 862 | for output in outputs: |
| 859 | id_name = f'{output.identifier}_attribute_name' | |
| 863 | attr_data, attr_prop = compat.output_attribute_name_prop(md, output.identifier) | |
| 860 | 864 | |
| 861 | if id_name not in md: continue | |
| 865 | if not attr_data: continue | |
| 862 | 866 | |
| 863 | 867 | row = layout.row(align=True) |
| 864 | 868 | row.label(text=output.name) |
| 865 | 869 | |
| 866 | 870 | HOPS_MT_helper_node_attr_search.__class__.modifier_name = md.name |
| 867 | HOPS_MT_helper_node_attr_search.__class__.attribute_name = id_name | |
| 871 | HOPS_MT_helper_node_attr_search.__class__.attribute_name = output.identifier | |
| 872 | HOPS_MT_helper_node_attr_search.__class__.is_output = True | |
| 868 | 873 | row.menu(HOPS_MT_helper_node_attr_search.bl_idname, text='', icon='DOWNARROW_HLT') |
| 869 | 874 | |
| 870 | row.prop(md, f'["{id_name}"]', text='') | |
| 875 | row.prop(attr_data, attr_prop, text='') | |
| 871 | 876 | |
| 872 | 877 | def OCEAN(self, layout, _ob, md): |
| 873 | 878 | if not bpy.app.build_options.mod_oceansim: |
| … | … | @@ -1902,7 +1907,8 @@ class HOPS_MT_helper_node_attr_search(bpy.types.Menu): |
| 1902 | 1907 | bl_idname = "HOPS_MT_helper_attr_search" |
| 1903 | 1908 | |
| 1904 | 1909 | modifier_name: str |
| 1905 | attribute_name: str | |
| 1910 | attribute_name: str # socket identifier | |
| 1911 | is_output: bool | |
| 1906 | 1912 | |
| 1907 | 1913 | def draw(self, context): |
| 1908 | 1914 | layout = self.layout |
| … | … | @@ -1931,6 +1937,7 @@ class HOPS_MT_helper_node_attr_search(bpy.types.Menu): |
| 1931 | 1937 | op = layout.operator(HOPS_OT_helper_node_attr_set.bl_idname, text=attribute) |
| 1932 | 1938 | op.modifier = self.__class__.modifier_name |
| 1933 | 1939 | op.attribute = self.__class__.attribute_name |
| 1940 | op.is_output = getattr(self.__class__, 'is_output', False) | |
| 1934 | 1941 | op.value = attribute |
| 1935 | 1942 | |
| 1936 | 1943 | class HOPS_OT_helper_node_attr_set(bpy.types.Operator): |
| … | … | @@ -1938,14 +1945,19 @@ class HOPS_OT_helper_node_attr_set(bpy.types.Operator): |
| 1938 | 1945 | bl_idname = 'hops.helper_node_attr_set' |
| 1939 | 1946 | |
| 1940 | 1947 | modifier: bpy.props.StringProperty() |
| 1941 | attribute: bpy.props.StringProperty() | |
| 1948 | attribute: bpy.props.StringProperty() # socket identifier | |
| 1942 | 1949 | value: bpy.props.StringProperty() |
| 1950 | is_output: bpy.props.BoolProperty() | |
| 1943 | 1951 | |
| 1944 | 1952 | def execute(self, context): |
| 1945 | 1953 | obj = bpy.context.object |
| 1946 | 1954 | mod = obj.modifiers[self.modifier] |
| 1947 | 1955 | |
| 1948 | mod[self.attribute] = self.value | |
| 1956 | if self.is_output: | |
| 1957 | compat.set_output_attribute_name(mod, self.attribute, self.value) | |
| 1958 | ||
| 1959 | else: | |
| 1960 | compat.set_attribute_name(mod, self.attribute, self.value) | |
| 1949 | 1961 | |
| 1950 | 1962 | for area in context.screen.areas: |
| 1951 | 1963 | if area.type == 'PROPERTIES': |
| … | … | @@ -1,3 +1,4 @@ |
| 1 | from . import compat | |
| 1 | 2 | from . node_modifier import NodeModifier |
| 2 | 3 | from . smooth_by_angle import SmoothByAngle |
| 3 | 4 | from .node_boolean import NodeBoolean |
| … | … | @@ -0,0 +1,159 @@ |
| 1 | import bpy | |
| 2 | ||
| 3 | ||
| 4 | '''Bridge for the geometry nodes modifier input storage change in Blender 5.2.\n | |
| 5 | Up to 5.1 inputs live in the modifier's IDProperties, keyed by socket identifier:\n | |
| 6 | modifier['Socket_1']\n | |
| 7 | modifier['Socket_1_use_attribute']\n | |
| 8 | modifier['Socket_1_attribute_name']\n | |
| 9 | 5.2 dropped IDProperty support on NodesModifier entirely and moved inputs to typed RNA:\n | |
| 10 | modifier.properties.inputs.Socket_1.value\n | |
| 11 | modifier.properties.inputs.Socket_1.type # VALUE / ATTRIBUTE / LAYER\n | |
| 12 | modifier.properties.inputs.Socket_1.attribute_name\n | |
| 13 | Subscripting a modifier on 5.2 raises TypeError: this type doesn't support IDProperties. | |
| 14 | ''' | |
| 15 | ||
| 16 | RNA_INPUTS = 'properties' in bpy.types.NodesModifier.bl_rna.properties | |
| 17 | ||
| 18 | ||
| 19 | def socket(modifier, identifier): | |
| 20 | '''RNA input struct for identifier, or None. 5.2+ only''' | |
| 21 | ||
| 22 | return getattr(modifier.properties.inputs, identifier, None) | |
| 23 | ||
| 24 | ||
| 25 | class ModifierInputs: | |
| 26 | '''Dict-like view of a geometry nodes modifier's inputs, keyed by socket identifier''' | |
| 27 | ||
| 28 | __slots__ = ('modifier',) | |
| 29 | ||
| 30 | def __init__(self, modifier): | |
| 31 | self.modifier = modifier | |
| 32 | ||
| 33 | def __contains__(self, identifier): | |
| 34 | if not RNA_INPUTS: | |
| 35 | return identifier in self.modifier | |
| 36 | ||
| 37 | return hasattr(self.modifier.properties.inputs, identifier) | |
| 38 | ||
| 39 | def __getitem__(self, identifier): | |
| 40 | if not RNA_INPUTS: | |
| 41 | return self.modifier[identifier] | |
| 42 | ||
| 43 | input = socket(self.modifier, identifier) | |
| 44 | if input is None: | |
| 45 | raise KeyError(identifier) | |
| 46 | ||
| 47 | try: | |
| 48 | return input.value | |
| 49 | ||
| 50 | except AttributeError: # geometry sockets carry no value | |
| 51 | raise KeyError(identifier) | |
| 52 | ||
| 53 | def __setitem__(self, identifier, value): | |
| 54 | if not RNA_INPUTS: | |
| 55 | self.modifier[identifier] = value | |
| 56 | return | |
| 57 | ||
| 58 | input = socket(self.modifier, identifier) | |
| 59 | if input is None: | |
| 60 | raise KeyError(identifier) | |
| 61 | ||
| 62 | input.value = value | |
| 63 | ||
| 64 | def get(self, identifier, default=None): | |
| 65 | try: | |
| 66 | return self[identifier] | |
| 67 | ||
| 68 | except KeyError: | |
| 69 | return default | |
| 70 | ||
| 71 | ||
| 72 | def supports_attribute(modifier, identifier) -> bool: | |
| 73 | '''True if the input can be driven by an attribute''' | |
| 74 | ||
| 75 | if not RNA_INPUTS: | |
| 76 | return f'{identifier}_use_attribute' in modifier | |
| 77 | ||
| 78 | input = socket(modifier, identifier) | |
| 79 | ||
| 80 | return bool(input) and hasattr(input, 'attribute_name') | |
| 81 | ||
| 82 | ||
| 83 | def get_use_attribute(modifier, identifier) -> bool: | |
| 84 | if not RNA_INPUTS: | |
| 85 | return bool(modifier.get(f'{identifier}_use_attribute', False)) | |
| 86 | ||
| 87 | input = socket(modifier, identifier) | |
| 88 | ||
| 89 | return bool(input) and input.type != 'VALUE' | |
| 90 | ||
| 91 | ||
| 92 | def get_attribute_name(modifier, identifier) -> str: | |
| 93 | if not RNA_INPUTS: | |
| 94 | return modifier.get(f'{identifier}_attribute_name', '') | |
| 95 | ||
| 96 | input = socket(modifier, identifier) | |
| 97 | if input is None: | |
| 98 | return '' | |
| 99 | ||
| 100 | return getattr(input, 'attribute_name', '') | |
| 101 | ||
| 102 | ||
| 103 | def set_attribute_name(modifier, identifier, name): | |
| 104 | if not RNA_INPUTS: | |
| 105 | modifier[f'{identifier}_attribute_name'] = name | |
| 106 | return | |
| 107 | ||
| 108 | input = socket(modifier, identifier) | |
| 109 | if input is None: | |
| 110 | return | |
| 111 | ||
| 112 | input.attribute_name = name | |
| 113 | ||
| 114 | ||
| 115 | def value_prop(modifier, identifier): | |
| 116 | '''(data, property) pair for drawing the input's value with layout.prop''' | |
| 117 | ||
| 118 | if not RNA_INPUTS: | |
| 119 | return modifier, f'["{identifier}"]' | |
| 120 | ||
| 121 | return socket(modifier, identifier), 'value' | |
| 122 | ||
| 123 | ||
| 124 | def attribute_name_prop(modifier, identifier): | |
| 125 | '''(data, property) pair for drawing the input's attribute name with layout.prop''' | |
| 126 | ||
| 127 | if not RNA_INPUTS: | |
| 128 | return modifier, f'["{identifier}_attribute_name"]' | |
| 129 | ||
| 130 | return socket(modifier, identifier), 'attribute_name' | |
| 131 | ||
| 132 | ||
| 133 | def output_attribute_name_prop(modifier, identifier): | |
| 134 | '''(data, property) pair for drawing an output's attribute name, or (None, '') if absent''' | |
| 135 | ||
| 136 | if not RNA_INPUTS: | |
| 137 | id_name = f'{identifier}_attribute_name' | |
| 138 | if id_name not in modifier: | |
| 139 | return None, '' | |
| 140 | ||
| 141 | return modifier, f'["{id_name}"]' | |
| 142 | ||
| 143 | output = getattr(modifier.properties.outputs, identifier, None) | |
| 144 | if output is None or not hasattr(output, 'attribute_name'): | |
| 145 | return None, '' | |
| 146 | ||
| 147 | return output, 'attribute_name' | |
| 148 | ||
| 149 | ||
| 150 | def set_output_attribute_name(modifier, identifier, name): | |
| 151 | if not RNA_INPUTS: | |
| 152 | modifier[f'{identifier}_attribute_name'] = name | |
| 153 | return | |
| 154 | ||
| 155 | output = getattr(modifier.properties.outputs, identifier, None) | |
| 156 | if output is None: | |
| 157 | return | |
| 158 | ||
| 159 | output.attribute_name = name |
| … | … | @@ -1,5 +1,6 @@ |
| 1 | 1 | import bpy, pathlib |
| 2 | 2 | from .node_modifier import NodeModifier |
| 3 | from . import compat | |
| 3 | 4 | |
| 4 | 5 | |
| 5 | 6 | NODES_FOLDER = 'HOPS_Nodes' |
| … | … | @@ -7,6 +8,11 @@ INPUTS = {'Tag Inset Slice Face Value', 'Clean Attribute 4', 'Tag Faces Name', ' |
| 7 | 8 | OPERATION_INPUT = {0: 'INTERSECT', 'INTERSECT': 0, 1: 'UNION', 'UNION': 1, 2: 'DIFFERENCE', 'DIFFERENCE': 2, 3: 'INSET', 'INSET': 3, 4: 'KNIFE', 'KNIFE': 4} |
| 8 | 9 | SOLVER_INPUT = [itm.identifier for itm in bpy.types.BooleanModifier.bl_rna.properties['solver'].enum_items] # FLOAT/FAST, EXACT, MANIFOLD |
| 9 | 10 | |
| 11 | # 5.2 stores menu sockets as their enum identifier instead of an index | |
| 12 | OPERATION_MENU = {'INTERSECT': 'Intersect', 'UNION': 'Union', 'DIFFERENCE': 'Difference', 'INSET': 'Inset', 'KNIFE': 'Knife'} | |
| 13 | OPERATION_MENU.update({menu: hops for hops, menu in OPERATION_MENU.items()}) | |
| 14 | SOLVER_MENU = ['Float', 'Exact', 'Manifold'] # ordered as SOLVER_INPUT | |
| 15 | ||
| 10 | 16 | class NodeBoolean(NodeModifier): |
| 11 | 17 | default_name = 'HOPS_Boolean' |
| 12 | 18 | asset_name = 'HOPS_Boolean' |
| … | … | @@ -15,247 +21,256 @@ class NodeBoolean(NodeModifier): |
| 15 | 21 | # input wrappers |
| 16 | 22 | @property |
| 17 | 23 | def input_geometry(self): |
| 18 | return self.modifier[self.input_map["Input Geometry"]] | |
| 24 | return self.inputs[self.input_map["Input Geometry"]] | |
| 19 | 25 | |
| 20 | 26 | @input_geometry.setter |
| 21 | 27 | def input_geometry(self, val): |
| 22 | self.modifier[self.input_map["Input Geometry"]] = val | |
| 28 | self.inputs[self.input_map["Input Geometry"]] = val | |
| 23 | 29 | self.update_tag() |
| 24 | 30 | |
| 25 | 31 | @property |
| 26 | 32 | def operation(self): |
| 27 | 33 | '''DIFFERNCE, UNION, INTERSECT, INSET, KNIFE''' |
| 28 | return OPERATION_INPUT[self.modifier[self.input_map["Operation"]]] | |
| 34 | value = self.inputs[self.input_map["Operation"]] | |
| 35 | ||
| 36 | if compat.RNA_INPUTS: return OPERATION_MENU[value] | |
| 37 | ||
| 38 | return OPERATION_INPUT[value] | |
| 29 | 39 | |
| 30 | 40 | @operation.setter |
| 31 | 41 | def operation(self, val): |
| 32 | self.modifier[self.input_map["Operation"]] = OPERATION_INPUT[val] | |
| 42 | self.inputs[self.input_map["Operation"]] = OPERATION_MENU[val] if compat.RNA_INPUTS else OPERATION_INPUT[val] | |
| 33 | 43 | self.update_tag() |
| 34 | 44 | |
| 35 | 45 | @property |
| 36 | 46 | def object(self): |
| 37 | return self.modifier[self.input_map["Object"]] | |
| 47 | return self.inputs[self.input_map["Object"]] | |
| 38 | 48 | |
| 39 | 49 | @object.setter |
| 40 | 50 | def object(self, val): |
| 41 | self.modifier[self.input_map["Object"]] = val | |
| 51 | self.inputs[self.input_map["Object"]] = val | |
| 42 | 52 | self.update_tag() |
| 43 | 53 | |
| 44 | 54 | @property |
| 45 | 55 | def solver(self): |
| 46 | 56 | '''FLOAT, EXACT, MANIFOLD''' |
| 47 | return SOLVER_INPUT[self.modifier[self.input_map["Solver"]]] | |
| 57 | value = self.inputs[self.input_map["Solver"]] | |
| 58 | ||
| 59 | if compat.RNA_INPUTS: return SOLVER_INPUT[SOLVER_MENU.index(value)] | |
| 60 | ||
| 61 | return SOLVER_INPUT[value] | |
| 48 | 62 | |
| 49 | 63 | @solver.setter |
| 50 | 64 | def solver(self, val): |
| 51 | self.modifier[self.input_map["Solver"]] = SOLVER_INPUT.index(val) | |
| 65 | index = SOLVER_INPUT.index(val) | |
| 66 | self.inputs[self.input_map["Solver"]] = SOLVER_MENU[index] if compat.RNA_INPUTS else index | |
| 52 | 67 | self.update_tag() |
| 53 | 68 | |
| 54 | 69 | @property |
| 55 | 70 | def inset_thickness(self): |
| 56 | return self.modifier[self.input_map["Inset Thickness"]] | |
| 71 | return self.inputs[self.input_map["Inset Thickness"]] | |
| 57 | 72 | |
| 58 | 73 | @inset_thickness.setter |
| 59 | 74 | def inset_thickness(self, val): |
| 60 | self.modifier[self.input_map["Inset Thickness"]] = float(val) | |
| 75 | self.inputs[self.input_map["Inset Thickness"]] = float(val) | |
| 61 | 76 | self.update_tag() |
| 62 | 77 | |
| 63 | 78 | @property |
| 64 | 79 | def stitch_distance(self): |
| 65 | return self.modifier[self.input_map["Stitch Distance"]] | |
| 80 | return self.inputs[self.input_map["Stitch Distance"]] | |
| 66 | 81 | |
| 67 | 82 | @stitch_distance.setter |
| 68 | 83 | def stitch_distance(self, val): |
| 69 | self.modifier[self.input_map["Stitch Distance"]] = float(val) | |
| 84 | self.inputs[self.input_map["Stitch Distance"]] = float(val) | |
| 70 | 85 | self.update_tag() |
| 71 | 86 | |
| 72 | 87 | @property |
| 73 | 88 | def non_manifold(self): |
| 74 | return self.modifier[self.input_map["Non Manifold"]] | |
| 89 | return self.inputs[self.input_map["Non Manifold"]] | |
| 75 | 90 | |
| 76 | 91 | @non_manifold.setter |
| 77 | 92 | def non_manifold(self, val): |
| 78 | self.modifier[self.input_map["Non Manifold"]] = bool(val) | |
| 93 | self.inputs[self.input_map["Non Manifold"]] = bool(val) | |
| 79 | 94 | self.update_tag() |
| 80 | 95 | |
| 81 | 96 | @property |
| 82 | 97 | def inset_slice(self): |
| 83 | return self.modifier[self.input_map["Inset Slice"]] | |
| 98 | return self.inputs[self.input_map["Inset Slice"]] | |
| 84 | 99 | |
| 85 | 100 | @inset_slice.setter |
| 86 | 101 | def inset_slice(self, val): |
| 87 | self.modifier[self.input_map["Inset Slice"]] = bool(val) | |
| 102 | self.inputs[self.input_map["Inset Slice"]] = bool(val) | |
| 88 | 103 | self.update_tag() |
| 89 | 104 | |
| 90 | 105 | @property |
| 91 | 106 | def inset_slice_offset(self): |
| 92 | return self.modifier[self.input_map["Inset Slice Offset"]] | |
| 107 | return self.inputs[self.input_map["Inset Slice Offset"]] | |
| 93 | 108 | |
| 94 | 109 | @inset_slice_offset.setter |
| 95 | 110 | def inset_slice_offset(self, val): |
| 96 | self.modifier[self.input_map["Inset Slice Offset"]] = float(val) | |
| 111 | self.inputs[self.input_map["Inset Slice Offset"]] = float(val) | |
| 97 | 112 | self.update_tag() |
| 98 | 113 | |
| 99 | 114 | @property |
| 100 | 115 | def transfer_normals(self): |
| 101 | return self.modifier[self.input_map["Transfer Normals"]] | |
| 116 | return self.inputs[self.input_map["Transfer Normals"]] | |
| 102 | 117 | |
| 103 | 118 | @transfer_normals.setter |
| 104 | 119 | def transfer_normals(self, val): |
| 105 | self.modifier[self.input_map["Transfer Normals"]] = bool(val) | |
| 120 | self.inputs[self.input_map["Transfer Normals"]] = bool(val) | |
| 106 | 121 | self.update_tag() |
| 107 | 122 | |
| 108 | 123 | @property |
| 109 | 124 | def material_cut(self): |
| 110 | return self.modifier[self.input_map["Material Cut"]] | |
| 125 | return self.inputs[self.input_map["Material Cut"]] | |
| 111 | 126 | |
| 112 | 127 | @material_cut.setter |
| 113 | 128 | def material_cut(self, val): |
| 114 | self.modifier[self.input_map["Material Cut"]] = bool(val) | |
| 129 | self.inputs[self.input_map["Material Cut"]] = bool(val) | |
| 115 | 130 | self.update_tag() |
| 116 | 131 | |
| 117 | 132 | @property |
| 118 | 133 | def material(self): |
| 119 | return self.modifier[self.input_map["Material"]] | |
| 134 | return self.inputs[self.input_map["Material"]] | |
| 120 | 135 | |
| 121 | 136 | @material.setter |
| 122 | 137 | def material(self, val): |
| 123 | self.modifier[self.input_map["Material"]] = val | |
| 138 | self.inputs[self.input_map["Material"]] = val | |
| 124 | 139 | self.update_tag() |
| 125 | 140 | |
| 126 | 141 | @property |
| 127 | 142 | def weld(self): |
| 128 | return self.modifier[self.input_map["Weld"]] | |
| 143 | return self.inputs[self.input_map["Weld"]] | |
| 129 | 144 | |
| 130 | 145 | @weld.setter |
| 131 | 146 | def weld(self, val): |
| 132 | self.modifier[self.input_map["Weld"]] = bool(val) | |
| 147 | self.inputs[self.input_map["Weld"]] = bool(val) | |
| 133 | 148 | self.update_tag() |
| 134 | 149 | |
| 135 | 150 | @property |
| 136 | 151 | def merge_threshold(self): |
| 137 | return self.modifier[self.input_map["Merge Threshold"]] | |
| 152 | return self.inputs[self.input_map["Merge Threshold"]] | |
| 138 | 153 | |
| 139 | 154 | @merge_threshold.setter |
| 140 | 155 | def merge_threshold(self, val): |
| 141 | self.modifier[self.input_map["Merge Threshold"]] = float(val) | |
| 156 | self.inputs[self.input_map["Merge Threshold"]] = float(val) | |
| 142 | 157 | self.update_tag() |
| 143 | 158 | |
| 144 | 159 | @property |
| 145 | 160 | def tag_faces_name(self): |
| 146 | return self.modifier[self.input_map["Tag Faces Name"]] | |
| 161 | return self.inputs[self.input_map["Tag Faces Name"]] | |
| 147 | 162 | |
| 148 | 163 | @tag_faces_name.setter |
| 149 | 164 | def tag_faces_name(self, val): |
| 150 | self.modifier[self.input_map["Tag Faces Name"]] = val | |
| 165 | self.inputs[self.input_map["Tag Faces Name"]] = val | |
| 151 | 166 | self.update_tag() |
| 152 | 167 | |
| 153 | 168 | @property |
| 154 | 169 | def tag_faces_value(self): |
| 155 | return self.modifier[self.input_map["Tag Faces Value"]] | |
| 170 | return self.inputs[self.input_map["Tag Faces Value"]] | |
| 156 | 171 | |
| 157 | 172 | @tag_faces_value.setter |
| 158 | 173 | def tag_faces_value(self, val): |
| 159 | self.modifier[self.input_map["Tag Faces Value"]] = float(val) | |
| 174 | self.inputs[self.input_map["Tag Faces Value"]] = float(val) | |
| 160 | 175 | self.update_tag() |
| 161 | 176 | |
| 162 | 177 | @property |
| 163 | 178 | def tag_edge_boundary_name(self): |
| 164 | return self.modifier[self.input_map["Tag Edge Boundary Name"]] | |
| 179 | return self.inputs[self.input_map["Tag Edge Boundary Name"]] | |
| 165 | 180 | |
| 166 | 181 | @tag_edge_boundary_name.setter |
| 167 | 182 | def tag_edge_boundary_name(self, val): |
| 168 | self.modifier[self.input_map["Tag Edge Boundary Name"]] = val | |
| 183 | self.inputs[self.input_map["Tag Edge Boundary Name"]] = val | |
| 169 | 184 | self.update_tag() |
| 170 | 185 | |
| 171 | 186 | @property |
| 172 | 187 | def tag_edge_boundary_value(self): |
| 173 | return self.modifier[self.input_map["Tag Edge Boundary Value"]] | |
| 188 | return self.inputs[self.input_map["Tag Edge Boundary Value"]] | |
| 174 | 189 | |
| 175 | 190 | @tag_edge_boundary_value.setter |
| 176 | 191 | def tag_edge_boundary_value(self, val): |
| 177 | self.modifier[self.input_map["Tag Edge Boundary Value"]] = float(val) | |
| 192 | self.inputs[self.input_map["Tag Edge Boundary Value"]] = float(val) | |
| 178 | 193 | self.update_tag() |
| 179 | 194 | |
| 180 | 195 | @property |
| 181 | 196 | def tag_inset_slice_face_name(self): |
| 182 | return self.modifier[self.input_map["Tag Inset Slice Face Name"]] | |
| 197 | return self.inputs[self.input_map["Tag Inset Slice Face Name"]] | |
| 183 | 198 | |
| 184 | 199 | @tag_inset_slice_face_name.setter |
| 185 | 200 | def tag_inset_slice_face_name(self, val): |
| 186 | self.modifier[self.input_map["Tag Inset Slice Face Name"]] = val | |
| 201 | self.inputs[self.input_map["Tag Inset Slice Face Name"]] = val | |
| 187 | 202 | self.update_tag() |
| 188 | 203 | |
| 189 | 204 | @property |
| 190 | 205 | def tag_inset_slice_face_value(self): |
| 191 | return self.modifier[self.input_map["Tag Inset Slice Face Value"]] | |
| 206 | return self.inputs[self.input_map["Tag Inset Slice Face Value"]] | |
| 192 | 207 | |
| 193 | 208 | @tag_inset_slice_face_value.setter |
| 194 | 209 | def tag_inset_slice_face_value(self, val): |
| 195 | self.modifier[self.input_map["Tag Inset Slice Face Value"]] = float(val) | |
| 210 | self.inputs[self.input_map["Tag Inset Slice Face Value"]] = float(val) | |
| 196 | 211 | self.update_tag() |
| 197 | 212 | |
| 198 | 213 | @property |
| 199 | 214 | def tag_inset_slice_edge_name(self): |
| 200 | return self.modifier[self.input_map["Tag Inset Slice Edge Name"]] | |
| 215 | return self.inputs[self.input_map["Tag Inset Slice Edge Name"]] | |
| 201 | 216 | |
| 202 | 217 | @tag_inset_slice_edge_name.setter |
| 203 | 218 | def tag_inset_slice_edge_name(self, val): |
| 204 | self.modifier[self.input_map["Tag Inset Slice Edge Name"]] = val | |
| 219 | self.inputs[self.input_map["Tag Inset Slice Edge Name"]] = val | |
| 205 | 220 | self.update_tag() |
| 206 | 221 | |
| 207 | 222 | @property |
| 208 | 223 | def tag_inset_slice_edge_value(self): |
| 209 | return self.modifier[self.input_map["Tag Inset Slice Edge Value"]] | |
| 224 | return self.inputs[self.input_map["Tag Inset Slice Edge Value"]] | |
| 210 | 225 | |
| 211 | 226 | @tag_inset_slice_edge_value.setter |
| 212 | 227 | def tag_inset_slice_edge_value(self, val): |
| 213 | self.modifier[self.input_map["Tag Inset Slice Edge Value"]] = float(val) | |
| 228 | self.inputs[self.input_map["Tag Inset Slice Edge Value"]] = float(val) | |
| 214 | 229 | self.update_tag() |
| 215 | 230 | |
| 216 | 231 | @property |
| 217 | 232 | def clean_attribute_1(self): |
| 218 | return self.modifier[self.input_map["Clean Attribute 1"]] | |
| 233 | return self.inputs[self.input_map["Clean Attribute 1"]] | |
| 219 | 234 | |
| 220 | 235 | @clean_attribute_1.setter |
| 221 | 236 | def clean_attribute_1(self, val): |
| 222 | self.modifier[self.input_map["Clean Attribute 1"]] = val | |
| 237 | self.inputs[self.input_map["Clean Attribute 1"]] = val | |
| 223 | 238 | self.update_tag() |
| 224 | 239 | |
| 225 | 240 | @property |
| 226 | 241 | def clean_attribute_2(self): |
| 227 | return self.modifier[self.input_map["Clean Attribute 2"]] | |
| 242 | return self.inputs[self.input_map["Clean Attribute 2"]] | |
| 228 | 243 | |
| 229 | 244 | @clean_attribute_2.setter |
| 230 | 245 | def clean_attribute_2(self, val): |
| 231 | self.modifier[self.input_map["Clean Attribute 2"]] = val | |
| 246 | self.inputs[self.input_map["Clean Attribute 2"]] = val | |
| 232 | 247 | self.update_tag() |
| 233 | 248 | |
| 234 | 249 | @property |
| 235 | 250 | def clean_attribute_3(self): |
| 236 | return self.modifier[self.input_map["Clean Attribute 3"]] | |
| 251 | return self.inputs[self.input_map["Clean Attribute 3"]] | |
| 237 | 252 | |
| 238 | 253 | @clean_attribute_3.setter |
| 239 | 254 | def clean_attribute_3(self, val): |
| 240 | self.modifier[self.input_map["Clean Attribute 3"]] = val | |
| 255 | self.inputs[self.input_map["Clean Attribute 3"]] = val | |
| 241 | 256 | self.update_tag() |
| 242 | 257 | |
| 243 | 258 | @property |
| 244 | 259 | def clean_attribute_4(self): |
| 245 | return self.modifier[self.input_map["Clean Attribute 4"]] | |
| 260 | return self.inputs[self.input_map["Clean Attribute 4"]] | |
| 246 | 261 | |
| 247 | 262 | @clean_attribute_4.setter |
| 248 | 263 | def clean_attribute_4(self, val): |
| 249 | self.modifier[self.input_map["Clean Attribute 4"]] = val | |
| 264 | self.inputs[self.input_map["Clean Attribute 4"]] = val | |
| 250 | 265 | self.update_tag() |
| 251 | 266 | |
| 252 | 267 | @property |
| 253 | 268 | def clean_attribute_5(self): |
| 254 | return self.modifier[self.input_map["Clean Attribute 5"]] | |
| 269 | return self.inputs[self.input_map["Clean Attribute 5"]] | |
| 255 | 270 | |
| 256 | 271 | @clean_attribute_5.setter |
| 257 | 272 | def clean_attribute_5(self, val): |
| 258 | self.modifier[self.input_map["Clean Attribute 5"]] = val | |
| 273 | self.inputs[self.input_map["Clean Attribute 5"]] = val | |
| 259 | 274 | self.update_tag() |
| 260 | 275 | |
| 261 | 276 | @classmethod |
| … | … | @@ -1,4 +1,5 @@ |
| 1 | 1 | import bpy, pathlib, sys |
| 2 | from . import compat | |
| 2 | 3 | |
| 3 | 4 | |
| 4 | 5 | class NodeModifier(): |
| … | … | @@ -13,6 +14,11 @@ class NodeModifier(): |
| 13 | 14 | _filepath = '' # used for cacheing string |
| 14 | 15 | blend_filename = 'geometry_nodes_essentials.blend' #5.0 node lib |
| 15 | 16 | |
| 17 | # Input access, keyed by socket identifier. 5.2 moved inputs off IDProperties | |
| 18 | @property | |
| 19 | def inputs(self): | |
| 20 | return compat.ModifierInputs(self.modifier) | |
| 21 | ||
| 16 | 22 | # Copy shared modifier interface |
| 17 | 23 | @property |
| 18 | 24 | def show_on_cage(self): |
| … | … | @@ -199,20 +205,24 @@ class NodeModifier(): |
| 199 | 205 | return path / 'geometry_nodes' |
| 200 | 206 | |
| 201 | 207 | @classmethod |
| 202 | def prop_draw_string(cls, modifier, string) -> str: | |
| 203 | '''Get string for drawing modifier's prop \n | |
| 204 | Returns input string if string can't be calculated e.g. input modifier of incompatible type''' | |
| 208 | def prop_draw_data(cls, modifier, string) -> tuple: | |
| 209 | '''Get (data, property) pair for drawing modifier's prop with layout.prop\n | |
| 210 | Inputs live on the modifier's IDProperties up to 5.1 and on their own RNA struct from 5.2\n | |
| 211 | Returns (modifier, string) if the pair can't be calculated e.g. input modifier of incompatible type''' | |
| 205 | 212 | |
| 206 | if hasattr(modifier, string): return string | |
| 213 | if hasattr(modifier, string): return modifier, string | |
| 207 | 214 | |
| 208 | 215 | if not cls.prop_draw_map: |
| 209 | if not cls.is_valid_modifier(modifier): return string | |
| 216 | if not cls.is_valid_modifier(modifier): return modifier, string | |
| 210 | 217 | cls.prop_draw_map_build(modifier) |
| 211 | 218 | |
| 212 | 219 | identifier = cls.prop_draw_map.get(string, None) |
| 213 | if not identifier: return string | |
| 220 | if not identifier: return modifier, string | |
| 221 | ||
| 222 | data, prop = compat.value_prop(modifier, identifier) | |
| 223 | if data is None: return modifier, string | |
| 214 | 224 | |
| 215 | return f'["{identifier}"]' | |
| 225 | return data, prop | |
| 216 | 226 | |
| 217 | 227 | |
| 218 | 228 | def update_tag(self): |
| … | … | @@ -10,20 +10,20 @@ class SmoothByAngle(NodeModifier): |
| 10 | 10 | |
| 11 | 11 | @property |
| 12 | 12 | def angle (self): |
| 13 | return self.modifier[self.input_map['Angle']] | |
| 13 | return self.inputs[self.input_map['Angle']] | |
| 14 | 14 | |
| 15 | 15 | @angle.setter |
| 16 | 16 | def angle(self, value): |
| 17 | self.modifier[self.input_map['Angle']] = float(value) | |
| 17 | self.inputs[self.input_map['Angle']] = float(value) | |
| 18 | 18 | self.update_tag() |
| 19 | 19 | |
| 20 | 20 | @property |
| 21 | 21 | def ignore_sharpness (self): |
| 22 | return self.modifier[self.input_map['Ignore Sharpness']] | |
| 22 | return self.inputs[self.input_map['Ignore Sharpness']] | |
| 23 | 23 | |
| 24 | 24 | @ignore_sharpness.setter |
| 25 | 25 | def ignore_sharpness(self, value): |
| 26 | self.modifier[self.input_map['Ignore Sharpness']] = bool(value) | |
| 26 | self.inputs[self.input_map['Ignore Sharpness']] = bool(value) | |
| 27 | 27 | self.update_tag() |
| 28 | 28 | |
| 29 | 29 | @classmethod |