feat: add BME category display in blender.
- add BME category display in blender, including add menu and side menu.
This commit is contained in:
@ -280,16 +280,24 @@ class BBP_OT_add_bme_struct(bpy.types.Operator):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def draw_blc_menu(cls, layout: bpy.types.UILayout):
|
def draw_blc_menu(cls, layout: bpy.types.UILayout):
|
||||||
for ident in _g_EnumHelper_BmeStructType.get_bme_identifiers():
|
for category, idents in _g_EnumHelper_BmeStructType.get_bme_categories().items():
|
||||||
# draw operator
|
# draw category label
|
||||||
cop = layout.operator(
|
layout.label(text=category, text_ctxt=UTIL_translation.build_prototype_showcase_category_context())
|
||||||
cls.bl_idname,
|
|
||||||
text = _g_EnumHelper_BmeStructType.get_bme_showcase_title(ident),
|
# draw prototypes list
|
||||||
icon_value = _g_EnumHelper_BmeStructType.get_bme_showcase_icon(ident),
|
for ident in idents:
|
||||||
text_ctxt = UTIL_translation.build_prototype_showcase_context(ident),
|
# draw operator
|
||||||
)
|
cop = layout.operator(
|
||||||
# and assign its init type value
|
cls.bl_idname,
|
||||||
cop.bme_struct_type = _g_EnumHelper_BmeStructType.to_selection(ident)
|
text = _g_EnumHelper_BmeStructType.get_bme_showcase_title(ident),
|
||||||
|
icon_value = _g_EnumHelper_BmeStructType.get_bme_showcase_icon(ident),
|
||||||
|
text_ctxt = UTIL_translation.build_prototype_showcase_title_context(ident),
|
||||||
|
)
|
||||||
|
# and assign its init type value
|
||||||
|
cop.bme_struct_type = _g_EnumHelper_BmeStructType.to_selection(ident)
|
||||||
|
|
||||||
|
# draw separator
|
||||||
|
layout.separator()
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ class BBP_OT_export_virtools(bpy.types.Operator, UTIL_file_browser.ExportVirtool
|
|||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
# check selecting first
|
# check selecting first
|
||||||
objls: tuple[bpy.types.Object] | None = self.general_get_export_objects(context)
|
objls: tuple[bpy.types.Object, ...] | None = self.general_get_export_objects(context)
|
||||||
if objls is None:
|
if objls is None:
|
||||||
self.report({'ERROR'}, 'No selected target!')
|
self.report({'ERROR'}, 'No selected target!')
|
||||||
return {'CANCELLED'}
|
return {'CANCELLED'}
|
||||||
|
@ -24,6 +24,7 @@ TOKEN_IDENTIFIER: str = 'identifier'
|
|||||||
|
|
||||||
TOKEN_SHOWCASE: str = 'showcase'
|
TOKEN_SHOWCASE: str = 'showcase'
|
||||||
TOKEN_SHOWCASE_TITLE: str = 'title'
|
TOKEN_SHOWCASE_TITLE: str = 'title'
|
||||||
|
TOKEN_SHOWCASE_CATEGORY: str = 'category'
|
||||||
TOKEN_SHOWCASE_ICON: str = 'icon'
|
TOKEN_SHOWCASE_ICON: str = 'icon'
|
||||||
TOKEN_SHOWCASE_TYPE: str = 'type'
|
TOKEN_SHOWCASE_TYPE: str = 'type'
|
||||||
TOKEN_SHOWCASE_CFGS: str = 'cfgs'
|
TOKEN_SHOWCASE_CFGS: str = 'cfgs'
|
||||||
@ -64,10 +65,10 @@ TOKEN_INSTANCES_TRANSFORM: str = 'transform'
|
|||||||
|
|
||||||
#region Prototype Loader
|
#region Prototype Loader
|
||||||
|
|
||||||
## The list storing BME prototype.
|
|
||||||
_g_BMEPrototypes: list[dict[str, typing.Any]] = []
|
_g_BMEPrototypes: list[dict[str, typing.Any]] = []
|
||||||
## The dict. Key is prototype identifier. value is the index of prototype in prototype list.
|
"""The list storing BME prototype."""
|
||||||
_g_BMEPrototypeIndexMap: dict[str, int] = {}
|
_g_BMEPrototypeIndexMap: dict[str, int] = {}
|
||||||
|
"""The dict. Key is prototype identifier. Value is the index of prototype in prototype list."""
|
||||||
|
|
||||||
# the core loader
|
# the core loader
|
||||||
for walk_root, walk_dirs, walk_files in os.walk(os.path.join(os.path.dirname(__file__), 'jsons')):
|
for walk_root, walk_dirs, walk_files in os.walk(os.path.join(os.path.dirname(__file__), 'jsons')):
|
||||||
@ -99,7 +100,7 @@ def _env_fct_angle(x1: float, y1: float, x2: float, y2: float) -> float:
|
|||||||
# second, its direction (clockwise is positive) is opposite with blender rotation direction (counter-clockwise is positive).
|
# second, its direction (clockwise is positive) is opposite with blender rotation direction (counter-clockwise is positive).
|
||||||
diff = mathutils.Vector((x2, y2)) - mathutils.Vector((x1, y1))
|
diff = mathutils.Vector((x2, y2)) - mathutils.Vector((x1, y1))
|
||||||
bld_angle = math.degrees(mathutils.Vector((1,0)).angle_signed(diff, 0))
|
bld_angle = math.degrees(mathutils.Vector((1,0)).angle_signed(diff, 0))
|
||||||
|
|
||||||
# flip it first
|
# flip it first
|
||||||
bld_angle = -bld_angle
|
bld_angle = -bld_angle
|
||||||
# process positove number and negative number respectively
|
# process positove number and negative number respectively
|
||||||
@ -141,7 +142,7 @@ _g_ProgFieldGlobals: dict[str, typing.Any] = {
|
|||||||
'rot': lambda x, y, z: mathutils.Matrix.LocRotScale(None, mathutils.Euler((math.radians(x), math.radians(y), math.radians(z)), 'XYZ'), None),
|
'rot': lambda x, y, z: mathutils.Matrix.LocRotScale(None, mathutils.Euler((math.radians(x), math.radians(y), math.radians(z)), 'XYZ'), None),
|
||||||
'scale': lambda x, y, z: mathutils.Matrix.LocRotScale(None, None, (x, y, z)),
|
'scale': lambda x, y, z: mathutils.Matrix.LocRotScale(None, None, (x, y, z)),
|
||||||
'ident': lambda: mathutils.Matrix.Identity(4),
|
'ident': lambda: mathutils.Matrix.Identity(4),
|
||||||
|
|
||||||
# my misc custom functions
|
# my misc custom functions
|
||||||
'distance': _env_fct_distance,
|
'distance': _env_fct_distance,
|
||||||
'angle': _env_fct_angle,
|
'angle': _env_fct_angle,
|
||||||
@ -191,8 +192,32 @@ class EnumPropHelper(UTIL_functions.EnumPropHelper[str]):
|
|||||||
"""
|
"""
|
||||||
The BME specialized Blender EnumProperty helper.
|
The BME specialized Blender EnumProperty helper.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
showcase_identifiers: tuple[str, ...]
|
||||||
|
showcase_categories: dict[str, tuple[str, ...]]
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
# build cache for showcase identifiers and categories
|
||||||
|
# prepare cache value
|
||||||
|
identifiers: list[str] = []
|
||||||
|
categories: dict[str, list[str]] = {}
|
||||||
|
# iterate showcase prototypes
|
||||||
|
for x in filter(lambda x: x[TOKEN_SHOWCASE] is not None, _g_BMEPrototypes):
|
||||||
|
# fetch identifier and category
|
||||||
|
identifier = typing.cast(str, x[TOKEN_IDENTIFIER])
|
||||||
|
category = typing.cast(str, x[TOKEN_SHOWCASE][TOKEN_SHOWCASE_CATEGORY])
|
||||||
|
# add into identifier list
|
||||||
|
identifiers.append(identifier)
|
||||||
|
# add into categories
|
||||||
|
categories_inner = categories.get(category, None)
|
||||||
|
if categories_inner is None:
|
||||||
|
categories_inner = []
|
||||||
|
categories[category] = categories_inner
|
||||||
|
categories_inner.append(identifier)
|
||||||
|
# tuple the result
|
||||||
|
self.showcase_identifiers = tuple(identifiers)
|
||||||
|
self.showcase_categories = {k: tuple(v) for k, v in categories.items()}
|
||||||
|
|
||||||
# init parent class
|
# init parent class
|
||||||
super().__init__(
|
super().__init__(
|
||||||
self.get_bme_identifiers(),
|
self.get_bme_identifiers(),
|
||||||
@ -202,17 +227,20 @@ class EnumPropHelper(UTIL_functions.EnumPropHelper[str]):
|
|||||||
lambda _: '',
|
lambda _: '',
|
||||||
lambda x: self.get_bme_showcase_icon(x)
|
lambda x: self.get_bme_showcase_icon(x)
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_bme_identifiers(self) -> tuple[str, ...]:
|
def get_bme_identifiers(self) -> tuple[str, ...]:
|
||||||
"""
|
"""
|
||||||
Get the identifier of prototype which need to be exposed to user.
|
Get the identifier of prototype which need to be exposed to user.
|
||||||
Template prototype is not included.
|
In other words, template prototype is not included.
|
||||||
"""
|
"""
|
||||||
return tuple(
|
return self.showcase_identifiers
|
||||||
x[TOKEN_IDENTIFIER] # get identifier
|
|
||||||
for x in filter(lambda x: x[TOKEN_SHOWCASE] is not None, _g_BMEPrototypes) # filter() to filter no showcase template.
|
|
||||||
)
|
|
||||||
|
|
||||||
|
def get_bme_categories(self) -> dict[str, tuple[str, ...]]:
|
||||||
|
"""
|
||||||
|
Get user-oriented identifier list grouped by category.
|
||||||
|
"""
|
||||||
|
return self.showcase_categories
|
||||||
|
|
||||||
def get_bme_showcase_title(self, ident: str) -> str:
|
def get_bme_showcase_title(self, ident: str) -> str:
|
||||||
"""
|
"""
|
||||||
Get BME display title by prototype identifier.
|
Get BME display title by prototype identifier.
|
||||||
@ -326,14 +354,14 @@ def create_bme_struct(
|
|||||||
# create mtl slot remap to help following mesh adding
|
# create mtl slot remap to help following mesh adding
|
||||||
# because mesh writer do not accept string format mtl slot visiting,
|
# because mesh writer do not accept string format mtl slot visiting,
|
||||||
# it only accept int based mtl slot index.
|
# it only accept int based mtl slot index.
|
||||||
#
|
#
|
||||||
# Also we build face used mtl slot index at the same time.
|
# Also we build face used mtl slot index at the same time.
|
||||||
# So we do not analyse texture field again when providing face data.
|
# So we do not analyse texture field again when providing face data.
|
||||||
# The result is in `prebuild_face_mtl_idx` and please note it will store all face's mtl index.
|
# The result is in `prebuild_face_mtl_idx` and please note it will store all face's mtl index.
|
||||||
# For example: if face 0 is skipped and face 1 is used, the first entry in `prebuild_face_mtl_idx`
|
# For example: if face 0 is skipped and face 1 is used, the first entry in `prebuild_face_mtl_idx`
|
||||||
# will be the mtl slot index used by face 0, not 1. And its length is equal to the face count.
|
# will be the mtl slot index used by face 0, not 1. And its length is equal to the face count.
|
||||||
# However, because face 0 is skipped, so the entry is not used and default set to 0.
|
# However, because face 0 is skipped, so the entry is not used and default set to 0.
|
||||||
#
|
#
|
||||||
# NOTE: since Python 3.6, the item of builtin dict is ordered by inserting order.
|
# NOTE: since Python 3.6, the item of builtin dict is ordered by inserting order.
|
||||||
# we rely on this to implement following features.
|
# we rely on this to implement following features.
|
||||||
mtl_remap: dict[str, int] = {}
|
mtl_remap: dict[str, int] = {}
|
||||||
@ -351,7 +379,7 @@ def create_bme_struct(
|
|||||||
# if existing, no need to add into remap
|
# if existing, no need to add into remap
|
||||||
# but we need get its index from remap
|
# but we need get its index from remap
|
||||||
prebuild_face_mtl_idx[face_idx] = mtl_remap.get(mtl_name, 0)
|
prebuild_face_mtl_idx[face_idx] = mtl_remap.get(mtl_name, 0)
|
||||||
|
|
||||||
# pre-compute vertices data because we may need used later.
|
# pre-compute vertices data because we may need used later.
|
||||||
# Because if face normal data is null, it mean that we need to compute it
|
# Because if face normal data is null, it mean that we need to compute it
|
||||||
# by given vertices.
|
# by given vertices.
|
||||||
@ -366,7 +394,7 @@ def create_bme_struct(
|
|||||||
cache_bv = typing.cast(mathutils.Vector, transform @ cache_bv)
|
cache_bv = typing.cast(mathutils.Vector, transform @ cache_bv)
|
||||||
# get result
|
# get result
|
||||||
prebuild_vec_data.append((cache_bv.x, cache_bv.y, cache_bv.z))
|
prebuild_vec_data.append((cache_bv.x, cache_bv.y, cache_bv.z))
|
||||||
|
|
||||||
# Check whether given transform is mirror matrix
|
# Check whether given transform is mirror matrix
|
||||||
# because mirror matrix will reverse triangle indice order.
|
# because mirror matrix will reverse triangle indice order.
|
||||||
# If matrix is mirror matrix, we need reverse it again in following procession,
|
# If matrix is mirror matrix, we need reverse it again in following procession,
|
||||||
|
@ -55,14 +55,22 @@ import bpy
|
|||||||
CTX_BBP: str = 'BBP'
|
CTX_BBP: str = 'BBP'
|
||||||
|
|
||||||
# The universal translation context prefix for BME module in BBP_NG plugin.
|
# The universal translation context prefix for BME module in BBP_NG plugin.
|
||||||
CTX_BBP_BME: str = CTX_BBP + '/BME'
|
CTX_BBP_BME: str = f'{CTX_BBP}/BME'
|
||||||
def build_prototype_showcase_context(identifier: str) -> str:
|
CTX_BBP_BME_CATEGORY: str = f'{CTX_BBP_BME}/Category'
|
||||||
|
CTX_BBP_BME_PROTOTYPE: str = f'{CTX_BBP_BME}/Proto'
|
||||||
|
def build_prototype_showcase_category_context() -> str:
|
||||||
|
"""
|
||||||
|
Build the context for getting the translation for BME prototype showcase category.
|
||||||
|
@return The context for getting translation.
|
||||||
|
"""
|
||||||
|
return CTX_BBP_BME_CATEGORY
|
||||||
|
def build_prototype_showcase_title_context(identifier: str) -> str:
|
||||||
"""
|
"""
|
||||||
Build the context for getting the translation for BME prototype showcase title.
|
Build the context for getting the translation for BME prototype showcase title.
|
||||||
@param[in] identifier The identifier of this prototype.
|
@param[in] identifier The identifier of this prototype.
|
||||||
@return The context for getting translation.
|
@return The context for getting translation.
|
||||||
"""
|
"""
|
||||||
return CTX_BBP_BME + '/' + identifier
|
return f'{CTX_BBP_BME_PROTOTYPE}/{identifier}'
|
||||||
def build_prototype_showcase_cfg_context(identifier: str, cfg_index: int) -> str:
|
def build_prototype_showcase_cfg_context(identifier: str, cfg_index: int) -> str:
|
||||||
"""
|
"""
|
||||||
Build the context for getting the translation for BME prototype showcase configuration title or description.
|
Build the context for getting the translation for BME prototype showcase configuration title or description.
|
||||||
@ -70,7 +78,7 @@ def build_prototype_showcase_cfg_context(identifier: str, cfg_index: int) -> str
|
|||||||
@param[in] cfg_index The index of this configuration in this prototype showcase.
|
@param[in] cfg_index The index of this configuration in this prototype showcase.
|
||||||
@return The context for getting translation.
|
@return The context for getting translation.
|
||||||
"""
|
"""
|
||||||
return CTX_BBP_BME + f'/{identifier}/[{cfg_index}]'
|
return f'{CTX_BBP_BME_PROTOTYPE}/{identifier}/[{cfg_index}]'
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user