Compare commits
1 Commits
Author | SHA1 | Date | |
---|---|---|---|
f29e4e9478 |
11
.gitattributes
vendored
@ -1,6 +1,7 @@
|
|||||||
# All PNG image are binary
|
# all png are binary
|
||||||
*.png binary
|
*.png binary
|
||||||
# Element placeholder mesh should be save as binary
|
# our generated mesh should be save as binary
|
||||||
*.ph binary
|
*.bin binary
|
||||||
# Raw json data should be binary, although i edit it manually
|
# the raw json data should be binary
|
||||||
assets/jsons/*.json5 binary
|
# although i edit it manually
|
||||||
|
bbp_ng/raw_jsons/*.json binary
|
||||||
|
4
.gitignore
vendored
@ -1,3 +1,3 @@
|
|||||||
## ===== Personal =====
|
## ===== Personal =====
|
||||||
# Disable VSCode
|
# Disable distribution build folder
|
||||||
.vscode/
|
redist/
|
||||||
|
Before Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 42 KiB |
13
bbp_ng/.gitignore
vendored
@ -1,15 +1,20 @@
|
|||||||
## ===== Personal =====
|
## ===== Personal =====
|
||||||
# Do not include PyBMap in this repository.
|
# Do not include PyBMap in this repository.
|
||||||
# Order user build and fetch it manually.
|
# Order build fetch it manually.
|
||||||
PyBMap/
|
PyBMap/
|
||||||
|
|
||||||
# Disable generated assets but keep the directory hierarchy.
|
# Disable generated icons and jsons but keep the directory hierarchy.
|
||||||
icons/*
|
icons/*
|
||||||
!icons/.gitkeep
|
!icons/.gitkeep
|
||||||
jsons/*
|
jsons/*
|
||||||
!jsons/.gitkeep
|
!jsons/.gitkeep
|
||||||
meshes/*
|
|
||||||
!meshes/.gitkeep
|
# Do not include intermidiate translation template (POT)
|
||||||
|
# NOTE: it seems that Python default gitignore (written following) disable POT file in default.
|
||||||
|
# so, as it wish, I also remove it from git.
|
||||||
|
i18n/*
|
||||||
|
!i18n/blender.pot
|
||||||
|
!i18n/*.po
|
||||||
|
|
||||||
## ===== Python =====
|
## ===== Python =====
|
||||||
# Byte-compiled / optimized / DLL files
|
# Byte-compiled / optimized / DLL files
|
||||||
|
@ -5,7 +5,7 @@ from . import UTIL_functions, UTIL_translation, UTIL_bme
|
|||||||
|
|
||||||
#region BME Adder
|
#region BME Adder
|
||||||
|
|
||||||
_g_EnumHelper_BmeStructType = UTIL_bme.EnumPropHelper()
|
_g_EnumHelper_BmeStructType: UTIL_bme.EnumPropHelper = UTIL_bme.EnumPropHelper()
|
||||||
|
|
||||||
class BBP_PG_bme_adder_cfgs(bpy.types.PropertyGroup):
|
class BBP_PG_bme_adder_cfgs(bpy.types.PropertyGroup):
|
||||||
prop_int: bpy.props.IntProperty(
|
prop_int: bpy.props.IntProperty(
|
||||||
@ -37,44 +37,39 @@ class BBP_OT_add_bme_struct(bpy.types.Operator):
|
|||||||
bl_options = {'REGISTER', 'UNDO'}
|
bl_options = {'REGISTER', 'UNDO'}
|
||||||
bl_translation_context = 'BBP_OT_add_bme_struct'
|
bl_translation_context = 'BBP_OT_add_bme_struct'
|
||||||
|
|
||||||
# YYC MARK:
|
## There is a compromise due to the shitty Blender design.
|
||||||
# ===== 20231217 =====
|
#
|
||||||
# There is a compromise due to the shitty Blender design.
|
# The passed `self` of Blender Property update function is not the instance of operator,
|
||||||
# The passed `self` of Blender Property update function is not the instance of operator,
|
# but a simple OperatorProperties.
|
||||||
# but a simple OperatorProperties.
|
# It mean that I can not visit the full operator, only what I can do is visit existing
|
||||||
# It mean that I can not visit the full operator, only what I can do is visit existing
|
# Blender properties.
|
||||||
# Blender properties.
|
#
|
||||||
#
|
# So these is the solution about generating cache list according to the change of bme struct type.
|
||||||
# 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.
|
||||||
# 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.
|
||||||
# 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`.
|
||||||
# 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.
|
||||||
# In this internal cache list updator, check "outdated" flag first, if cache is outdated, update and reset flag.
|
# Otherwise do nothing.
|
||||||
# Otherwise do nothing.
|
#
|
||||||
#
|
# Reference: https://docs.blender.org/api/current/bpy.props.html#update-example
|
||||||
# Reference: https://docs.blender.org/api/current/bpy.props.html#update-example
|
|
||||||
#
|
## Compromise used "outdated" flag.
|
||||||
# ===== 20250131 =====
|
outdated_flag: bpy.props.BoolProperty(
|
||||||
# There is a fatal performance bug when I adding BME operator list into 3D View sidebar panels (N Menu).
|
# TR: Property not showen should not have name and desc.
|
||||||
# It will cause calling my Panel's `draw` function infinityly of Panel in each render tick,
|
# name = "Outdated Type",
|
||||||
# which calls `BBP_OT_add_bme_struct.draw_blc_menu` directly,
|
# description = "Internal flag.",
|
||||||
# eat too much CPU and GPU resources and make the whole Blender be laggy.
|
options = {'HIDDEN', 'SKIP_SAVE'},
|
||||||
#
|
default = False
|
||||||
# After some research, I found that if I comment the parameter `update` of the member `bme_struct_type`,
|
) # type: ignore
|
||||||
# everything will be resolved.
|
|
||||||
# It even doesn't work that do nothing in update function.
|
|
||||||
# So I realize that sidebar panel may not be compatible with update function.
|
|
||||||
# After reading the note written above, I decide to remove the whole feature of this ugly implementation,
|
|
||||||
# so that I need to remove the ability that changing BME prototype type in left-bottom window.
|
|
||||||
#
|
|
||||||
# After talking with the requestor of this feature, ZZQ,
|
|
||||||
# he agree with my decision and I think this change will not broke any experience of BBP.
|
|
||||||
|
|
||||||
## A BME struct cfgs descriptor cache list
|
## A BME struct cfgs descriptor cache list
|
||||||
# Not only the descriptor self, also the cfg associated index in bme_struct_cfgs
|
# 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]]
|
bme_struct_cfg_index_cache: list[tuple[UTIL_bme.PrototypeShowcaseCfgDescriptor, int]]
|
||||||
|
|
||||||
def __build_bme_struct_cfg_index_cache(self) -> None:
|
def __internal_update_bme_struct_type(self) -> None:
|
||||||
|
# if not outdated, skip
|
||||||
|
if not self.outdated_flag: return
|
||||||
|
|
||||||
# get available cfg entires
|
# get available cfg entires
|
||||||
cfgs: typing.Iterator[UTIL_bme.PrototypeShowcaseCfgDescriptor]
|
cfgs: typing.Iterator[UTIL_bme.PrototypeShowcaseCfgDescriptor]
|
||||||
cfgs = _g_EnumHelper_BmeStructType.get_bme_showcase_cfgs(
|
cfgs = _g_EnumHelper_BmeStructType.get_bme_showcase_cfgs(
|
||||||
@ -130,10 +125,21 @@ class BBP_OT_add_bme_struct(bpy.types.Operator):
|
|||||||
for i in range(6):
|
for i in range(6):
|
||||||
op_cfgs_visitor[cfg_index + i].prop_bool = default_values[i]
|
op_cfgs_visitor[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(
|
bme_struct_type: bpy.props.EnumProperty(
|
||||||
name = "Type",
|
name = "Type",
|
||||||
description = "The type of BME structure.",
|
description = "The type of BME structure.",
|
||||||
items = _g_EnumHelper_BmeStructType.generate_items(),
|
items = _g_EnumHelper_BmeStructType.generate_items(),
|
||||||
|
update = bme_struct_type_updated,
|
||||||
translation_context = 'BBP_OT_add_bme_struct/property'
|
translation_context = 'BBP_OT_add_bme_struct/property'
|
||||||
) # type: ignore
|
) # type: ignore
|
||||||
|
|
||||||
@ -174,16 +180,19 @@ class BBP_OT_add_bme_struct(bpy.types.Operator):
|
|||||||
self.extra_translation = (0.0, 0.0, 0.0)
|
self.extra_translation = (0.0, 0.0, 0.0)
|
||||||
self.extra_rotation = (0.0, 0.0, 0.0)
|
self.extra_rotation = (0.0, 0.0, 0.0)
|
||||||
self.extra_scale = (1.0, 1.0, 1.0)
|
self.extra_scale = (1.0, 1.0, 1.0)
|
||||||
|
|
||||||
# create internal list
|
# create internal list
|
||||||
self.bme_struct_cfg_index_cache = []
|
self.bme_struct_cfg_index_cache = []
|
||||||
# call internal builder to load prototype data inside it
|
# trigger default bme struct type updator
|
||||||
self.__build_bme_struct_cfg_index_cache()
|
self.bme_struct_type_updated(context)
|
||||||
|
# call internal updator
|
||||||
|
self.__internal_update_bme_struct_type()
|
||||||
# run execute() function
|
# run execute() function
|
||||||
return self.execute(context)
|
return self.execute(context)
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
|
# call internal updator
|
||||||
|
self.__internal_update_bme_struct_type()
|
||||||
|
|
||||||
# create cfg visitor
|
# create cfg visitor
|
||||||
op_cfgs_visitor: UTIL_functions.CollectionVisitor[BBP_PG_bme_adder_cfgs]
|
op_cfgs_visitor: UTIL_functions.CollectionVisitor[BBP_PG_bme_adder_cfgs]
|
||||||
op_cfgs_visitor = UTIL_functions.CollectionVisitor(self.bme_struct_cfgs)
|
op_cfgs_visitor = UTIL_functions.CollectionVisitor(self.bme_struct_cfgs)
|
||||||
@ -222,8 +231,13 @@ class BBP_OT_add_bme_struct(bpy.types.Operator):
|
|||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
|
# call internal updator
|
||||||
|
self.__internal_update_bme_struct_type()
|
||||||
|
|
||||||
# start drawing
|
# start drawing
|
||||||
layout: bpy.types.UILayout = self.layout
|
layout: bpy.types.UILayout = self.layout
|
||||||
|
# show type
|
||||||
|
layout.prop(self, 'bme_struct_type')
|
||||||
|
|
||||||
# create cfg visitor
|
# create cfg visitor
|
||||||
op_cfgs_visitor: UTIL_functions.CollectionVisitor[BBP_PG_bme_adder_cfgs]
|
op_cfgs_visitor: UTIL_functions.CollectionVisitor[BBP_PG_bme_adder_cfgs]
|
||||||
@ -250,7 +264,7 @@ class BBP_OT_add_bme_struct(bpy.types.Operator):
|
|||||||
case UTIL_bme.PrototypeShowcaseCfgsTypes.Float:
|
case UTIL_bme.PrototypeShowcaseCfgsTypes.Float:
|
||||||
box_layout.prop(op_cfgs_visitor[cfg_index], 'prop_float', text='')
|
box_layout.prop(op_cfgs_visitor[cfg_index], 'prop_float', text='')
|
||||||
case UTIL_bme.PrototypeShowcaseCfgsTypes.Boolean:
|
case UTIL_bme.PrototypeShowcaseCfgsTypes.Boolean:
|
||||||
box_layout.prop(op_cfgs_visitor[cfg_index], 'prop_bool', toggle=1, text='Yes', text_ctxt='BBP_OT_add_bme_struct/draw')
|
box_layout.prop(op_cfgs_visitor[cfg_index], 'prop_bool', text='')
|
||||||
case UTIL_bme.PrototypeShowcaseCfgsTypes.Face:
|
case UTIL_bme.PrototypeShowcaseCfgsTypes.Face:
|
||||||
# face will show a special layout (grid view)
|
# face will show a special layout (grid view)
|
||||||
grids = box_layout.grid_flow(
|
grids = box_layout.grid_flow(
|
||||||
@ -280,24 +294,16 @@ 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 category, idents in _g_EnumHelper_BmeStructType.get_bme_categories().items():
|
for ident in _g_EnumHelper_BmeStructType.get_bme_identifiers():
|
||||||
# draw category label
|
# draw operator
|
||||||
layout.label(text=category, text_ctxt=UTIL_translation.build_prototype_showcase_category_context())
|
cop = layout.operator(
|
||||||
|
cls.bl_idname,
|
||||||
# draw prototypes list
|
text = _g_EnumHelper_BmeStructType.get_bme_showcase_title(ident),
|
||||||
for ident in idents:
|
icon_value = _g_EnumHelper_BmeStructType.get_bme_showcase_icon(ident),
|
||||||
# draw operator
|
text_ctxt = UTIL_translation.build_prototype_showcase_context(ident)
|
||||||
cop = layout.operator(
|
)
|
||||||
cls.bl_idname,
|
# and assign its init type value
|
||||||
text = _g_EnumHelper_BmeStructType.get_bme_showcase_title(ident),
|
cop.bme_struct_type = _g_EnumHelper_BmeStructType.to_selection(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
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import bpy, mathutils
|
import bpy, mathutils
|
||||||
import math, typing
|
import math, typing
|
||||||
from . import UTIL_functions, UTIL_icons_manager, UTIL_naming_convention
|
from . import UTIL_functions, UTIL_icons_manager, UTIL_naming_convension
|
||||||
from . import PROP_ballance_element, PROP_virtools_group, PROP_ballance_map_info
|
from . import PROP_ballance_element, PROP_virtools_group, PROP_ballance_map_info
|
||||||
|
|
||||||
#region Param Help Classes
|
#region Param Help Classes
|
||||||
@ -41,33 +41,33 @@ class ComponentCountParam():
|
|||||||
|
|
||||||
#region Help Classes & Functions
|
#region Help Classes & Functions
|
||||||
|
|
||||||
def _get_component_info(comp_type: PROP_ballance_element.BallanceElementType, comp_sector: int) -> UTIL_naming_convention.BallanceObjectInfo:
|
def _get_component_info(comp_type: PROP_ballance_element.BallanceElementType, comp_sector: int) -> UTIL_naming_convension.BallanceObjectInfo:
|
||||||
match(comp_type):
|
match(comp_type):
|
||||||
# process for 2 special unique components
|
# process for 2 special unique components
|
||||||
case PROP_ballance_element.BallanceElementType.PS_FourFlames:
|
case PROP_ballance_element.BallanceElementType.PS_FourFlames:
|
||||||
return UTIL_naming_convention.BallanceObjectInfo.create_from_others(UTIL_naming_convention.BallanceObjectType.LEVEL_START)
|
return UTIL_naming_convension.BallanceObjectInfo.create_from_others(UTIL_naming_convension.BallanceObjectType.LEVEL_START)
|
||||||
case PROP_ballance_element.BallanceElementType.PE_Balloon:
|
case PROP_ballance_element.BallanceElementType.PE_Balloon:
|
||||||
return UTIL_naming_convention.BallanceObjectInfo.create_from_others(UTIL_naming_convention.BallanceObjectType.LEVEL_END)
|
return UTIL_naming_convension.BallanceObjectInfo.create_from_others(UTIL_naming_convension.BallanceObjectType.LEVEL_END)
|
||||||
# process naming convention required special components
|
# process naming convention required special components
|
||||||
case PROP_ballance_element.BallanceElementType.PC_TwoFlames:
|
case PROP_ballance_element.BallanceElementType.PC_TwoFlames:
|
||||||
return UTIL_naming_convention.BallanceObjectInfo.create_from_checkpoint(comp_sector)
|
return UTIL_naming_convension.BallanceObjectInfo.create_from_checkpoint(comp_sector)
|
||||||
case PROP_ballance_element.BallanceElementType.PR_Resetpoint:
|
case PROP_ballance_element.BallanceElementType.PR_Resetpoint:
|
||||||
return UTIL_naming_convention.BallanceObjectInfo.create_from_resetpoint(comp_sector)
|
return UTIL_naming_convension.BallanceObjectInfo.create_from_resetpoint(comp_sector)
|
||||||
# process for other components
|
# process for other components
|
||||||
case _:
|
case _:
|
||||||
return UTIL_naming_convention.BallanceObjectInfo.create_from_component(
|
return UTIL_naming_convension.BallanceObjectInfo.create_from_component(
|
||||||
PROP_ballance_element.get_ballance_element_name(comp_type),
|
PROP_ballance_element.get_ballance_element_name(comp_type),
|
||||||
comp_sector
|
comp_sector
|
||||||
)
|
)
|
||||||
|
|
||||||
def _set_component_by_info(obj: bpy.types.Object, info: UTIL_naming_convention.BallanceObjectInfo) -> None:
|
def _set_component_by_info(obj: bpy.types.Object, info: UTIL_naming_convension.BallanceObjectInfo) -> None:
|
||||||
# set component name and grouping it into virtools group at the same time
|
# set component name and grouping it into virtools group at the same time
|
||||||
# set name first
|
# set name first
|
||||||
if not UTIL_naming_convention.YYCToolchainConvention.set_to_object(obj, info, None):
|
if not UTIL_naming_convension.YYCToolchainConvention.set_to_object(obj, info, None):
|
||||||
raise UTIL_functions.BBPException('impossible fail to set component name.')
|
raise UTIL_functions.BBPException('impossible fail to set component name.')
|
||||||
|
|
||||||
# set vt group next
|
# set vt group next
|
||||||
if not UTIL_naming_convention.VirtoolsGroupConvention.set_to_object(obj, info, None):
|
if not UTIL_naming_convension.VirtoolsGroupConvention.set_to_object(obj, info, None):
|
||||||
raise UTIL_functions.BBPException('impossible fail to set component virtools groups.')
|
raise UTIL_functions.BBPException('impossible fail to set component virtools groups.')
|
||||||
|
|
||||||
def _check_component_existance(comp_type: PROP_ballance_element.BallanceElementType, comp_sector: int) -> str | None:
|
def _check_component_existance(comp_type: PROP_ballance_element.BallanceElementType, comp_sector: int) -> str | None:
|
||||||
@ -85,10 +85,10 @@ def _check_component_existance(comp_type: PROP_ballance_element.BallanceElementT
|
|||||||
return None # return, do not check
|
return None # return, do not check
|
||||||
|
|
||||||
# get info
|
# get info
|
||||||
comp_info: UTIL_naming_convention.BallanceObjectInfo = _get_component_info(comp_type, comp_sector)
|
comp_info: UTIL_naming_convension.BallanceObjectInfo = _get_component_info(comp_type, comp_sector)
|
||||||
|
|
||||||
# get expected name
|
# get expected name
|
||||||
expect_name: str | None = UTIL_naming_convention.YYCToolchainConvention.set_to_name(comp_info, None)
|
expect_name: str | None = UTIL_naming_convension.YYCToolchainConvention.set_to_name(comp_info, None)
|
||||||
if expect_name is None:
|
if expect_name is None:
|
||||||
raise UTIL_functions.BBPException('impossible fail to get component name.')
|
raise UTIL_functions.BBPException('impossible fail to get component name.')
|
||||||
|
|
||||||
@ -129,7 +129,7 @@ class _GeneralComponentCreator():
|
|||||||
@return The created component instance.
|
@return The created component instance.
|
||||||
"""
|
"""
|
||||||
# get element info first
|
# get element info first
|
||||||
ele_info: UTIL_naming_convention.BallanceObjectInfo = _get_component_info(comp_type, comp_sector)
|
ele_info: UTIL_naming_convension.BallanceObjectInfo = _get_component_info(comp_type, comp_sector)
|
||||||
|
|
||||||
# create blc element context
|
# create blc element context
|
||||||
with PROP_ballance_element.BallanceElementsHelper(bpy.context.scene) as creator:
|
with PROP_ballance_element.BallanceElementsHelper(bpy.context.scene) as creator:
|
||||||
@ -152,12 +152,12 @@ class _GeneralComponentCreator():
|
|||||||
enlarged_sector: int
|
enlarged_sector: int
|
||||||
# check component type to get enlarged value
|
# check component type to get enlarged value
|
||||||
match(ele_info.mBasicType):
|
match(ele_info.mBasicType):
|
||||||
case UTIL_naming_convention.BallanceObjectType.COMPONENT:
|
case UTIL_naming_convension.BallanceObjectType.COMPONENT:
|
||||||
enlarged_sector = comp_sector
|
enlarged_sector = comp_sector
|
||||||
case UTIL_naming_convention.BallanceObjectType.CHECKPOINT:
|
case UTIL_naming_convension.BallanceObjectType.CHECKPOINT:
|
||||||
# checkpoint 1 means that there is sector 2, so we plus 1 for it.
|
# checkpoint 1 means that there is sector 2, so we plus 1 for it.
|
||||||
enlarged_sector = comp_sector + 1
|
enlarged_sector = comp_sector + 1
|
||||||
case UTIL_naming_convention.BallanceObjectType.RESETPOINT:
|
case UTIL_naming_convension.BallanceObjectType.RESETPOINT:
|
||||||
enlarged_sector = comp_sector
|
enlarged_sector = comp_sector
|
||||||
case _:
|
case _:
|
||||||
# this component is not a sector based component
|
# this component is not a sector based component
|
||||||
@ -176,7 +176,7 @@ class _GeneralComponentCreator():
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Normal Component Adder
|
#region Noemal Component Adder
|
||||||
|
|
||||||
# element enum prop helper
|
# element enum prop helper
|
||||||
|
|
||||||
@ -184,7 +184,7 @@ def _get_component_icon_by_name(elename: str):
|
|||||||
icon: int | None = UTIL_icons_manager.get_component_icon(elename)
|
icon: int | None = UTIL_icons_manager.get_component_icon(elename)
|
||||||
if icon is None: return UTIL_icons_manager.get_empty_icon()
|
if icon is None: return UTIL_icons_manager.get_empty_icon()
|
||||||
else: return icon
|
else: return icon
|
||||||
_g_EnumHelper_Component = UTIL_functions.EnumPropHelper(
|
_g_EnumHelper_Component: UTIL_functions.EnumPropHelper = UTIL_functions.EnumPropHelper(
|
||||||
PROP_ballance_element.BallanceElementType,
|
PROP_ballance_element.BallanceElementType,
|
||||||
lambda x: str(x.value),
|
lambda x: str(x.value),
|
||||||
lambda x: PROP_ballance_element.BallanceElementType(int(x)),
|
lambda x: PROP_ballance_element.BallanceElementType(int(x)),
|
||||||
@ -217,7 +217,7 @@ class BBP_OT_add_component(bpy.types.Operator, ComponentSectorParam):
|
|||||||
layout.prop(self, "component_type")
|
layout.prop(self, "component_type")
|
||||||
|
|
||||||
# only show sector for non-PE/PS component
|
# only show sector for non-PE/PS component
|
||||||
eletype = _g_EnumHelper_Component.get_selection(self.component_type)
|
eletype: PROP_ballance_element.BallanceElementType = _g_EnumHelper_Component.get_selection(self.component_type)
|
||||||
if eletype != PROP_ballance_element.BallanceElementType.PS_FourFlames and eletype != PROP_ballance_element.BallanceElementType.PE_Balloon:
|
if eletype != PROP_ballance_element.BallanceElementType.PS_FourFlames and eletype != PROP_ballance_element.BallanceElementType.PE_Balloon:
|
||||||
self.draw_component_sector_params(layout)
|
self.draw_component_sector_params(layout)
|
||||||
|
|
||||||
@ -574,17 +574,14 @@ class BBP_OT_add_sector_component_pair(bpy.types.Operator, ComponentSectorParam)
|
|||||||
# get type and sector data first
|
# get type and sector data first
|
||||||
(checkp_ty, checkp_sector) = self.__get_checkpoint()
|
(checkp_ty, checkp_sector) = self.__get_checkpoint()
|
||||||
(resetp_ty, resetp_sector) = self.__get_resetpoint()
|
(resetp_ty, resetp_sector) = self.__get_resetpoint()
|
||||||
# calc resetpoint offset and checkpoint rotation
|
# calc resetpoint offset
|
||||||
# resetpoint need a extra offset between checkpoint but it is different in FourFlams and TwoFlams.
|
# resetpoint need a extra offset between checkpoint
|
||||||
# 4 flames startpoint need a extra 90 degree rotation to correspond with ballance asset library (and the direction of resetpoint).
|
# but it is different in FourFlams and TwoFlams
|
||||||
resetp_offset: float
|
resetp_offset: float
|
||||||
checkp_degree: float
|
|
||||||
if checkp_ty == PROP_ballance_element.BallanceElementType.PS_FourFlames:
|
if checkp_ty == PROP_ballance_element.BallanceElementType.PS_FourFlames:
|
||||||
resetp_offset = 3.65
|
resetp_offset = 3.25
|
||||||
checkp_degree = 90
|
|
||||||
else:
|
else:
|
||||||
resetp_offset = 3.3258
|
resetp_offset = 2.0
|
||||||
checkp_degree = 0
|
|
||||||
|
|
||||||
# add elements
|
# add elements
|
||||||
# create checkpoint
|
# create checkpoint
|
||||||
@ -593,7 +590,7 @@ class BBP_OT_add_sector_component_pair(bpy.types.Operator, ComponentSectorParam)
|
|||||||
checkp_ty,
|
checkp_ty,
|
||||||
checkp_sector,
|
checkp_sector,
|
||||||
1, # only create one
|
1, # only create one
|
||||||
lambda _: mathutils.Matrix.Rotation(math.radians(checkp_degree), 4, 'Z')
|
lambda _: mathutils.Matrix.Identity(4)
|
||||||
)
|
)
|
||||||
# create resetpoint
|
# create resetpoint
|
||||||
creator.create_component(
|
creator.create_component(
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import bpy, mathutils, math
|
import bpy, mathutils, math
|
||||||
import typing
|
import typing
|
||||||
from . import UTIL_rail_creator, PROP_preferences
|
from . import UTIL_rail_creator
|
||||||
|
|
||||||
## Const Value Hint:
|
## Const Value Hint:
|
||||||
# Default Rail Radius: 0.35 (in measure)
|
# Default Rail Radius: 0.35 (in measure)
|
||||||
@ -233,10 +233,6 @@ class BBP_OT_add_rail_section(SharedRailSectionInputProperty, bpy.types.Operator
|
|||||||
bl_options = {'REGISTER', 'UNDO'}
|
bl_options = {'REGISTER', 'UNDO'}
|
||||||
bl_translation_context = 'BBP_OT_add_rail_section'
|
bl_translation_context = 'BBP_OT_add_rail_section'
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def poll(cls, context):
|
|
||||||
return PROP_preferences.get_raw_preferences().has_valid_blc_tex_folder()
|
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
UTIL_rail_creator.rail_creator_wrapper(
|
UTIL_rail_creator.rail_creator_wrapper(
|
||||||
lambda bm: UTIL_rail_creator.create_rail_section(
|
lambda bm: UTIL_rail_creator.create_rail_section(
|
||||||
@ -258,10 +254,6 @@ class BBP_OT_add_transition_section(bpy.types.Operator):
|
|||||||
bl_options = {'REGISTER', 'UNDO'}
|
bl_options = {'REGISTER', 'UNDO'}
|
||||||
bl_translation_context = 'BBP_OT_add_transition_section'
|
bl_translation_context = 'BBP_OT_add_transition_section'
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def poll(cls, context):
|
|
||||||
return PROP_preferences.get_raw_preferences().has_valid_blc_tex_folder()
|
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
UTIL_rail_creator.rail_creator_wrapper(
|
UTIL_rail_creator.rail_creator_wrapper(
|
||||||
lambda bm: UTIL_rail_creator.create_transition_section(bm, c_DefaultRailRadius, c_DefaultRailSpan),
|
lambda bm: UTIL_rail_creator.create_transition_section(bm, c_DefaultRailRadius, c_DefaultRailSpan),
|
||||||
@ -280,10 +272,6 @@ class BBP_OT_add_straight_rail(SharedExtraTransform, SharedRailSectionInputPrope
|
|||||||
bl_options = {'REGISTER', 'UNDO'}
|
bl_options = {'REGISTER', 'UNDO'}
|
||||||
bl_translation_context = 'BBP_OT_add_straight_rail'
|
bl_translation_context = 'BBP_OT_add_straight_rail'
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def poll(cls, context):
|
|
||||||
return PROP_preferences.get_raw_preferences().has_valid_blc_tex_folder()
|
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
UTIL_rail_creator.rail_creator_wrapper(
|
UTIL_rail_creator.rail_creator_wrapper(
|
||||||
lambda bm: UTIL_rail_creator.create_straight_rail(
|
lambda bm: UTIL_rail_creator.create_straight_rail(
|
||||||
@ -313,10 +301,6 @@ class BBP_OT_add_transition_rail(SharedExtraTransform, SharedRailCapInputPropert
|
|||||||
bl_options = {'REGISTER', 'UNDO'}
|
bl_options = {'REGISTER', 'UNDO'}
|
||||||
bl_translation_context = 'BBP_OT_add_transition_rail'
|
bl_translation_context = 'BBP_OT_add_transition_rail'
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def poll(cls, context):
|
|
||||||
return PROP_preferences.get_raw_preferences().has_valid_blc_tex_folder()
|
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
UTIL_rail_creator.rail_creator_wrapper(
|
UTIL_rail_creator.rail_creator_wrapper(
|
||||||
lambda bm: UTIL_rail_creator.create_transition_rail(
|
lambda bm: UTIL_rail_creator.create_transition_rail(
|
||||||
@ -356,10 +340,6 @@ class BBP_OT_add_side_rail(SharedExtraTransform, SharedRailCapInputProperty, Sha
|
|||||||
translation_context = 'BBP_OT_add_side_rail/property'
|
translation_context = 'BBP_OT_add_side_rail/property'
|
||||||
) # type: ignore
|
) # type: ignore
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def poll(cls, context):
|
|
||||||
return PROP_preferences.get_raw_preferences().has_valid_blc_tex_folder()
|
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
UTIL_rail_creator.rail_creator_wrapper(
|
UTIL_rail_creator.rail_creator_wrapper(
|
||||||
lambda bm: UTIL_rail_creator.create_straight_rail(
|
lambda bm: UTIL_rail_creator.create_straight_rail(
|
||||||
@ -399,10 +379,6 @@ class BBP_OT_add_arc_rail(SharedExtraTransform, SharedRailSectionInputProperty,
|
|||||||
translation_context = 'BBP_OT_add_arc_rail/property'
|
translation_context = 'BBP_OT_add_arc_rail/property'
|
||||||
) # type: ignore
|
) # type: ignore
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def poll(cls, context):
|
|
||||||
return PROP_preferences.get_raw_preferences().has_valid_blc_tex_folder()
|
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
UTIL_rail_creator.rail_creator_wrapper(
|
UTIL_rail_creator.rail_creator_wrapper(
|
||||||
lambda bm: UTIL_rail_creator.create_screw_rail(
|
lambda bm: UTIL_rail_creator.create_screw_rail(
|
||||||
@ -454,10 +430,6 @@ class BBP_OT_add_spiral_rail(SharedExtraTransform, SharedRailCapInputProperty, S
|
|||||||
translation_context = 'BBP_OT_add_spiral_rail/property'
|
translation_context = 'BBP_OT_add_spiral_rail/property'
|
||||||
) # type: ignore
|
) # type: ignore
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def poll(cls, context):
|
|
||||||
return PROP_preferences.get_raw_preferences().has_valid_blc_tex_folder()
|
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
UTIL_rail_creator.rail_creator_wrapper(
|
UTIL_rail_creator.rail_creator_wrapper(
|
||||||
lambda bm: UTIL_rail_creator.create_screw_rail(
|
lambda bm: UTIL_rail_creator.create_screw_rail(
|
||||||
@ -502,10 +474,6 @@ class BBP_OT_add_side_spiral_rail(SharedExtraTransform, SharedRailSectionInputPr
|
|||||||
translation_context = 'BBP_OT_add_side_spiral_rail/property'
|
translation_context = 'BBP_OT_add_side_spiral_rail/property'
|
||||||
) # type: ignore
|
) # type: ignore
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def poll(cls, context):
|
|
||||||
return PROP_preferences.get_raw_preferences().has_valid_blc_tex_folder()
|
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
UTIL_rail_creator.rail_creator_wrapper(
|
UTIL_rail_creator.rail_creator_wrapper(
|
||||||
lambda bm: UTIL_rail_creator.create_screw_rail(
|
lambda bm: UTIL_rail_creator.create_screw_rail(
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import bpy, mathutils
|
import bpy, mathutils
|
||||||
from bpy_extras.wm_utils.progress_report import ProgressReport
|
from bpy_extras.wm_utils.progress_report import ProgressReport
|
||||||
import tempfile, os, typing
|
import tempfile, os, typing
|
||||||
from . import PROP_preferences, UTIL_ioport_shared, UTIL_naming_convention
|
from . import PROP_preferences, UTIL_ioport_shared
|
||||||
from . import UTIL_virtools_types, UTIL_functions, UTIL_file_browser, UTIL_blender_mesh, UTIL_ballance_texture
|
from . import UTIL_virtools_types, UTIL_functions, UTIL_file_browser, UTIL_blender_mesh, UTIL_ballance_texture, UTIL_naming_convension
|
||||||
from . import PROP_virtools_group, PROP_virtools_material, PROP_virtools_mesh, PROP_virtools_texture, PROP_virtools_light
|
from . import PROP_virtools_group, PROP_virtools_material, PROP_virtools_mesh, PROP_virtools_texture, PROP_virtools_light
|
||||||
from .PyBMap import bmap_wrapper as bmap
|
from .PyBMap import bmap_wrapper as bmap
|
||||||
|
|
||||||
@ -18,16 +18,10 @@ class BBP_OT_export_virtools(bpy.types.Operator, UTIL_file_browser.ExportVirtool
|
|||||||
return (
|
return (
|
||||||
PROP_preferences.get_raw_preferences().has_valid_blc_tex_folder()
|
PROP_preferences.get_raw_preferences().has_valid_blc_tex_folder()
|
||||||
and bmap.is_bmap_available())
|
and bmap.is_bmap_available())
|
||||||
|
|
||||||
def invoke(self, context, event):
|
|
||||||
# preset virtools encoding if possible
|
|
||||||
self.preset_vt_encodings_if_possible(context)
|
|
||||||
# call parent invoke function (same reason written in IMPORT module)
|
|
||||||
return super().invoke(context, event)
|
|
||||||
|
|
||||||
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'}
|
||||||
@ -44,16 +38,10 @@ class BBP_OT_export_virtools(bpy.types.Operator, UTIL_file_browser.ExportVirtool
|
|||||||
self.report({'ERROR'}, 'You must specify at least one encoding for file saving (e.g. cp1252, gbk)!')
|
self.report({'ERROR'}, 'You must specify at least one encoding for file saving (e.g. cp1252, gbk)!')
|
||||||
return {'CANCELLED'}
|
return {'CANCELLED'}
|
||||||
|
|
||||||
# check file name
|
|
||||||
filename = self.general_get_filename()
|
|
||||||
if not os.path.isfile(filename):
|
|
||||||
self.report({'ERROR'}, 'No file was selected!')
|
|
||||||
return {'CANCELLED'}
|
|
||||||
|
|
||||||
# start exporting
|
# start exporting
|
||||||
with UTIL_ioport_shared.ExportEditModeBackup() as editmode_guard:
|
with UTIL_ioport_shared.ExportEditModeBackup() as editmode_guard:
|
||||||
_export_virtools(
|
_export_virtools(
|
||||||
filename,
|
self.general_get_filename(),
|
||||||
encodings,
|
encodings,
|
||||||
texture_save_opt,
|
texture_save_opt,
|
||||||
self.general_get_use_compress(),
|
self.general_get_use_compress(),
|
||||||
@ -80,7 +68,7 @@ _TTexturePair = tuple[bpy.types.Image, bmap.BMTexture]
|
|||||||
|
|
||||||
def _export_virtools(
|
def _export_virtools(
|
||||||
file_name_: str,
|
file_name_: str,
|
||||||
encodings_: tuple[str, ...],
|
encodings_: tuple[str],
|
||||||
texture_save_opt_: UTIL_virtools_types.CK_TEXTURE_SAVEOPTIONS,
|
texture_save_opt_: UTIL_virtools_types.CK_TEXTURE_SAVEOPTIONS,
|
||||||
use_compress_: bool,
|
use_compress_: bool,
|
||||||
compress_level_: int,
|
compress_level_: int,
|
||||||
@ -202,7 +190,7 @@ def _export_virtools_groups(
|
|||||||
# So we create all needed sector group in here to make sure exported virtools file can be read by Ballancde correctly.
|
# So we create all needed sector group in here to make sure exported virtools file can be read by Ballancde correctly.
|
||||||
if successive_sector:
|
if successive_sector:
|
||||||
for i in range(successive_sector_count):
|
for i in range(successive_sector_count):
|
||||||
gp_name: str = UTIL_naming_convention.build_name_from_sector_index(i + 1)
|
gp_name: str = UTIL_naming_convension.build_name_from_sector_index(i + 1)
|
||||||
vtgroup: bmap.BMGroup | None = group_cret_map.get(gp_name, None)
|
vtgroup: bmap.BMGroup | None = group_cret_map.get(gp_name, None)
|
||||||
if vtgroup is None:
|
if vtgroup is None:
|
||||||
vtgroup = writer.create_group()
|
vtgroup = writer.create_group()
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import bpy, mathutils
|
import bpy, mathutils
|
||||||
from bpy_extras.wm_utils.progress_report import ProgressReport
|
from bpy_extras.wm_utils.progress_report import ProgressReport
|
||||||
import tempfile, os, typing
|
import tempfile, os, typing
|
||||||
from . import PROP_preferences, UTIL_ioport_shared, UTIL_naming_convention
|
from . import PROP_preferences, UTIL_ioport_shared
|
||||||
from . import UTIL_virtools_types, UTIL_functions, UTIL_file_browser, UTIL_blender_mesh, UTIL_ballance_texture
|
from . import UTIL_virtools_types, UTIL_functions, UTIL_file_browser, UTIL_blender_mesh, UTIL_ballance_texture, UTIL_naming_convension
|
||||||
from . import PROP_virtools_group, PROP_virtools_material, PROP_virtools_mesh, PROP_virtools_texture, PROP_virtools_light, PROP_ballance_map_info
|
from . import PROP_virtools_group, PROP_virtools_material, PROP_virtools_mesh, PROP_virtools_texture, PROP_virtools_light, PROP_ballance_map_info
|
||||||
from .PyBMap import bmap_wrapper as bmap
|
from .PyBMap import bmap_wrapper as bmap
|
||||||
|
|
||||||
@ -18,12 +18,6 @@ class BBP_OT_import_virtools(bpy.types.Operator, UTIL_file_browser.ImportVirtool
|
|||||||
return (
|
return (
|
||||||
PROP_preferences.get_raw_preferences().has_valid_blc_tex_folder()
|
PROP_preferences.get_raw_preferences().has_valid_blc_tex_folder()
|
||||||
and bmap.is_bmap_available())
|
and bmap.is_bmap_available())
|
||||||
|
|
||||||
def invoke(self, context, event):
|
|
||||||
# preset virtools encoding if possible
|
|
||||||
self.preset_vt_encodings_if_possible(context)
|
|
||||||
# call parent invoke function (do no call self "execute", because we need show a modal window)
|
|
||||||
return super().invoke(context, event)
|
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
# check whether encoding list is empty to avoid real stupid user.
|
# check whether encoding list is empty to avoid real stupid user.
|
||||||
@ -32,14 +26,8 @@ class BBP_OT_import_virtools(bpy.types.Operator, UTIL_file_browser.ImportVirtool
|
|||||||
self.report({'ERROR'}, 'You must specify at least one encoding for file loading (e.g. cp1252, gbk)!')
|
self.report({'ERROR'}, 'You must specify at least one encoding for file loading (e.g. cp1252, gbk)!')
|
||||||
return {'CANCELLED'}
|
return {'CANCELLED'}
|
||||||
|
|
||||||
# check file name
|
|
||||||
filename = self.general_get_filename()
|
|
||||||
if not os.path.isfile(filename):
|
|
||||||
self.report({'ERROR'}, 'No file was selected!')
|
|
||||||
return {'CANCELLED'}
|
|
||||||
|
|
||||||
_import_virtools(
|
_import_virtools(
|
||||||
filename,
|
self.general_get_filename(),
|
||||||
encodings,
|
encodings,
|
||||||
self.general_get_conflict_resolver()
|
self.general_get_conflict_resolver()
|
||||||
)
|
)
|
||||||
@ -52,7 +40,7 @@ class BBP_OT_import_virtools(bpy.types.Operator, UTIL_file_browser.ImportVirtool
|
|||||||
self.draw_virtools_params(context, layout, True)
|
self.draw_virtools_params(context, layout, True)
|
||||||
self.draw_ballance_params(layout, True)
|
self.draw_ballance_params(layout, True)
|
||||||
|
|
||||||
def _import_virtools(file_name_: str, encodings_: tuple[str, ...], resolver: UTIL_ioport_shared.ConflictResolver) -> None:
|
def _import_virtools(file_name_: str, encodings_: tuple[str], resolver: UTIL_ioport_shared.ConflictResolver) -> None:
|
||||||
# create temp folder
|
# create temp folder
|
||||||
with tempfile.TemporaryDirectory() as vt_temp_folder:
|
with tempfile.TemporaryDirectory() as vt_temp_folder:
|
||||||
tr_text: str = bpy.app.translations.pgettext_rpt(
|
tr_text: str = bpy.app.translations.pgettext_rpt(
|
||||||
@ -444,7 +432,7 @@ def _import_virtools_groups(
|
|||||||
if group_name is None: continue
|
if group_name is None: continue
|
||||||
|
|
||||||
# try extracting sector info
|
# try extracting sector info
|
||||||
potential_sector_count: int | None = UTIL_naming_convention.extract_sector_from_name(group_name)
|
potential_sector_count: int | None = UTIL_naming_convension.extract_sector_from_name(group_name)
|
||||||
if potential_sector_count is not None:
|
if potential_sector_count is not None:
|
||||||
sector_count = max(sector_count, potential_sector_count)
|
sector_count = max(sector_count, potential_sector_count)
|
||||||
|
|
||||||
|
@ -2,12 +2,12 @@ import bpy
|
|||||||
from . import UTIL_functions
|
from . import UTIL_functions
|
||||||
from . import PROP_virtools_material, PROP_preferences
|
from . import PROP_virtools_material, PROP_preferences
|
||||||
|
|
||||||
class BBP_OT_fix_all_materials(bpy.types.Operator):
|
class BBP_OT_fix_all_material(bpy.types.Operator):
|
||||||
"""Fix All Materials by Its Referred Ballance Texture Name."""
|
"""Fix All Materials by Its Referred Ballance Texture Name."""
|
||||||
bl_idname = "bbp.fix_all_materials"
|
bl_idname = "bbp.fix_all_material"
|
||||||
bl_label = "Fix All Materials"
|
bl_label = "Fix All Materials"
|
||||||
bl_options = {'UNDO'}
|
bl_options = {'UNDO'}
|
||||||
bl_translation_context = 'BBP_OT_fix_all_materials'
|
bl_translation_context = 'BBP_OT_fix_all_material'
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
@ -31,12 +31,12 @@ class BBP_OT_fix_all_materials(bpy.types.Operator):
|
|||||||
|
|
||||||
# report and return
|
# report and return
|
||||||
tr_text: str = bpy.app.translations.pgettext_rpt(
|
tr_text: str = bpy.app.translations.pgettext_rpt(
|
||||||
'Fix {0}/{1} materials.', 'BBP_OT_fix_all_materials/draw')
|
'Fix {0}/{1} materials.', 'BBP_OT_fix_all_material/draw')
|
||||||
self.report({'INFO'}, tr_text.format(counter_suc, counter_all))
|
self.report({'INFO'}, tr_text.format(counter_suc, counter_all))
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
def register() -> None:
|
def register() -> None:
|
||||||
bpy.utils.register_class(BBP_OT_fix_all_materials)
|
bpy.utils.register_class(BBP_OT_fix_all_material)
|
||||||
|
|
||||||
def unregister() -> None:
|
def unregister() -> None:
|
||||||
bpy.utils.unregister_class(BBP_OT_fix_all_materials)
|
bpy.utils.unregister_class(BBP_OT_fix_all_material)
|
@ -1,474 +0,0 @@
|
|||||||
import bpy, mathutils
|
|
||||||
import typing, enum, math
|
|
||||||
from . import UTIL_functions
|
|
||||||
|
|
||||||
# TODO:
|
|
||||||
# This file should have fully refactor after we finish Virtools Camera import and export,
|
|
||||||
# because this module is highly rely on it. Current implementation is a compromise.
|
|
||||||
# There is a list of things to be done:
|
|
||||||
# - Remove BBP_OT_game_resolution operator, because Virtools Camera will have similar function in panel.
|
|
||||||
# - Update BBP_OT_game_cameraoperator with Virtools Camera.
|
|
||||||
|
|
||||||
#region Game Resolution
|
|
||||||
|
|
||||||
class ResolutionKind(enum.IntEnum):
|
|
||||||
Normal = enum.auto()
|
|
||||||
Extended = enum.auto()
|
|
||||||
Widescreen = enum.auto()
|
|
||||||
Panoramic = enum.auto()
|
|
||||||
|
|
||||||
def to_resolution(self) -> tuple[int, int]:
|
|
||||||
match self:
|
|
||||||
case ResolutionKind.Normal: return (1024, 768)
|
|
||||||
case ResolutionKind.Extended: return (1280, 720)
|
|
||||||
case ResolutionKind.Widescreen: return (1400, 600)
|
|
||||||
case ResolutionKind.Panoramic: return (2000, 700)
|
|
||||||
|
|
||||||
_g_ResolutionKindDesc: dict[ResolutionKind, tuple[str, str]] = {
|
|
||||||
ResolutionKind.Normal: ("Normal", "Aspect ratio: 4:3."),
|
|
||||||
ResolutionKind.Extended: ("Extended", "Aspect ratio: 16:9."),
|
|
||||||
ResolutionKind.Widescreen: ("Widescreen", "Aspect ratio: 7:3."),
|
|
||||||
ResolutionKind.Panoramic: ("Panoramic", "Aspect ratio: 20:7."),
|
|
||||||
}
|
|
||||||
_g_EnumHelper_ResolutionKind = UTIL_functions.EnumPropHelper(
|
|
||||||
ResolutionKind,
|
|
||||||
lambda x: str(x.value),
|
|
||||||
lambda x: ResolutionKind(int(x)),
|
|
||||||
lambda x: _g_ResolutionKindDesc[x][0],
|
|
||||||
lambda x: _g_ResolutionKindDesc[x][1],
|
|
||||||
lambda _: ""
|
|
||||||
)
|
|
||||||
|
|
||||||
class BBP_OT_game_resolution(bpy.types.Operator):
|
|
||||||
"""Set Blender render resolution to Ballance game"""
|
|
||||||
bl_idname = "bbp.game_resolution"
|
|
||||||
bl_label = "Game Resolution"
|
|
||||||
bl_options = {'REGISTER', 'UNDO'}
|
|
||||||
bl_translation_context = 'BBP_OT_game_resolution'
|
|
||||||
|
|
||||||
resolution_kind: bpy.props.EnumProperty(
|
|
||||||
name = "Resolution Kind",
|
|
||||||
description = "The type of preset resolution.",
|
|
||||||
items = _g_EnumHelper_ResolutionKind.generate_items(),
|
|
||||||
default = _g_EnumHelper_ResolutionKind.to_selection(ResolutionKind.Normal),
|
|
||||||
translation_context = 'BBP_OT_game_resolution/property'
|
|
||||||
) # type: ignore
|
|
||||||
|
|
||||||
def invoke(self, context, event):
|
|
||||||
return self.execute(context)
|
|
||||||
|
|
||||||
def draw(self, context):
|
|
||||||
layout = self.layout
|
|
||||||
layout.use_property_split = True
|
|
||||||
layout.prop(self, 'resolution_kind')
|
|
||||||
|
|
||||||
def execute(self, context):
|
|
||||||
# fetch resolution
|
|
||||||
resolution_kind = _g_EnumHelper_ResolutionKind.get_selection(self.resolution_kind)
|
|
||||||
resolution = resolution_kind.to_resolution()
|
|
||||||
# setup resolution
|
|
||||||
render_settings = bpy.context.scene.render
|
|
||||||
render_settings.resolution_x = resolution[0]
|
|
||||||
render_settings.resolution_y = resolution[1]
|
|
||||||
return {'FINISHED'}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Game Camera
|
|
||||||
|
|
||||||
#region Enum Defines
|
|
||||||
|
|
||||||
class TargetKind(enum.IntEnum):
|
|
||||||
Cursor = enum.auto()
|
|
||||||
ActiveObject = enum.auto()
|
|
||||||
_g_TargetKindDesc: dict[TargetKind, tuple[str, str, str]] = {
|
|
||||||
TargetKind.Cursor: ("3D Cursor", "3D cursor is player ball.", "CURSOR"),
|
|
||||||
TargetKind.ActiveObject: ("Active Object", "The origin point of active object is player ball.", "OBJECT_DATA"),
|
|
||||||
}
|
|
||||||
_g_EnumHelper_TargetKind = UTIL_functions.EnumPropHelper(
|
|
||||||
TargetKind,
|
|
||||||
lambda x: str(x.value),
|
|
||||||
lambda x: TargetKind(int(x)),
|
|
||||||
lambda x: _g_TargetKindDesc[x][0],
|
|
||||||
lambda x: _g_TargetKindDesc[x][1],
|
|
||||||
lambda x: _g_TargetKindDesc[x][2],
|
|
||||||
)
|
|
||||||
|
|
||||||
class RotationKind(enum.IntEnum):
|
|
||||||
Preset = enum.auto()
|
|
||||||
Custom = enum.auto()
|
|
||||||
_g_RotationKindDesc: dict[RotationKind, tuple[str, str]] = {
|
|
||||||
RotationKind.Preset: ("Preset", "8 preset rotation angles usually used in game."),
|
|
||||||
RotationKind.Custom: ("Custom", "User manually input rotation angle.")
|
|
||||||
}
|
|
||||||
_g_EnumHelper_RotationKind = UTIL_functions.EnumPropHelper(
|
|
||||||
RotationKind,
|
|
||||||
lambda x: str(x.value),
|
|
||||||
lambda x: RotationKind(int(x)),
|
|
||||||
lambda x: _g_RotationKindDesc[x][0],
|
|
||||||
lambda x: _g_RotationKindDesc[x][1],
|
|
||||||
lambda _: ""
|
|
||||||
)
|
|
||||||
|
|
||||||
class RotationAngle(enum.IntEnum):
|
|
||||||
Deg0 = enum.auto()
|
|
||||||
Deg45 = enum.auto()
|
|
||||||
Deg90 = enum.auto()
|
|
||||||
Deg135 = enum.auto()
|
|
||||||
Deg180 = enum.auto()
|
|
||||||
Deg225 = enum.auto()
|
|
||||||
Deg270 = enum.auto()
|
|
||||||
Deg315 = enum.auto()
|
|
||||||
|
|
||||||
def to_degree(self) -> float:
|
|
||||||
match self:
|
|
||||||
case RotationAngle.Deg0: return 0
|
|
||||||
case RotationAngle.Deg45: return 45
|
|
||||||
case RotationAngle.Deg90: return 90
|
|
||||||
case RotationAngle.Deg135: return 135
|
|
||||||
case RotationAngle.Deg180: return 180
|
|
||||||
case RotationAngle.Deg225: return 225
|
|
||||||
case RotationAngle.Deg270: return 270
|
|
||||||
case RotationAngle.Deg315: return 315
|
|
||||||
|
|
||||||
def to_radians(self) -> float:
|
|
||||||
return math.radians(self.to_degree())
|
|
||||||
|
|
||||||
_g_RotationAngleDesc: dict[RotationAngle, tuple[str, str]] = {
|
|
||||||
# TODO: Add axis direction in description after we add Camera support when importing
|
|
||||||
# (because we only can confirm game camera behavior after that).
|
|
||||||
RotationAngle.Deg0: ("0 Degree", "0 degree"),
|
|
||||||
RotationAngle.Deg45: ("45 Degree", "45 degree"),
|
|
||||||
RotationAngle.Deg90: ("90 Degree", "90 degree"),
|
|
||||||
RotationAngle.Deg135: ("135 Degree", "135 degree"),
|
|
||||||
RotationAngle.Deg180: ("180 Degree", "180 degree"),
|
|
||||||
RotationAngle.Deg225: ("225 Degree", "225 degree"),
|
|
||||||
RotationAngle.Deg270: ("270 Degree", "270 degree"),
|
|
||||||
RotationAngle.Deg315: ("315 Degree", "315 degree"),
|
|
||||||
}
|
|
||||||
_g_EnumHelper_RotationAngle = UTIL_functions.EnumPropHelper(
|
|
||||||
RotationAngle,
|
|
||||||
lambda x: str(x.value),
|
|
||||||
lambda x: RotationAngle(int(x)),
|
|
||||||
lambda x: _g_RotationAngleDesc[x][0],
|
|
||||||
lambda x: _g_RotationAngleDesc[x][1],
|
|
||||||
lambda _: ""
|
|
||||||
)
|
|
||||||
|
|
||||||
class PerspectiveKind(enum.IntEnum):
|
|
||||||
Ordinary = enum.auto()
|
|
||||||
Lift = enum.auto()
|
|
||||||
EasterEgg = enum.auto()
|
|
||||||
_g_PerspectiveKindDesc: dict[PerspectiveKind, tuple[str, str]] = {
|
|
||||||
PerspectiveKind.Ordinary: ("Ordinary", "The default perspective for game camera."),
|
|
||||||
PerspectiveKind.Lift: ("Lift", "Lifted camera in game for downcast level."),
|
|
||||||
PerspectiveKind.EasterEgg: ("Easter Egg", "A very close view to player ball in game."),
|
|
||||||
}
|
|
||||||
_g_EnumHelper_PerspectiveKind = UTIL_functions.EnumPropHelper(
|
|
||||||
PerspectiveKind,
|
|
||||||
lambda x: str(x.value),
|
|
||||||
lambda x: PerspectiveKind(int(x)),
|
|
||||||
lambda x: _g_PerspectiveKindDesc[x][0],
|
|
||||||
lambda x: _g_PerspectiveKindDesc[x][1],
|
|
||||||
lambda _: ""
|
|
||||||
)
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
class BBP_OT_game_camera(bpy.types.Operator):
|
|
||||||
"""Order active camera look at target like Ballance does"""
|
|
||||||
bl_idname = "bbp.game_camera"
|
|
||||||
bl_label = "Game Camera"
|
|
||||||
bl_options = {'REGISTER', 'UNDO'}
|
|
||||||
bl_translation_context = 'BBP_OT_game_camera'
|
|
||||||
|
|
||||||
target_kind: bpy.props.EnumProperty(
|
|
||||||
name = "Target Kind",
|
|
||||||
description = "",
|
|
||||||
items = _g_EnumHelper_TargetKind.generate_items(),
|
|
||||||
default = _g_EnumHelper_TargetKind.to_selection(TargetKind.Cursor),
|
|
||||||
translation_context = 'BBP_OT_game_camera/property'
|
|
||||||
) # type: ignore
|
|
||||||
|
|
||||||
rotation_kind: bpy.props.EnumProperty(
|
|
||||||
# YYC MAKR:
|
|
||||||
# This property is not shown on UI layout,
|
|
||||||
# but it should be translated because it is not PURE assistant property.
|
|
||||||
name = "Rotation Angle Kind",
|
|
||||||
description = "",
|
|
||||||
items = _g_EnumHelper_RotationKind.generate_items(),
|
|
||||||
default = _g_EnumHelper_RotationKind.to_selection(RotationKind.Preset),
|
|
||||||
translation_context = 'BBP_OT_game_camera/property'
|
|
||||||
) # type: ignore
|
|
||||||
preset_rotation_angle: bpy.props.EnumProperty(
|
|
||||||
name = "Preset Rotation Angle",
|
|
||||||
description = "",
|
|
||||||
translation_context = 'BBP_OT_game_camera/property',
|
|
||||||
options = {'HIDDEN'},
|
|
||||||
items = _g_EnumHelper_RotationAngle.generate_items(),
|
|
||||||
default = _g_EnumHelper_RotationAngle.to_selection(RotationAngle.Deg0),
|
|
||||||
) # type: ignore
|
|
||||||
def preset_rotation_angle_deg_getter(self, probe) -> bool:
|
|
||||||
return _g_EnumHelper_RotationAngle.get_selection(self.preset_rotation_angle) == probe
|
|
||||||
def preset_rotation_angle_deg_setter(self, val) -> None:
|
|
||||||
self.preset_rotation_angle = _g_EnumHelper_RotationAngle.to_selection(val)
|
|
||||||
return None
|
|
||||||
preset_rotation_angle_deg0: bpy.props.BoolProperty(
|
|
||||||
name = "0 Degree",
|
|
||||||
translation_context = 'BBP_OT_game_camera/property',
|
|
||||||
get = lambda self: BBP_OT_game_camera.preset_rotation_angle_deg_getter(self, RotationAngle.Deg0),
|
|
||||||
set = lambda self, _: BBP_OT_game_camera.preset_rotation_angle_deg_setter(self, RotationAngle.Deg0)
|
|
||||||
) # type: ignore
|
|
||||||
preset_rotation_angle_deg45: bpy.props.BoolProperty(
|
|
||||||
name = "45 Degree",
|
|
||||||
translation_context = 'BBP_OT_game_camera/property',
|
|
||||||
get = lambda self: BBP_OT_game_camera.preset_rotation_angle_deg_getter(self, RotationAngle.Deg45),
|
|
||||||
set = lambda self, _: BBP_OT_game_camera.preset_rotation_angle_deg_setter(self, RotationAngle.Deg45)
|
|
||||||
) # type: ignore
|
|
||||||
preset_rotation_angle_deg90: bpy.props.BoolProperty(
|
|
||||||
name = "90 Degree",
|
|
||||||
translation_context = 'BBP_OT_game_camera/property',
|
|
||||||
get = lambda self: BBP_OT_game_camera.preset_rotation_angle_deg_getter(self, RotationAngle.Deg90),
|
|
||||||
set = lambda self, _: BBP_OT_game_camera.preset_rotation_angle_deg_setter(self, RotationAngle.Deg90)
|
|
||||||
) # type: ignore
|
|
||||||
preset_rotation_angle_deg135: bpy.props.BoolProperty(
|
|
||||||
name = "135 Degree",
|
|
||||||
translation_context = 'BBP_OT_game_camera/property',
|
|
||||||
get = lambda self: BBP_OT_game_camera.preset_rotation_angle_deg_getter(self, RotationAngle.Deg135),
|
|
||||||
set = lambda self, _: BBP_OT_game_camera.preset_rotation_angle_deg_setter(self, RotationAngle.Deg135)
|
|
||||||
) # type: ignore
|
|
||||||
preset_rotation_angle_deg180: bpy.props.BoolProperty(
|
|
||||||
name = "180 Degree",
|
|
||||||
translation_context = 'BBP_OT_game_camera/property',
|
|
||||||
get = lambda self: BBP_OT_game_camera.preset_rotation_angle_deg_getter(self, RotationAngle.Deg180),
|
|
||||||
set = lambda self, _: BBP_OT_game_camera.preset_rotation_angle_deg_setter(self, RotationAngle.Deg180)
|
|
||||||
) # type: ignore
|
|
||||||
preset_rotation_angle_deg225: bpy.props.BoolProperty(
|
|
||||||
name = "225 Degree",
|
|
||||||
translation_context = 'BBP_OT_game_camera/property',
|
|
||||||
get = lambda self: BBP_OT_game_camera.preset_rotation_angle_deg_getter(self, RotationAngle.Deg225),
|
|
||||||
set = lambda self, _: BBP_OT_game_camera.preset_rotation_angle_deg_setter(self, RotationAngle.Deg225)
|
|
||||||
) # type: ignore
|
|
||||||
preset_rotation_angle_deg270: bpy.props.BoolProperty(
|
|
||||||
name = "270 Degree",
|
|
||||||
translation_context = 'BBP_OT_game_camera/property',
|
|
||||||
get = lambda self: BBP_OT_game_camera.preset_rotation_angle_deg_getter(self, RotationAngle.Deg270),
|
|
||||||
set = lambda self, _: BBP_OT_game_camera.preset_rotation_angle_deg_setter(self, RotationAngle.Deg270)
|
|
||||||
) # type: ignore
|
|
||||||
preset_rotation_angle_deg315: bpy.props.BoolProperty(
|
|
||||||
name = "315 Degree",
|
|
||||||
translation_context = 'BBP_OT_game_camera/property',
|
|
||||||
get = lambda self: BBP_OT_game_camera.preset_rotation_angle_deg_getter(self, RotationAngle.Deg315),
|
|
||||||
set = lambda self, _: BBP_OT_game_camera.preset_rotation_angle_deg_setter(self, RotationAngle.Deg315)
|
|
||||||
) # type: ignore
|
|
||||||
custom_rotation_angle: bpy.props.FloatProperty(
|
|
||||||
name = "Custom Rotation Angle",
|
|
||||||
description = "The rotation angle of camera relative to 3D Cursor or Active Object",
|
|
||||||
subtype = 'ANGLE',
|
|
||||||
min = 0, max = math.radians(360),
|
|
||||||
step = 100,
|
|
||||||
# MARK: What the fuck of the precision?
|
|
||||||
# I set it to 2 but it doesn't work so I forcely set it to 100.
|
|
||||||
precision = 100,
|
|
||||||
translation_context = 'BBP_OT_game_camera/property'
|
|
||||||
) # type: ignore
|
|
||||||
|
|
||||||
perspective_kind: bpy.props.EnumProperty(
|
|
||||||
name = "Rotation Angle Kind",
|
|
||||||
description = "",
|
|
||||||
items = _g_EnumHelper_PerspectiveKind.generate_items(),
|
|
||||||
default = _g_EnumHelper_PerspectiveKind.to_selection(PerspectiveKind.Ordinary),
|
|
||||||
translation_context = 'BBP_OT_game_camera/property'
|
|
||||||
) # type: ignore
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def poll(cls, context):
|
|
||||||
# find camera object
|
|
||||||
camera_obj = _find_camera_obj()
|
|
||||||
if camera_obj is None: return False
|
|
||||||
# find active object
|
|
||||||
active_obj = bpy.context.active_object
|
|
||||||
if active_obj is None: return False
|
|
||||||
# camera object should not be active object
|
|
||||||
return camera_obj != active_obj
|
|
||||||
|
|
||||||
def invoke(self, context, event):
|
|
||||||
# order user enter camera view
|
|
||||||
_enter_camera_view()
|
|
||||||
# then execute following code
|
|
||||||
return self.execute(context)
|
|
||||||
|
|
||||||
def draw(self, context):
|
|
||||||
layout = self.layout
|
|
||||||
|
|
||||||
# Show target picker
|
|
||||||
layout.label(text='Target', text_ctxt='BBP_OT_game_camera/draw')
|
|
||||||
layout.row().prop(self, 'target_kind', expand=True)
|
|
||||||
|
|
||||||
# Show rotation angle according to different types.
|
|
||||||
layout.separator()
|
|
||||||
layout.label(text='Rotation', text_ctxt='BBP_OT_game_camera/draw')
|
|
||||||
layout.row().prop(self, 'rotation_kind', expand=True)
|
|
||||||
rot_kind = _g_EnumHelper_RotationKind.get_selection(self.rotation_kind)
|
|
||||||
match rot_kind:
|
|
||||||
case RotationKind.Preset:
|
|
||||||
# for preset angles, we show a special layout (grid view)
|
|
||||||
subgrid = layout.grid_flow(row_major=True, columns=3, even_columns=True, even_rows=True, align=True)
|
|
||||||
subgrid.prop(self, 'preset_rotation_angle_deg315', toggle = 1)
|
|
||||||
subgrid.prop(self, 'preset_rotation_angle_deg0', toggle = 1)
|
|
||||||
subgrid.prop(self, 'preset_rotation_angle_deg45', toggle = 1)
|
|
||||||
subgrid.prop(self, 'preset_rotation_angle_deg270', toggle = 1)
|
|
||||||
subicon = subgrid.row()
|
|
||||||
subicon.alignment = 'CENTER'
|
|
||||||
subicon.label(text='', icon='MESH_CIRCLE') # show a 3d circle as icon
|
|
||||||
subgrid.prop(self, 'preset_rotation_angle_deg90', toggle = 1)
|
|
||||||
subgrid.prop(self, 'preset_rotation_angle_deg225', toggle = 1)
|
|
||||||
subgrid.prop(self, 'preset_rotation_angle_deg180', toggle = 1)
|
|
||||||
subgrid.prop(self, 'preset_rotation_angle_deg135', toggle = 1)
|
|
||||||
case RotationKind.Custom:
|
|
||||||
layout.prop(self, 'custom_rotation_angle', text='')
|
|
||||||
|
|
||||||
# Show perspective kind
|
|
||||||
layout.separator()
|
|
||||||
layout.label(text='Perspective', text_ctxt='BBP_OT_game_camera/draw')
|
|
||||||
layout.row().prop(self, 'perspective_kind', expand=True)
|
|
||||||
|
|
||||||
def execute(self, context):
|
|
||||||
# fetch angle
|
|
||||||
angle: float
|
|
||||||
rot_kind = _g_EnumHelper_RotationKind.get_selection(self.rotation_kind)
|
|
||||||
match rot_kind:
|
|
||||||
case RotationKind.Preset:
|
|
||||||
rot_angle = _g_EnumHelper_RotationAngle.get_selection(self.preset_rotation_angle)
|
|
||||||
angle = rot_angle.to_radians()
|
|
||||||
case RotationKind.Custom:
|
|
||||||
angle = float(self.custom_rotation_angle)
|
|
||||||
# fetch others
|
|
||||||
camera_obj = typing.cast(bpy.types.Object, _find_camera_obj())
|
|
||||||
target_kind = _g_EnumHelper_TargetKind.get_selection(self.target_kind)
|
|
||||||
perspective_kind = _g_EnumHelper_PerspectiveKind.get_selection(self.perspective_kind)
|
|
||||||
|
|
||||||
# setup its transform and properties
|
|
||||||
glob_trans = _fetch_glob_translation(camera_obj, target_kind)
|
|
||||||
_setup_camera_transform(camera_obj, angle, perspective_kind, glob_trans)
|
|
||||||
_setup_camera_properties(camera_obj)
|
|
||||||
|
|
||||||
# return
|
|
||||||
return {'FINISHED'}
|
|
||||||
|
|
||||||
def _find_3d_view_space() -> bpy.types.SpaceView3D | None:
|
|
||||||
# get current area
|
|
||||||
area = bpy.context.area
|
|
||||||
if area is None: return None
|
|
||||||
|
|
||||||
# check whether it is 3d view
|
|
||||||
if area.type != 'VIEW_3D': return None
|
|
||||||
|
|
||||||
# get the active space in area
|
|
||||||
space = area.spaces.active
|
|
||||||
if space is None: return None
|
|
||||||
|
|
||||||
# okey. cast its type and return
|
|
||||||
return typing.cast(bpy.types.SpaceView3D, space)
|
|
||||||
|
|
||||||
def _enter_camera_view() -> None:
|
|
||||||
space = _find_3d_view_space()
|
|
||||||
if space is None: return
|
|
||||||
|
|
||||||
region = space.region_3d
|
|
||||||
if region is None: return
|
|
||||||
|
|
||||||
region.view_perspective = 'CAMERA'
|
|
||||||
|
|
||||||
def _find_camera_obj() -> bpy.types.Object | None:
|
|
||||||
space = _find_3d_view_space()
|
|
||||||
if space is None: return None
|
|
||||||
|
|
||||||
return space.camera
|
|
||||||
|
|
||||||
def _fetch_glob_translation(camobj: bpy.types.Object, target_kind: TargetKind) -> mathutils.Vector:
|
|
||||||
# we have checked any bad cases in "poll",
|
|
||||||
# so we can simply return value in there without any check.
|
|
||||||
match target_kind:
|
|
||||||
case TargetKind.Cursor:
|
|
||||||
return bpy.context.scene.cursor.location
|
|
||||||
case TargetKind.ActiveObject:
|
|
||||||
return bpy.context.active_object.location
|
|
||||||
|
|
||||||
def _setup_camera_transform(camobj: bpy.types.Object, angle: float, perspective: PerspectiveKind, glob_trans: mathutils.Vector) -> None:
|
|
||||||
# decide the camera offset with ref point
|
|
||||||
ingamecam_pos: mathutils.Vector
|
|
||||||
match perspective:
|
|
||||||
case PerspectiveKind.Ordinary:
|
|
||||||
ingamecam_pos = mathutils.Vector((22, 0, 35))
|
|
||||||
case PerspectiveKind.Lift:
|
|
||||||
ingamecam_pos = mathutils.Vector((22, 0, 35 + 20))
|
|
||||||
case PerspectiveKind.EasterEgg:
|
|
||||||
ingamecam_pos = mathutils.Vector((22, 0, 3.86))
|
|
||||||
|
|
||||||
# decide the position of ref point
|
|
||||||
refpot_pos: mathutils.Vector
|
|
||||||
match perspective:
|
|
||||||
case PerspectiveKind.EasterEgg:
|
|
||||||
refpot_pos = mathutils.Vector((4.4, 0, 0))
|
|
||||||
case _:
|
|
||||||
refpot_pos = mathutils.Vector((0, 0, 0))
|
|
||||||
|
|
||||||
# perform rotation for both positions
|
|
||||||
player_rot_mat = mathutils.Matrix.Rotation(angle, 4, 'Z')
|
|
||||||
ingamecam_pos = ingamecam_pos @ player_rot_mat
|
|
||||||
refpot_pos = refpot_pos @ player_rot_mat
|
|
||||||
|
|
||||||
# calculate the rotation of camera
|
|
||||||
|
|
||||||
# YYC MARK:
|
|
||||||
# Following code are linear algebra required.
|
|
||||||
#
|
|
||||||
# We can calulate the direction of camera by simply substracting 2 vector.
|
|
||||||
# In default, the direction of camera is -Z, up direction is +Y.
|
|
||||||
# So this computed direction is -Z in new cooredinate system.
|
|
||||||
# Now we can compute +Z axis in this new coordinate system.
|
|
||||||
new_z = (ingamecam_pos - refpot_pos)
|
|
||||||
new_z.normalize()
|
|
||||||
# For ballance camera, all camera is +Z up.
|
|
||||||
# So we can use it to compute +X axis in new coordinate system
|
|
||||||
assistant_y = mathutils.Vector((0, 0, 1))
|
|
||||||
new_x = typing.cast(mathutils.Vector, assistant_y.cross(new_z))
|
|
||||||
new_x.normalize()
|
|
||||||
# now we calc the final axis
|
|
||||||
new_y = typing.cast(mathutils.Vector, new_z.cross(new_x))
|
|
||||||
new_y.normalize()
|
|
||||||
# okey, we conbine them as a matrix
|
|
||||||
rot_mat = mathutils.Matrix((
|
|
||||||
(new_x.x, new_y.x, new_z.x, 0),
|
|
||||||
(new_x.y, new_y.y, new_z.y, 0),
|
|
||||||
(new_x.z, new_y.z, new_z.z, 0),
|
|
||||||
(0, 0, 0, 1)
|
|
||||||
))
|
|
||||||
|
|
||||||
# calc the final transform matrix and apply it
|
|
||||||
trans_mat = mathutils.Matrix.Translation(ingamecam_pos)
|
|
||||||
glob_trans_mat = mathutils.Matrix.Translation(glob_trans)
|
|
||||||
camobj.matrix_world = glob_trans_mat @ trans_mat @ rot_mat
|
|
||||||
|
|
||||||
def _setup_camera_properties(camobj: bpy.types.Object) -> None:
|
|
||||||
# fetch camera
|
|
||||||
camera = typing.cast(bpy.types.Camera, camobj.data)
|
|
||||||
|
|
||||||
# set clipping
|
|
||||||
camera.clip_start = 4
|
|
||||||
camera.clip_end = 1200
|
|
||||||
# set FOV
|
|
||||||
camera.lens_unit = 'FOV'
|
|
||||||
camera.angle = math.radians(58)
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
def register() -> None:
|
|
||||||
bpy.utils.register_class(BBP_OT_game_resolution)
|
|
||||||
bpy.utils.register_class(BBP_OT_game_camera)
|
|
||||||
|
|
||||||
def unregister() -> None:
|
|
||||||
bpy.utils.unregister_class(BBP_OT_game_camera)
|
|
||||||
bpy.utils.unregister_class(BBP_OT_game_resolution)
|
|
||||||
|
|
@ -9,35 +9,19 @@ class AlignMode(enum.IntEnum):
|
|||||||
BBoxCenter = enum.auto()
|
BBoxCenter = enum.auto()
|
||||||
AxisCenter = enum.auto()
|
AxisCenter = enum.auto()
|
||||||
Max = enum.auto()
|
Max = enum.auto()
|
||||||
_g_AlignModeDesc: dict[AlignMode, tuple[str, str, str]] = {
|
_g_AlignModeDesc: dict[AlignMode, tuple[str, str]] = {
|
||||||
AlignMode.Min: ("Min", "The min value in specified axis.", "REMOVE"),
|
AlignMode.Min: ("Min", "The min value in specified axis."),
|
||||||
AlignMode.BBoxCenter: ("Center (Bounding Box)", "The bounding box center in specified axis.", "SHADING_BBOX"),
|
AlignMode.BBoxCenter: ("Center (Bounding Box)", "The bounding box center in specified axis."),
|
||||||
AlignMode.AxisCenter: ("Center (Axis)", "The object's source point in specified axis.", "OBJECT_ORIGIN"),
|
AlignMode.AxisCenter: ("Center (Axis)", "The object's source point in specified axis."),
|
||||||
AlignMode.Max: ("Max", "The max value in specified axis.", "ADD"),
|
AlignMode.Max: ("Max", "The max value in specified axis."),
|
||||||
}
|
}
|
||||||
_g_EnumHelper_AlignMode = UTIL_functions.EnumPropHelper(
|
_g_EnumHelper_AlignMode: UTIL_functions.EnumPropHelper = UTIL_functions.EnumPropHelper(
|
||||||
AlignMode,
|
AlignMode,
|
||||||
lambda x: str(x.value),
|
lambda x: str(x.value),
|
||||||
lambda x: AlignMode(int(x)),
|
lambda x: AlignMode(int(x)),
|
||||||
lambda x: _g_AlignModeDesc[x][0],
|
lambda x: _g_AlignModeDesc[x][0],
|
||||||
lambda x: _g_AlignModeDesc[x][1],
|
lambda x: _g_AlignModeDesc[x][1],
|
||||||
lambda x: _g_AlignModeDesc[x][2]
|
lambda _: ''
|
||||||
)
|
|
||||||
|
|
||||||
class CurrentInstance(enum.IntEnum):
|
|
||||||
ActiveObject = enum.auto()
|
|
||||||
Cursor = enum.auto()
|
|
||||||
_g_CurrentInstanceDesc: dict[CurrentInstance, tuple[str, str, str]] = {
|
|
||||||
CurrentInstance.ActiveObject: ("Active Object", "Use Active Object as Current Object", "OBJECT_DATA"),
|
|
||||||
CurrentInstance.Cursor: ("3D Cursor", "Use 3D Cursor as Current Object", "CURSOR"),
|
|
||||||
}
|
|
||||||
_g_EnumHelper_CurrentInstance = UTIL_functions.EnumPropHelper(
|
|
||||||
CurrentInstance,
|
|
||||||
lambda x: str(x.value),
|
|
||||||
lambda x: CurrentInstance(int(x)),
|
|
||||||
lambda x: _g_CurrentInstanceDesc[x][0],
|
|
||||||
lambda x: _g_CurrentInstanceDesc[x][1],
|
|
||||||
lambda x: _g_CurrentInstanceDesc[x][2]
|
|
||||||
)
|
)
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
@ -71,23 +55,14 @@ class BBP_PG_legacy_align_history(bpy.types.PropertyGroup):
|
|||||||
default = False,
|
default = False,
|
||||||
translation_context = 'BBP_PG_legacy_align_history/property'
|
translation_context = 'BBP_PG_legacy_align_history/property'
|
||||||
) # type: ignore
|
) # type: ignore
|
||||||
current_instance: bpy.props.EnumProperty(
|
|
||||||
name = "Current Instance",
|
|
||||||
description = "Decide which instance should be used as Current Object",
|
|
||||||
items = _g_EnumHelper_CurrentInstance.generate_items(),
|
|
||||||
default = _g_EnumHelper_CurrentInstance.to_selection(CurrentInstance.ActiveObject),
|
|
||||||
translation_context = 'BBP_PG_legacy_align_history/property'
|
|
||||||
) # type: ignore
|
|
||||||
current_align_mode: bpy.props.EnumProperty(
|
current_align_mode: bpy.props.EnumProperty(
|
||||||
name = "Current Object",
|
name = "Current Object (Active Object)",
|
||||||
description = "The align mode applied to Current Object",
|
|
||||||
items = _g_EnumHelper_AlignMode.generate_items(),
|
items = _g_EnumHelper_AlignMode.generate_items(),
|
||||||
default = _g_EnumHelper_AlignMode.to_selection(AlignMode.AxisCenter),
|
default = _g_EnumHelper_AlignMode.to_selection(AlignMode.AxisCenter),
|
||||||
translation_context = 'BBP_PG_legacy_align_history/property'
|
translation_context = 'BBP_PG_legacy_align_history/property'
|
||||||
) # type: ignore
|
) # type: ignore
|
||||||
target_align_mode: bpy.props.EnumProperty(
|
target_align_mode: bpy.props.EnumProperty(
|
||||||
name = "Target Objects",
|
name = "Target Objects (Selected Objects)",
|
||||||
description = "The align mode applied to Target Objects (selected objects except active object if Current Instance is active object)",
|
|
||||||
items = _g_EnumHelper_AlignMode.generate_items(),
|
items = _g_EnumHelper_AlignMode.generate_items(),
|
||||||
default = _g_EnumHelper_AlignMode.to_selection(AlignMode.AxisCenter),
|
default = _g_EnumHelper_AlignMode.to_selection(AlignMode.AxisCenter),
|
||||||
translation_context = 'BBP_PG_legacy_align_history/property'
|
translation_context = 'BBP_PG_legacy_align_history/property'
|
||||||
@ -137,7 +112,7 @@ class BBP_OT_legacy_align(bpy.types.Operator):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
apply_flag: bpy.props.BoolProperty(
|
apply_flag: bpy.props.BoolProperty(
|
||||||
# I18N: Property not showen should not have name and desc.
|
# TR: Property not showen should not have name and desc.
|
||||||
# name = "Apply Flag",
|
# name = "Apply Flag",
|
||||||
# description = "Internal flag.",
|
# description = "Internal flag.",
|
||||||
options = {'HIDDEN', 'SKIP_SAVE'},
|
options = {'HIDDEN', 'SKIP_SAVE'},
|
||||||
@ -145,14 +120,14 @@ class BBP_OT_legacy_align(bpy.types.Operator):
|
|||||||
update = apply_flag_updated
|
update = apply_flag_updated
|
||||||
) # type: ignore
|
) # type: ignore
|
||||||
recursive_hinder: bpy.props.BoolProperty(
|
recursive_hinder: bpy.props.BoolProperty(
|
||||||
# I18N: Property not showen should not have name and desc.
|
# TR: Property not showen should not have name and desc.
|
||||||
# name = "Recursive Hinder",
|
# name = "Recursive Hinder",
|
||||||
# description = "An internal flag to prevent the loop calling to apply_flags's updator.",
|
# description = "An internal flag to prevent the loop calling to apply_flags's updator.",
|
||||||
options = {'HIDDEN', 'SKIP_SAVE'},
|
options = {'HIDDEN', 'SKIP_SAVE'},
|
||||||
default = False
|
default = False
|
||||||
) # type: ignore
|
) # type: ignore
|
||||||
align_history : bpy.props.CollectionProperty(
|
align_history : bpy.props.CollectionProperty(
|
||||||
# I18N: Property not showen should not have name and desc.
|
# TR: Property not showen should not have name and desc.
|
||||||
# name = "Historys",
|
# name = "Historys",
|
||||||
# description = "Align history.",
|
# description = "Align history.",
|
||||||
type = BBP_PG_legacy_align_history
|
type = BBP_PG_legacy_align_history
|
||||||
@ -173,7 +148,7 @@ class BBP_OT_legacy_align(bpy.types.Operator):
|
|||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
# get processed objects
|
# get processed objects
|
||||||
(current_obj, current_cursor, target_objs) = _prepare_objects()
|
(current_obj, target_objs) = _prepare_objects()
|
||||||
# YYC MARK:
|
# YYC MARK:
|
||||||
# This statement is VERY IMPORTANT.
|
# This statement is VERY IMPORTANT.
|
||||||
# If this statement is not presented, Blender will return identity matrix
|
# If this statement is not presented, Blender will return identity matrix
|
||||||
@ -187,8 +162,7 @@ class BBP_OT_legacy_align(bpy.types.Operator):
|
|||||||
histories = UTIL_functions.CollectionVisitor(self.align_history)
|
histories = UTIL_functions.CollectionVisitor(self.align_history)
|
||||||
for entry in histories:
|
for entry in histories:
|
||||||
_align_objects(
|
_align_objects(
|
||||||
_g_EnumHelper_CurrentInstance.get_selection(entry.current_instance),
|
current_obj, target_objs,
|
||||||
current_obj, current_cursor, target_objs,
|
|
||||||
entry.align_x, entry.align_y, entry.align_z,
|
entry.align_x, entry.align_y, entry.align_z,
|
||||||
_g_EnumHelper_AlignMode.get_selection(entry.current_align_mode),
|
_g_EnumHelper_AlignMode.get_selection(entry.current_align_mode),
|
||||||
_g_EnumHelper_AlignMode.get_selection(entry.target_align_mode)
|
_g_EnumHelper_AlignMode.get_selection(entry.target_align_mode)
|
||||||
@ -211,22 +185,11 @@ class BBP_OT_legacy_align(bpy.types.Operator):
|
|||||||
row.prop(entry, "align_y", toggle = 1)
|
row.prop(entry, "align_y", toggle = 1)
|
||||||
row.prop(entry, "align_z", toggle = 1)
|
row.prop(entry, "align_z", toggle = 1)
|
||||||
|
|
||||||
# show current instance
|
# show mode
|
||||||
col.separator()
|
col.separator()
|
||||||
col.label(text='Current Object', text_ctxt='BBP_OT_legacy_align/draw')
|
col.label(text='Current Object (Active Object)', text_ctxt='BBP_OT_legacy_align/draw')
|
||||||
# it should be shown in horizon so we create a new sublayout
|
col.prop(entry, "current_align_mode", expand = True)
|
||||||
row = col.row()
|
col.label(text='Target Objects (Selected Objects)', text_ctxt='BBP_OT_legacy_align/draw')
|
||||||
row.prop(entry, 'current_instance', expand=True)
|
|
||||||
|
|
||||||
# show instance and mode
|
|
||||||
col.separator()
|
|
||||||
# only show current object mode if current instance is active object,
|
|
||||||
# because there is no mode for 3d cursor.
|
|
||||||
current_instnce = _g_EnumHelper_CurrentInstance.get_selection(entry.current_instance)
|
|
||||||
if current_instnce == CurrentInstance.ActiveObject:
|
|
||||||
col.label(text='Current Object Align Mode', text_ctxt='BBP_OT_legacy_align/draw')
|
|
||||||
col.prop(entry, "current_align_mode", expand = True)
|
|
||||||
col.label(text='Target Objects Align Mode', text_ctxt='BBP_OT_legacy_align/draw')
|
|
||||||
col.prop(entry, "target_align_mode", expand = True)
|
col.prop(entry, "target_align_mode", expand = True)
|
||||||
|
|
||||||
# show apply button
|
# show apply button
|
||||||
@ -243,66 +206,44 @@ class BBP_OT_legacy_align(bpy.types.Operator):
|
|||||||
#region Core Functions
|
#region Core Functions
|
||||||
|
|
||||||
def _check_align_requirement() -> bool:
|
def _check_align_requirement() -> bool:
|
||||||
# If we are not in object mode, do not do legacy align
|
# if we are not in object mode, do not do legacy align
|
||||||
if not UTIL_functions.is_in_object_mode():
|
if not UTIL_functions.is_in_object_mode():
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# YYC MARK:
|
# check current obj
|
||||||
# We still need to check active object (as current object)
|
|
||||||
# although we can choose align with active object or 3d cursor.
|
|
||||||
# Because we can not make any promise that user will
|
|
||||||
# select Active Object or 3D Cursor as current object before executing this operator.
|
|
||||||
if bpy.context.active_object is None:
|
if bpy.context.active_object is None:
|
||||||
return False
|
return False
|
||||||
|
# check target obj with filter of current obj
|
||||||
# YYC MARK:
|
length = len(bpy.context.selected_objects)
|
||||||
# Roughly check selected objects.
|
if bpy.context.active_object in bpy.context.selected_objects:
|
||||||
# We do not need exclude active object from selected objects,
|
length -= 1
|
||||||
# because active object may be moved when 3D Cursor is current object.
|
return length != 0
|
||||||
if len(bpy.context.selected_objects) == 0:
|
|
||||||
return False
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
def _prepare_objects() -> tuple[bpy.types.Object, mathutils.Vector, list[bpy.types.Object]]:
|
def _prepare_objects() -> tuple[bpy.types.Object, set[bpy.types.Object]]:
|
||||||
# Fetch current object
|
# get current object
|
||||||
current_obj = typing.cast(bpy.types.Object, bpy.context.active_object)
|
current_obj: bpy.types.Object = bpy.context.active_object
|
||||||
|
|
||||||
# Fetch 3d cursor location
|
# get target objects
|
||||||
current_cursor: mathutils.Vector = bpy.context.scene.cursor.location
|
target_objs: set[bpy.types.Object] = set(bpy.context.selected_objects)
|
||||||
|
# remove active one
|
||||||
# YYC MARK:
|
if current_obj in target_objs:
|
||||||
# Fetch target objects and do NOT remove active object from it.
|
target_objs.remove(current_obj)
|
||||||
# because active object will be moved when current instance is 3D Cursor.
|
|
||||||
target_objs: list[bpy.types.Object] = bpy.context.selected_objects[:]
|
|
||||||
|
|
||||||
# return value
|
# return value
|
||||||
return (current_obj, current_cursor, target_objs)
|
return (current_obj, target_objs)
|
||||||
|
|
||||||
def _align_objects(
|
def _align_objects(
|
||||||
current_instance: CurrentInstance,
|
current_obj: bpy.types.Object, target_objs: set[bpy.types.Object],
|
||||||
current_obj: bpy.types.Object, current_cursor: mathutils.Vector, target_objs: list[bpy.types.Object],
|
|
||||||
align_x: bool, align_y: bool, align_z: bool, current_mode: AlignMode, target_mode: AlignMode) -> None:
|
align_x: bool, align_y: bool, align_z: bool, current_mode: AlignMode, target_mode: AlignMode) -> None:
|
||||||
# if no align, skip
|
# if no align, skip
|
||||||
if not (align_x or align_y or align_z):
|
if not (align_x or align_y or align_z):
|
||||||
return
|
return
|
||||||
|
|
||||||
# calc current object data
|
# calc current object data
|
||||||
current_obj_ref: mathutils.Vector
|
current_obj_ref: mathutils.Vector = _get_object_ref_point(current_obj, current_mode)
|
||||||
match current_instance:
|
|
||||||
case CurrentInstance.ActiveObject:
|
|
||||||
current_obj_ref = _get_object_ref_point(current_obj, current_mode)
|
|
||||||
case CurrentInstance.Cursor:
|
|
||||||
current_obj_ref = current_cursor
|
|
||||||
|
|
||||||
# process each target obj
|
# process each target obj
|
||||||
for target_obj in target_objs:
|
for target_obj in target_objs:
|
||||||
# YYC MARK:
|
|
||||||
# If we use active object as current instance, we need exclude it from target objects,
|
|
||||||
# because there is no pre-exclude considering the scenario that 3D Cursor is current instance.
|
|
||||||
if current_instance == CurrentInstance.ActiveObject and current_obj == target_obj:
|
|
||||||
continue
|
|
||||||
|
|
||||||
# calc target object data
|
# calc target object data
|
||||||
target_obj_ref: mathutils.Vector = _get_object_ref_point(target_obj, target_mode)
|
target_obj_ref: mathutils.Vector = _get_object_ref_point(target_obj, target_mode)
|
||||||
# build translation transform
|
# build translation transform
|
||||||
@ -315,21 +256,21 @@ def _align_objects(
|
|||||||
# apply translation transform to left side (add into original matrix)
|
# apply translation transform to left side (add into original matrix)
|
||||||
target_obj.matrix_world = target_obj_translation_matrix @ target_obj.matrix_world
|
target_obj.matrix_world = target_obj_translation_matrix @ target_obj.matrix_world
|
||||||
|
|
||||||
|
bpy.context.scene.update_tag
|
||||||
|
|
||||||
def _get_object_ref_point(obj: bpy.types.Object, mode: AlignMode) -> mathutils.Vector:
|
def _get_object_ref_point(obj: bpy.types.Object, mode: AlignMode) -> mathutils.Vector:
|
||||||
ref_pos = mathutils.Vector((0, 0, 0))
|
ref_pos: mathutils.Vector = mathutils.Vector((0, 0, 0))
|
||||||
|
|
||||||
# calc bounding box data
|
# calc bounding box data
|
||||||
corners: tuple[mathutils.Vector, ...] = tuple(obj.matrix_world @ mathutils.Vector(corner) for corner in obj.bound_box)
|
corners: tuple[mathutils.Vector] = tuple(obj.matrix_world @ mathutils.Vector(corner) for corner in obj.bound_box)
|
||||||
bbox_min_corner = mathutils.Vector((
|
bbox_min_corner: mathutils.Vector = mathutils.Vector((0, 0, 0))
|
||||||
min((vec.x for vec in corners)),
|
bbox_min_corner.x = min((vec.x for vec in corners))
|
||||||
min((vec.y for vec in corners)),
|
bbox_min_corner.y = min((vec.y for vec in corners))
|
||||||
min((vec.z for vec in corners)),
|
bbox_min_corner.z = min((vec.z for vec in corners))
|
||||||
))
|
bbox_max_corner: mathutils.Vector = mathutils.Vector((0, 0, 0))
|
||||||
bbox_max_corner = mathutils.Vector((
|
bbox_max_corner.x = max((vec.x for vec in corners))
|
||||||
max((vec.x for vec in corners)),
|
bbox_max_corner.y = max((vec.y for vec in corners))
|
||||||
max((vec.y for vec in corners)),
|
bbox_max_corner.z = max((vec.z for vec in corners))
|
||||||
max((vec.z for vec in corners)),
|
|
||||||
))
|
|
||||||
|
|
||||||
# return value by given align mode
|
# return value by given align mode
|
||||||
match(mode):
|
match(mode):
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import bpy
|
import bpy
|
||||||
import typing
|
import typing
|
||||||
from . import UTIL_functions, UTIL_icons_manager, UTIL_naming_convention
|
from . import UTIL_naming_convension, UTIL_functions, UTIL_icons_manager
|
||||||
|
|
||||||
class BBP_OT_regulate_objects_name(bpy.types.Operator):
|
class BBP_OT_regulate_objects_name(bpy.types.Operator):
|
||||||
"""Regulate Objects Name by Virtools Group and Naming Convention"""
|
"""Regulate Objects Name by Virtools Group and Naming Convention"""
|
||||||
@ -15,8 +15,8 @@ class BBP_OT_regulate_objects_name(bpy.types.Operator):
|
|||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
_rename_core(
|
_rename_core(
|
||||||
UTIL_naming_convention.VirtoolsGroupConvention.parse_from_object,
|
UTIL_naming_convension.VirtoolsGroupConvention.parse_from_object,
|
||||||
UTIL_naming_convention.YYCToolchainConvention.set_to_object
|
UTIL_naming_convension.YYCToolchainConvention.set_to_object
|
||||||
)
|
)
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
@ -33,8 +33,8 @@ class BBP_OT_auto_grouping(bpy.types.Operator):
|
|||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
_rename_core(
|
_rename_core(
|
||||||
UTIL_naming_convention.YYCToolchainConvention.parse_from_object,
|
UTIL_naming_convension.YYCToolchainConvention.parse_from_object,
|
||||||
UTIL_naming_convention.VirtoolsGroupConvention.set_to_object
|
UTIL_naming_convension.VirtoolsGroupConvention.set_to_object
|
||||||
)
|
)
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
@ -51,26 +51,26 @@ class BBP_OT_convert_to_imengyu(bpy.types.Operator):
|
|||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
_rename_core(
|
_rename_core(
|
||||||
UTIL_naming_convention.YYCToolchainConvention.parse_from_object,
|
UTIL_naming_convension.YYCToolchainConvention.parse_from_object,
|
||||||
UTIL_naming_convention.ImengyuConvention.set_to_object
|
UTIL_naming_convension.ImengyuConvention.set_to_object
|
||||||
)
|
)
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
def _rename_core(
|
def _rename_core(
|
||||||
fct_get_info: typing.Callable[[bpy.types.Object, UTIL_naming_convention.RenameErrorReporter], UTIL_naming_convention.BallanceObjectInfo | None],
|
fct_get_info: typing.Callable[[bpy.types.Object, UTIL_naming_convension.RenameErrorReporter], UTIL_naming_convension.BallanceObjectInfo | None],
|
||||||
ftc_set_info: typing.Callable[[bpy.types.Object, UTIL_naming_convention.BallanceObjectInfo, UTIL_naming_convention.RenameErrorReporter], bool]
|
ftc_set_info: typing.Callable[[bpy.types.Object, UTIL_naming_convension.BallanceObjectInfo, UTIL_naming_convension.RenameErrorReporter], bool]
|
||||||
) -> None:
|
) -> None:
|
||||||
# get selected objects. allow nested collection
|
# get selected objects. allow nested collection
|
||||||
selected_objects: typing.Iterable[bpy.types.Object] = bpy.context.view_layer.active_layer_collection.collection.all_objects
|
selected_objects: typing.Iterable[bpy.types.Object] = bpy.context.view_layer.active_layer_collection.collection.all_objects
|
||||||
|
|
||||||
# create reporter
|
# create reporter
|
||||||
with UTIL_naming_convention.RenameErrorReporter() as reporter:
|
with UTIL_naming_convension.RenameErrorReporter() as reporter:
|
||||||
# iterate objects
|
# iterate objects
|
||||||
for obj in selected_objects:
|
for obj in selected_objects:
|
||||||
reporter.enter_object(obj)
|
reporter.enter_object(obj)
|
||||||
|
|
||||||
# try get info
|
# try get info
|
||||||
info: UTIL_naming_convention.BallanceObjectInfo | None = fct_get_info(obj, reporter)
|
info: UTIL_naming_convension.BallanceObjectInfo | None = fct_get_info(obj, reporter)
|
||||||
if info is not None:
|
if info is not None:
|
||||||
# if info is valid, try assign it
|
# if info is valid, try assign it
|
||||||
if not ftc_set_info(obj, info, reporter):
|
if not ftc_set_info(obj, info, reporter):
|
||||||
|
@ -18,7 +18,7 @@ _g_SelectModeDesc: dict[SelectMode, tuple[str, str, str]] = {
|
|||||||
SelectMode.Difference: ('Invert', 'Inverts the selection.', 'SELECT_DIFFERENCE'),
|
SelectMode.Difference: ('Invert', 'Inverts the selection.', 'SELECT_DIFFERENCE'),
|
||||||
SelectMode.Intersect: ('Intersect', 'Selects items that intersect with the existing selection.', 'SELECT_INTERSECT')
|
SelectMode.Intersect: ('Intersect', 'Selects items that intersect with the existing selection.', 'SELECT_INTERSECT')
|
||||||
}
|
}
|
||||||
_g_EnumHelper_SelectMode = UTIL_functions.EnumPropHelper(
|
_g_EnumHelper_SelectMode: UTIL_functions.EnumPropHelper = UTIL_functions.EnumPropHelper(
|
||||||
SelectMode,
|
SelectMode,
|
||||||
lambda x: str(x.value),
|
lambda x: str(x.value),
|
||||||
lambda x: SelectMode(int(x)),
|
lambda x: SelectMode(int(x)),
|
||||||
|
@ -124,7 +124,7 @@ def _load_element(mesh: bpy.types.Mesh, element_type: BallanceElementType) -> No
|
|||||||
element_filename: str = os.path.join(
|
element_filename: str = os.path.join(
|
||||||
os.path.dirname(__file__),
|
os.path.dirname(__file__),
|
||||||
"meshes",
|
"meshes",
|
||||||
element_name + '.ph'
|
element_name + '.bin'
|
||||||
)
|
)
|
||||||
|
|
||||||
# open file and read
|
# open file and read
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import bpy
|
import bpy
|
||||||
import os, typing
|
import os, typing
|
||||||
from . import UTIL_naming_convention
|
from . import UTIL_naming_convension
|
||||||
|
|
||||||
class RawPreferences():
|
class RawPreferences():
|
||||||
cBallanceTextureFolder: typing.ClassVar[str] = ""
|
cBallanceTextureFolder: typing.ClassVar[str] = ""
|
||||||
|
@ -43,7 +43,7 @@ class BBP_PG_ptrprop_resolver(bpy.types.PropertyGroup):
|
|||||||
translation_context = 'BBP_PG_ptrprop_resolver/property'
|
translation_context = 'BBP_PG_ptrprop_resolver/property'
|
||||||
) # type: ignore
|
) # type: ignore
|
||||||
|
|
||||||
# I18N: Properties not showen should not have name and desc.
|
# TR: Properties not showen should not have name and desc.
|
||||||
ioport_encodings: bpy.props.CollectionProperty(type = BBP_PG_bmap_encoding) # type: ignore
|
ioport_encodings: bpy.props.CollectionProperty(type = BBP_PG_bmap_encoding) # type: ignore
|
||||||
active_ioport_encodings: bpy.props.IntProperty() # type: ignore
|
active_ioport_encodings: bpy.props.IntProperty() # type: ignore
|
||||||
|
|
||||||
@ -195,16 +195,6 @@ class PropsVisitor():
|
|||||||
def get_ioport_encodings(self) -> tuple[str, ...]:
|
def get_ioport_encodings(self) -> tuple[str, ...]:
|
||||||
encodings = get_ioport_encodings(self.__mAssocScene)
|
encodings = get_ioport_encodings(self.__mAssocScene)
|
||||||
return tuple(i.encoding for i in encodings)
|
return tuple(i.encoding for i in encodings)
|
||||||
def preset_ioport_encodings(self) -> None:
|
|
||||||
"""
|
|
||||||
Set IOPort used encodings list as preset encoding list.
|
|
||||||
Please note that all old values will be overwritten.
|
|
||||||
"""
|
|
||||||
encodings = get_ioport_encodings(self.__mAssocScene)
|
|
||||||
encodings.clear()
|
|
||||||
for default_enc in UTIL_virtools_types.g_PyBMapDefaultEncodings:
|
|
||||||
item = encodings.add()
|
|
||||||
item.encoding = default_enc
|
|
||||||
def draw_ioport_encodings(self, layout: bpy.types.UILayout) -> None:
|
def draw_ioport_encodings(self, layout: bpy.types.UILayout) -> None:
|
||||||
target = get_ptrprop_resolver(self.__mAssocScene)
|
target = get_ptrprop_resolver(self.__mAssocScene)
|
||||||
row = layout.row()
|
row = layout.row()
|
||||||
@ -228,11 +218,24 @@ class PropsVisitor():
|
|||||||
col.separator()
|
col.separator()
|
||||||
col.operator(BBP_OT_clear_ioport_encodings.bl_idname, icon='TRASH', text='')
|
col.operator(BBP_OT_clear_ioport_encodings.bl_idname, icon='TRASH', text='')
|
||||||
|
|
||||||
|
@bpy.app.handlers.persistent
|
||||||
|
def _ioport_encodings_initializer(file_path: str):
|
||||||
|
# if we can fetch property, and it is empty after loading file
|
||||||
|
# we fill it with default value
|
||||||
|
encodings = get_ioport_encodings(bpy.context.scene)
|
||||||
|
if len(encodings) == 0:
|
||||||
|
for default_enc in UTIL_virtools_types.g_PyBMapDefaultEncodings:
|
||||||
|
item = encodings.add()
|
||||||
|
item.encoding = default_enc
|
||||||
|
|
||||||
def register() -> None:
|
def register() -> None:
|
||||||
bpy.utils.register_class(BBP_PG_bmap_encoding)
|
bpy.utils.register_class(BBP_PG_bmap_encoding)
|
||||||
bpy.utils.register_class(BBP_UL_bmap_encoding)
|
bpy.utils.register_class(BBP_UL_bmap_encoding)
|
||||||
bpy.utils.register_class(BBP_PG_ptrprop_resolver)
|
bpy.utils.register_class(BBP_PG_ptrprop_resolver)
|
||||||
|
|
||||||
|
# register ioport encodings default value
|
||||||
|
bpy.app.handlers.load_post.append(_ioport_encodings_initializer)
|
||||||
|
|
||||||
bpy.utils.register_class(BBP_OT_add_ioport_encodings)
|
bpy.utils.register_class(BBP_OT_add_ioport_encodings)
|
||||||
bpy.utils.register_class(BBP_OT_rm_ioport_encodings)
|
bpy.utils.register_class(BBP_OT_rm_ioport_encodings)
|
||||||
bpy.utils.register_class(BBP_OT_up_ioport_encodings)
|
bpy.utils.register_class(BBP_OT_up_ioport_encodings)
|
||||||
@ -250,6 +253,9 @@ def unregister() -> None:
|
|||||||
bpy.utils.unregister_class(BBP_OT_rm_ioport_encodings)
|
bpy.utils.unregister_class(BBP_OT_rm_ioport_encodings)
|
||||||
bpy.utils.unregister_class(BBP_OT_add_ioport_encodings)
|
bpy.utils.unregister_class(BBP_OT_add_ioport_encodings)
|
||||||
|
|
||||||
|
# unregister ioport encodings default value
|
||||||
|
bpy.app.handlers.load_post.remove(_ioport_encodings_initializer)
|
||||||
|
|
||||||
bpy.utils.unregister_class(BBP_PG_ptrprop_resolver)
|
bpy.utils.unregister_class(BBP_PG_ptrprop_resolver)
|
||||||
bpy.utils.unregister_class(BBP_UL_bmap_encoding)
|
bpy.utils.unregister_class(BBP_UL_bmap_encoding)
|
||||||
bpy.utils.unregister_class(BBP_PG_bmap_encoding)
|
bpy.utils.unregister_class(BBP_PG_bmap_encoding)
|
||||||
|
@ -205,7 +205,7 @@ class VirtoolsGroupsPreset(enum.Enum):
|
|||||||
|
|
||||||
Shadow = "Shadow"
|
Shadow = "Shadow"
|
||||||
|
|
||||||
_g_VtGrpPresetValues: tuple[str, ...] = tuple(map(lambda x: x.value, VirtoolsGroupsPreset))
|
_g_VtGrpPresetValues: tuple[str] = tuple(map(lambda x: x.value, VirtoolsGroupsPreset))
|
||||||
|
|
||||||
## Some of group names are not matched with icon name
|
## Some of group names are not matched with icon name
|
||||||
# So we create a convertion map to convert them.
|
# So we create a convertion map to convert them.
|
||||||
@ -236,7 +236,7 @@ def _get_group_icon_by_name(gp_name: str) -> int:
|
|||||||
if value is not None: return value
|
if value is not None: return value
|
||||||
else: return UTIL_icons_manager.get_empty_icon()
|
else: return UTIL_icons_manager.get_empty_icon()
|
||||||
# blender group name prop helper
|
# blender group name prop helper
|
||||||
_g_EnumHelper_Group = UTIL_functions.EnumPropHelper(
|
_g_EnumHelper_Group: UTIL_functions.EnumPropHelper = UTIL_functions.EnumPropHelper(
|
||||||
VirtoolsGroupsPreset,
|
VirtoolsGroupsPreset,
|
||||||
lambda x: x.value, # member is string self
|
lambda x: x.value, # member is string self
|
||||||
lambda x: VirtoolsGroupsPreset(x), # convert directly because it is StrEnum.
|
lambda x: VirtoolsGroupsPreset(x), # convert directly because it is StrEnum.
|
||||||
|
@ -73,7 +73,7 @@ class RawVirtoolsLight():
|
|||||||
|
|
||||||
# Blender Property Group
|
# Blender Property Group
|
||||||
|
|
||||||
_g_Helper_VXLIGHT_TYPE = UTIL_virtools_types.EnumPropHelper(UTIL_virtools_types.VXLIGHT_TYPE)
|
_g_Helper_VXLIGHT_TYPE: UTIL_virtools_types.EnumPropHelper = UTIL_virtools_types.EnumPropHelper(UTIL_virtools_types.VXLIGHT_TYPE)
|
||||||
|
|
||||||
class BBP_PG_virtools_light(bpy.types.PropertyGroup):
|
class BBP_PG_virtools_light(bpy.types.PropertyGroup):
|
||||||
light_type: bpy.props.EnumProperty(
|
light_type: bpy.props.EnumProperty(
|
||||||
|
@ -114,13 +114,13 @@ class RawVirtoolsMaterial():
|
|||||||
|
|
||||||
#region Blender Enum Prop Helper (Virtools type specified)
|
#region Blender Enum Prop Helper (Virtools type specified)
|
||||||
|
|
||||||
_g_Helper_VXTEXTURE_BLENDMODE = UTIL_virtools_types.EnumPropHelper(UTIL_virtools_types.VXTEXTURE_BLENDMODE)
|
_g_Helper_VXTEXTURE_BLENDMODE: UTIL_virtools_types.EnumPropHelper = UTIL_virtools_types.EnumPropHelper(UTIL_virtools_types.VXTEXTURE_BLENDMODE)
|
||||||
_g_Helper_VXTEXTURE_FILTERMODE = UTIL_virtools_types.EnumPropHelper(UTIL_virtools_types.VXTEXTURE_FILTERMODE)
|
_g_Helper_VXTEXTURE_FILTERMODE: UTIL_virtools_types.EnumPropHelper = UTIL_virtools_types.EnumPropHelper(UTIL_virtools_types.VXTEXTURE_FILTERMODE)
|
||||||
_g_Helper_VXTEXTURE_ADDRESSMODE = UTIL_virtools_types.EnumPropHelper(UTIL_virtools_types.VXTEXTURE_ADDRESSMODE)
|
_g_Helper_VXTEXTURE_ADDRESSMODE: UTIL_virtools_types.EnumPropHelper = UTIL_virtools_types.EnumPropHelper(UTIL_virtools_types.VXTEXTURE_ADDRESSMODE)
|
||||||
_g_Helper_VXBLEND_MODE = UTIL_virtools_types.EnumPropHelper(UTIL_virtools_types.VXBLEND_MODE)
|
_g_Helper_VXBLEND_MODE: UTIL_virtools_types.EnumPropHelper = UTIL_virtools_types.EnumPropHelper(UTIL_virtools_types.VXBLEND_MODE)
|
||||||
_g_Helper_VXFILL_MODE = UTIL_virtools_types.EnumPropHelper(UTIL_virtools_types.VXFILL_MODE)
|
_g_Helper_VXFILL_MODE: UTIL_virtools_types.EnumPropHelper = UTIL_virtools_types.EnumPropHelper(UTIL_virtools_types.VXFILL_MODE)
|
||||||
_g_Helper_VXSHADE_MODE = UTIL_virtools_types.EnumPropHelper(UTIL_virtools_types.VXSHADE_MODE)
|
_g_Helper_VXSHADE_MODE: UTIL_virtools_types.EnumPropHelper = UTIL_virtools_types.EnumPropHelper(UTIL_virtools_types.VXSHADE_MODE)
|
||||||
_g_Helper_VXCMPFUNC = UTIL_virtools_types.EnumPropHelper(UTIL_virtools_types.VXCMPFUNC)
|
_g_Helper_VXCMPFUNC: UTIL_virtools_types.EnumPropHelper = UTIL_virtools_types.EnumPropHelper(UTIL_virtools_types.VXCMPFUNC)
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@ -558,7 +558,7 @@ def preset_virtools_material(mtl: bpy.types.Material, preset_type: MaterialPrese
|
|||||||
set_raw_virtools_material(mtl, preset_data)
|
set_raw_virtools_material(mtl, preset_data)
|
||||||
|
|
||||||
# create preset enum blender helper
|
# create preset enum blender helper
|
||||||
_g_Helper_MtlPreset = UTIL_functions.EnumPropHelper(
|
_g_Helper_MtlPreset: UTIL_functions.EnumPropHelper = UTIL_functions.EnumPropHelper(
|
||||||
MaterialPresetType,
|
MaterialPresetType,
|
||||||
lambda x: str(x.value),
|
lambda x: str(x.value),
|
||||||
lambda x: MaterialPresetType(int(x)),
|
lambda x: MaterialPresetType(int(x)),
|
||||||
@ -572,13 +572,13 @@ _g_Helper_MtlPreset = UTIL_functions.EnumPropHelper(
|
|||||||
#region Fix Material
|
#region Fix Material
|
||||||
|
|
||||||
def fix_material(mtl: bpy.types.Material) -> bool:
|
def fix_material(mtl: bpy.types.Material) -> bool:
|
||||||
"""
|
"""!
|
||||||
Fix single Blender material.
|
Fix single Blender material.
|
||||||
|
|
||||||
The implementation of this function is copied from `BallanceVirtoolsHelper/bvh/features/mapping/bmfile_fix_texture.cpp`
|
@remark The implementation of this function is copied from BallanceVirtoolsHelper/bvh/features/mapping/bmfile_fix_texture.cpp
|
||||||
|
|
||||||
:param mtl: The blender material need to be processed.
|
@param mtl[in] The blender material need to be processed.
|
||||||
:return: True if we do a fix, otherwise return False.
|
@return True if we do a fix, otherwise return False.
|
||||||
"""
|
"""
|
||||||
# prepare return value first
|
# prepare return value first
|
||||||
ret: bool = False
|
ret: bool = False
|
||||||
@ -954,7 +954,6 @@ class BBP_OT_preset_virtools_material(bpy.types.Operator):
|
|||||||
name = "Preset",
|
name = "Preset",
|
||||||
description = "The preset which you want to apply.",
|
description = "The preset which you want to apply.",
|
||||||
items = _g_Helper_MtlPreset.generate_items(),
|
items = _g_Helper_MtlPreset.generate_items(),
|
||||||
translation_context = 'BBP_OT_preset_virtools_material/property'
|
|
||||||
) # type: ignore
|
) # type: ignore
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -15,7 +15,7 @@ class RawVirtoolsMesh():
|
|||||||
self.mLitMode = kwargs.get('mLitMode', RawVirtoolsMesh.cDefaultLitMode)
|
self.mLitMode = kwargs.get('mLitMode', RawVirtoolsMesh.cDefaultLitMode)
|
||||||
|
|
||||||
# blender enum prop helper defines
|
# blender enum prop helper defines
|
||||||
_g_Helper_VXMESH_LITMODE = UTIL_virtools_types.EnumPropHelper(UTIL_virtools_types.VXMESH_LITMODE)
|
_g_Helper_VXMESH_LITMODE: UTIL_virtools_types.EnumPropHelper = UTIL_virtools_types.EnumPropHelper(UTIL_virtools_types.VXMESH_LITMODE)
|
||||||
|
|
||||||
# Blender Property Group
|
# Blender Property Group
|
||||||
|
|
||||||
|
@ -20,8 +20,8 @@ class RawVirtoolsTexture():
|
|||||||
self.mVideoFormat = kwargs.get('mVideoFormat', RawVirtoolsTexture.cDefaultVideoFormat)
|
self.mVideoFormat = kwargs.get('mVideoFormat', RawVirtoolsTexture.cDefaultVideoFormat)
|
||||||
|
|
||||||
# blender enum prop helper defines
|
# blender enum prop helper defines
|
||||||
_g_Helper_CK_TEXTURE_SAVEOPTIONS = UTIL_virtools_types.EnumPropHelper(UTIL_virtools_types.CK_TEXTURE_SAVEOPTIONS)
|
_g_Helper_CK_TEXTURE_SAVEOPTIONS: UTIL_virtools_types.EnumPropHelper = UTIL_virtools_types.EnumPropHelper(UTIL_virtools_types.CK_TEXTURE_SAVEOPTIONS)
|
||||||
_g_Helper_VX_PIXELFORMAT = UTIL_virtools_types.EnumPropHelper(UTIL_virtools_types.VX_PIXELFORMAT)
|
_g_Helper_VX_PIXELFORMAT: UTIL_virtools_types.EnumPropHelper = UTIL_virtools_types.EnumPropHelper(UTIL_virtools_types.VX_PIXELFORMAT)
|
||||||
|
|
||||||
class BBP_PG_virtools_texture(bpy.types.PropertyGroup):
|
class BBP_PG_virtools_texture(bpy.types.PropertyGroup):
|
||||||
|
|
||||||
@ -64,10 +64,12 @@ def set_raw_virtools_texture(img: bpy.types.Image, rawdata: RawVirtoolsTexture)
|
|||||||
|
|
||||||
#region Virtools Texture Drawer
|
#region Virtools Texture Drawer
|
||||||
|
|
||||||
# YYC MARK:
|
"""!
|
||||||
# Because Image do not have its unique properties window,
|
@remark
|
||||||
# so we only can draw Virtools Texture properties in other window.
|
Because Image do not have its unique properties window
|
||||||
# We provide various functions to help draw properties.
|
so we only can draw virtools texture properties in other window
|
||||||
|
we provide various function to help draw property.
|
||||||
|
"""
|
||||||
|
|
||||||
def draw_virtools_texture(img: bpy.types.Image, layout: bpy.types.UILayout):
|
def draw_virtools_texture(img: bpy.types.Image, layout: bpy.types.UILayout):
|
||||||
props: BBP_PG_virtools_texture = get_virtools_texture(img)
|
props: BBP_PG_virtools_texture = get_virtools_texture(img)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import bpy, mathutils
|
import bpy, mathutils
|
||||||
import os, json, enum, typing, math
|
import os, json, enum, typing, math
|
||||||
from . import PROP_virtools_group, PROP_bme_material, UTIL_naming_convention
|
from . import PROP_virtools_group, PROP_bme_material
|
||||||
from . import UTIL_functions, UTIL_icons_manager, UTIL_blender_mesh, UTIL_virtools_types
|
from . import UTIL_functions, UTIL_icons_manager, UTIL_blender_mesh, UTIL_virtools_types, UTIL_naming_convension
|
||||||
|
|
||||||
## NOTE: Outside caller should use BME struct's unique indetifier to visit each prototype
|
## NOTE: Outside caller should use BME struct's unique indetifier to visit each prototype
|
||||||
# and drive this class' functions to work.
|
# and drive this class' functions to work.
|
||||||
@ -24,7 +24,6 @@ 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'
|
||||||
@ -65,10 +64,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 list storing BME prototype."""
|
## The dict. Key is prototype identifier. value is the index of prototype in prototype list.
|
||||||
_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')):
|
||||||
@ -100,7 +99,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
|
||||||
@ -142,7 +141,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,
|
||||||
@ -188,38 +187,15 @@ class PrototypeShowcaseCfgDescriptor():
|
|||||||
def get_default(self) -> typing.Any:
|
def get_default(self) -> typing.Any:
|
||||||
return _eval_showcase_cfgs_default(self.__mRawCfg[TOKEN_SHOWCASE_CFGS_DEFAULT])
|
return _eval_showcase_cfgs_default(self.__mRawCfg[TOKEN_SHOWCASE_CFGS_DEFAULT])
|
||||||
|
|
||||||
class EnumPropHelper(UTIL_functions.EnumPropHelper[str]):
|
class EnumPropHelper(UTIL_functions.EnumPropHelper):
|
||||||
"""
|
"""
|
||||||
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__(
|
UTIL_functions.EnumPropHelper.__init__(
|
||||||
|
self,
|
||||||
self.get_bme_identifiers(),
|
self.get_bme_identifiers(),
|
||||||
lambda x: x,
|
lambda x: x,
|
||||||
lambda x: x,
|
lambda x: x,
|
||||||
@ -227,20 +203,17 @@ 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.
|
||||||
In other words, template prototype is not included.
|
Template prototype is not included.
|
||||||
"""
|
"""
|
||||||
return self.showcase_identifiers
|
return tuple(
|
||||||
|
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.
|
||||||
@ -298,23 +271,23 @@ def create_bme_struct_wrapper(ident: str, cfgs: dict[str, typing.Any]) -> bpy.ty
|
|||||||
|
|
||||||
# create object and assign prop
|
# create object and assign prop
|
||||||
# get obj info first
|
# get obj info first
|
||||||
obj_info: UTIL_naming_convention.BallanceObjectInfo
|
obj_info: UTIL_naming_convension.BallanceObjectInfo
|
||||||
match(PrototypeShowcaseTypes(proto[TOKEN_SHOWCASE][TOKEN_SHOWCASE_TYPE])):
|
match(PrototypeShowcaseTypes(proto[TOKEN_SHOWCASE][TOKEN_SHOWCASE_TYPE])):
|
||||||
case PrototypeShowcaseTypes.No:
|
case PrototypeShowcaseTypes.No:
|
||||||
obj_info = UTIL_naming_convention.BallanceObjectInfo.create_from_others(UTIL_naming_convention.BallanceObjectType.DECORATION)
|
obj_info = UTIL_naming_convension.BallanceObjectInfo.create_from_others(UTIL_naming_convension.BallanceObjectType.DECORATION)
|
||||||
case PrototypeShowcaseTypes.Floor:
|
case PrototypeShowcaseTypes.Floor:
|
||||||
obj_info = UTIL_naming_convention.BallanceObjectInfo.create_from_others(UTIL_naming_convention.BallanceObjectType.FLOOR)
|
obj_info = UTIL_naming_convension.BallanceObjectInfo.create_from_others(UTIL_naming_convension.BallanceObjectType.FLOOR)
|
||||||
case PrototypeShowcaseTypes.Rail:
|
case PrototypeShowcaseTypes.Rail:
|
||||||
obj_info = UTIL_naming_convention.BallanceObjectInfo.create_from_others(UTIL_naming_convention.BallanceObjectType.RAIL)
|
obj_info = UTIL_naming_convension.BallanceObjectInfo.create_from_others(UTIL_naming_convension.BallanceObjectType.RAIL)
|
||||||
case PrototypeShowcaseTypes.Wood:
|
case PrototypeShowcaseTypes.Wood:
|
||||||
obj_info = UTIL_naming_convention.BallanceObjectInfo.create_from_others(UTIL_naming_convention.BallanceObjectType.WOOD)
|
obj_info = UTIL_naming_convension.BallanceObjectInfo.create_from_others(UTIL_naming_convension.BallanceObjectType.WOOD)
|
||||||
# then get object name
|
# then get object name
|
||||||
obj_name: str | None = UTIL_naming_convention.YYCToolchainConvention.set_to_name(obj_info, None)
|
obj_name: str | None = UTIL_naming_convension.YYCToolchainConvention.set_to_name(obj_info, None)
|
||||||
if obj_name is None: raise UTIL_functions.BBPException('impossible null name')
|
if obj_name is None: raise UTIL_functions.BBPException('impossible null name')
|
||||||
# create object by name
|
# create object by name
|
||||||
obj: bpy.types.Object = bpy.data.objects.new(obj_name, mesh)
|
obj: bpy.types.Object = bpy.data.objects.new(obj_name, mesh)
|
||||||
# assign virtools groups
|
# assign virtools groups
|
||||||
UTIL_naming_convention.VirtoolsGroupConvention.set_to_object(obj, obj_info, None)
|
UTIL_naming_convension.VirtoolsGroupConvention.set_to_object(obj, obj_info, None)
|
||||||
|
|
||||||
# return object
|
# return object
|
||||||
return obj
|
return obj
|
||||||
@ -354,14 +327,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] = {}
|
||||||
@ -379,7 +352,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.
|
||||||
@ -394,7 +367,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,
|
||||||
|
@ -2,35 +2,37 @@ import bpy, mathutils
|
|||||||
import math, typing, enum, sys
|
import math, typing, enum, sys
|
||||||
|
|
||||||
class BBPException(Exception):
|
class BBPException(Exception):
|
||||||
""" The exception thrown by Ballance Blender Plugin"""
|
"""
|
||||||
|
The exception thrown by Ballance Blender Plugin
|
||||||
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def clamp_float(v: float, min_val: float, max_val: float) -> float:
|
def clamp_float(v: float, min_val: float, max_val: float) -> float:
|
||||||
"""
|
"""!
|
||||||
Clamp a float value
|
@brief Clamp a float value
|
||||||
|
|
||||||
:param v: The value need to be clamp.
|
@param v[in] The value need to be clamp.
|
||||||
:param min_val: The allowed minium value (inclusive).
|
@param min_val[in] The allowed minium value, including self.
|
||||||
:param max_val: The allowed maxium value (inclusive).
|
@param max_val[in] The allowed maxium value, including self.
|
||||||
:return: Clamped value.
|
@return Clamped value.
|
||||||
"""
|
"""
|
||||||
if (max_val < min_val): raise BBPException("Invalid range of clamp_float().")
|
if (max_val < min_val): raise BBPException("Invalid range of clamp_float().")
|
||||||
|
|
||||||
if (v < min_val): return min_val
|
if (v < min_val): return min_val
|
||||||
elif (v > max_val): return max_val
|
elif (v > max_val): return max_val
|
||||||
else: return v
|
else: return v
|
||||||
|
|
||||||
def clamp_int(v: int, min_val: int, max_val: int) -> int:
|
def clamp_int(v: int, min_val: int, max_val: int) -> int:
|
||||||
"""
|
"""!
|
||||||
Clamp a int value
|
@brief Clamp a int value
|
||||||
|
|
||||||
:param v: The value need to be clamp.
|
@param v[in] The value need to be clamp.
|
||||||
:param min_val: The allowed minium value (inclusive).
|
@param min_val[in] The allowed minium value, including self.
|
||||||
:param max_val: The allowed maxium value (inclusive).
|
@param max_val[in] The allowed maxium value, including self.
|
||||||
:return: Clamped value.
|
@return Clamped value.
|
||||||
"""
|
"""
|
||||||
if (max_val < min_val): raise BBPException("Invalid range of clamp_int().")
|
if (max_val < min_val): raise BBPException("Invalid range of clamp_int().")
|
||||||
|
|
||||||
if (v < min_val): return min_val
|
if (v < min_val): return min_val
|
||||||
elif (v > max_val): return max_val
|
elif (v > max_val): return max_val
|
||||||
else: return v
|
else: return v
|
||||||
@ -39,65 +41,41 @@ def message_box(message: tuple[str, ...], title: str, icon: str):
|
|||||||
"""
|
"""
|
||||||
Show a message box in Blender. Non-block mode.
|
Show a message box in Blender. Non-block mode.
|
||||||
|
|
||||||
:param message: The text this message box displayed. Each item in this param will show as a single line.
|
@param message[in] The text this message box displayed. Each item in this param will show as a single line.
|
||||||
:param title: Message box title text.
|
@param title[in] Message box title text.
|
||||||
:param icon: The icon this message box displayed.
|
@param icon[in] The icon this message box displayed.
|
||||||
"""
|
"""
|
||||||
def draw(self, context: bpy.types.Context):
|
def draw(self, context: bpy.types.Context):
|
||||||
layout = self.layout
|
layout = self.layout
|
||||||
for item in message:
|
for item in message:
|
||||||
layout.label(text=item, translate=False)
|
layout.label(text=item, translate=False)
|
||||||
|
|
||||||
bpy.context.window_manager.popup_menu(draw, title = title, icon = icon)
|
bpy.context.window_manager.popup_menu(draw, title = title, icon = icon)
|
||||||
|
|
||||||
def add_into_scene(obj: bpy.types.Object):
|
def add_into_scene(obj: bpy.types.Object):
|
||||||
"""
|
|
||||||
Add given object into active scene.
|
|
||||||
|
|
||||||
:param obj: The 3d object to be added.
|
|
||||||
"""
|
|
||||||
view_layer = bpy.context.view_layer
|
view_layer = bpy.context.view_layer
|
||||||
collection = view_layer.active_layer_collection.collection
|
collection = view_layer.active_layer_collection.collection
|
||||||
collection.objects.link(obj)
|
collection.objects.link(obj)
|
||||||
|
|
||||||
def move_to_cursor(obj: bpy.types.Object):
|
def move_to_cursor(obj: bpy.types.Object):
|
||||||
"""
|
# use obj.matrix_world to move, not obj.location because this bug:
|
||||||
Move given object to the position of cursor.
|
|
||||||
|
|
||||||
:param obj: The 3d object to be moved.
|
|
||||||
"""
|
|
||||||
# YYC MARK:
|
|
||||||
# Use `obj.matrix_world` to move, not `obj.location`, because this bug:
|
|
||||||
# https://blender.stackexchange.com/questions/27667/incorrect-matrix-world-after-transformation
|
# https://blender.stackexchange.com/questions/27667/incorrect-matrix-world-after-transformation
|
||||||
# The update of `matrix_world` after setting `location` is not immediately.
|
# the update of matrix_world after setting location is not immediately.
|
||||||
# And it is inviable that calling `update()` function for `view_layer` to update these fields,
|
# and calling update() function for view_layer for the translation of each object is not suit for too much objects.
|
||||||
# because it involve too much objects and cost too much time.
|
|
||||||
|
|
||||||
# obj.location = bpy.context.scene.cursor.location
|
# obj.location = bpy.context.scene.cursor.location
|
||||||
obj.matrix_world = obj.matrix_world @ mathutils.Matrix.Translation(bpy.context.scene.cursor.location - obj.location)
|
obj.matrix_world = obj.matrix_world @ mathutils.Matrix.Translation(bpy.context.scene.cursor.location - obj.location)
|
||||||
|
|
||||||
def add_into_scene_and_move_to_cursor(obj: bpy.types.Object):
|
def add_into_scene_and_move_to_cursor(obj: bpy.types.Object):
|
||||||
"""
|
|
||||||
Add given object into active scene and move it to cursor position.
|
|
||||||
|
|
||||||
This function is just a simple combination of previous functions.
|
|
||||||
|
|
||||||
:param obj: The 3d object to be processed.
|
|
||||||
"""
|
|
||||||
add_into_scene(obj)
|
add_into_scene(obj)
|
||||||
move_to_cursor(obj)
|
move_to_cursor(obj)
|
||||||
|
|
||||||
def select_certain_objects(objs: tuple[bpy.types.Object, ...]) -> None:
|
def select_certain_objects(objs: tuple[bpy.types.Object, ...]) -> None:
|
||||||
"""
|
|
||||||
Deselect all objects and then select given 3d objects.
|
|
||||||
|
|
||||||
:param objs: The tuple of 3d objects to be selected.
|
|
||||||
"""
|
|
||||||
# deselect all objects first
|
# deselect all objects first
|
||||||
bpy.ops.object.select_all(action = 'DESELECT')
|
bpy.ops.object.select_all(action = 'DESELECT')
|
||||||
# if no objects, return
|
# if no objects, return
|
||||||
if len(objs) == 0: return
|
if len(objs) == 0: return
|
||||||
|
|
||||||
# set selection for each object
|
# set selection for each object
|
||||||
for obj in objs:
|
for obj in objs:
|
||||||
obj.select_set(True)
|
obj.select_set(True)
|
||||||
@ -105,79 +83,66 @@ def select_certain_objects(objs: tuple[bpy.types.Object, ...]) -> None:
|
|||||||
bpy.context.view_layer.objects.active = objs[0]
|
bpy.context.view_layer.objects.active = objs[0]
|
||||||
|
|
||||||
def is_in_object_mode() -> bool:
|
def is_in_object_mode() -> bool:
|
||||||
"""
|
|
||||||
Check whether we are in Blender Object Mode.
|
|
||||||
|
|
||||||
:return: True if we are in object mode which suit for exporting something.
|
|
||||||
"""
|
|
||||||
# get active object from context
|
# get active object from context
|
||||||
obj = bpy.context.active_object
|
obj = bpy.context.active_object
|
||||||
|
|
||||||
# if there is no active object, we think it is in object mode
|
# if there is no active object, we think it is in object mode
|
||||||
if obj is None: return True
|
if obj is None: return True
|
||||||
|
|
||||||
# simply check active object mode
|
# simply check active object mode
|
||||||
return obj.mode == 'OBJECT'
|
return obj.mode == 'OBJECT'
|
||||||
|
|
||||||
#region Blender Enum Property Helper
|
class EnumPropHelper():
|
||||||
|
|
||||||
_TRawEnum = typing.TypeVar('_TRawEnum')
|
|
||||||
|
|
||||||
_TFctToStr = typing.Callable[[_TRawEnum], str]
|
|
||||||
_TFctFromStr = typing.Callable[[str], _TRawEnum]
|
|
||||||
_TFctName = typing.Callable[[_TRawEnum], str]
|
|
||||||
_TFctDesc = typing.Callable[[_TRawEnum], str]
|
|
||||||
_TFctIcon = typing.Callable[[_TRawEnum], str | int]
|
|
||||||
|
|
||||||
class EnumPropHelper(typing.Generic[_TRawEnum]):
|
|
||||||
"""
|
"""
|
||||||
These class contain all functions related to EnumProperty, including generating `items`,
|
These class contain all functions related to EnumProperty, including generating `items`,
|
||||||
parsing data from EnumProperty string value and getting EnumProperty acceptable string format from data.
|
parsing data from EnumProperty string value and getting EnumProperty acceptable string format from data.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# YYC MARK:
|
|
||||||
# I don't know why I can have subscripting for a `typing.Callable` object.
|
|
||||||
# It was not introduced in any document and I just know it from AI.
|
|
||||||
# If I am not doing this, the type hint will crash into Unknown type.
|
|
||||||
# But it works now I don't want to touch it anymore.
|
|
||||||
|
|
||||||
__mCollections: typing.Iterable[_TRawEnum]
|
# define some type hint
|
||||||
__mFctToStr: _TFctToStr[_TRawEnum]
|
_TFctToStr = typing.Callable[[typing.Any], str]
|
||||||
__mFctFromStr: _TFctFromStr[_TRawEnum]
|
_TFctFromStr = typing.Callable[[str], typing.Any]
|
||||||
__mFctName: _TFctName[_TRawEnum]
|
_TFctName = typing.Callable[[typing.Any], str]
|
||||||
__mFctDesc: _TFctDesc[_TRawEnum]
|
_TFctDesc = typing.Callable[[typing.Any], str]
|
||||||
__mFctIcon: _TFctIcon[_TRawEnum]
|
_TFctIcon = typing.Callable[[typing.Any], str | int]
|
||||||
|
|
||||||
def __init__(self, collections: typing.Iterable[_TRawEnum],
|
# define class member
|
||||||
fct_to_str: _TFctToStr[_TRawEnum], fct_from_str: _TFctFromStr[_TRawEnum],
|
|
||||||
fct_name: _TFctName[_TRawEnum], fct_desc: _TFctDesc[_TRawEnum],
|
__mCollections: typing.Iterable[typing.Any]
|
||||||
fct_icon: _TFctIcon[_TRawEnum]):
|
__mFctToStr: _TFctToStr
|
||||||
|
__mFctFromStr: _TFctFromStr
|
||||||
|
__mFctName: _TFctName
|
||||||
|
__mFctDesc: _TFctDesc
|
||||||
|
__mFctIcon: _TFctIcon
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
collections_: typing.Iterable[typing.Any],
|
||||||
|
fct_to_str: _TFctToStr,
|
||||||
|
fct_from_str: _TFctFromStr,
|
||||||
|
fct_name: _TFctName,
|
||||||
|
fct_desc: _TFctDesc,
|
||||||
|
fct_icon: _TFctIcon):
|
||||||
"""
|
"""
|
||||||
Initialize an EnumProperty helper.
|
Initialize a EnumProperty helper.
|
||||||
|
|
||||||
:param collections: The collection containing all available enum property entries.
|
@param collections_ [in] The collection all available enum property entries contained.
|
||||||
It can be `enum.Enum` or a simple list/tuple.
|
It can be enum.Enum or a simple list/tuple/dict.
|
||||||
:param fct_to_str: A function pointer converting data collection member to its string format.
|
@param fct_to_str [in] A function pointer converting data collection member to its string format.
|
||||||
You must make sure that each members built name is unique in collection!
|
For enum.IntEnum, it can be simply `lambda x: str(x.value)`
|
||||||
For `enum.IntEnum`, it can be simple `lambda x: str(x.value)`
|
@param fct_from_str [in] A function pointer getting data collection member from its string format.
|
||||||
:param fct_from_str: A function pointer getting data collection member from its string format.
|
For enum.IntEnum, it can be simply `lambda x: TEnum(int(x))`
|
||||||
This class promise that given string must can be parsed.
|
@param fct_name [in] A function pointer converting data collection member to its display name.
|
||||||
For `enum.IntEnum`, it can be simple `lambda x: TEnum(int(x))`
|
@param fct_desc [in] Same as `fct_name` but return description instead. Return empty string, not None if no description.
|
||||||
:param fct_name: A function pointer converting data collection member to its display name which shown in Blender.
|
@param fct_icon [in] Same as `fct_name` but return the used icon instead. Return empty string if no icon.
|
||||||
:param fct_desc: Same as `fct_name` but return description instead which shown in Blender
|
|
||||||
If no description, return empty string, not None.
|
|
||||||
:param fct_icon: Same as `fct_name` but return the used icon instead which shown in Blender.
|
|
||||||
It can be a Blender builtin icon string, or any loaded icon integer ID.
|
|
||||||
If no icon, return empty string.
|
|
||||||
"""
|
"""
|
||||||
# assign member
|
# assign member
|
||||||
self.__mCollections = collections
|
self.__mCollections = collections_
|
||||||
self.__mFctToStr = fct_to_str
|
self.__mFctToStr = fct_to_str
|
||||||
self.__mFctFromStr = fct_from_str
|
self.__mFctFromStr = fct_from_str
|
||||||
self.__mFctName = fct_name
|
self.__mFctName = fct_name
|
||||||
self.__mFctDesc = fct_desc
|
self.__mFctDesc = fct_desc
|
||||||
self.__mFctIcon = fct_icon
|
self.__mFctIcon = fct_icon
|
||||||
|
|
||||||
def generate_items(self) -> tuple[tuple[str, str, str, int | str, int], ...]:
|
def generate_items(self) -> tuple[tuple[str, str, str, int | str, int], ...]:
|
||||||
"""
|
"""
|
||||||
Generate a tuple which can be applied to Blender EnumProperty's "items".
|
Generate a tuple which can be applied to Blender EnumProperty's "items".
|
||||||
@ -187,29 +152,27 @@ class EnumPropHelper(typing.Generic[_TRawEnum]):
|
|||||||
return tuple(
|
return tuple(
|
||||||
(
|
(
|
||||||
self.__mFctToStr(member), # call to_str as its token.
|
self.__mFctToStr(member), # call to_str as its token.
|
||||||
self.__mFctName(member),
|
self.__mFctName(member),
|
||||||
self.__mFctDesc(member),
|
self.__mFctDesc(member),
|
||||||
self.__mFctIcon(member),
|
self.__mFctIcon(member),
|
||||||
idx # use hardcode index, not the collection member self.
|
idx # use hardcode index, not the collection member self.
|
||||||
) for idx, member in enumerate(self.__mCollections)
|
) for idx, member in enumerate(self.__mCollections)
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_selection(self, prop: str) -> _TRawEnum:
|
def get_selection(self, prop: str) -> typing.Any:
|
||||||
"""
|
"""
|
||||||
Return collection member from given Blender EnumProp string data.
|
Return collection member from given Blender EnumProp string data.
|
||||||
"""
|
"""
|
||||||
# call from_str fct ptr
|
# call from_str fct ptr
|
||||||
return self.__mFctFromStr(prop)
|
return self.__mFctFromStr(prop)
|
||||||
|
|
||||||
def to_selection(self, val: _TRawEnum) -> str:
|
def to_selection(self, val: typing.Any) -> str:
|
||||||
"""
|
"""
|
||||||
Parse collection member to Blender EnumProp acceptable string format.
|
Parse collection member to Blender EnumProp acceptable string format.
|
||||||
"""
|
"""
|
||||||
# call to_str fct ptr
|
# call to_str fct ptr
|
||||||
return self.__mFctToStr(val)
|
return self.__mFctToStr(val)
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Blender Collection Visitor
|
#region Blender Collection Visitor
|
||||||
|
|
||||||
_TPropertyGroup = typing.TypeVar('_TPropertyGroup', bound = bpy.types.PropertyGroup)
|
_TPropertyGroup = typing.TypeVar('_TPropertyGroup', bound = bpy.types.PropertyGroup)
|
||||||
@ -220,43 +183,40 @@ class CollectionVisitor(typing.Generic[_TPropertyGroup]):
|
|||||||
Blender collcetion property lack essential type hint and document.
|
Blender collcetion property lack essential type hint and document.
|
||||||
So I create a wrapper for my personal use to reduce type hint errors raised by my linter.
|
So I create a wrapper for my personal use to reduce type hint errors raised by my linter.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__mSrcProp: bpy.types.CollectionProperty
|
__mSrcProp: bpy.types.CollectionProperty
|
||||||
|
|
||||||
def __init__(self, src_prop: bpy.types.CollectionProperty):
|
def __init__(self, src_prop: bpy.types.CollectionProperty):
|
||||||
self.__mSrcProp = src_prop
|
self.__mSrcProp = src_prop
|
||||||
|
|
||||||
def add(self) -> _TPropertyGroup:
|
|
||||||
"""
|
|
||||||
Adds a new item to the collection.
|
|
||||||
|
|
||||||
:return: The instance of newly created item.
|
def add(self) -> _TPropertyGroup:
|
||||||
|
"""!
|
||||||
|
@brief Adds a new item to the collection.
|
||||||
|
@return The instance of newly created item.
|
||||||
"""
|
"""
|
||||||
return self.__mSrcProp.add()
|
return self.__mSrcProp.add()
|
||||||
|
|
||||||
def remove(self, index: int) -> None:
|
|
||||||
"""
|
|
||||||
Removes the item at the specified index from the collection.
|
|
||||||
|
|
||||||
:param index: The index of the item to remove.
|
def remove(self, index: int) -> None:
|
||||||
|
"""!
|
||||||
|
@brief Removes the item at the specified index from the collection.
|
||||||
|
@param[in] index The index of the item to remove.
|
||||||
"""
|
"""
|
||||||
self.__mSrcProp.remove(index)
|
self.__mSrcProp.remove(index)
|
||||||
|
|
||||||
def move(self, from_index: int, to_index: int) -> None:
|
|
||||||
"""
|
|
||||||
Moves an item from one index to another within the collection.
|
|
||||||
|
|
||||||
:param from_index: The current index of the item to move.
|
def move(self, from_index: int, to_index: int) -> None:
|
||||||
:param to_index: The target index where the item should be moved.
|
"""!
|
||||||
|
@brief Moves an item from one index to another within the collection.
|
||||||
|
@param[in] from_index The current index of the item to move.
|
||||||
|
@param[in] to_index The target index where the item should be moved.
|
||||||
"""
|
"""
|
||||||
self.__mSrcProp.move(from_index, to_index)
|
self.__mSrcProp.move(from_index, to_index)
|
||||||
|
|
||||||
def clear(self) -> None:
|
def clear(self) -> None:
|
||||||
"""
|
"""!
|
||||||
Clears all items from the collection.
|
@brief Clears all items from the collection.
|
||||||
"""
|
"""
|
||||||
self.__mSrcProp.clear()
|
self.__mSrcProp.clear()
|
||||||
|
|
||||||
def __len__(self) -> int:
|
def __len__(self) -> int:
|
||||||
return self.__mSrcProp.__len__()
|
return self.__mSrcProp.__len__()
|
||||||
def __getitem__(self, index: int | str) -> _TPropertyGroup:
|
def __getitem__(self, index: int | str) -> _TPropertyGroup:
|
||||||
@ -278,50 +238,32 @@ _TMutexObject = typing.TypeVar('_TMutexObject')
|
|||||||
|
|
||||||
class TinyMutex(typing.Generic[_TMutexObject]):
|
class TinyMutex(typing.Generic[_TMutexObject]):
|
||||||
"""
|
"""
|
||||||
In this plugin, some classes have "with" context feature.
|
In this plugin, some class have "with" context feature.
|
||||||
However, in some cases, it is essential to block any futher visiting if some "with" context are operating on some object.
|
However, it is essential to block any futher visiting if some "with" context are operating on some object.
|
||||||
This is the reason why this tiny mutex is designed.
|
This is the reason why this tiny mutex is designed.
|
||||||
|
|
||||||
Please note this class is not a real MUTEX.
|
Please note this class is not a real MUTEX.
|
||||||
We just want to make sure the resources only can be visited by one "with" context.
|
We just want to make sure the resources only can be visited by one "with" context.
|
||||||
So it doesn't matter that we do not use lock before operating something.
|
So it doesn't matter that we do not use lock before operating something.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__mProtectedObjects: set[_TMutexObject]
|
__mProtectedObjects: set[_TMutexObject]
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.__mProtectedObjects = set()
|
self.__mProtectedObjects = set()
|
||||||
|
|
||||||
def lock(self, obj: _TMutexObject) -> None:
|
|
||||||
"""
|
|
||||||
Lock given object.
|
|
||||||
|
|
||||||
:raise BBPException: Raised if given object has been locked.
|
def lock(self, obj: _TMutexObject) -> None:
|
||||||
:param obj: The resource to be locked.
|
|
||||||
"""
|
|
||||||
if obj in self.__mProtectedObjects:
|
if obj in self.__mProtectedObjects:
|
||||||
raise BBPException('It is not allowed that operate multiple "with" contexts on a single object.')
|
raise BBPException('It is not allowed that operate multiple "with" contexts on a single object.')
|
||||||
self.__mProtectedObjects.add(obj)
|
self.__mProtectedObjects.add(obj)
|
||||||
|
|
||||||
def try_lock(self, obj: _TMutexObject) -> bool:
|
|
||||||
"""
|
|
||||||
Try lock given object.
|
|
||||||
|
|
||||||
:param obj: The resource to be locked.
|
def try_lock(self, obj: _TMutexObject) -> bool:
|
||||||
:return: True if we successfully lock it, otherwise false.
|
|
||||||
"""
|
|
||||||
if obj in self.__mProtectedObjects:
|
if obj in self.__mProtectedObjects:
|
||||||
return False
|
return False
|
||||||
self.__mProtectedObjects.add(obj)
|
self.__mProtectedObjects.add(obj)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def unlock(self, obj: _TMutexObject) -> None:
|
|
||||||
"""
|
|
||||||
Unlock given object.
|
|
||||||
|
|
||||||
:raise BBPException: Raised if given object is not locked.
|
def unlock(self, obj: _TMutexObject) -> None:
|
||||||
:param obj: The resource to be unlocked.
|
|
||||||
"""
|
|
||||||
if obj not in self.__mProtectedObjects:
|
if obj not in self.__mProtectedObjects:
|
||||||
raise BBPException('It is not allowed that unlock an non-existent object.')
|
raise BBPException('It is not allowed that unlock an non-existent object.')
|
||||||
self.__mProtectedObjects.remove(obj)
|
self.__mProtectedObjects.remove(obj)
|
||||||
|
@ -3,13 +3,11 @@ import enum, typing
|
|||||||
from . import UTIL_virtools_types, UTIL_functions
|
from . import UTIL_virtools_types, UTIL_functions
|
||||||
from . import PROP_ptrprop_resolver, PROP_ballance_map_info
|
from . import PROP_ptrprop_resolver, PROP_ballance_map_info
|
||||||
|
|
||||||
# INTENT:
|
## Intent
|
||||||
# Some importer or exporter may share same properties.
|
# Some importer or exporter may share same properties.
|
||||||
# So we create some shared class and user just need inherit them
|
# So we create some shared class and user just need inherit them
|
||||||
# and call general getter to get user selected data.
|
# and call general getter to get user selected data.
|
||||||
# Also provide draw function thus caller do not need draw the params themselves.
|
# Also provide draw function thus caller do not need draw the params themselves.
|
||||||
|
|
||||||
#region Import Params
|
|
||||||
|
|
||||||
class ConflictStrategy(enum.IntEnum):
|
class ConflictStrategy(enum.IntEnum):
|
||||||
Rename = enum.auto()
|
Rename = enum.auto()
|
||||||
@ -18,7 +16,7 @@ _g_ConflictStrategyDesc: dict[ConflictStrategy, tuple[str, str]] = {
|
|||||||
ConflictStrategy.Rename: ('Rename', 'Rename the new one'),
|
ConflictStrategy.Rename: ('Rename', 'Rename the new one'),
|
||||||
ConflictStrategy.Current: ('Use Current', 'Use current one'),
|
ConflictStrategy.Current: ('Use Current', 'Use current one'),
|
||||||
}
|
}
|
||||||
_g_EnumHelper_ConflictStrategy = UTIL_functions.EnumPropHelper(
|
_g_EnumHelper_ConflictStrategy: UTIL_functions.EnumPropHelper = UTIL_functions.EnumPropHelper(
|
||||||
ConflictStrategy,
|
ConflictStrategy,
|
||||||
lambda x: str(x.value),
|
lambda x: str(x.value),
|
||||||
lambda x: ConflictStrategy(int(x)),
|
lambda x: ConflictStrategy(int(x)),
|
||||||
@ -27,6 +25,39 @@ _g_EnumHelper_ConflictStrategy = UTIL_functions.EnumPropHelper(
|
|||||||
lambda _: ''
|
lambda _: ''
|
||||||
)
|
)
|
||||||
|
|
||||||
|
#region Assist Classes
|
||||||
|
|
||||||
|
class ExportEditModeBackup():
|
||||||
|
"""
|
||||||
|
The class which save Edit Mode when exporting and restore it after exporting.
|
||||||
|
Because edit mode is not allowed when exporting.
|
||||||
|
Support `with` statement.
|
||||||
|
|
||||||
|
```
|
||||||
|
with ExportEditModeBackup():
|
||||||
|
# do some exporting work
|
||||||
|
blabla()
|
||||||
|
# restore automatically when exiting "with"
|
||||||
|
```
|
||||||
|
"""
|
||||||
|
mInEditMode: bool
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
if bpy.context.object and bpy.context.object.mode == "EDIT":
|
||||||
|
# set and toggle it. otherwise exporting will failed.
|
||||||
|
self.mInEditMode = True
|
||||||
|
bpy.ops.object.editmode_toggle()
|
||||||
|
else:
|
||||||
|
self.mInEditMode = False
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __exit__(self, exc_type, exc_value, traceback):
|
||||||
|
if self.mInEditMode:
|
||||||
|
bpy.ops.object.editmode_toggle()
|
||||||
|
self.mInEditMode = False
|
||||||
|
|
||||||
class ConflictResolver():
|
class ConflictResolver():
|
||||||
"""
|
"""
|
||||||
This class frequently used when importing objects.
|
This class frequently used when importing objects.
|
||||||
@ -120,6 +151,8 @@ class ConflictResolver():
|
|||||||
tex.name = name
|
tex.name = name
|
||||||
return (tex, True)
|
return (tex, True)
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
class ImportParams():
|
class ImportParams():
|
||||||
texture_conflict_strategy: bpy.props.EnumProperty(
|
texture_conflict_strategy: bpy.props.EnumProperty(
|
||||||
name = "Texture Name Conflict",
|
name = "Texture Name Conflict",
|
||||||
@ -206,67 +239,13 @@ class ImportParams():
|
|||||||
self.general_get_texture_conflict_strategy()
|
self.general_get_texture_conflict_strategy()
|
||||||
)
|
)
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Export Params
|
|
||||||
|
|
||||||
class ExportEditModeBackup():
|
|
||||||
"""
|
|
||||||
The class which save Edit Mode when exporting and restore it after exporting.
|
|
||||||
Because edit mode is not allowed when exporting.
|
|
||||||
Support `with` statement.
|
|
||||||
|
|
||||||
```
|
|
||||||
with ExportEditModeBackup():
|
|
||||||
# do some exporting work
|
|
||||||
blabla()
|
|
||||||
# restore automatically when exiting "with"
|
|
||||||
```
|
|
||||||
"""
|
|
||||||
mInEditMode: bool
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
if bpy.context.object and bpy.context.object.mode == "EDIT":
|
|
||||||
# set and toggle it. otherwise exporting will failed.
|
|
||||||
self.mInEditMode = True
|
|
||||||
bpy.ops.object.editmode_toggle()
|
|
||||||
else:
|
|
||||||
self.mInEditMode = False
|
|
||||||
|
|
||||||
def __enter__(self):
|
|
||||||
return self
|
|
||||||
|
|
||||||
def __exit__(self, exc_type, exc_value, traceback):
|
|
||||||
if self.mInEditMode:
|
|
||||||
bpy.ops.object.editmode_toggle()
|
|
||||||
self.mInEditMode = False
|
|
||||||
|
|
||||||
class ExportMode(enum.IntEnum):
|
|
||||||
BldColl = enum.auto()
|
|
||||||
BldObj = enum.auto()
|
|
||||||
BldSelObjs = enum.auto()
|
|
||||||
BldAllObjs = enum.auto()
|
|
||||||
_g_ExportModeDesc: dict[ExportMode, tuple[str, str, str]] = {
|
|
||||||
ExportMode.BldColl: ('Collection', 'Export a collection', 'OUTLINER_COLLECTION'),
|
|
||||||
ExportMode.BldObj: ('Object', 'Export an object', 'OBJECT_DATA'),
|
|
||||||
ExportMode.BldSelObjs: ('Selected Objects', 'Export selected objects', 'SELECT_SET'),
|
|
||||||
ExportMode.BldAllObjs: ('All Objects', 'Export all objects stored in this file', 'FILE_BLEND'),
|
|
||||||
}
|
|
||||||
_g_EnumHelper_ExportMode = UTIL_functions.EnumPropHelper(
|
|
||||||
ExportMode,
|
|
||||||
lambda x: str(x.value),
|
|
||||||
lambda x: ExportMode(int(x)),
|
|
||||||
lambda x: _g_ExportModeDesc[x][0],
|
|
||||||
lambda x: _g_ExportModeDesc[x][1],
|
|
||||||
lambda x: _g_ExportModeDesc[x][2]
|
|
||||||
)
|
|
||||||
|
|
||||||
class ExportParams():
|
class ExportParams():
|
||||||
export_mode: bpy.props.EnumProperty(
|
export_mode: bpy.props.EnumProperty(
|
||||||
name = "Export Mode",
|
name = "Export Mode",
|
||||||
description = "Define which 3D objects should be exported",
|
items = (
|
||||||
items = _g_EnumHelper_ExportMode.generate_items(),
|
('COLLECTION', "Collection", "Export a collection", 'OUTLINER_COLLECTION', 0),
|
||||||
default = _g_EnumHelper_ExportMode.to_selection(ExportMode.BldColl),
|
('OBJECT', "Object", "Export an object", 'OBJECT_DATA', 1),
|
||||||
|
),
|
||||||
translation_context = 'BBP/UTIL_ioport_shared.ExportParams/property'
|
translation_context = 'BBP/UTIL_ioport_shared.ExportParams/property'
|
||||||
) # type: ignore
|
) # type: ignore
|
||||||
|
|
||||||
@ -277,48 +256,35 @@ class ExportParams():
|
|||||||
header.label(text='Export Parameters', text_ctxt='BBP/UTIL_ioport_shared.ExportParams/draw')
|
header.label(text='Export Parameters', text_ctxt='BBP/UTIL_ioport_shared.ExportParams/draw')
|
||||||
if body is None: return
|
if body is None: return
|
||||||
|
|
||||||
|
# make prop expand horizontaly, not vertical.
|
||||||
|
horizon_body = body.row()
|
||||||
# draw switch
|
# draw switch
|
||||||
body.prop(self, "export_mode", expand=True)
|
horizon_body.prop(self, "export_mode", expand=True)
|
||||||
|
|
||||||
# draw picker
|
# draw picker
|
||||||
export_mode = _g_EnumHelper_ExportMode.get_selection(self.export_mode)
|
|
||||||
ptrprops = PROP_ptrprop_resolver.PropsVisitor(context.scene)
|
ptrprops = PROP_ptrprop_resolver.PropsVisitor(context.scene)
|
||||||
match export_mode:
|
if self.export_mode == 'COLLECTION':
|
||||||
case ExportMode.BldColl:
|
ptrprops.draw_export_collection(body)
|
||||||
ptrprops.draw_export_collection(body)
|
elif self.export_mode == 'OBJECT':
|
||||||
case ExportMode.BldObj:
|
ptrprops.draw_export_object(body)
|
||||||
ptrprops.draw_export_object(body)
|
|
||||||
case ExportMode.BldSelObjs:
|
|
||||||
pass # Draw nothing
|
|
||||||
case ExportMode.BldAllObjs:
|
|
||||||
pass # Draw nothing
|
|
||||||
|
|
||||||
def general_get_export_objects(self, context: bpy.types.Context) -> tuple[bpy.types.Object, ...] | None:
|
def general_get_export_objects(self, context: bpy.types.Context) -> tuple[bpy.types.Object] | None:
|
||||||
"""
|
"""
|
||||||
Return resolved exported objects or None if no selection.
|
Return resolved exported objects or None if no selection.
|
||||||
"""
|
"""
|
||||||
export_mode = _g_EnumHelper_ExportMode.get_selection(self.export_mode)
|
|
||||||
ptrprops = PROP_ptrprop_resolver.PropsVisitor(context.scene)
|
ptrprops = PROP_ptrprop_resolver.PropsVisitor(context.scene)
|
||||||
match export_mode:
|
if self.export_mode == 'COLLECTION':
|
||||||
case ExportMode.BldColl:
|
col: bpy.types.Collection = ptrprops.get_export_collection()
|
||||||
col: bpy.types.Collection = ptrprops.get_export_collection()
|
if col is None: return None
|
||||||
if col is None: return None
|
else:
|
||||||
else: return tuple(col.all_objects)
|
return tuple(col.all_objects)
|
||||||
case ExportMode.BldObj:
|
else:
|
||||||
obj: bpy.types.Object = ptrprops.get_export_object()
|
obj: bpy.types.Object = ptrprops.get_export_object()
|
||||||
if obj is None: return None
|
if obj is None: return None
|
||||||
else: return (obj, )
|
else: return (obj, )
|
||||||
case ExportMode.BldSelObjs:
|
|
||||||
return tuple(context.selected_objects)
|
|
||||||
case ExportMode.BldAllObjs:
|
|
||||||
return tuple(bpy.data.objects)
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Virtools Params
|
|
||||||
|
|
||||||
# define global tex save opt blender enum prop helper
|
# define global tex save opt blender enum prop helper
|
||||||
_g_EnumHelper_CK_TEXTURE_SAVEOPTIONS = UTIL_virtools_types.EnumPropHelper(UTIL_virtools_types.CK_TEXTURE_SAVEOPTIONS)
|
_g_EnumHelper_CK_TEXTURE_SAVEOPTIONS: UTIL_virtools_types.EnumPropHelper = UTIL_virtools_types.EnumPropHelper(UTIL_virtools_types.CK_TEXTURE_SAVEOPTIONS)
|
||||||
|
|
||||||
class VirtoolsParams():
|
class VirtoolsParams():
|
||||||
texture_save_opt: bpy.props.EnumProperty(
|
texture_save_opt: bpy.props.EnumProperty(
|
||||||
@ -344,14 +310,6 @@ class VirtoolsParams():
|
|||||||
translation_context = 'BBP/UTIL_ioport_shared.VirtoolsParams/property'
|
translation_context = 'BBP/UTIL_ioport_shared.VirtoolsParams/property'
|
||||||
) # type: ignore
|
) # type: ignore
|
||||||
|
|
||||||
def preset_vt_encodings_if_possible(self, context: bpy.types.Context):
|
|
||||||
"""
|
|
||||||
Set preset value for Virtools Encoding list if there is no value inside it.
|
|
||||||
"""
|
|
||||||
ptrprops = PROP_ptrprop_resolver.PropsVisitor(context.scene)
|
|
||||||
if len(ptrprops.get_ioport_encodings()) == 0:
|
|
||||||
ptrprops.preset_ioport_encodings()
|
|
||||||
|
|
||||||
def draw_virtools_params(self, context: bpy.types.Context, layout: bpy.types.UILayout, is_importer: bool) -> None:
|
def draw_virtools_params(self, context: bpy.types.Context, layout: bpy.types.UILayout, is_importer: bool) -> None:
|
||||||
header: bpy.types.UILayout
|
header: bpy.types.UILayout
|
||||||
body: bpy.types.UILayout
|
body: bpy.types.UILayout
|
||||||
@ -376,6 +334,7 @@ class VirtoolsParams():
|
|||||||
if self.use_compress:
|
if self.use_compress:
|
||||||
body.prop(self, 'compress_level')
|
body.prop(self, 'compress_level')
|
||||||
|
|
||||||
|
|
||||||
def general_get_vt_encodings(self, context: bpy.types.Context) -> tuple[str, ...]:
|
def general_get_vt_encodings(self, context: bpy.types.Context) -> tuple[str, ...]:
|
||||||
# get from ptrprop resolver then filter empty item
|
# get from ptrprop resolver then filter empty item
|
||||||
ptrprops = PROP_ptrprop_resolver.PropsVisitor(context.scene)
|
ptrprops = PROP_ptrprop_resolver.PropsVisitor(context.scene)
|
||||||
@ -390,10 +349,6 @@ class VirtoolsParams():
|
|||||||
def general_get_compress_level(self) -> int:
|
def general_get_compress_level(self) -> int:
|
||||||
return self.compress_level
|
return self.compress_level
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Ballance Params
|
|
||||||
|
|
||||||
class BallanceParams():
|
class BallanceParams():
|
||||||
successive_sector: bpy.props.BoolProperty(
|
successive_sector: bpy.props.BoolProperty(
|
||||||
name="Successive Sector",
|
name="Successive Sector",
|
||||||
@ -432,5 +387,3 @@ class BallanceParams():
|
|||||||
map_info: PROP_ballance_map_info.RawBallanceMapInfo
|
map_info: PROP_ballance_map_info.RawBallanceMapInfo
|
||||||
map_info = PROP_ballance_map_info.get_raw_ballance_map_info(bpy.context.scene)
|
map_info = PROP_ballance_map_info.get_raw_ballance_map_info(bpy.context.scene)
|
||||||
return map_info.mSectorCount
|
return map_info.mSectorCount
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
@ -72,7 +72,7 @@ class RenameErrorReporter():
|
|||||||
print('============')
|
print('============')
|
||||||
print(bpy.app.translations.pgettext_rpt(
|
print(bpy.app.translations.pgettext_rpt(
|
||||||
'Rename Report',
|
'Rename Report',
|
||||||
'BBP/UTIL_naming_convention.RenameErrorReporter'
|
'BBP/UTIL_naming_convension.RenameErrorReporter'
|
||||||
))
|
))
|
||||||
print('------------')
|
print('------------')
|
||||||
# return self as context
|
# return self as context
|
||||||
@ -82,7 +82,7 @@ class RenameErrorReporter():
|
|||||||
# print console report tail
|
# print console report tail
|
||||||
print('------------')
|
print('------------')
|
||||||
tr_text: str = bpy.app.translations.pgettext_rpt(
|
tr_text: str = bpy.app.translations.pgettext_rpt(
|
||||||
'All / Failed - {0} / {1}', 'BBP/UTIL_naming_convention.RenameErrorReporter')
|
'All / Failed - {0} / {1}', 'BBP/UTIL_naming_convension.RenameErrorReporter')
|
||||||
print(tr_text.format(self.mAllObjCounter, self.mFailedObjCounter))
|
print(tr_text.format(self.mAllObjCounter, self.mFailedObjCounter))
|
||||||
print('============')
|
print('============')
|
||||||
# reset variables
|
# reset variables
|
||||||
@ -107,10 +107,10 @@ class RenameErrorReporter():
|
|||||||
tr_text: str
|
tr_text: str
|
||||||
new_name: str = obj.name
|
new_name: str = obj.name
|
||||||
if self.mOldName == new_name:
|
if self.mOldName == new_name:
|
||||||
tr_text = bpy.app.translations.pgettext_rpt('For object "{0}"', 'BBP/UTIL_naming_convention.RenameErrorReporter')
|
tr_text = bpy.app.translations.pgettext_rpt('For object "{0}"', 'BBP/UTIL_naming_convension.RenameErrorReporter')
|
||||||
print(tr_text.format(new_name))
|
print(tr_text.format(new_name))
|
||||||
else:
|
else:
|
||||||
tr_text = bpy.app.translations.pgettext_rpt('For object "{0}" (Old name: "{1}")', 'BBP/UTIL_naming_convention.RenameErrorReporter')
|
tr_text = bpy.app.translations.pgettext_rpt('For object "{0}" (Old name: "{1}")', 'BBP/UTIL_naming_convension.RenameErrorReporter')
|
||||||
print(tr_text.format(new_name, self.mOldName))
|
print(tr_text.format(new_name, self.mOldName))
|
||||||
|
|
||||||
# output error list with indent
|
# output error list with indent
|
||||||
@ -125,11 +125,11 @@ class RenameErrorReporter():
|
|||||||
def __errtype_to_string(err_v: _RenameErrorType) -> str:
|
def __errtype_to_string(err_v: _RenameErrorType) -> str:
|
||||||
match(err_v):
|
match(err_v):
|
||||||
case _RenameErrorType.ERROR:
|
case _RenameErrorType.ERROR:
|
||||||
return bpy.app.translations.pgettext_rpt('ERROR', 'BBP/UTIL_naming_convention.RenameErrorReporter')
|
return bpy.app.translations.pgettext_rpt('ERROR', 'BBP/UTIL_naming_convension.RenameErrorReporter')
|
||||||
case _RenameErrorType.WARNING:
|
case _RenameErrorType.WARNING:
|
||||||
return bpy.app.translations.pgettext_rpt('WARN', 'BBP/UTIL_naming_convention.RenameErrorReporter')
|
return bpy.app.translations.pgettext_rpt('WARN', 'BBP/UTIL_naming_convension.RenameErrorReporter')
|
||||||
case _RenameErrorType.INFO:
|
case _RenameErrorType.INFO:
|
||||||
return bpy.app.translations.pgettext_rpt('INFO', 'BBP/UTIL_naming_convention.RenameErrorReporter')
|
return bpy.app.translations.pgettext_rpt('INFO', 'BBP/UTIL_naming_convension.RenameErrorReporter')
|
||||||
case _: raise UTIL_functions.BBPException("Unknown error type.")
|
case _: raise UTIL_functions.BBPException("Unknown error type.")
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def __erritem_to_string(item: _RenameErrorItem) -> str:
|
def __erritem_to_string(item: _RenameErrorItem) -> str:
|
||||||
@ -292,7 +292,7 @@ class VirtoolsGroupConvention():
|
|||||||
)
|
)
|
||||||
|
|
||||||
tr_text: str = bpy.app.translations.pgettext_rpt(
|
tr_text: str = bpy.app.translations.pgettext_rpt(
|
||||||
"PC_Checkpoints or PR_Resetpoints detected. But couldn't get sector from name.", 'BBP/UTIL_naming_convention.VirtoolsGroupConvention')
|
"PC_Checkpoints or PR_Resetpoints detected. But couldn't get sector from name.", 'BBP/UTIL_naming_convension.VirtoolsGroupConvention')
|
||||||
if reporter: reporter.add_error(tr_text)
|
if reporter: reporter.add_error(tr_text)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@ -337,12 +337,12 @@ class VirtoolsGroupConvention():
|
|||||||
return VirtoolsGroupConvention.__get_pcpr_from_name(obj.name, reporter)
|
return VirtoolsGroupConvention.__get_pcpr_from_name(obj.name, reporter)
|
||||||
case _:
|
case _:
|
||||||
tr_text = bpy.app.translations.pgettext_rpt(
|
tr_text = bpy.app.translations.pgettext_rpt(
|
||||||
"The match of Unique Component lost.", 'BBP/UTIL_naming_convention.VirtoolsGroupConvention')
|
"The match of Unique Component lost.", 'BBP/UTIL_naming_convension.VirtoolsGroupConvention')
|
||||||
if reporter: reporter.add_error(tr_text)
|
if reporter: reporter.add_error(tr_text)
|
||||||
return None
|
return None
|
||||||
elif len(inter_gps) != 0:
|
elif len(inter_gps) != 0:
|
||||||
tr_text = bpy.app.translations.pgettext_rpt(
|
tr_text = bpy.app.translations.pgettext_rpt(
|
||||||
"A Multi-grouping Unique Component.", 'BBP/UTIL_naming_convention.VirtoolsGroupConvention')
|
"A Multi-grouping Unique Component.", 'BBP/UTIL_naming_convension.VirtoolsGroupConvention')
|
||||||
if reporter: reporter.add_error(tr_text)
|
if reporter: reporter.add_error(tr_text)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@ -356,7 +356,7 @@ class VirtoolsGroupConvention():
|
|||||||
if gotten_sector is None:
|
if gotten_sector is None:
|
||||||
# fail to get sector
|
# fail to get sector
|
||||||
tr_text = bpy.app.translations.pgettext_rpt(
|
tr_text = bpy.app.translations.pgettext_rpt(
|
||||||
"Component detected. But couldn't get sector from CKGroup data.", 'BBP/UTIL_naming_convention.VirtoolsGroupConvention')
|
"Component detected. But couldn't get sector from CKGroup data.", 'BBP/UTIL_naming_convension.VirtoolsGroupConvention')
|
||||||
if reporter: reporter.add_error(tr_text)
|
if reporter: reporter.add_error(tr_text)
|
||||||
return None
|
return None
|
||||||
return BallanceObjectInfo.create_from_component(
|
return BallanceObjectInfo.create_from_component(
|
||||||
@ -366,7 +366,7 @@ class VirtoolsGroupConvention():
|
|||||||
elif len(inter_gps) != 0:
|
elif len(inter_gps) != 0:
|
||||||
# must be a weird grouping, report it
|
# must be a weird grouping, report it
|
||||||
tr_text = bpy.app.translations.pgettext_rpt(
|
tr_text = bpy.app.translations.pgettext_rpt(
|
||||||
"A Multi-grouping Component.", 'BBP/UTIL_naming_convention.VirtoolsGroupConvention')
|
"A Multi-grouping Component.", 'BBP/UTIL_naming_convension.VirtoolsGroupConvention')
|
||||||
if reporter: reporter.add_error(tr_text)
|
if reporter: reporter.add_error(tr_text)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@ -384,7 +384,7 @@ class VirtoolsGroupConvention():
|
|||||||
return BallanceObjectInfo.create_from_others(BallanceObjectType.WOOD)
|
return BallanceObjectInfo.create_from_others(BallanceObjectType.WOOD)
|
||||||
else:
|
else:
|
||||||
tr_text = bpy.app.translations.pgettext_rpt(
|
tr_text = bpy.app.translations.pgettext_rpt(
|
||||||
"Can't distinguish object between Floors and Rails. Suppose it is Floors.", 'BBP/UTIL_naming_convention.VirtoolsGroupConvention')
|
"Can't distinguish object between Floors and Rails. Suppose it is Floors.", 'BBP/UTIL_naming_convension.VirtoolsGroupConvention')
|
||||||
if reporter: reporter.add_warning(tr_text)
|
if reporter: reporter.add_warning(tr_text)
|
||||||
return BallanceObjectInfo.create_from_others(BallanceObjectType.FLOOR)
|
return BallanceObjectInfo.create_from_others(BallanceObjectType.FLOOR)
|
||||||
elif gp.contain_group('Phys_FloorStopper'):
|
elif gp.contain_group('Phys_FloorStopper'):
|
||||||
@ -394,7 +394,7 @@ class VirtoolsGroupConvention():
|
|||||||
|
|
||||||
# no matched
|
# no matched
|
||||||
tr_text = bpy.app.translations.pgettext_rpt(
|
tr_text = bpy.app.translations.pgettext_rpt(
|
||||||
"Group match lost.", 'BBP/UTIL_naming_convention.VirtoolsGroupConvention')
|
"Group match lost.", 'BBP/UTIL_naming_convension.VirtoolsGroupConvention')
|
||||||
if reporter: reporter.add_error(tr_text)
|
if reporter: reporter.add_error(tr_text)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@ -445,7 +445,7 @@ class VirtoolsGroupConvention():
|
|||||||
|
|
||||||
case _:
|
case _:
|
||||||
tr_text: str = bpy.app.translations.pgettext_rpt(
|
tr_text: str = bpy.app.translations.pgettext_rpt(
|
||||||
"No matched info.", 'BBP/UTIL_naming_convention.VirtoolsGroupConvention')
|
"No matched info.", 'BBP/UTIL_naming_convension.VirtoolsGroupConvention')
|
||||||
if reporter: reporter.add_error(tr_text)
|
if reporter: reporter.add_error(tr_text)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -499,7 +499,7 @@ class YYCToolchainConvention():
|
|||||||
return BallanceObjectInfo.create_from_others(BallanceObjectType.SKYLAYER)
|
return BallanceObjectInfo.create_from_others(BallanceObjectType.SKYLAYER)
|
||||||
|
|
||||||
tr_text: str = bpy.app.translations.pgettext_rpt(
|
tr_text: str = bpy.app.translations.pgettext_rpt(
|
||||||
"Name match lost.", 'BBP/UTIL_naming_convention.YYCToolchainConvention')
|
"Name match lost.", 'BBP/UTIL_naming_convension.YYCToolchainConvention')
|
||||||
if reporter: reporter.add_error(tr_text)
|
if reporter: reporter.add_error(tr_text)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@ -543,7 +543,7 @@ class YYCToolchainConvention():
|
|||||||
|
|
||||||
case _:
|
case _:
|
||||||
tr_text: str = bpy.app.translations.pgettext_rpt(
|
tr_text: str = bpy.app.translations.pgettext_rpt(
|
||||||
"No matched info.", 'BBP/UTIL_naming_convention.YYCToolchainConvention')
|
"No matched info.", 'BBP/UTIL_naming_convension.YYCToolchainConvention')
|
||||||
if reporter: reporter.add_error(tr_text)
|
if reporter: reporter.add_error(tr_text)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@ -607,7 +607,7 @@ class ImengyuConvention():
|
|||||||
return BallanceObjectInfo.create_from_others(BallanceObjectType.SKYLAYER)
|
return BallanceObjectInfo.create_from_others(BallanceObjectType.SKYLAYER)
|
||||||
|
|
||||||
tr_text: str = bpy.app.translations.pgettext_rpt(
|
tr_text: str = bpy.app.translations.pgettext_rpt(
|
||||||
"Name match lost.", 'BBP/UTIL_naming_convention.ImengyuConvention')
|
"Name match lost.", 'BBP/UTIL_naming_convension.ImengyuConvention')
|
||||||
if reporter: reporter.add_error(tr_text)
|
if reporter: reporter.add_error(tr_text)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@ -653,7 +653,7 @@ class ImengyuConvention():
|
|||||||
|
|
||||||
case _:
|
case _:
|
||||||
tr_text: str = bpy.app.translations.pgettext_rpt(
|
tr_text: str = bpy.app.translations.pgettext_rpt(
|
||||||
"No matched info.", 'BBP/UTIL_naming_convention.ImengyuConvention')
|
"No matched info.", 'BBP/UTIL_naming_convension.ImengyuConvention')
|
||||||
if reporter: reporter.add_error(tr_text)
|
if reporter: reporter.add_error(tr_text)
|
||||||
return None
|
return None
|
||||||
|
|
@ -1,6 +1,6 @@
|
|||||||
import bpy, bmesh, mathutils, math
|
import bpy, bmesh, mathutils, math
|
||||||
import typing
|
import typing
|
||||||
from . import UTIL_functions, UTIL_naming_convention
|
from . import UTIL_functions, UTIL_naming_convension
|
||||||
from . import PROP_bme_material
|
from . import PROP_bme_material
|
||||||
|
|
||||||
#region BMesh Operations Helper
|
#region BMesh Operations Helper
|
||||||
@ -132,16 +132,16 @@ def rail_creator_wrapper(fct_poly_cret: typing.Callable[[bmesh.types.BMesh], Non
|
|||||||
|
|
||||||
# create object and assoc with it
|
# create object and assoc with it
|
||||||
# create info first
|
# create info first
|
||||||
rail_info: UTIL_naming_convention.BallanceObjectInfo = UTIL_naming_convention.BallanceObjectInfo.create_from_others(
|
rail_info: UTIL_naming_convension.BallanceObjectInfo = UTIL_naming_convension.BallanceObjectInfo.create_from_others(
|
||||||
UTIL_naming_convention.BallanceObjectType.RAIL
|
UTIL_naming_convension.BallanceObjectType.RAIL
|
||||||
)
|
)
|
||||||
# then get object name
|
# then get object name
|
||||||
rail_name: str | None = UTIL_naming_convention.YYCToolchainConvention.set_to_name(rail_info, None)
|
rail_name: str | None = UTIL_naming_convension.YYCToolchainConvention.set_to_name(rail_info, None)
|
||||||
if rail_name is None: raise UTIL_functions.BBPException('impossible null name')
|
if rail_name is None: raise UTIL_functions.BBPException('impossible null name')
|
||||||
# create object by name
|
# create object by name
|
||||||
obj: bpy.types.Object = bpy.data.objects.new(rail_name, mesh)
|
obj: bpy.types.Object = bpy.data.objects.new(rail_name, mesh)
|
||||||
# assign virtools groups
|
# assign virtools groups
|
||||||
UTIL_naming_convention.VirtoolsGroupConvention.set_to_object(obj, rail_info, None)
|
UTIL_naming_convension.VirtoolsGroupConvention.set_to_object(obj, rail_info, None)
|
||||||
|
|
||||||
# move to cursor
|
# move to cursor
|
||||||
UTIL_functions.add_into_scene_and_move_to_cursor(obj)
|
UTIL_functions.add_into_scene_and_move_to_cursor(obj)
|
||||||
|
@ -48,29 +48,21 @@ import bpy
|
|||||||
# - If we use `bpy.app.translations.pgettext` with other non-Blender functions, such as `print`.
|
# - If we use `bpy.app.translations.pgettext` with other non-Blender functions, such as `print`.
|
||||||
# * Use it as a normal function.
|
# * Use it as a normal function.
|
||||||
#
|
#
|
||||||
# All translation annotation are started with `I18N:`
|
# All translation annotation are started with `TR:`
|
||||||
#
|
#
|
||||||
|
|
||||||
# The universal translation context prefix for BBP_NG plugin.
|
# The universal translation context prefix for BBP_NG plugin.
|
||||||
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 = f'{CTX_BBP}/BME'
|
CTX_BBP_BME: str = CTX_BBP + '/BME'
|
||||||
CTX_BBP_BME_CATEGORY: str = f'{CTX_BBP_BME}/Category'
|
def build_prototype_showcase_context(identifier: str) -> str:
|
||||||
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 f'{CTX_BBP_BME_PROTOTYPE}/{identifier}'
|
return CTX_BBP_BME + '/' + 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.
|
||||||
@ -78,7 +70,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 f'{CTX_BBP_BME_PROTOTYPE}/{identifier}/[{cfg_index}]'
|
return CTX_BBP_BME + f'/{identifier}/[{cfg_index}]'
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
@ -235,28 +235,21 @@ _g_Annotation: dict[type, dict[int, EnumAnnotation]] = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_TRawEnum = typing.TypeVar('_TRawEnum', bound = enum.Enum)
|
class EnumPropHelper(UTIL_functions.EnumPropHelper):
|
||||||
|
|
||||||
class EnumPropHelper(UTIL_functions.EnumPropHelper[_TRawEnum]):
|
|
||||||
"""
|
"""
|
||||||
Virtools type specified Blender EnumProp helper.
|
Virtools type specified Blender EnumProp helper.
|
||||||
"""
|
"""
|
||||||
__mAnnotationDict: dict[int, EnumAnnotation]
|
__mAnnotationDict: dict[int, EnumAnnotation]
|
||||||
__mEnumTy: type[_TRawEnum]
|
__mEnumTy: type[enum.Enum]
|
||||||
|
|
||||||
def __init__(self, ty: type[_TRawEnum]):
|
def __init__(self, ty: type[enum.Enum]):
|
||||||
# set enum type and annotation ref first
|
# set enum type and annotation ref first
|
||||||
self.__mEnumTy = ty
|
self.__mEnumTy = ty
|
||||||
self.__mAnnotationDict = _g_Annotation[ty]
|
self.__mAnnotationDict = _g_Annotation[ty]
|
||||||
|
# init parent data
|
||||||
# YYC MARK:
|
UTIL_functions.EnumPropHelper.__init__(
|
||||||
# It seems that Pylance has bad generic analyse ability in there.
|
self,
|
||||||
# It can not deduce the correct generic type in lambda.
|
self.__mEnumTy, # enum.Enum it self is iterable
|
||||||
# I gave up.
|
|
||||||
|
|
||||||
# Init parent data
|
|
||||||
super().__init__(
|
|
||||||
self.__mEnumTy, # enum.Enum its self is iterable
|
|
||||||
lambda x: str(x.value), # convert enum.Enum's value to string
|
lambda x: str(x.value), # convert enum.Enum's value to string
|
||||||
lambda x: self.__mEnumTy(int(x)), # use stored enum type and int() to get enum member
|
lambda x: self.__mEnumTy(int(x)), # use stored enum type and int() to get enum member
|
||||||
lambda x: self.__mAnnotationDict[x.value].mDisplayName,
|
lambda x: self.__mAnnotationDict[x.value].mDisplayName,
|
||||||
@ -272,11 +265,11 @@ def virtools_name_regulator(name: str | None) -> str:
|
|||||||
if name: return name
|
if name: return name
|
||||||
else: return bpy.app.translations.pgettext_data('annoymous', 'BME/UTIL_virtools_types.virtools_name_regulator()')
|
else: return bpy.app.translations.pgettext_data('annoymous', 'BME/UTIL_virtools_types.virtools_name_regulator()')
|
||||||
|
|
||||||
# YYC MARK:
|
## Default Encoding for PyBMap
|
||||||
# There are default encodings for PyBMap. We support Western European and Simplified Chinese in default.
|
# Use semicolon split each encodings. Support Western European and Simplified Chinese in default.
|
||||||
# Since LibCmo 0.2, the encoding name of LibCmo become universal encoding which is platfoorm independent.
|
# Since LibCmo 0.2, the encoding name of LibCmo become universal encoding which is platfoorm independent.
|
||||||
# So no need set it according to different platform.
|
# So no need set it according to different platform.
|
||||||
# Use universal encoding name (like Python).
|
# Use universal encoding name (like Python).
|
||||||
g_PyBMapDefaultEncodings: tuple[str, ...] = (
|
g_PyBMapDefaultEncodings: tuple[str, ...] = (
|
||||||
'cp1252',
|
'cp1252',
|
||||||
'gbk'
|
'gbk'
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
#region Import and Reload
|
#region Reload and Import
|
||||||
|
|
||||||
# import core lib
|
# import core lib
|
||||||
import bpy
|
import bpy
|
||||||
import typing, enum
|
import typing, collections
|
||||||
|
|
||||||
# reload if needed
|
# reload if needed
|
||||||
# TODO: finish reload feature if needed.
|
# TODO: finish reload feature if needed.
|
||||||
@ -10,6 +10,8 @@ import typing, enum
|
|||||||
if "bpy" in locals():
|
if "bpy" in locals():
|
||||||
import importlib
|
import importlib
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
# we must load icons manager first
|
# we must load icons manager first
|
||||||
# and register it
|
# and register it
|
||||||
from . import UTIL_icons_manager
|
from . import UTIL_icons_manager
|
||||||
@ -21,147 +23,13 @@ from . import PROP_preferences, PROP_ptrprop_resolver, PROP_virtools_material, P
|
|||||||
from . import PROP_ballance_element, PROP_bme_material, PROP_ballance_map_info
|
from . import PROP_ballance_element, PROP_bme_material, PROP_ballance_map_info
|
||||||
from . import OP_IMPORT_bmfile, OP_EXPORT_bmfile, OP_IMPORT_virtools, OP_EXPORT_virtools
|
from . import OP_IMPORT_bmfile, OP_EXPORT_bmfile, OP_IMPORT_virtools, OP_EXPORT_virtools
|
||||||
from . import OP_UV_flatten_uv, OP_UV_rail_uv
|
from . import OP_UV_flatten_uv, OP_UV_rail_uv
|
||||||
from . import OP_MTL_fix_materials
|
from . import OP_MTL_fix_material
|
||||||
from . import OP_ADDS_component, OP_ADDS_bme, OP_ADDS_rail
|
from . import OP_ADDS_component, OP_ADDS_bme, OP_ADDS_rail
|
||||||
from . import OP_OBJECT_legacy_align, OP_OBJECT_virtools_group, OP_OBJECT_snoop_group_then_to_mesh, OP_OBJECT_naming_convention, OP_OBJECT_game_view
|
from . import OP_OBJECT_legacy_align, OP_OBJECT_virtools_group, OP_OBJECT_snoop_group_then_to_mesh, OP_OBJECT_naming_convention
|
||||||
|
|
||||||
#endregion
|
#region Menu
|
||||||
|
|
||||||
#region Menu and Sidebar Panel
|
# ===== Menu Defines =====
|
||||||
|
|
||||||
#region Ballance Adder Menu and Panel
|
|
||||||
|
|
||||||
class DrawTarget(enum.IntEnum):
|
|
||||||
BldMenu = enum.auto()
|
|
||||||
BldPanel = enum.auto()
|
|
||||||
|
|
||||||
def reuse_create_layout(layout: bpy.types.UILayout, target: DrawTarget) -> bpy.types.UILayout:
|
|
||||||
# If we are draw for Panel, we need use Grid to use space enough.
|
|
||||||
match target:
|
|
||||||
case DrawTarget.BldMenu:
|
|
||||||
return layout
|
|
||||||
case DrawTarget.BldPanel:
|
|
||||||
return layout.grid_flow(even_columns=True, even_rows=True)
|
|
||||||
|
|
||||||
def reuse_draw_add_bme(layout: bpy.types.UILayout, target: DrawTarget):
|
|
||||||
# Draw operators.
|
|
||||||
OP_ADDS_bme.BBP_OT_add_bme_struct.draw_blc_menu(reuse_create_layout(layout, target))
|
|
||||||
|
|
||||||
def reuse_draw_add_rail(layout: bpy.types.UILayout, target: DrawTarget):
|
|
||||||
layout.label(text="Sections", icon='MESH_CIRCLE', text_ctxt='BBP/__init__.reuse_draw_add_rail()')
|
|
||||||
sublayout = reuse_create_layout(layout, target)
|
|
||||||
sublayout.operator(OP_ADDS_rail.BBP_OT_add_rail_section.bl_idname)
|
|
||||||
sublayout.operator(OP_ADDS_rail.BBP_OT_add_transition_section.bl_idname)
|
|
||||||
|
|
||||||
layout.separator()
|
|
||||||
layout.label(text="Straight Rails", icon='IPO_CONSTANT', text_ctxt='BBP/__init__.reuse_draw_add_rail()')
|
|
||||||
sublayout = reuse_create_layout(layout, target)
|
|
||||||
sublayout.operator(OP_ADDS_rail.BBP_OT_add_straight_rail.bl_idname)
|
|
||||||
sublayout.operator(OP_ADDS_rail.BBP_OT_add_transition_rail.bl_idname)
|
|
||||||
sublayout.operator(OP_ADDS_rail.BBP_OT_add_side_rail.bl_idname)
|
|
||||||
|
|
||||||
layout.separator()
|
|
||||||
layout.label(text="Curve Rails", icon='MOD_SCREW', text_ctxt='BBP/__init__.reuse_draw_add_rail()')
|
|
||||||
sublayout = reuse_create_layout(layout, target)
|
|
||||||
sublayout.operator(OP_ADDS_rail.BBP_OT_add_arc_rail.bl_idname)
|
|
||||||
sublayout.operator(OP_ADDS_rail.BBP_OT_add_spiral_rail.bl_idname)
|
|
||||||
sublayout.operator(OP_ADDS_rail.BBP_OT_add_side_spiral_rail.bl_idname)
|
|
||||||
|
|
||||||
def reuse_draw_add_component(layout: bpy.types.UILayout, target: DrawTarget):
|
|
||||||
# We only use Grid for basic components
|
|
||||||
layout.label(text="Basic Components", text_ctxt='BBP/__init__.reuse_draw_add_component()')
|
|
||||||
OP_ADDS_component.BBP_OT_add_component.draw_blc_menu(reuse_create_layout(layout, target))
|
|
||||||
|
|
||||||
layout.separator()
|
|
||||||
layout.label(text="Nong Components", text_ctxt='BBP/__init__.reuse_draw_add_component()')
|
|
||||||
sublayout = reuse_create_layout(layout, target)
|
|
||||||
OP_ADDS_component.BBP_OT_add_nong_extra_point.draw_blc_menu(sublayout)
|
|
||||||
OP_ADDS_component.BBP_OT_add_nong_ventilator.draw_blc_menu(sublayout)
|
|
||||||
|
|
||||||
layout.separator()
|
|
||||||
layout.label(text="Series Components", text_ctxt='BBP/__init__.reuse_draw_add_component()')
|
|
||||||
sublayout = reuse_create_layout(layout, target)
|
|
||||||
OP_ADDS_component.BBP_OT_add_tilting_block_series.draw_blc_menu(sublayout)
|
|
||||||
OP_ADDS_component.BBP_OT_add_swing_series.draw_blc_menu(sublayout)
|
|
||||||
OP_ADDS_component.BBP_OT_add_ventilator_series.draw_blc_menu(sublayout)
|
|
||||||
|
|
||||||
layout.separator()
|
|
||||||
layout.label(text="Components Pair", text_ctxt='BBP/__init__.reuse_draw_add_component()')
|
|
||||||
sublayout = reuse_create_layout(layout, target)
|
|
||||||
OP_ADDS_component.BBP_OT_add_sector_component_pair.draw_blc_menu(sublayout)
|
|
||||||
|
|
||||||
class BBP_MT_AddBmeMenu(bpy.types.Menu):
|
|
||||||
"""Add Ballance Floor"""
|
|
||||||
bl_idname = "BBP_MT_AddBmeMenu"
|
|
||||||
bl_label = "Floors"
|
|
||||||
bl_translation_context = 'BBP_MT_AddBmeMenu'
|
|
||||||
|
|
||||||
def draw(self, context):
|
|
||||||
reuse_draw_add_bme(self.layout, DrawTarget.BldMenu)
|
|
||||||
|
|
||||||
class BBP_MT_AddRailMenu(bpy.types.Menu):
|
|
||||||
"""Add Ballance Rail"""
|
|
||||||
bl_idname = "BBP_MT_AddRailMenu"
|
|
||||||
bl_label = "Rails"
|
|
||||||
bl_translation_context = 'BBP_MT_AddRailMenu'
|
|
||||||
|
|
||||||
def draw(self, context):
|
|
||||||
reuse_draw_add_rail(self.layout, DrawTarget.BldMenu)
|
|
||||||
|
|
||||||
class BBP_MT_AddComponentMenu(bpy.types.Menu):
|
|
||||||
"""Add Ballance Component"""
|
|
||||||
bl_idname = "BBP_MT_AddComponentsMenu"
|
|
||||||
bl_label = "Components"
|
|
||||||
bl_translation_context = 'BBP_MT_AddComponentsMenu'
|
|
||||||
|
|
||||||
def draw(self, context):
|
|
||||||
reuse_draw_add_component(self.layout, DrawTarget.BldMenu)
|
|
||||||
|
|
||||||
class BBP_PT_SidebarAddBmePanel(bpy.types.Panel):
|
|
||||||
"""Add Ballance Floor"""
|
|
||||||
bl_label = "Floors"
|
|
||||||
bl_idname = "BBP_PT_SidebarAddBmePanel"
|
|
||||||
bl_space_type = 'VIEW_3D'
|
|
||||||
bl_region_type = 'UI'
|
|
||||||
bl_context = "objectmode"
|
|
||||||
bl_category = 'Ballance'
|
|
||||||
bl_options = {'DEFAULT_CLOSED'}
|
|
||||||
bl_translation_context = 'BBP_PT_SidebarAddBmePanel'
|
|
||||||
|
|
||||||
def draw(self, context):
|
|
||||||
reuse_draw_add_bme(self.layout, DrawTarget.BldPanel)
|
|
||||||
|
|
||||||
class BBP_PT_SidebarAddRailPanel(bpy.types.Panel):
|
|
||||||
"""Add Ballance Rail"""
|
|
||||||
bl_label = "Rails"
|
|
||||||
bl_idname = "BBP_PT_SidebarAddRailPanel"
|
|
||||||
bl_space_type = 'VIEW_3D'
|
|
||||||
bl_region_type = 'UI'
|
|
||||||
bl_context = "objectmode"
|
|
||||||
bl_category = 'Ballance'
|
|
||||||
bl_options = {'DEFAULT_CLOSED'}
|
|
||||||
bl_translation_context = 'BBP_PT_SidebarAddRailPanel'
|
|
||||||
|
|
||||||
def draw(self, context):
|
|
||||||
reuse_draw_add_rail(self.layout, DrawTarget.BldPanel)
|
|
||||||
|
|
||||||
class BBP_PT_SidebarAddComponentPanel(bpy.types.Panel):
|
|
||||||
"""Add Ballance Component"""
|
|
||||||
bl_label = "Components"
|
|
||||||
bl_idname = "BBP_PT_SidebarAddComponentPanel"
|
|
||||||
bl_space_type = 'VIEW_3D'
|
|
||||||
bl_region_type = 'UI'
|
|
||||||
bl_context = "objectmode"
|
|
||||||
bl_category = 'Ballance'
|
|
||||||
bl_options = {'DEFAULT_CLOSED'}
|
|
||||||
bl_translation_context = 'BBP_PT_SidebarAddComponentPanel'
|
|
||||||
|
|
||||||
def draw(self, context):
|
|
||||||
reuse_draw_add_component(self.layout, DrawTarget.BldPanel)
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Other Menu
|
|
||||||
|
|
||||||
class BBP_MT_View3DMenu(bpy.types.Menu):
|
class BBP_MT_View3DMenu(bpy.types.Menu):
|
||||||
"""Ballance 3D related operators"""
|
"""Ballance 3D related operators"""
|
||||||
@ -170,7 +38,7 @@ class BBP_MT_View3DMenu(bpy.types.Menu):
|
|||||||
bl_translation_context = 'BBP_MT_View3DMenu'
|
bl_translation_context = 'BBP_MT_View3DMenu'
|
||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
layout = typing.cast(bpy.types.UILayout, self.layout)
|
layout = self.layout
|
||||||
layout.label(text='UV', icon='UV', text_ctxt='BBP_MT_View3DMenu/draw')
|
layout.label(text='UV', icon='UV', text_ctxt='BBP_MT_View3DMenu/draw')
|
||||||
layout.operator(OP_UV_flatten_uv.BBP_OT_flatten_uv.bl_idname)
|
layout.operator(OP_UV_flatten_uv.BBP_OT_flatten_uv.bl_idname)
|
||||||
layout.operator(OP_UV_rail_uv.BBP_OT_rail_uv.bl_idname)
|
layout.operator(OP_UV_rail_uv.BBP_OT_rail_uv.bl_idname)
|
||||||
@ -178,21 +46,77 @@ class BBP_MT_View3DMenu(bpy.types.Menu):
|
|||||||
layout.label(text='Align', icon='SNAP_ON', text_ctxt='BBP_MT_View3DMenu/draw')
|
layout.label(text='Align', icon='SNAP_ON', text_ctxt='BBP_MT_View3DMenu/draw')
|
||||||
layout.operator(OP_OBJECT_legacy_align.BBP_OT_legacy_align.bl_idname)
|
layout.operator(OP_OBJECT_legacy_align.BBP_OT_legacy_align.bl_idname)
|
||||||
layout.separator()
|
layout.separator()
|
||||||
layout.label(text='Camera', icon='CAMERA_DATA', text_ctxt='BBP_MT_View3DMenu/draw')
|
|
||||||
layout.operator(OP_OBJECT_game_view.BBP_OT_game_resolution.bl_idname)
|
|
||||||
layout.operator(OP_OBJECT_game_view.BBP_OT_game_camera.bl_idname)
|
|
||||||
layout.separator()
|
|
||||||
layout.label(text='Select', icon='SELECT_SET', text_ctxt='BBP_MT_View3DMenu/draw')
|
layout.label(text='Select', icon='SELECT_SET', text_ctxt='BBP_MT_View3DMenu/draw')
|
||||||
layout.operator(OP_OBJECT_virtools_group.BBP_OT_select_object_by_virtools_group.bl_idname)
|
layout.operator(OP_OBJECT_virtools_group.BBP_OT_select_object_by_virtools_group.bl_idname)
|
||||||
layout.separator()
|
layout.separator()
|
||||||
layout.label(text='Material', icon='MATERIAL', text_ctxt='BBP_MT_View3DMenu/draw')
|
layout.label(text='Material', icon='MATERIAL', text_ctxt='BBP_MT_View3DMenu/draw')
|
||||||
layout.operator(OP_MTL_fix_materials.BBP_OT_fix_all_materials.bl_idname)
|
layout.operator(OP_MTL_fix_material.BBP_OT_fix_all_material.bl_idname)
|
||||||
|
|
||||||
#endregion
|
class BBP_MT_AddBmeMenu(bpy.types.Menu):
|
||||||
|
"""Add Ballance Floor"""
|
||||||
|
bl_idname = "BBP_MT_AddBmeMenu"
|
||||||
|
bl_label = "Floors"
|
||||||
|
bl_translation_context = 'BBP_MT_AddBmeMenu'
|
||||||
|
|
||||||
#region Menu Drawer
|
def draw(self, context):
|
||||||
|
layout = self.layout
|
||||||
|
OP_ADDS_bme.BBP_OT_add_bme_struct.draw_blc_menu(layout)
|
||||||
|
|
||||||
|
class BBP_MT_AddRailMenu(bpy.types.Menu):
|
||||||
|
"""Add Ballance Rail"""
|
||||||
|
bl_idname = "BBP_MT_AddRailMenu"
|
||||||
|
bl_label = "Rails"
|
||||||
|
bl_translation_context = 'BBP_MT_AddRailMenu'
|
||||||
|
|
||||||
TFctMenuDrawer = typing.Callable[[typing.Any, typing.Any], None]
|
def draw(self, context):
|
||||||
|
layout = self.layout
|
||||||
|
|
||||||
|
layout.label(text="Sections", icon='MESH_CIRCLE', text_ctxt='BBP_MT_AddRailMenu/draw')
|
||||||
|
layout.operator(OP_ADDS_rail.BBP_OT_add_rail_section.bl_idname)
|
||||||
|
layout.operator(OP_ADDS_rail.BBP_OT_add_transition_section.bl_idname)
|
||||||
|
|
||||||
|
layout.separator()
|
||||||
|
layout.label(text="Straight Rails", icon='IPO_CONSTANT', text_ctxt='BBP_MT_AddRailMenu/draw')
|
||||||
|
layout.operator(OP_ADDS_rail.BBP_OT_add_straight_rail.bl_idname)
|
||||||
|
layout.operator(OP_ADDS_rail.BBP_OT_add_transition_rail.bl_idname)
|
||||||
|
layout.operator(OP_ADDS_rail.BBP_OT_add_side_rail.bl_idname)
|
||||||
|
|
||||||
|
layout.separator()
|
||||||
|
layout.label(text="Curve Rails", icon='MOD_SCREW', text_ctxt='BBP_MT_AddRailMenu/draw')
|
||||||
|
layout.operator(OP_ADDS_rail.BBP_OT_add_arc_rail.bl_idname)
|
||||||
|
layout.operator(OP_ADDS_rail.BBP_OT_add_spiral_rail.bl_idname)
|
||||||
|
layout.operator(OP_ADDS_rail.BBP_OT_add_side_spiral_rail.bl_idname)
|
||||||
|
|
||||||
|
class BBP_MT_AddComponentsMenu(bpy.types.Menu):
|
||||||
|
"""Add Ballance Component"""
|
||||||
|
bl_idname = "BBP_MT_AddComponentsMenu"
|
||||||
|
bl_label = "Components"
|
||||||
|
bl_translation_context = 'BBP_MT_AddComponentsMenu'
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
layout = self.layout
|
||||||
|
|
||||||
|
layout.label(text="Basic Components", text_ctxt='BBP_MT_AddComponentsMenu/draw')
|
||||||
|
OP_ADDS_component.BBP_OT_add_component.draw_blc_menu(layout)
|
||||||
|
|
||||||
|
layout.separator()
|
||||||
|
layout.label(text="Nong Components", text_ctxt='BBP_MT_AddComponentsMenu/draw')
|
||||||
|
OP_ADDS_component.BBP_OT_add_nong_extra_point.draw_blc_menu(layout)
|
||||||
|
OP_ADDS_component.BBP_OT_add_nong_ventilator.draw_blc_menu(layout)
|
||||||
|
|
||||||
|
layout.separator()
|
||||||
|
layout.label(text="Series Components", text_ctxt='BBP_MT_AddComponentsMenu/draw')
|
||||||
|
OP_ADDS_component.BBP_OT_add_tilting_block_series.draw_blc_menu(layout)
|
||||||
|
OP_ADDS_component.BBP_OT_add_swing_series.draw_blc_menu(layout)
|
||||||
|
OP_ADDS_component.BBP_OT_add_ventilator_series.draw_blc_menu(layout)
|
||||||
|
|
||||||
|
layout.separator()
|
||||||
|
layout.label(text="Components Pair", text_ctxt='BBP_MT_AddComponentsMenu/draw')
|
||||||
|
OP_ADDS_component.BBP_OT_add_sector_component_pair.draw_blc_menu(layout)
|
||||||
|
|
||||||
|
# ===== Menu Drawer =====
|
||||||
|
|
||||||
|
MenuDrawer_t = typing.Callable[[typing.Any, typing.Any], None]
|
||||||
|
|
||||||
def menu_drawer_import(self, context) -> None:
|
def menu_drawer_import(self, context) -> None:
|
||||||
layout: bpy.types.UILayout = self.layout
|
layout: bpy.types.UILayout = self.layout
|
||||||
@ -230,17 +154,16 @@ def menu_drawer_add(self, context) -> None:
|
|||||||
layout.label(text="Ballance", text_ctxt='BBP/__init__.menu_drawer_add()')
|
layout.label(text="Ballance", text_ctxt='BBP/__init__.menu_drawer_add()')
|
||||||
layout.menu(BBP_MT_AddBmeMenu.bl_idname, icon='MESH_CUBE')
|
layout.menu(BBP_MT_AddBmeMenu.bl_idname, icon='MESH_CUBE')
|
||||||
layout.menu(BBP_MT_AddRailMenu.bl_idname, icon='MESH_CIRCLE')
|
layout.menu(BBP_MT_AddRailMenu.bl_idname, icon='MESH_CIRCLE')
|
||||||
layout.menu(BBP_MT_AddComponentMenu.bl_idname, icon='MESH_ICOSPHERE')
|
layout.menu(BBP_MT_AddComponentsMenu.bl_idname, icon='MESH_ICOSPHERE')
|
||||||
|
|
||||||
def menu_drawer_grouping(self, context) -> None:
|
def menu_drawer_grouping(self, context) -> None:
|
||||||
layout: bpy.types.UILayout = self.layout
|
layout: bpy.types.UILayout = self.layout
|
||||||
layout.separator()
|
layout.separator()
|
||||||
|
|
||||||
# YYC MARK:
|
# NOTE: because outline context may change operator context
|
||||||
# Because outline context change operator context into EXEC_*,
|
|
||||||
# so it will cause no popup window when click operator in outline.
|
# so it will cause no popup window when click operator in outline.
|
||||||
# Thus we create a sub layout and set its operator context as 'INVOKE_DEFAULT',
|
# thus we create a sub layout and set its operator context as 'INVOKE_DEFAULT'
|
||||||
# so that all operators can pop up normally.
|
# thus, all operators can pop up normally.
|
||||||
col = layout.column()
|
col = layout.column()
|
||||||
col.operator_context = 'INVOKE_DEFAULT'
|
col.operator_context = 'INVOKE_DEFAULT'
|
||||||
|
|
||||||
@ -265,8 +188,7 @@ def menu_drawer_naming_convention(self, context) -> None:
|
|||||||
layout: bpy.types.UILayout = self.layout
|
layout: bpy.types.UILayout = self.layout
|
||||||
layout.separator()
|
layout.separator()
|
||||||
|
|
||||||
# YYC MARK:
|
# same reason in `menu_drawer_grouping()``
|
||||||
# Same reason for changing operator context introduced in `menu_drawer_grouping()`
|
|
||||||
col = layout.column()
|
col = layout.column()
|
||||||
col.operator_context = 'INVOKE_DEFAULT'
|
col.operator_context = 'INVOKE_DEFAULT'
|
||||||
|
|
||||||
@ -277,43 +199,37 @@ def menu_drawer_naming_convention(self, context) -> None:
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Register and Unregister.
|
#region Register and Unregister.
|
||||||
|
|
||||||
g_BldClasses: tuple[typing.Any, ...] = (
|
g_BldClasses: tuple[typing.Any, ...] = (
|
||||||
BBP_MT_View3DMenu,
|
BBP_MT_View3DMenu,
|
||||||
BBP_MT_AddBmeMenu,
|
BBP_MT_AddBmeMenu,
|
||||||
BBP_MT_AddRailMenu,
|
BBP_MT_AddRailMenu,
|
||||||
BBP_MT_AddComponentMenu,
|
BBP_MT_AddComponentsMenu
|
||||||
|
|
||||||
BBP_PT_SidebarAddBmePanel,
|
|
||||||
BBP_PT_SidebarAddRailPanel,
|
|
||||||
BBP_PT_SidebarAddComponentPanel,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
class MenuEntry():
|
class MenuEntry():
|
||||||
mContainerMenu: bpy.types.Menu
|
mContainerMenu: bpy.types.Menu
|
||||||
mIsAppend: bool
|
mIsAppend: bool
|
||||||
mMenuDrawer: TFctMenuDrawer
|
mMenuDrawer: MenuDrawer_t
|
||||||
def __init__(self, cont: bpy.types.Menu, is_append: bool, menu_func: TFctMenuDrawer):
|
def __init__(self, cont: bpy.types.Menu, is_append: bool, menu_func: MenuDrawer_t):
|
||||||
self.mContainerMenu = cont
|
self.mContainerMenu = cont
|
||||||
self.mIsAppend = is_append
|
self.mIsAppend = is_append
|
||||||
self.mMenuDrawer = menu_func
|
self.mMenuDrawer = menu_func
|
||||||
|
|
||||||
g_BldMenus: tuple[MenuEntry, ...] = (
|
g_BldMenus: tuple[MenuEntry, ...] = (
|
||||||
MenuEntry(bpy.types.VIEW3D_MT_editor_menus, False, menu_drawer_view3d),
|
MenuEntry(bpy.types.VIEW3D_MT_editor_menus, False, menu_drawer_view3d),
|
||||||
MenuEntry(bpy.types.TOPBAR_MT_file_import, True, menu_drawer_import),
|
MenuEntry(bpy.types.TOPBAR_MT_file_import, True, menu_drawer_import),
|
||||||
MenuEntry(bpy.types.TOPBAR_MT_file_export, True, menu_drawer_export),
|
MenuEntry(bpy.types.TOPBAR_MT_file_export, True, menu_drawer_export),
|
||||||
MenuEntry(bpy.types.VIEW3D_MT_add, True, menu_drawer_add),
|
MenuEntry(bpy.types.VIEW3D_MT_add, True, menu_drawer_add),
|
||||||
|
|
||||||
MenuEntry(bpy.types.VIEW3D_MT_object_context_menu, True, menu_drawer_snoop_then_conv),
|
MenuEntry(bpy.types.VIEW3D_MT_object_context_menu, True, menu_drawer_snoop_then_conv),
|
||||||
|
|
||||||
# Register this twice (for 2 menus respectively)
|
# register double (for 2 menus)
|
||||||
MenuEntry(bpy.types.VIEW3D_MT_object_context_menu, True, menu_drawer_grouping),
|
MenuEntry(bpy.types.VIEW3D_MT_object_context_menu, True, menu_drawer_grouping),
|
||||||
MenuEntry(bpy.types.OUTLINER_MT_object, True, menu_drawer_grouping),
|
MenuEntry(bpy.types.OUTLINER_MT_object, True, menu_drawer_grouping),
|
||||||
|
|
||||||
MenuEntry(bpy.types.OUTLINER_MT_collection, True, menu_drawer_naming_convention),
|
MenuEntry(bpy.types.OUTLINER_MT_collection, True, menu_drawer_naming_convention),
|
||||||
)
|
)
|
||||||
|
|
||||||
def register() -> None:
|
def register() -> None:
|
||||||
@ -340,7 +256,7 @@ def register() -> None:
|
|||||||
OP_UV_rail_uv.register()
|
OP_UV_rail_uv.register()
|
||||||
OP_UV_flatten_uv.register()
|
OP_UV_flatten_uv.register()
|
||||||
|
|
||||||
OP_MTL_fix_materials.register()
|
OP_MTL_fix_material.register()
|
||||||
|
|
||||||
OP_ADDS_component.register()
|
OP_ADDS_component.register()
|
||||||
OP_ADDS_bme.register()
|
OP_ADDS_bme.register()
|
||||||
@ -350,7 +266,6 @@ def register() -> None:
|
|||||||
OP_OBJECT_virtools_group.register()
|
OP_OBJECT_virtools_group.register()
|
||||||
OP_OBJECT_snoop_group_then_to_mesh.register()
|
OP_OBJECT_snoop_group_then_to_mesh.register()
|
||||||
OP_OBJECT_naming_convention.register()
|
OP_OBJECT_naming_convention.register()
|
||||||
OP_OBJECT_game_view.register()
|
|
||||||
|
|
||||||
# register other classes
|
# register other classes
|
||||||
for cls in g_BldClasses:
|
for cls in g_BldClasses:
|
||||||
@ -373,7 +288,6 @@ def unregister() -> None:
|
|||||||
bpy.utils.unregister_class(cls)
|
bpy.utils.unregister_class(cls)
|
||||||
|
|
||||||
# unregister modules
|
# unregister modules
|
||||||
OP_OBJECT_game_view.unregister()
|
|
||||||
OP_OBJECT_naming_convention.unregister()
|
OP_OBJECT_naming_convention.unregister()
|
||||||
OP_OBJECT_snoop_group_then_to_mesh.unregister()
|
OP_OBJECT_snoop_group_then_to_mesh.unregister()
|
||||||
OP_OBJECT_virtools_group.unregister()
|
OP_OBJECT_virtools_group.unregister()
|
||||||
@ -383,7 +297,7 @@ def unregister() -> None:
|
|||||||
OP_ADDS_bme.unregister()
|
OP_ADDS_bme.unregister()
|
||||||
OP_ADDS_component.unregister()
|
OP_ADDS_component.unregister()
|
||||||
|
|
||||||
OP_MTL_fix_materials.unregister()
|
OP_MTL_fix_material.unregister()
|
||||||
|
|
||||||
OP_UV_flatten_uv.unregister()
|
OP_UV_flatten_uv.unregister()
|
||||||
OP_UV_rail_uv.unregister()
|
OP_UV_rail_uv.unregister()
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Full context are copied from https://docs.blender.org/manual/en/latest/advanced/extensions/getting_started.html
|
# Full context are copied from https://docs.blender.org/manual/en/dev/extensions/getting_started.html
|
||||||
# Please note any update of this manifest
|
# Please note any update of this manifest
|
||||||
|
|
||||||
schema_version = "1.0.0"
|
schema_version = "1.0.0"
|
||||||
@ -6,7 +6,7 @@ schema_version = "1.0.0"
|
|||||||
# Example of manifest file for a Blender extension
|
# Example of manifest file for a Blender extension
|
||||||
# Change the values according to your extension
|
# Change the values according to your extension
|
||||||
id = "bbp_ng"
|
id = "bbp_ng"
|
||||||
version = "4.3.0"
|
version = "4.2.1"
|
||||||
name = "Ballance Blender Plugin"
|
name = "Ballance Blender Plugin"
|
||||||
tagline = "The specialized add-on served for creating game map of Ballance"
|
tagline = "The specialized add-on served for creating game map of Ballance"
|
||||||
maintainer = "yyc12345 <yyc12321@outlook.com>"
|
maintainer = "yyc12345 <yyc12321@outlook.com>"
|
||||||
@ -20,7 +20,7 @@ website = "https://github.com/yyc12345/BallanceBlenderHelper"
|
|||||||
# https://docs.blender.org/manual/en/dev/advanced/extensions/tags.html
|
# https://docs.blender.org/manual/en/dev/advanced/extensions/tags.html
|
||||||
tags = ["Object", "Mesh", "UV", "Import-Export"]
|
tags = ["Object", "Mesh", "UV", "Import-Export"]
|
||||||
|
|
||||||
blender_version_min = "4.5.0"
|
blender_version_min = "4.2.0"
|
||||||
# # Optional: Blender version that the extension does not support, earlier versions are supported.
|
# # Optional: Blender version that the extension does not support, earlier versions are supported.
|
||||||
# # This can be omitted and defined later on the extensions platform if an issue is found.
|
# # This can be omitted and defined later on the extensions platform if an issue is found.
|
||||||
# blender_version_max = "5.1.0"
|
# blender_version_max = "5.1.0"
|
||||||
@ -71,8 +71,12 @@ files = "Import/export Virtools file from/to disk"
|
|||||||
[build]
|
[build]
|
||||||
paths_exclude_pattern = [
|
paths_exclude_pattern = [
|
||||||
"__pycache__/", # Python runtime cache
|
"__pycache__/", # Python runtime cache
|
||||||
".gitignore", # Git Ignore File
|
|
||||||
".style.yapf", # Python code style
|
".style.yapf", # Python code style
|
||||||
"*.gitkeep", # Git directory keeper
|
"*.gitkeep", # Git directory keeper
|
||||||
|
".gitignore", # Git Ignore File
|
||||||
"*.md", # Useless document.
|
"*.md", # Useless document.
|
||||||
|
"/raw_jsons", # Raw JSONs.
|
||||||
|
"/raw_icons", # Raw Icons.
|
||||||
|
"/tools", # Assistant tools.
|
||||||
|
"/i18n", # GNU gettext Translation.
|
||||||
]
|
]
|
Before Width: | Height: | Size: 785 B After Width: | Height: | Size: 785 B |
7
bbp_ng/raw_icons/README.md
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
# Raw Icons
|
||||||
|
|
||||||
|
This folder contain all images used by this Blender plugin.
|
||||||
|
|
||||||
|
This folder should not be distributed in production because all of these files are in original size. It is pretty need too much time to load them in blender.
|
||||||
|
So we keep these high quality images here and provide a tools in `tools` folder. Builder should run script to generate thumbnails in `icons` folder.
|
||||||
|
Then this Blender plugin can work normally.
|
Before Width: | Height: | Size: 6.3 KiB After Width: | Height: | Size: 6.3 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 6.7 KiB After Width: | Height: | Size: 6.7 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
BIN
bbp_ng/raw_icons/bme/NormalFloorTerminal.png
Normal file
After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 6.2 KiB After Width: | Height: | Size: 6.2 KiB |
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 7.0 KiB |
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 58 KiB |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 39 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 108 KiB |
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 6.8 KiB |
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
BIN
bbp_ng/raw_icons/bme/SinkFloorTerminal.png
Normal file
After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 6.5 KiB After Width: | Height: | Size: 6.5 KiB |
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 7.2 KiB |
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 108 KiB |
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 39 KiB |
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
BIN
bbp_ng/raw_icons/bme/WideFloorTerminal.png
Normal file
After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 58 KiB |
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 43 KiB |
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 663 B After Width: | Height: | Size: 663 B |