commit
feb1887a7d
17 changed files with 270 additions and 89 deletions
|
|
@ -1 +1 @@
|
|||
1.1.0
|
||||
1.1.0+build.1
|
||||
|
|
@ -5,4 +5,3 @@ if __name__ == "__main__":
|
|||
currentVersion = versioning.get_version()
|
||||
nextVersion = currentVersion.bump_minor()
|
||||
versioning.save_version(nextVersion)
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ classes = (
|
|||
BakeToIDMapPanel,
|
||||
BakeToIDInfoPanel,
|
||||
BakeToIDOptionsPanel,
|
||||
BakeToIDAdvancedMenu,
|
||||
BakeToIDProperties,
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,9 @@ import math
|
|||
|
||||
import bpy
|
||||
|
||||
from .. types import (get_source, get_target)
|
||||
from ..types.colors import get_color
|
||||
from .. types.sources import get_source
|
||||
from .. types.targets import get_target
|
||||
|
||||
|
||||
class BakeToIDMapOperator(bpy.types.Operator):
|
||||
|
|
@ -27,27 +29,44 @@ class BakeToIDMapOperator(bpy.types.Operator):
|
|||
def paint_id_mask(self, context, props):
|
||||
source = get_source(props.source)
|
||||
|
||||
targets = source.get_targets(context)
|
||||
targets = self.get_targets(context, source, props)
|
||||
|
||||
if len(targets) < 1:
|
||||
return
|
||||
|
||||
totalTargets = len(targets)
|
||||
colors = []
|
||||
|
||||
total_hues = props.adv_total_hues
|
||||
total_satuations = props.adv_total_satuations
|
||||
total_brightnesses = props.adv_total_brightnesses
|
||||
|
||||
satuations_break_point = math.pow(total_brightnesses, total_hues)
|
||||
|
||||
for i in range(totalTargets):
|
||||
h = (i / total_hues) % 1
|
||||
l = (math.ceil(i / total_hues) % total_brightnesses) / total_brightnesses
|
||||
s = math.ceil(i / satuations_break_point) / total_satuations
|
||||
|
||||
colors.append(colorsys.hls_to_rgb(h, l, s))
|
||||
color = get_color(props.colors)
|
||||
colors = color.get_colors(props)
|
||||
|
||||
target = get_target(props.target)
|
||||
self.paint_targets(props, target, targets, colors)
|
||||
if 'after_painting' in dir(source):
|
||||
source.after_painting(context, props)
|
||||
|
||||
def get_targets(self, context, source, props):
|
||||
if props.selection_mode == 'SINGLE':
|
||||
return source.get_targets([context.active_object])
|
||||
|
||||
if props.selection_mode == 'MULTIPLE_COMBINED':
|
||||
return source.get_targets(context.selected_objects)
|
||||
|
||||
if props.selection_mode == 'MULTIPLE_SEPARATE':
|
||||
result = []
|
||||
for obj in context.selected_objects:
|
||||
if obj.type != 'MESH':
|
||||
continue
|
||||
|
||||
obj_targets = source.get_targets([obj])
|
||||
result.append(obj_targets)
|
||||
|
||||
return result
|
||||
|
||||
raise ValueError('Invalid selection_mode')
|
||||
|
||||
def paint_targets(self, props, target, targets, colors):
|
||||
if props.selection_mode == 'MULTIPLE_SEPARATE':
|
||||
for targetList in targets:
|
||||
target.paint_targets(props,targetList, colors)
|
||||
|
||||
return
|
||||
|
||||
target.paint_targets(props, targets, colors)
|
||||
source.after_painting(context, props)
|
||||
|
|
@ -13,4 +13,19 @@ class BakeToIDMapPanel(bpy.types.Panel):
|
|||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False # No animation.
|
||||
|
||||
self.layout.operator("object.bake_to_id_map", text="Bake")
|
||||
props = context.scene.bake_to_id_props
|
||||
|
||||
operator_row = layout.row()
|
||||
operator_row.operator("object.bake_to_id_map", text="Bake")
|
||||
operator_row.enabled = self.check_if_props_valid(context, props)
|
||||
|
||||
layout.prop(props, "selection_mode")
|
||||
|
||||
def check_if_props_valid(self, context, props):
|
||||
if (props.selection_mode == "SINGLE" and context.active_object is None):
|
||||
return False
|
||||
|
||||
if (props.selection_mode != "SINGLE" and len(context.selected_objects) < 1):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
|
@ -21,4 +21,4 @@ class BakeToIDAdvancedMenu(bpy.types.Panel):
|
|||
layout.prop(props, "adv_total_satuations")
|
||||
layout.prop(props, "adv_total_brightnesses")
|
||||
layout.label(text="Max ID-count: " + str(
|
||||
math.pow(math.pow(props.adv_total_hues, props.adv_total_satuations), props.adv_total_brightnesses)))
|
||||
))
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import bpy
|
||||
|
||||
from .. operators.bake_to_id_map import BakeToIDMapOperator
|
||||
from ..types import get_source
|
||||
from ..types.colors import get_color
|
||||
from .. types.sources import get_source
|
||||
|
||||
|
||||
class BakeToIDInfoPanel(bpy.types.Panel):
|
||||
|
|
@ -19,5 +19,31 @@ class BakeToIDInfoPanel(bpy.types.Panel):
|
|||
|
||||
source = get_source(props.source)
|
||||
|
||||
layout.label(text="Selected Object-Count: " + str(len(context.selected_objects)))
|
||||
layout.label(text="Estimated ID-Count: " + str(source.estimate_ids(context, props)))
|
||||
if props.selection_mode != 'SINGLE':
|
||||
layout.label(text="Selected Object-Count: " + str(len(context.selected_objects)))
|
||||
|
||||
if props.selection_mode == 'SINGLE':
|
||||
layout.label(text="ID-Total: " + str(source.estimate_ids([context.active_object])))
|
||||
|
||||
if props.selection_mode == 'MULTIPLE_SEPARATE':
|
||||
total = 0
|
||||
count = 0
|
||||
for obj in context.selected_objects:
|
||||
if (obj.type != 'MESH'):
|
||||
continue
|
||||
|
||||
total += source.estimate_ids([obj])
|
||||
count += 1
|
||||
|
||||
layout.label(text="Estimated ID-Total: " + str(total))
|
||||
try:
|
||||
layout.label(text="Estimated ID-Average: " + str(total / count))
|
||||
except ZeroDivisionError:
|
||||
layout.label(text="Estimated ID-Average: 0")
|
||||
|
||||
if props.selection_mode == 'MULTIPLE_COMBINED':
|
||||
layout.label(text="ID-Total: " + str(source.estimate_ids(context.selected_objects)))
|
||||
|
||||
color = get_color(props.colors)
|
||||
|
||||
layout.label(text="Colors available: " + str(color.get_count(props)))
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@ import textwrap
|
|||
|
||||
import bpy
|
||||
|
||||
from src.types import get_source, get_target
|
||||
from .. types.sources import get_source
|
||||
from .. types.targets import get_target
|
||||
|
||||
|
||||
class BakeToIDOptionsPanel(bpy.types.Panel):
|
||||
|
|
@ -15,19 +16,28 @@ class BakeToIDOptionsPanel(bpy.types.Panel):
|
|||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False # No animation.
|
||||
|
||||
props = context.scene.bake_to_id_props
|
||||
|
||||
layout.prop(props, "source")
|
||||
source = get_source(props.source)
|
||||
source_settings_box = layout.box()
|
||||
for setting in source.connected_properties:
|
||||
source_settings_box.prop(props, setting)
|
||||
|
||||
if len(source.connected_properties) > 0:
|
||||
source_settings_box = layout.box()
|
||||
for setting in source.connected_properties:
|
||||
source_settings_box.prop(props, setting)
|
||||
|
||||
layout.separator()
|
||||
|
||||
layout.prop(props, "target")
|
||||
target = get_target(props.target)
|
||||
target_settings_box = layout.box()
|
||||
for setting in target.connected_properties:
|
||||
target_settings_box.prop(props, setting)
|
||||
if len(target.connected_properties) > 0:
|
||||
target_settings_box = layout.box()
|
||||
for setting in target.connected_properties:
|
||||
target_settings_box.prop(props, setting)
|
||||
|
||||
layout.separator()
|
||||
|
||||
layout.prop(props, "colors")
|
||||
|
|
|
|||
|
|
@ -1,17 +1,30 @@
|
|||
from bpy.types import (PropertyGroup)
|
||||
from bpy.props import (EnumProperty, BoolProperty, IntProperty, StringProperty)
|
||||
|
||||
from src.types import get_source_enum, get_targets_enum
|
||||
from src.types.colors import get_color_enum
|
||||
from src.types.sources import get_source_enum
|
||||
from src.types.targets import get_targets_enum
|
||||
|
||||
|
||||
class BakeToIDProperties(PropertyGroup):
|
||||
selection_mode: EnumProperty(
|
||||
items=[
|
||||
("SINGLE", "Only active element", "Only use the currently selected element", 0),
|
||||
("MULTIPLE_SEPARATE", "Use Selection, separate", "It performs the transfer for every selected element, but each get there own ids", 1),
|
||||
("MULTIPLE_COMBINED", "Use Selection, combined", "It performs the transfer for every selected element, combining the ids", 2)
|
||||
],
|
||||
name="Selection Mode",
|
||||
description="Specifies how the 3D View-Selection is gonna be used.",
|
||||
default="MULTIPLE_SEPARATE"
|
||||
)
|
||||
|
||||
source: EnumProperty(
|
||||
items=get_source_enum(),
|
||||
name="Source",
|
||||
description="From where should the IDs be taken",
|
||||
default = "MATERIAL_INDEX"
|
||||
|
||||
default="MATERIAL_INDEX"
|
||||
)
|
||||
|
||||
target: EnumProperty(
|
||||
items=get_targets_enum(),
|
||||
name="Target",
|
||||
|
|
@ -19,7 +32,14 @@ class BakeToIDProperties(PropertyGroup):
|
|||
default=get_targets_enum()[0][0]
|
||||
)
|
||||
|
||||
source_materials_remove_all : BoolProperty(
|
||||
colors: EnumProperty(
|
||||
items=get_color_enum(),
|
||||
name="Color Source",
|
||||
description="From where to take the colors",
|
||||
default=get_color_enum()[0][0]
|
||||
)
|
||||
|
||||
source_materials_remove_all: BoolProperty(
|
||||
name="Remove all source materials",
|
||||
default=False,
|
||||
description="Removes every material except the first one."
|
||||
|
|
@ -29,6 +49,11 @@ class BakeToIDProperties(PropertyGroup):
|
|||
name="Color Attribute",
|
||||
default="ID_MASK",
|
||||
)
|
||||
target_vertex_color_override_attribute: BoolProperty(
|
||||
name="Override Color Attribute",
|
||||
default=True,
|
||||
description="If set true, the attribute will be deleted and recreated, if it already exists. If set false, the data will just be overwritten."
|
||||
)
|
||||
|
||||
adv_total_hues: IntProperty(
|
||||
name="Total Hues",
|
||||
|
|
|
|||
|
|
@ -1,43 +0,0 @@
|
|||
from . sources import material_index as source_mat_index
|
||||
|
||||
from . targets import vertex_colors as target_vertex_colors
|
||||
|
||||
_sources = [
|
||||
source_mat_index
|
||||
]
|
||||
|
||||
_targets = [
|
||||
target_vertex_colors
|
||||
]
|
||||
|
||||
def get_source(id):
|
||||
for source in _sources:
|
||||
if source.source_id == id:
|
||||
return source
|
||||
|
||||
raise Exception("Source not found: " + id)
|
||||
|
||||
def get_target(id):
|
||||
for target in _targets:
|
||||
if target.target_id == id:
|
||||
return target
|
||||
|
||||
raise Exception("Target not found: " + id)
|
||||
|
||||
def get_source_enum():
|
||||
enumList = []
|
||||
i = 0
|
||||
for source in _sources:
|
||||
enumList.append((source.source_id, source.name, source.description, i))
|
||||
i += 1
|
||||
|
||||
return enumList
|
||||
|
||||
def get_targets_enum():
|
||||
enumList = []
|
||||
i = 0
|
||||
for target in _targets:
|
||||
enumList.append((target.target_id, target.name, target.description, i))
|
||||
i += 1
|
||||
|
||||
return enumList
|
||||
24
src/types/colors/__init__.py
Normal file
24
src/types/colors/__init__.py
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
from . import generated
|
||||
|
||||
_colors = [
|
||||
generated
|
||||
]
|
||||
|
||||
|
||||
def get_color(id):
|
||||
for color in _colors:
|
||||
if color.color_id == id:
|
||||
return color
|
||||
|
||||
raise Exception("Source not found: " + id)
|
||||
|
||||
|
||||
def get_color_enum():
|
||||
enum_list = []
|
||||
i = 0
|
||||
for color in _colors:
|
||||
enum_list.append((color.color_id, color.name, color.description, i))
|
||||
i += 1
|
||||
|
||||
return enum_list
|
||||
|
||||
32
src/types/colors/generated.py
Normal file
32
src/types/colors/generated.py
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
import colorsys
|
||||
import math
|
||||
|
||||
color_id = 'GENERATED'
|
||||
name = 'Generated'
|
||||
description = 'The colors get generated on-the-fly'
|
||||
|
||||
MAX_BRIGHTNESS_STEPS = 8
|
||||
MAX_SATURATION_STEPS = 8
|
||||
MAX_HUES_STEPS = 8
|
||||
|
||||
def get_colors(props):
|
||||
return gen_colors()
|
||||
|
||||
|
||||
def get_count(props):
|
||||
return len(gen_colors())
|
||||
|
||||
|
||||
def gen_colors():
|
||||
colors = []
|
||||
for brightnessStep in range(0, MAX_BRIGHTNESS_STEPS):
|
||||
v = 1 - (brightnessStep / MAX_BRIGHTNESS_STEPS)
|
||||
for satuationStep in range(0, MAX_SATURATION_STEPS):
|
||||
s = 1 - (satuationStep / MAX_SATURATION_STEPS)
|
||||
for hueStep in range(0, MAX_HUES_STEPS):
|
||||
h = hueStep / MAX_HUES_STEPS
|
||||
|
||||
color = colorsys.hsv_to_rgb(h, s, v)
|
||||
colors.append(color)
|
||||
|
||||
return colors
|
||||
25
src/types/sources/__init__.py
Normal file
25
src/types/sources/__init__.py
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
from . import material_index
|
||||
from . import object
|
||||
|
||||
_sources = [
|
||||
material_index,
|
||||
object
|
||||
]
|
||||
|
||||
def get_source(id):
|
||||
for source in _sources:
|
||||
if source.source_id == id:
|
||||
return source
|
||||
|
||||
raise Exception("Source not found: " + id)
|
||||
|
||||
|
||||
def get_source_enum():
|
||||
enum_list = []
|
||||
i = 0
|
||||
for source in _sources:
|
||||
enum_list.append((source.source_id, source.name, source.description, i))
|
||||
i += 1
|
||||
|
||||
return enum_list
|
||||
|
||||
|
|
@ -6,9 +6,9 @@ connected_properties = [
|
|||
'source_materials_remove_all'
|
||||
]
|
||||
|
||||
def get_targets(context):
|
||||
def get_targets(objects):
|
||||
targets = []
|
||||
for obj in context.selected_objects:
|
||||
for obj in objects:
|
||||
if not obj.material_slots: continue
|
||||
|
||||
mesh = obj.data
|
||||
|
|
@ -33,9 +33,9 @@ def after_painting(context, props):
|
|||
for obj in context.selected_objects:
|
||||
obj.data.materials.clear()
|
||||
|
||||
def estimate_ids(context, props):
|
||||
def estimate_ids(objects):
|
||||
count = 0
|
||||
for obj in context.selected_objects:
|
||||
for obj in objects:
|
||||
count += len(obj.material_slots)
|
||||
|
||||
return count
|
||||
|
|
|
|||
16
src/types/sources/object.py
Normal file
16
src/types/sources/object.py
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
source_id = 'OBJECT'
|
||||
name = 'Object ID'
|
||||
description = 'Uses the object id as basis for the ID mask.'
|
||||
|
||||
connected_properties = []
|
||||
|
||||
def get_targets(objects):
|
||||
targets = []
|
||||
for obj in objects:
|
||||
mesh = obj.data
|
||||
targets.append((mesh, mesh.polygons))
|
||||
|
||||
return targets
|
||||
|
||||
def estimate_ids(objects):
|
||||
return len(objects)
|
||||
23
src/types/targets/__init__.py
Normal file
23
src/types/targets/__init__.py
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
from . import vertex_colors
|
||||
|
||||
_targets = [
|
||||
vertex_colors
|
||||
]
|
||||
|
||||
|
||||
def get_target(id):
|
||||
for target in _targets:
|
||||
if target.target_id == id:
|
||||
return target
|
||||
|
||||
raise Exception("Target not found: " + id)
|
||||
|
||||
|
||||
def get_targets_enum():
|
||||
enum_list = []
|
||||
i = 0
|
||||
for target in _targets:
|
||||
enum_list.append((target.target_id, target.name, target.description, i))
|
||||
i += 1
|
||||
|
||||
return enum_list
|
||||
|
|
@ -3,7 +3,8 @@ name = 'Color Attribute / Vertex Color'
|
|||
description = 'Bakes the ID onto a color attribute (previously known as Vertex Color)'
|
||||
|
||||
connected_properties = [
|
||||
'target_vertex_color_attribute_name'
|
||||
'target_vertex_color_attribute_name',
|
||||
'target_vertex_color_override_attribute'
|
||||
]
|
||||
|
||||
|
||||
|
|
@ -21,12 +22,22 @@ def paint_targets(props, targets, colors):
|
|||
|
||||
layer_name = props.target_vertex_color_attribute_name
|
||||
for mesh in sorted_targets:
|
||||
|
||||
if layer_name in mesh.attributes:
|
||||
mesh.attributes.remove(mesh.attributes[layer_name])
|
||||
|
||||
color_attribute = mesh.attributes.new(name=layer_name, type='FLOAT_COLOR', domain='CORNER')
|
||||
color_attribute = get_color_attribute(props, mesh.attributes, layer_name)
|
||||
|
||||
for (indecies, color) in sorted_targets[mesh]:
|
||||
for polygon in indecies:
|
||||
for idx in polygon.loop_indices:
|
||||
color_attribute.data[idx].color = (color[0], color[1], color[2], 1.0)
|
||||
|
||||
def get_color_attribute(props, attributes, layer_name):
|
||||
if layer_name in attributes:
|
||||
if not props.target_vertex_color_override_attribute:
|
||||
return attributes[layer_name]
|
||||
|
||||
attributes.remove(attributes[layer_name])
|
||||
|
||||
return attributes.new(name=layer_name, type='FLOAT_COLOR', domain='CORNER')
|
||||
Loading…
Add table
Add a link
Reference in a new issue