yyc12345
084e7fbe61
- add extra transform for rail creation. - remove scale from extra transform because it is rarely used. because Ballance can not handle the physicalization of scaled object.
293 lines
12 KiB
Python
293 lines
12 KiB
Python
import bpy, mathutils
|
|
import typing
|
|
from . import PROP_preferences
|
|
from . import UTIL_functions, UTIL_bme
|
|
|
|
#region BME Adder
|
|
|
|
_g_EnumHelper_BmeStructType: UTIL_bme.EnumPropHelper = UTIL_bme.EnumPropHelper()
|
|
|
|
class BBP_PG_bme_adder_cfgs(bpy.types.PropertyGroup):
|
|
prop_int: bpy.props.IntProperty(
|
|
name = 'Single Int', description = 'Single Int',
|
|
min = 0, max = 64,
|
|
soft_min = 0, soft_max = 32,
|
|
step = 1,
|
|
default = 1,
|
|
) # type: ignore
|
|
prop_float: bpy.props.FloatProperty(
|
|
name = 'Single Float', description = 'Single Float',
|
|
min = 0.0, max = 1024.0,
|
|
soft_min = 0.0, soft_max = 512.0,
|
|
step = 50, # Step is in UI, in [1, 100] (WARNING: actual value is /100). So we choose 50, mean 0.5
|
|
default = 5.0,
|
|
) # type: ignore
|
|
prop_bool: bpy.props.BoolProperty(
|
|
name = 'Single Bool', description = 'Single Bool',
|
|
default = True
|
|
) # type: ignore
|
|
|
|
class BBP_OT_add_bme_struct(bpy.types.Operator):
|
|
"""Add BME Struct"""
|
|
bl_idname = "bbp.add_bme_struct"
|
|
bl_label = "Add BME Struct"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
|
|
## There is a compromise due to the shitty Blender design.
|
|
#
|
|
# The passed `self` of Blender Property update function is not the instance of operator,
|
|
# but a simple OperatorProperties.
|
|
# It mean that I can not visit the full operator, only what I can do is visit existing
|
|
# Blender properties.
|
|
#
|
|
# So these is the solution about generating cache list according to the change of bme struct type.
|
|
# First, update function will only set a "outdated" flag for operator which is a pre-registered Blender property.
|
|
# The "outdated" flags is not showen and not saved.
|
|
# Then call a internal cache list update function at the begin of `invoke`, `execute` and `draw`.
|
|
# In this internal cache list updator, check "outdated" flag first, if cache is outdated, update and reset flag.
|
|
# Otherwise do nothing.
|
|
#
|
|
# Reference: https://docs.blender.org/api/current/bpy.props.html#update-example
|
|
|
|
## Compromise used "outdated" flag.
|
|
outdated_flag: bpy.props.BoolProperty(
|
|
name = "Outdated Type",
|
|
description = "Internal flag.",
|
|
options = {'HIDDEN', 'SKIP_SAVE'},
|
|
default = False
|
|
) # type: ignore
|
|
|
|
## A BME struct cfgs descriptor cache list
|
|
# Not only the descriptor self, also the cfg associated index in bme_struct_cfgs
|
|
bme_struct_cfg_index_cache: list[tuple[UTIL_bme.PrototypeShowcaseCfgDescriptor, int]]
|
|
|
|
def __internal_update_bme_struct_type(self) -> None:
|
|
# if not outdated, skip
|
|
if not self.outdated_flag: return
|
|
|
|
# get available cfg entires
|
|
cfgs: typing.Iterator[UTIL_bme.PrototypeShowcaseCfgDescriptor]
|
|
cfgs = _g_EnumHelper_BmeStructType.get_bme_showcase_cfgs(
|
|
_g_EnumHelper_BmeStructType.get_selection(self.bme_struct_type)
|
|
)
|
|
|
|
# analyse cfgs.
|
|
# create counter first
|
|
counter_int: int = 0
|
|
counter_float: int = 0
|
|
counter_bool: int = 0
|
|
# create cache list
|
|
self.bme_struct_cfg_index_cache.clear()
|
|
# iterate cfgs and register them
|
|
for cfg in cfgs:
|
|
match(cfg.get_type()):
|
|
case UTIL_bme.PrototypeShowcaseCfgsTypes.Integer:
|
|
self.bme_struct_cfg_index_cache.append((cfg, counter_int))
|
|
counter_int += 1
|
|
case UTIL_bme.PrototypeShowcaseCfgsTypes.Float:
|
|
self.bme_struct_cfg_index_cache.append((cfg, counter_float))
|
|
counter_float += 1
|
|
case UTIL_bme.PrototypeShowcaseCfgsTypes.Boolean:
|
|
self.bme_struct_cfg_index_cache.append((cfg, counter_bool))
|
|
counter_bool += 1
|
|
case UTIL_bme.PrototypeShowcaseCfgsTypes.Face:
|
|
self.bme_struct_cfg_index_cache.append((cfg, counter_bool))
|
|
counter_bool += 6 # face will occupy 6 bool.
|
|
|
|
|
|
# init data collection
|
|
# clear first
|
|
self.bme_struct_cfgs.clear()
|
|
# create enough entries specified by gotten cfgs
|
|
for _ in range(max(counter_int, counter_float, counter_bool)):
|
|
self.bme_struct_cfgs.add()
|
|
|
|
# assign default value
|
|
for (cfg, cfg_index) in self.bme_struct_cfg_index_cache:
|
|
# show prop differently by cfg type
|
|
match(cfg.get_type()):
|
|
case UTIL_bme.PrototypeShowcaseCfgsTypes.Integer:
|
|
self.bme_struct_cfgs[cfg_index].prop_int = cfg.get_default()
|
|
case UTIL_bme.PrototypeShowcaseCfgsTypes.Float:
|
|
self.bme_struct_cfgs[cfg_index].prop_float = cfg.get_default()
|
|
case UTIL_bme.PrototypeShowcaseCfgsTypes.Boolean:
|
|
self.bme_struct_cfgs[cfg_index].prop_bool = cfg.get_default()
|
|
case UTIL_bme.PrototypeShowcaseCfgsTypes.Face:
|
|
# face is just 6 bool
|
|
default_values: tuple[bool, ...] = cfg.get_default()
|
|
for i in range(6):
|
|
self.bme_struct_cfgs[cfg_index + i].prop_bool = default_values[i]
|
|
|
|
# reset outdated flag
|
|
self.outdated_flag = False
|
|
|
|
# the updator for default side value
|
|
def bme_struct_type_updated(self, context):
|
|
# update outdated flag
|
|
self.outdated_flag = True
|
|
# blender required
|
|
return None
|
|
|
|
bme_struct_type: bpy.props.EnumProperty(
|
|
name = "Type",
|
|
description = "BME struct type",
|
|
items = _g_EnumHelper_BmeStructType.generate_items(),
|
|
update = bme_struct_type_updated
|
|
) # type: ignore
|
|
|
|
bme_struct_cfgs : bpy.props.CollectionProperty(
|
|
name = "Cfgs",
|
|
description = "Cfg collection.",
|
|
type = BBP_PG_bme_adder_cfgs,
|
|
) # type: ignore
|
|
|
|
## Extra transform for good "what you see is what you gotten".
|
|
# Extra transform will be added after moving this object to cursor.
|
|
extra_translation: bpy.props.FloatVectorProperty(
|
|
name = "Extra Translation",
|
|
description = "The extra translation applied to object after moving to cursor.",
|
|
size = 3,
|
|
subtype = 'TRANSLATION',
|
|
step = 50, # same step as the float entry of BBP_PG_bme_adder_cfgs
|
|
default = (0.0, 0.0, 0.0)
|
|
) # type: ignore
|
|
extra_rotation: bpy.props.FloatVectorProperty(
|
|
name = "Extra Rotation",
|
|
description = "The extra rotation applied to object after moving to cursor.",
|
|
size = 3,
|
|
subtype = 'EULER',
|
|
step = 100, # We choosen 100, mean 1. Sync with property window.
|
|
default = (0.0, 0.0, 0.0)
|
|
) # type: ignore
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
return PROP_preferences.get_raw_preferences().has_valid_blc_tex_folder()
|
|
|
|
def invoke(self, context, event):
|
|
# reset extra transform to identy
|
|
self.extra_translation = (0.0, 0.0, 0.0)
|
|
self.extra_rotation = (0.0, 0.0, 0.0)
|
|
self.extra_scale = (1.0, 1.0, 1.0)
|
|
# create internal list
|
|
self.bme_struct_cfg_index_cache = []
|
|
# trigger default bme struct type updator
|
|
self.bme_struct_type_updated(context)
|
|
# call internal updator
|
|
self.__internal_update_bme_struct_type()
|
|
# run execute() function
|
|
return self.execute(context)
|
|
|
|
def execute(self, context):
|
|
# call internal updator
|
|
self.__internal_update_bme_struct_type()
|
|
|
|
# collect cfgs data
|
|
cfgs: dict[str, typing.Any] = {}
|
|
for (cfg, cfg_index) in self.bme_struct_cfg_index_cache:
|
|
match(cfg.get_type()):
|
|
case UTIL_bme.PrototypeShowcaseCfgsTypes.Integer:
|
|
cfgs[cfg.get_field()] = self.bme_struct_cfgs[cfg_index].prop_int
|
|
case UTIL_bme.PrototypeShowcaseCfgsTypes.Float:
|
|
cfgs[cfg.get_field()] = self.bme_struct_cfgs[cfg_index].prop_float
|
|
case UTIL_bme.PrototypeShowcaseCfgsTypes.Boolean:
|
|
cfgs[cfg.get_field()] = self.bme_struct_cfgs[cfg_index].prop_bool
|
|
case UTIL_bme.PrototypeShowcaseCfgsTypes.Face:
|
|
# face is just 6 bool tuple
|
|
cfgs[cfg.get_field()] = tuple(
|
|
self.bme_struct_cfgs[cfg_index + i].prop_bool for i in range(6)
|
|
)
|
|
|
|
# call general creator
|
|
obj: bpy.types.Object = UTIL_bme.create_bme_struct_wrapper(
|
|
_g_EnumHelper_BmeStructType.get_selection(self.bme_struct_type),
|
|
cfgs
|
|
)
|
|
|
|
# add into scene and move to cursor
|
|
UTIL_functions.add_into_scene_and_move_to_cursor(obj)
|
|
# add extra transform
|
|
obj.matrix_world = obj.matrix_world @ mathutils.Matrix.LocRotScale(
|
|
mathutils.Vector(self.extra_translation),
|
|
mathutils.Euler(self.extra_rotation, 'XYZ'),
|
|
mathutils.Vector((1.0, 1.0, 1.0)) # no scale
|
|
)
|
|
# select created object
|
|
UTIL_functions.select_certain_objects((obj, ))
|
|
return {'FINISHED'}
|
|
|
|
def draw(self, context):
|
|
# call internal updator
|
|
self.__internal_update_bme_struct_type()
|
|
|
|
# start drawing
|
|
layout: bpy.types.UILayout = self.layout
|
|
# show type
|
|
layout.prop(self, 'bme_struct_type')
|
|
|
|
# visit cfgs cache list to show cfg
|
|
layout.label(text = "Prototype Configurations:")
|
|
for (cfg, cfg_index) in self.bme_struct_cfg_index_cache:
|
|
# create box for cfgs
|
|
box_layout: bpy.types.UILayout = layout.box()
|
|
|
|
# draw title and description first
|
|
box_layout.label(text = cfg.get_title())
|
|
box_layout.label(text = cfg.get_desc())
|
|
|
|
# show prop differently by cfg type
|
|
match(cfg.get_type()):
|
|
case UTIL_bme.PrototypeShowcaseCfgsTypes.Integer:
|
|
box_layout.prop(self.bme_struct_cfgs[cfg_index], 'prop_int', text = '')
|
|
case UTIL_bme.PrototypeShowcaseCfgsTypes.Float:
|
|
box_layout.prop(self.bme_struct_cfgs[cfg_index], 'prop_float', text = '')
|
|
case UTIL_bme.PrototypeShowcaseCfgsTypes.Boolean:
|
|
box_layout.prop(self.bme_struct_cfgs[cfg_index], 'prop_bool', text = '')
|
|
case UTIL_bme.PrototypeShowcaseCfgsTypes.Face:
|
|
# face will show a special layout (grid view)
|
|
grids = box_layout.grid_flow(
|
|
row_major=True, columns=3, even_columns=True, even_rows=True, align=True)
|
|
grids.alignment = 'CENTER'
|
|
grids.separator()
|
|
grids.prop(self.bme_struct_cfgs[cfg_index + 0], 'prop_bool', text = 'Top') # top
|
|
grids.prop(self.bme_struct_cfgs[cfg_index + 2], 'prop_bool', text = 'Front') # front
|
|
grids.prop(self.bme_struct_cfgs[cfg_index + 4], 'prop_bool', text = 'Left') # left
|
|
grids.label(text = '', icon = 'CUBE') # show a 3d cube as icon
|
|
grids.prop(self.bme_struct_cfgs[cfg_index + 5], 'prop_bool', text = 'Right') # right
|
|
grids.prop(self.bme_struct_cfgs[cfg_index + 3], 'prop_bool', text = 'Back') # back
|
|
grids.prop(self.bme_struct_cfgs[cfg_index + 1], 'prop_bool', text = 'Bottom') # bottom
|
|
grids.separator()
|
|
|
|
# show extra transform props
|
|
# forcely order that each one are placed horizontally
|
|
layout.label(text = "Extra Transform:")
|
|
# translation
|
|
layout.label(text = 'Translation')
|
|
hbox_layout: bpy.types.UILayout = layout.row()
|
|
hbox_layout.prop(self, 'extra_translation', text = '')
|
|
# rotation
|
|
layout.label(text = 'Rotation')
|
|
hbox_layout = layout.row()
|
|
hbox_layout.prop(self, 'extra_rotation', text = '')
|
|
|
|
@classmethod
|
|
def draw_blc_menu(cls, layout: bpy.types.UILayout):
|
|
for ident in _g_EnumHelper_BmeStructType.get_bme_identifiers():
|
|
# draw operator
|
|
cop = layout.operator(
|
|
cls.bl_idname,
|
|
text = _g_EnumHelper_BmeStructType.get_bme_showcase_title(ident),
|
|
icon_value = _g_EnumHelper_BmeStructType.get_bme_showcase_icon(ident)
|
|
)
|
|
# and assign its init type value
|
|
cop.bme_struct_type = _g_EnumHelper_BmeStructType.to_selection(ident)
|
|
|
|
#endregion
|
|
|
|
def register() -> None:
|
|
bpy.utils.register_class(BBP_PG_bme_adder_cfgs)
|
|
bpy.utils.register_class(BBP_OT_add_bme_struct)
|
|
|
|
def unregister() -> None:
|
|
bpy.utils.unregister_class(BBP_OT_add_bme_struct)
|
|
bpy.utils.unregister_class(BBP_PG_bme_adder_cfgs) |