Compare commits
1 Commits
v4.4-alpha
...
v4.2.1
| Author | SHA1 | Date | |
|---|---|---|---|
| f29e4e9478 |
11
.gitattributes
vendored
@@ -1,6 +1,7 @@
|
||||
# All PNG image are binary
|
||||
# all png are binary
|
||||
*.png binary
|
||||
# Element placeholder mesh should be save as binary
|
||||
*.ph binary
|
||||
# Raw json data should be binary, although i edit it manually
|
||||
assets/jsons/*.json5 binary
|
||||
# our generated mesh should be save as binary
|
||||
*.bin binary
|
||||
# the raw json data should be binary
|
||||
# although i edit it manually
|
||||
bbp_ng/raw_jsons/*.json binary
|
||||
|
||||
4
.gitignore
vendored
@@ -1,3 +1,3 @@
|
||||
## ===== Personal =====
|
||||
# Disable VSCode
|
||||
.vscode/
|
||||
# Disable distribution build folder
|
||||
redist/
|
||||
|
||||
@@ -5,9 +5,3 @@
|
||||
BBP NG, abbr **B**allance **B**lender **P**lugin **N**ext **G**eneration.
|
||||
|
||||
For an introduction to this plugin, installing it, compiling it, reporting bugs, etc., see the GitHub Page of this project: https://yyc12345.github.io/BallanceBlenderHelper
|
||||
|
||||
## Notes
|
||||
|
||||
This project is now suspended and may be still suspended eternally
|
||||
due to the lost interest and the lack of time of mine.
|
||||
In the rest of life time of this project, only easy-to-be-resolved critical bugs will be fixed.
|
||||
|
||||
@@ -5,8 +5,3 @@
|
||||
BBP NG,又名**B**allance **B**lender **P**lugin **N**ext **G**eneration(下一代Ballance Blender插件)。
|
||||
|
||||
有关此插件的介绍,安装,编译,汇报错误等,请参阅本项目的GitHub Page页面:https://yyc12345.github.io/BallanceBlenderHelper
|
||||
|
||||
## 注意事项
|
||||
|
||||
该项目的开发目前已暂停,并且由于我兴趣的丧失和时间的不足,可能会永远暂停。
|
||||
在该项目剩余的生命周期中,只会修复易于解决的致命错误。
|
||||
|
||||
|
Before Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 42 KiB |
17
bbp_ng/.gitignore
vendored
@@ -1,15 +1,20 @@
|
||||
## ===== Personal =====
|
||||
# Do not include pybmap in this repository.
|
||||
# Order user build and fetch it manually.
|
||||
pybmap/
|
||||
# Do not include PyBMap in this repository.
|
||||
# Order build fetch it manually.
|
||||
PyBMap/
|
||||
|
||||
# Disable generated assets but keep the directory hierarchy.
|
||||
# Disable generated icons and jsons but keep the directory hierarchy.
|
||||
icons/*
|
||||
!icons/.gitkeep
|
||||
jsons/*
|
||||
!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 =====
|
||||
# Byte-compiled / optimized / DLL files
|
||||
|
||||
@@ -5,7 +5,7 @@ from . import UTIL_functions, UTIL_translation, UTIL_bme
|
||||
|
||||
#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):
|
||||
prop_int: bpy.props.IntProperty(
|
||||
@@ -37,44 +37,39 @@ class BBP_OT_add_bme_struct(bpy.types.Operator):
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
bl_translation_context = 'BBP_OT_add_bme_struct'
|
||||
|
||||
# YYC MARK:
|
||||
# ===== 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,
|
||||
# but a simple OperatorProperties.
|
||||
# It mean that I can not visit the full operator, only what I can do is visit existing
|
||||
# Blender properties.
|
||||
#
|
||||
# So these is the solution about generating cache list according to the change of bme struct type.
|
||||
# First, update function will only set a "outdated" flag for operator which is a pre-registered Blender property.
|
||||
# The "outdated" flags is not showen and not saved.
|
||||
# Then call a internal cache list update function at the begin of `invoke`, `execute` and `draw`.
|
||||
# In this internal cache list updator, check "outdated" flag first, if cache is outdated, update and reset flag.
|
||||
# Otherwise do nothing.
|
||||
#
|
||||
# Reference: https://docs.blender.org/api/current/bpy.props.html#update-example
|
||||
#
|
||||
# ===== 20250131 =====
|
||||
# There is a fatal performance bug when I adding BME operator list into 3D View sidebar panels (N Menu).
|
||||
# It will cause calling my Panel's `draw` function infinityly of Panel in each render tick,
|
||||
# which calls `BBP_OT_add_bme_struct.draw_blc_menu` directly,
|
||||
# eat too much CPU and GPU resources and make the whole Blender be laggy.
|
||||
#
|
||||
# After some research, I found that if I comment the parameter `update` of the member `bme_struct_type`,
|
||||
# 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.
|
||||
## There is a compromise due to the shitty Blender design.
|
||||
#
|
||||
# The passed `self` of Blender Property update function is not the instance of operator,
|
||||
# but a simple OperatorProperties.
|
||||
# It mean that I can not visit the full operator, only what I can do is visit existing
|
||||
# Blender properties.
|
||||
#
|
||||
# So these is the solution about generating cache list according to the change of bme struct type.
|
||||
# First, update function will only set a "outdated" flag for operator which is a pre-registered Blender property.
|
||||
# The "outdated" flags is not showen and not saved.
|
||||
# Then call a internal cache list update function at the begin of `invoke`, `execute` and `draw`.
|
||||
# In this internal cache list updator, check "outdated" flag first, if cache is outdated, update and reset flag.
|
||||
# Otherwise do nothing.
|
||||
#
|
||||
# Reference: https://docs.blender.org/api/current/bpy.props.html#update-example
|
||||
|
||||
## Compromise used "outdated" flag.
|
||||
outdated_flag: bpy.props.BoolProperty(
|
||||
# TR: Property not showen should not have name and desc.
|
||||
# name = "Outdated Type",
|
||||
# description = "Internal flag.",
|
||||
options = {'HIDDEN', 'SKIP_SAVE'},
|
||||
default = False
|
||||
) # type: ignore
|
||||
|
||||
## A BME struct cfgs descriptor cache list
|
||||
# Not only the descriptor self, also the cfg associated index in bme_struct_cfgs
|
||||
bme_struct_cfg_index_cache: list[tuple[UTIL_bme.PrototypeShowcaseCfgDescriptor, int]]
|
||||
|
||||
def __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
|
||||
cfgs: typing.Iterator[UTIL_bme.PrototypeShowcaseCfgDescriptor]
|
||||
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):
|
||||
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(
|
||||
name = "Type",
|
||||
description = "The type of BME structure.",
|
||||
items = _g_EnumHelper_BmeStructType.generate_items(),
|
||||
update = bme_struct_type_updated,
|
||||
translation_context = 'BBP_OT_add_bme_struct/property'
|
||||
) # 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_rotation = (0.0, 0.0, 0.0)
|
||||
self.extra_scale = (1.0, 1.0, 1.0)
|
||||
|
||||
# create internal list
|
||||
self.bme_struct_cfg_index_cache = []
|
||||
# call internal builder to load prototype data inside it
|
||||
self.__build_bme_struct_cfg_index_cache()
|
||||
|
||||
# trigger default bme struct type updator
|
||||
self.bme_struct_type_updated(context)
|
||||
# call internal updator
|
||||
self.__internal_update_bme_struct_type()
|
||||
# run execute() function
|
||||
return self.execute(context)
|
||||
|
||||
def execute(self, context):
|
||||
# call internal updator
|
||||
self.__internal_update_bme_struct_type()
|
||||
|
||||
# create cfg visitor
|
||||
op_cfgs_visitor: UTIL_functions.CollectionVisitor[BBP_PG_bme_adder_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'}
|
||||
|
||||
def draw(self, context):
|
||||
# call internal updator
|
||||
self.__internal_update_bme_struct_type()
|
||||
|
||||
# start drawing
|
||||
layout: bpy.types.UILayout = self.layout
|
||||
# show type
|
||||
layout.prop(self, 'bme_struct_type')
|
||||
|
||||
# create cfg visitor
|
||||
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:
|
||||
box_layout.prop(op_cfgs_visitor[cfg_index], 'prop_float', text='')
|
||||
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:
|
||||
# face will show a special layout (grid view)
|
||||
grids = box_layout.grid_flow(
|
||||
@@ -280,24 +294,16 @@ class BBP_OT_add_bme_struct(bpy.types.Operator):
|
||||
|
||||
@classmethod
|
||||
def draw_blc_menu(cls, layout: bpy.types.UILayout):
|
||||
for category, idents in _g_EnumHelper_BmeStructType.get_bme_categories().items():
|
||||
# draw category label
|
||||
layout.label(text=category, text_ctxt=UTIL_translation.build_prototype_showcase_category_context())
|
||||
|
||||
# draw prototypes list
|
||||
for ident in idents:
|
||||
# draw operator
|
||||
cop = layout.operator(
|
||||
cls.bl_idname,
|
||||
text = _g_EnumHelper_BmeStructType.get_bme_showcase_title(ident),
|
||||
icon_value = _g_EnumHelper_BmeStructType.get_bme_showcase_icon(ident),
|
||||
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()
|
||||
for ident in _g_EnumHelper_BmeStructType.get_bme_identifiers():
|
||||
# draw operator
|
||||
cop = layout.operator(
|
||||
cls.bl_idname,
|
||||
text = _g_EnumHelper_BmeStructType.get_bme_showcase_title(ident),
|
||||
icon_value = _g_EnumHelper_BmeStructType.get_bme_showcase_icon(ident),
|
||||
text_ctxt = UTIL_translation.build_prototype_showcase_context(ident)
|
||||
)
|
||||
# and assign its init type value
|
||||
cop.bme_struct_type = _g_EnumHelper_BmeStructType.to_selection(ident)
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import bpy, mathutils
|
||||
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
|
||||
|
||||
#region Param Help Classes
|
||||
@@ -41,33 +41,33 @@ class ComponentCountParam():
|
||||
|
||||
#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):
|
||||
# process for 2 special unique components
|
||||
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:
|
||||
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
|
||||
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:
|
||||
return UTIL_naming_convention.BallanceObjectInfo.create_from_resetpoint(comp_sector)
|
||||
return UTIL_naming_convension.BallanceObjectInfo.create_from_resetpoint(comp_sector)
|
||||
# process for other components
|
||||
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),
|
||||
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 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.')
|
||||
|
||||
# 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.')
|
||||
|
||||
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
|
||||
|
||||
# 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
|
||||
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:
|
||||
raise UTIL_functions.BBPException('impossible fail to get component name.')
|
||||
|
||||
@@ -129,7 +129,7 @@ class _GeneralComponentCreator():
|
||||
@return The created component instance.
|
||||
"""
|
||||
# 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
|
||||
with PROP_ballance_element.BallanceElementsHelper(bpy.context.scene) as creator:
|
||||
@@ -152,12 +152,12 @@ class _GeneralComponentCreator():
|
||||
enlarged_sector: int
|
||||
# check component type to get enlarged value
|
||||
match(ele_info.mBasicType):
|
||||
case UTIL_naming_convention.BallanceObjectType.COMPONENT:
|
||||
case UTIL_naming_convension.BallanceObjectType.COMPONENT:
|
||||
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.
|
||||
enlarged_sector = comp_sector + 1
|
||||
case UTIL_naming_convention.BallanceObjectType.RESETPOINT:
|
||||
case UTIL_naming_convension.BallanceObjectType.RESETPOINT:
|
||||
enlarged_sector = comp_sector
|
||||
case _:
|
||||
# this component is not a sector based component
|
||||
@@ -176,7 +176,7 @@ class _GeneralComponentCreator():
|
||||
|
||||
#endregion
|
||||
|
||||
#region Normal Component Adder
|
||||
#region Noemal Component Adder
|
||||
|
||||
# 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)
|
||||
if icon is None: return UTIL_icons_manager.get_empty_icon()
|
||||
else: return icon
|
||||
_g_EnumHelper_Component = UTIL_functions.EnumPropHelper(
|
||||
_g_EnumHelper_Component: UTIL_functions.EnumPropHelper = UTIL_functions.EnumPropHelper(
|
||||
PROP_ballance_element.BallanceElementType,
|
||||
lambda x: str(x.value),
|
||||
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")
|
||||
|
||||
# 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:
|
||||
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
|
||||
(checkp_ty, checkp_sector) = self.__get_checkpoint()
|
||||
(resetp_ty, resetp_sector) = self.__get_resetpoint()
|
||||
# calc resetpoint offset and checkpoint rotation
|
||||
# resetpoint need a extra offset between checkpoint but it is different in FourFlams and TwoFlams.
|
||||
# 4 flames startpoint need a extra 90 degree rotation to correspond with ballance asset library (and the direction of resetpoint).
|
||||
# calc resetpoint offset
|
||||
# resetpoint need a extra offset between checkpoint
|
||||
# but it is different in FourFlams and TwoFlams
|
||||
resetp_offset: float
|
||||
checkp_degree: float
|
||||
if checkp_ty == PROP_ballance_element.BallanceElementType.PS_FourFlames:
|
||||
resetp_offset = 3.65
|
||||
checkp_degree = 90
|
||||
resetp_offset = 3.25
|
||||
else:
|
||||
resetp_offset = 3.3258
|
||||
checkp_degree = 0
|
||||
resetp_offset = 2.0
|
||||
|
||||
# add elements
|
||||
# create checkpoint
|
||||
@@ -593,7 +590,7 @@ class BBP_OT_add_sector_component_pair(bpy.types.Operator, ComponentSectorParam)
|
||||
checkp_ty,
|
||||
checkp_sector,
|
||||
1, # only create one
|
||||
lambda _: mathutils.Matrix.Rotation(math.radians(checkp_degree), 4, 'Z')
|
||||
lambda _: mathutils.Matrix.Identity(4)
|
||||
)
|
||||
# create resetpoint
|
||||
creator.create_component(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import bpy, mathutils, math
|
||||
import typing
|
||||
from . import UTIL_rail_creator, PROP_preferences
|
||||
from . import UTIL_rail_creator
|
||||
|
||||
## Const Value Hint:
|
||||
# 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_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):
|
||||
UTIL_rail_creator.rail_creator_wrapper(
|
||||
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_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):
|
||||
UTIL_rail_creator.rail_creator_wrapper(
|
||||
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_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):
|
||||
UTIL_rail_creator.rail_creator_wrapper(
|
||||
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_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):
|
||||
UTIL_rail_creator.rail_creator_wrapper(
|
||||
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'
|
||||
) # type: ignore
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return PROP_preferences.get_raw_preferences().has_valid_blc_tex_folder()
|
||||
|
||||
def execute(self, context):
|
||||
UTIL_rail_creator.rail_creator_wrapper(
|
||||
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'
|
||||
) # type: ignore
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return PROP_preferences.get_raw_preferences().has_valid_blc_tex_folder()
|
||||
|
||||
def execute(self, context):
|
||||
UTIL_rail_creator.rail_creator_wrapper(
|
||||
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'
|
||||
) # type: ignore
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return PROP_preferences.get_raw_preferences().has_valid_blc_tex_folder()
|
||||
|
||||
def execute(self, context):
|
||||
UTIL_rail_creator.rail_creator_wrapper(
|
||||
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'
|
||||
) # type: ignore
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return PROP_preferences.get_raw_preferences().has_valid_blc_tex_folder()
|
||||
|
||||
def execute(self, context):
|
||||
UTIL_rail_creator.rail_creator_wrapper(
|
||||
lambda bm: UTIL_rail_creator.create_screw_rail(
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import bpy, mathutils
|
||||
from bpy_extras.wm_utils.progress_report import ProgressReport
|
||||
import tempfile, os, typing
|
||||
from dataclasses import dataclass
|
||||
from . import PROP_preferences, UTIL_ioport_shared, UTIL_naming_convention
|
||||
from . import UTIL_virtools_types, UTIL_functions, UTIL_file_browser, UTIL_blender_mesh, UTIL_ballance_texture
|
||||
from . import PROP_virtools_group, PROP_virtools_material, PROP_virtools_mesh, PROP_virtools_texture, PROP_virtools_light, PROP_virtools_camera
|
||||
from .pybmap import bmap_wrapper as bmap
|
||||
from . import PROP_preferences, UTIL_ioport_shared
|
||||
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 .PyBMap import bmap_wrapper as bmap
|
||||
|
||||
class BBP_OT_export_virtools(bpy.types.Operator, UTIL_file_browser.ExportVirtoolsFile, UTIL_ioport_shared.ExportParams, UTIL_ioport_shared.VirtoolsParams, UTIL_ioport_shared.BallanceParams):
|
||||
"""Export Virtools File"""
|
||||
@@ -19,16 +18,10 @@ class BBP_OT_export_virtools(bpy.types.Operator, UTIL_file_browser.ExportVirtool
|
||||
return (
|
||||
PROP_preferences.get_raw_preferences().has_valid_blc_tex_folder()
|
||||
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):
|
||||
# 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:
|
||||
self.report({'ERROR'}, 'No selected target!')
|
||||
return {'CANCELLED'}
|
||||
@@ -45,25 +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)!')
|
||||
return {'CANCELLED'}
|
||||
|
||||
# check file name, but has slightly difference with importer.
|
||||
# when exporting, file can be inexisting in file system, so we can't check it directly.
|
||||
filename = self.general_get_filename()
|
||||
bad_filename: bool = False
|
||||
# for filename, at first, it should not be empty surely.
|
||||
if len(filename) == 0:
|
||||
bad_filename = True
|
||||
# then, if it is existing, it should be a file, otherwise everything is okey.
|
||||
if os.path.exists(filename) and (not os.path.isfile(filename)):
|
||||
bad_filename = True
|
||||
# now, show message and exit if we have bad file name
|
||||
if bad_filename:
|
||||
self.report({'ERROR'}, 'No file was selected!')
|
||||
return {'CANCELLED'}
|
||||
|
||||
# start exporting
|
||||
with UTIL_ioport_shared.ExportEditModeBackup() as editmode_guard:
|
||||
_export_virtools(
|
||||
filename,
|
||||
self.general_get_filename(),
|
||||
encodings,
|
||||
texture_save_opt,
|
||||
self.general_get_use_compress(),
|
||||
@@ -82,22 +60,15 @@ class BBP_OT_export_virtools(bpy.types.Operator, UTIL_file_browser.ExportVirtool
|
||||
self.draw_virtools_params(context, layout, False)
|
||||
self.draw_ballance_params(layout, False)
|
||||
|
||||
_Object3dPair = tuple[bpy.types.Object, bmap.BM3dObject]
|
||||
_LightPair = tuple[bpy.types.Object, bpy.types.Light, bmap.BMTargetLight]
|
||||
_CameraPair = tuple[bpy.types.Object, bpy.types.Camera, bmap.BMTargetCamera]
|
||||
_MeshPair = tuple[bpy.types.Object, bpy.types.Mesh, bmap.BMMesh]
|
||||
_MaterialPair = tuple[bpy.types.Material, bmap.BMMaterial]
|
||||
_TexturePair = tuple[bpy.types.Image, bmap.BMTexture]
|
||||
|
||||
@dataclass
|
||||
class _PreparedCrets:
|
||||
obj3d_crets: tuple[_Object3dPair, ...]
|
||||
light_crets: tuple[_LightPair, ...]
|
||||
camera_crets: tuple[_CameraPair, ...]
|
||||
_TObj3dPair = tuple[bpy.types.Object, bmap.BM3dObject]
|
||||
_TLightPair = tuple[bpy.types.Object, bpy.types.Light, bmap.BMTargetLight]
|
||||
_TMeshPair = tuple[bpy.types.Object, bpy.types.Mesh, bmap.BMMesh]
|
||||
_TMaterialPair = tuple[bpy.types.Material, bmap.BMMaterial]
|
||||
_TTexturePair = tuple[bpy.types.Image, bmap.BMTexture]
|
||||
|
||||
def _export_virtools(
|
||||
file_name_: str,
|
||||
encodings_: tuple[str, ...],
|
||||
encodings_: tuple[str],
|
||||
texture_save_opt_: UTIL_virtools_types.CK_TEXTURE_SAVEOPTIONS,
|
||||
use_compress_: bool,
|
||||
compress_level_: int,
|
||||
@@ -120,21 +91,22 @@ def _export_virtools(
|
||||
|
||||
# prepare progress reporter
|
||||
with ProgressReport(wm = bpy.context.window_manager) as progress:
|
||||
# prepare 3dobject, light and camera
|
||||
prep_crets = _prepare_virtools_3dobjects(writer, progress, export_objects)
|
||||
# prepare 3dobject and light
|
||||
obj3d_crets: tuple[_TObj3dPair, ...]
|
||||
light_crets: tuple[_TLightPair, ...]
|
||||
(obj3d_crets, light_crets) = _prepare_virtools_3dobjects(writer, progress, export_objects)
|
||||
# export group according to prepared 3dobject
|
||||
_export_virtools_groups(writer, progress, successive_sector_, successive_sector_count_, prep_crets.obj3d_crets)
|
||||
# export prepared light and camera
|
||||
_export_virtools_light(writer, progress, prep_crets.light_crets)
|
||||
_export_virtools_camera(writer, progress, prep_crets.camera_crets)
|
||||
_export_virtools_groups(writer, progress, successive_sector_, successive_sector_count_, obj3d_crets)
|
||||
# export prepared light
|
||||
_export_virtools_light(writer, progress, light_crets)
|
||||
# export prepared 3dobject
|
||||
mesh_crets: tuple[_MeshPair, ...] = _export_virtools_3dobjects(
|
||||
writer, progress, prep_crets.obj3d_crets)
|
||||
mesh_crets: tuple[_TMeshPair, ...] = _export_virtools_3dobjects(
|
||||
writer, progress, obj3d_crets)
|
||||
# export mesh
|
||||
material_crets: tuple[_MaterialPair, ...] = _export_virtools_meshes(
|
||||
material_crets: tuple[_TMaterialPair, ...] = _export_virtools_meshes(
|
||||
writer, progress, mesh_crets)
|
||||
# export material
|
||||
texture_crets: tuple[_TexturePair, ...] = _export_virtools_materials(
|
||||
texture_crets: tuple[_TTexturePair, ...] = _export_virtools_materials(
|
||||
writer, progress, material_crets)
|
||||
# export texture
|
||||
_export_virtools_textures(writer, progress, vt_temp_folder, texture_crets)
|
||||
@@ -147,7 +119,7 @@ def _prepare_virtools_3dobjects(
|
||||
writer: bmap.BMFileWriter,
|
||||
progress: ProgressReport,
|
||||
export_objects: tuple[bpy.types.Object, ...]
|
||||
) -> _PreparedCrets:
|
||||
) -> tuple[tuple[_TObj3dPair, ...], tuple[_TLightPair, ...]]:
|
||||
# this function only create equvalent entries in virtools engine and do not export anything
|
||||
# because _export_virtools_3dobjects() and _export_virtools_groups() are need use the return value of this function
|
||||
#
|
||||
@@ -155,65 +127,52 @@ def _prepare_virtools_3dobjects(
|
||||
# we also need extract exported lights and create equvalent entries in virtools for them.
|
||||
|
||||
# create 3dobject hashset and result
|
||||
obj3d_crets: list[_Object3dPair] = []
|
||||
obj3d_crets: list[_TObj3dPair] = []
|
||||
obj3d_cret_set: set[bpy.types.Object] = set()
|
||||
# create light hashset and result
|
||||
light_crets: list[_LightPair] = []
|
||||
light_crets: list[_TLightPair] = []
|
||||
light_cret_set: set[bpy.types.Object] = set()
|
||||
# create camera hashset and result
|
||||
camera_crets: list[_CameraPair] = []
|
||||
camera_cret_set: set[bpy.types.Object] = set()
|
||||
# start saving
|
||||
tr_text: str = bpy.app.translations.pgettext_rpt('Creating 3dObjects and Lights', 'BBP_OT_export_virtools/execute')
|
||||
progress.enter_substeps(len(export_objects), tr_text)
|
||||
|
||||
# iterate exported object list
|
||||
for obj3d in export_objects:
|
||||
# only accept mesh-like object, camera and light object
|
||||
# only accept mesh object and light object
|
||||
# all of other objects will be discard.
|
||||
if UTIL_blender_mesh.TemporaryMesh.has_geometry(obj3d):
|
||||
# mesh-like object
|
||||
if obj3d not in obj3d_cret_set:
|
||||
# add into set
|
||||
obj3d_cret_set.add(obj3d)
|
||||
# create virtools instance
|
||||
vtobj3d: bmap.BM3dObject = writer.create_3dobject()
|
||||
# add into result list
|
||||
obj3d_crets.append((obj3d, vtobj3d))
|
||||
else:
|
||||
match(obj3d.type):
|
||||
case 'CAMERA':
|
||||
# camera object
|
||||
if obj3d not in camera_cret_set:
|
||||
# add into set
|
||||
camera_cret_set.add(obj3d)
|
||||
# create virtools instance
|
||||
vtcamera: bmap.BMTargetCamera = writer.create_target_camera()
|
||||
# add into result list
|
||||
camera_crets.append((obj3d, typing.cast(bpy.types.Camera, obj3d.data), vtcamera))
|
||||
case 'LIGHT':
|
||||
# light object
|
||||
if obj3d not in light_cret_set:
|
||||
# add into set
|
||||
light_cret_set.add(obj3d)
|
||||
# create virtools instance
|
||||
vtlight: bmap.BMTargetLight = writer.create_target_light()
|
||||
# add into result list
|
||||
light_crets.append((obj3d, typing.cast(bpy.types.Light, obj3d.data), vtlight))
|
||||
|
||||
match(obj3d.type):
|
||||
case 'MESH':
|
||||
# mesh object
|
||||
if obj3d not in obj3d_cret_set:
|
||||
# add into set
|
||||
obj3d_cret_set.add(obj3d)
|
||||
# create virtools instance
|
||||
vtobj3d: bmap.BM3dObject = writer.create_3dobject()
|
||||
# add into result list
|
||||
obj3d_crets.append((obj3d, vtobj3d))
|
||||
case 'LIGHT':
|
||||
# light object
|
||||
if obj3d not in light_cret_set:
|
||||
# add into set
|
||||
light_cret_set.add(obj3d)
|
||||
# create virtools instance
|
||||
vtlight: bmap.BMTargetLight = writer.create_target_light()
|
||||
# add into result list
|
||||
light_crets.append((obj3d, typing.cast(bpy.types.Light, obj3d.data), vtlight))
|
||||
|
||||
# step progress no matter whether create new one
|
||||
progress.step()
|
||||
|
||||
# leave progress and return
|
||||
progress.leave_substeps()
|
||||
return _PreparedCrets(tuple(obj3d_crets), tuple(light_crets), tuple(camera_crets))
|
||||
return (tuple(obj3d_crets), tuple(light_crets))
|
||||
|
||||
def _export_virtools_groups(
|
||||
writer: bmap.BMFileWriter,
|
||||
progress: ProgressReport,
|
||||
successive_sector: bool,
|
||||
successive_sector_count: int,
|
||||
obj3d_crets: tuple[_Object3dPair, ...]
|
||||
obj3d_crets: tuple[_TObj3dPair, ...]
|
||||
) -> None:
|
||||
# create virtools group
|
||||
group_cret_map: dict[str, bmap.BMGroup] = {}
|
||||
@@ -231,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.
|
||||
if successive_sector:
|
||||
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)
|
||||
if vtgroup is None:
|
||||
vtgroup = writer.create_group()
|
||||
@@ -261,7 +220,7 @@ def _export_virtools_groups(
|
||||
def _export_virtools_light(
|
||||
writer: bmap.BMFileWriter,
|
||||
progress: ProgressReport,
|
||||
light_crets: tuple[_LightPair, ...]
|
||||
light_crets: tuple[_TLightPair, ...]
|
||||
) -> None:
|
||||
# start saving
|
||||
tr_text: str = bpy.app.translations.pgettext_rpt('Saving Lights', 'BBP_OT_export_virtools/execute')
|
||||
@@ -303,56 +262,13 @@ def _export_virtools_light(
|
||||
# leave progress and return
|
||||
progress.leave_substeps()
|
||||
|
||||
def _export_virtools_camera(
|
||||
writer: bmap.BMFileWriter,
|
||||
progress: ProgressReport,
|
||||
camera_crets: tuple[_CameraPair, ...]
|
||||
) -> None:
|
||||
# start saving
|
||||
tr_text: str = bpy.app.translations.pgettext_rpt('Saving Cameras', 'BBP_OT_export_virtools/execute')
|
||||
progress.enter_substeps(len(camera_crets), tr_text)
|
||||
|
||||
for obj3d, camera, vtcamera in camera_crets:
|
||||
# set name
|
||||
vtcamera.set_name(obj3d.name)
|
||||
|
||||
# setup 3d entity parts
|
||||
# set world matrix
|
||||
vtmat: UTIL_virtools_types.VxMatrix = UTIL_virtools_types.VxMatrix()
|
||||
bldmat: mathutils.Matrix = UTIL_virtools_types.bldmatrix_restore_camera_obj(obj3d.matrix_world)
|
||||
UTIL_virtools_types.vxmatrix_from_blender(vtmat, bldmat)
|
||||
UTIL_virtools_types.vxmatrix_conv_co(vtmat)
|
||||
vtcamera.set_world_matrix(vtmat)
|
||||
# set visibility
|
||||
vtcamera.set_visibility(not obj3d.hide_get())
|
||||
|
||||
# setup camera data
|
||||
rawcamera: PROP_virtools_camera.RawVirtoolsCamera = PROP_virtools_camera.get_raw_virtools_camera(camera)
|
||||
|
||||
vtcamera.set_projection_type(rawcamera.mProjectionType)
|
||||
|
||||
vtcamera.set_orthographic_zoom(rawcamera.mOrthographicZoom)
|
||||
|
||||
vtcamera.set_front_plane(rawcamera.mFrontPlane)
|
||||
vtcamera.set_back_plane(rawcamera.mBackPlane)
|
||||
vtcamera.set_fov(rawcamera.mFov)
|
||||
|
||||
(w, h) = rawcamera.mAspectRatio
|
||||
vtcamera.set_aspect_ratio(w, h)
|
||||
|
||||
# step
|
||||
progress.step()
|
||||
|
||||
# leave progress and return
|
||||
progress.leave_substeps()
|
||||
|
||||
def _export_virtools_3dobjects(
|
||||
writer: bmap.BMFileWriter,
|
||||
progress: ProgressReport,
|
||||
obj3d_crets: tuple[_Object3dPair, ...]
|
||||
) -> tuple[_MeshPair, ...]:
|
||||
obj3d_crets: tuple[_TObj3dPair, ...]
|
||||
) -> tuple[_TMeshPair, ...]:
|
||||
# create virtools mesh
|
||||
mesh_crets: list[_MeshPair] = []
|
||||
mesh_crets: list[_TMeshPair] = []
|
||||
mesh_cret_map: dict[bpy.types.Mesh, bmap.BMMesh] = {}
|
||||
# start saving
|
||||
tr_text: str = bpy.app.translations.pgettext_rpt('Saving 3dObjects', 'BBP_OT_export_virtools/execute')
|
||||
@@ -396,10 +312,10 @@ def _export_virtools_3dobjects(
|
||||
def _export_virtools_meshes(
|
||||
writer: bmap.BMFileWriter,
|
||||
progress: ProgressReport,
|
||||
mesh_crets: tuple[_MeshPair, ...]
|
||||
) -> tuple[_MaterialPair, ...]:
|
||||
mesh_crets: tuple[_TMeshPair, ...]
|
||||
) -> tuple[_TMaterialPair, ...]:
|
||||
# create virtools mesh
|
||||
material_crets: list[_MaterialPair] = []
|
||||
material_crets: list[_TMaterialPair] = []
|
||||
material_cret_map: dict[bpy.types.Material, bmap.BMMaterial] = {}
|
||||
# start saving
|
||||
tr_text: str = bpy.app.translations.pgettext_rpt('Saving Meshes', 'BBP_OT_export_virtools/execute')
|
||||
@@ -511,10 +427,10 @@ def _export_virtools_meshes(
|
||||
def _export_virtools_materials(
|
||||
writer: bmap.BMFileWriter,
|
||||
progress: ProgressReport,
|
||||
material_crets: tuple[_MaterialPair, ...]
|
||||
) -> tuple[_TexturePair, ...]:
|
||||
material_crets: tuple[_TMaterialPair, ...]
|
||||
) -> tuple[_TTexturePair, ...]:
|
||||
# create virtools mesh
|
||||
texture_crets: list[_TexturePair] = []
|
||||
texture_crets: list[_TTexturePair] = []
|
||||
texture_cret_map: dict[bpy.types.Image, bmap.BMTexture] = {}
|
||||
# start saving
|
||||
tr_text: str = bpy.app.translations.pgettext_rpt('Saving Materials', 'BBP_OT_export_virtools/execute')
|
||||
@@ -580,7 +496,7 @@ def _export_virtools_textures(
|
||||
writer: bmap.BMFileWriter,
|
||||
progress: ProgressReport,
|
||||
vt_temp_folder: str,
|
||||
texture_crets: tuple[_TexturePair, ...]
|
||||
texture_crets: tuple[_TTexturePair, ...]
|
||||
) -> None:
|
||||
# start saving
|
||||
tr_text: str = bpy.app.translations.pgettext_rpt('Saving Textures', 'BBP_OT_export_virtools/execute')
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import bpy, mathutils
|
||||
from bpy_extras.wm_utils.progress_report import ProgressReport
|
||||
import tempfile, os, typing
|
||||
from . import PROP_preferences, UTIL_ioport_shared, UTIL_naming_convention
|
||||
from . import UTIL_virtools_types, UTIL_functions, UTIL_file_browser, UTIL_blender_mesh, UTIL_ballance_texture
|
||||
from . import PROP_virtools_group, PROP_virtools_material, PROP_virtools_mesh, PROP_virtools_texture, PROP_virtools_light, PROP_virtools_camera, PROP_ballance_map_info
|
||||
from .pybmap import bmap_wrapper as bmap
|
||||
from . import PROP_preferences, UTIL_ioport_shared
|
||||
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 .PyBMap import bmap_wrapper as bmap
|
||||
|
||||
class BBP_OT_import_virtools(bpy.types.Operator, UTIL_file_browser.ImportVirtoolsFile, UTIL_ioport_shared.ImportParams, UTIL_ioport_shared.VirtoolsParams, UTIL_ioport_shared.BallanceParams):
|
||||
"""Import Virtools File"""
|
||||
@@ -18,12 +18,6 @@ class BBP_OT_import_virtools(bpy.types.Operator, UTIL_file_browser.ImportVirtool
|
||||
return (
|
||||
PROP_preferences.get_raw_preferences().has_valid_blc_tex_folder()
|
||||
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):
|
||||
# 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)!')
|
||||
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(
|
||||
filename,
|
||||
self.general_get_filename(),
|
||||
encodings,
|
||||
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_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
|
||||
with tempfile.TemporaryDirectory() as vt_temp_folder:
|
||||
tr_text: str = bpy.app.translations.pgettext_rpt(
|
||||
@@ -80,9 +68,8 @@ def _import_virtools(file_name_: str, encodings_: tuple[str, ...], resolver: UTI
|
||||
# import 3dobjects
|
||||
obj3d_cret_map: dict[bmap.BM3dObject, bpy.types.Object] = _import_virtools_3dobjects(
|
||||
reader, progress, resolver, mesh_cret_map)
|
||||
# import light and camera
|
||||
# import light
|
||||
_import_virtools_lights(reader, progress, resolver)
|
||||
_import_virtools_cameras(reader, progress, resolver)
|
||||
# import groups
|
||||
_import_virtools_groups(reader, progress, obj3d_cret_map)
|
||||
|
||||
@@ -424,53 +411,6 @@ def _import_virtools_lights(
|
||||
# leave progress
|
||||
progress.leave_substeps()
|
||||
|
||||
def _import_virtools_cameras(
|
||||
reader: bmap.BMFileReader,
|
||||
progress: ProgressReport,
|
||||
resolver: UTIL_ioport_shared.ConflictResolver
|
||||
) -> None:
|
||||
# prepare progress
|
||||
tr_text: str = bpy.app.translations.pgettext_rpt('Loading Cameras', 'BBP_OT_import_virtools/execute')
|
||||
progress.enter_substeps(reader.get_target_camera_count(), tr_text)
|
||||
|
||||
# same creation notes like light
|
||||
for vtcamera in reader.get_target_cameras():
|
||||
# create camera data block and 3d object together
|
||||
(camera_3dobj, camera, init_camera) = resolver.create_camera(
|
||||
UTIL_virtools_types.virtools_name_regulator(vtcamera.get_name())
|
||||
)
|
||||
|
||||
if init_camera:
|
||||
# setup camera data block
|
||||
rawcamera: PROP_virtools_camera.RawVirtoolsCamera = PROP_virtools_camera.RawVirtoolsCamera()
|
||||
|
||||
rawcamera.mProjectionType = vtcamera.get_projection_type()
|
||||
|
||||
rawcamera.mOrthographicZoom = vtcamera.get_orthographic_zoom()
|
||||
|
||||
rawcamera.mFrontPlane = vtcamera.get_front_plane()
|
||||
rawcamera.mBackPlane = vtcamera.get_back_plane()
|
||||
rawcamera.mFov = vtcamera.get_fov()
|
||||
|
||||
rawcamera.mAspectRatio = vtcamera.get_aspect_ratio()
|
||||
|
||||
PROP_virtools_camera.set_raw_virtools_camera(camera, rawcamera)
|
||||
PROP_virtools_camera.apply_to_blender_camera(camera)
|
||||
|
||||
# setup camera associated 3d object
|
||||
# add into scene
|
||||
UTIL_functions.add_into_scene(camera_3dobj)
|
||||
# set world matrix
|
||||
vtmat: UTIL_virtools_types.VxMatrix = vtcamera.get_world_matrix()
|
||||
UTIL_virtools_types.vxmatrix_conv_co(vtmat)
|
||||
bldmat: mathutils.Matrix = UTIL_virtools_types.vxmatrix_to_blender(vtmat)
|
||||
camera_3dobj.matrix_world = UTIL_virtools_types.bldmatrix_patch_camera_obj(bldmat)
|
||||
# set visibility
|
||||
camera_3dobj.hide_set(not vtcamera.get_visibility())
|
||||
|
||||
# leave progress
|
||||
progress.leave_substeps()
|
||||
|
||||
def _import_virtools_groups(
|
||||
reader: bmap.BMFileReader,
|
||||
progress: ProgressReport,
|
||||
@@ -492,7 +432,7 @@ def _import_virtools_groups(
|
||||
if group_name is None: continue
|
||||
|
||||
# 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:
|
||||
sector_count = max(sector_count, potential_sector_count)
|
||||
|
||||
|
||||
@@ -2,12 +2,12 @@ import bpy
|
||||
from . import UTIL_functions
|
||||
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."""
|
||||
bl_idname = "bbp.fix_all_materials"
|
||||
bl_idname = "bbp.fix_all_material"
|
||||
bl_label = "Fix All Materials"
|
||||
bl_options = {'UNDO'}
|
||||
bl_translation_context = 'BBP_OT_fix_all_materials'
|
||||
bl_translation_context = 'BBP_OT_fix_all_material'
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
@@ -31,12 +31,12 @@ class BBP_OT_fix_all_materials(bpy.types.Operator):
|
||||
|
||||
# report and return
|
||||
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))
|
||||
return {'FINISHED'}
|
||||
|
||||
def register() -> None:
|
||||
bpy.utils.register_class(BBP_OT_fix_all_materials)
|
||||
bpy.utils.register_class(BBP_OT_fix_all_material)
|
||||
|
||||
def unregister() -> None:
|
||||
bpy.utils.unregister_class(BBP_OT_fix_all_materials)
|
||||
bpy.utils.unregister_class(BBP_OT_fix_all_material)
|
||||
@@ -1,453 +0,0 @@
|
||||
import bpy, mathutils
|
||||
import typing, enum, math
|
||||
from . import UTIL_functions, PROP_virtools_camera
|
||||
|
||||
#region Enum Defines
|
||||
|
||||
class ResolutionKind(enum.IntEnum):
|
||||
Normal = enum.auto()
|
||||
WideScreen = enum.auto()
|
||||
_g_ResolutionKindDesc: dict[ResolutionKind, tuple[str, str]] = {
|
||||
ResolutionKind.Normal: ("Normal", "Vanilla Ballance Resolution"),
|
||||
ResolutionKind.WideScreen: ("Wide Screen", "Ballance Resolution with Wide Screen Fix"),
|
||||
}
|
||||
_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 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
|
||||
|
||||
|
||||
modify_resolution: bpy.props.BoolProperty(
|
||||
name = 'Modify Resolution',
|
||||
description = 'Whether modify the resolution of camera.',
|
||||
default = False,
|
||||
translation_context = 'BBP_OT_game_camera/property'
|
||||
) # type: ignore
|
||||
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_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)
|
||||
|
||||
# Show resolution kind
|
||||
layout.separator()
|
||||
layout.prop(self, 'modify_resolution', text='Resolution', text_ctxt='BBP_OT_game_camera/draw')
|
||||
if self.modify_resolution:
|
||||
layout.row().prop(self, 'resolution_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)
|
||||
resolution_kind = _g_EnumHelper_ResolutionKind.get_selection(self.resolution_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, resolution_kind)
|
||||
|
||||
# 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, resolution_kind: ResolutionKind | None) -> None:
|
||||
# fetch camera and its raw data
|
||||
camera = typing.cast(bpy.types.Camera, camobj.data)
|
||||
rawdata = PROP_virtools_camera.get_raw_virtools_camera(camera)
|
||||
|
||||
# set clipping
|
||||
rawdata.mFrontPlane = 4
|
||||
rawdata.mBackPlane = 1200
|
||||
# set FOV and aspect ratio according to presented resolution kind
|
||||
if resolution_kind is not None:
|
||||
match resolution_kind:
|
||||
case ResolutionKind.Normal:
|
||||
rawdata.mFov = math.radians(58)
|
||||
rawdata.mAspectRatio = (4, 3)
|
||||
case ResolutionKind.WideScreen:
|
||||
# prepare input arguments
|
||||
aspect_ratio = (16, 9)
|
||||
fov = math.radians(58)
|
||||
# FOV correction reference:
|
||||
# https://github.com/doyaGu/BallanceModLoaderPlus/blob/c4ab4386fd834af69a960c156fca97237b2fd4c5/src/RenderHook.cpp#L46
|
||||
aspect = aspect_ratio[0] / aspect_ratio[1]
|
||||
rawdata.mFov = math.atan2(math.tan(fov * 0.5) * 0.75 * aspect, 1.0) * 2.0
|
||||
rawdata.mAspectRatio = aspect_ratio
|
||||
|
||||
# rewrite it back
|
||||
PROP_virtools_camera.set_raw_virtools_camera(camera, rawdata)
|
||||
# and apply it into camera and blender scene
|
||||
PROP_virtools_camera.apply_to_blender_camera(camera)
|
||||
PROP_virtools_camera.apply_to_blender_scene_resolution(camera)
|
||||
|
||||
def register() -> None:
|
||||
bpy.utils.register_class(BBP_OT_game_camera)
|
||||
|
||||
def unregister() -> None:
|
||||
bpy.utils.unregister_class(BBP_OT_game_camera)
|
||||
|
||||
@@ -9,35 +9,19 @@ class AlignMode(enum.IntEnum):
|
||||
BBoxCenter = enum.auto()
|
||||
AxisCenter = enum.auto()
|
||||
Max = enum.auto()
|
||||
_g_AlignModeDesc: dict[AlignMode, tuple[str, str, str]] = {
|
||||
AlignMode.Min: ("Min", "The min value in specified axis.", "REMOVE"),
|
||||
AlignMode.BBoxCenter: ("Center (Bounding Box)", "The bounding box center in specified axis.", "SHADING_BBOX"),
|
||||
AlignMode.AxisCenter: ("Center (Axis)", "The object's source point in specified axis.", "OBJECT_ORIGIN"),
|
||||
AlignMode.Max: ("Max", "The max value in specified axis.", "ADD"),
|
||||
_g_AlignModeDesc: dict[AlignMode, tuple[str, str]] = {
|
||||
AlignMode.Min: ("Min", "The min value in specified axis."),
|
||||
AlignMode.BBoxCenter: ("Center (Bounding Box)", "The bounding box center in specified axis."),
|
||||
AlignMode.AxisCenter: ("Center (Axis)", "The object's source point in specified axis."),
|
||||
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,
|
||||
lambda x: str(x.value),
|
||||
lambda x: AlignMode(int(x)),
|
||||
lambda x: _g_AlignModeDesc[x][0],
|
||||
lambda x: _g_AlignModeDesc[x][1],
|
||||
lambda x: _g_AlignModeDesc[x][2]
|
||||
)
|
||||
|
||||
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]
|
||||
lambda _: ''
|
||||
)
|
||||
|
||||
#endregion
|
||||
@@ -71,23 +55,14 @@ class BBP_PG_legacy_align_history(bpy.types.PropertyGroup):
|
||||
default = False,
|
||||
translation_context = 'BBP_PG_legacy_align_history/property'
|
||||
) # 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(
|
||||
name = "Current Object",
|
||||
description = "The align mode applied to Current Object",
|
||||
name = "Current Object (Active Object)",
|
||||
items = _g_EnumHelper_AlignMode.generate_items(),
|
||||
default = _g_EnumHelper_AlignMode.to_selection(AlignMode.AxisCenter),
|
||||
translation_context = 'BBP_PG_legacy_align_history/property'
|
||||
) # type: ignore
|
||||
target_align_mode: bpy.props.EnumProperty(
|
||||
name = "Target Objects",
|
||||
description = "The align mode applied to Target Objects (selected objects except active object if Current Instance is active object)",
|
||||
name = "Target Objects (Selected Objects)",
|
||||
items = _g_EnumHelper_AlignMode.generate_items(),
|
||||
default = _g_EnumHelper_AlignMode.to_selection(AlignMode.AxisCenter),
|
||||
translation_context = 'BBP_PG_legacy_align_history/property'
|
||||
@@ -137,7 +112,7 @@ class BBP_OT_legacy_align(bpy.types.Operator):
|
||||
return None
|
||||
|
||||
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",
|
||||
# description = "Internal flag.",
|
||||
options = {'HIDDEN', 'SKIP_SAVE'},
|
||||
@@ -145,14 +120,14 @@ class BBP_OT_legacy_align(bpy.types.Operator):
|
||||
update = apply_flag_updated
|
||||
) # type: ignore
|
||||
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",
|
||||
# description = "An internal flag to prevent the loop calling to apply_flags's updator.",
|
||||
options = {'HIDDEN', 'SKIP_SAVE'},
|
||||
default = False
|
||||
) # type: ignore
|
||||
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",
|
||||
# description = "Align history.",
|
||||
type = BBP_PG_legacy_align_history
|
||||
@@ -173,7 +148,7 @@ class BBP_OT_legacy_align(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
# get processed objects
|
||||
(current_obj, current_cursor, target_objs) = _prepare_objects()
|
||||
(current_obj, target_objs) = _prepare_objects()
|
||||
# YYC MARK:
|
||||
# This statement is VERY IMPORTANT.
|
||||
# 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)
|
||||
for entry in histories:
|
||||
_align_objects(
|
||||
_g_EnumHelper_CurrentInstance.get_selection(entry.current_instance),
|
||||
current_obj, current_cursor, target_objs,
|
||||
current_obj, target_objs,
|
||||
entry.align_x, entry.align_y, entry.align_z,
|
||||
_g_EnumHelper_AlignMode.get_selection(entry.current_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_z", toggle = 1)
|
||||
|
||||
# show current instance
|
||||
# show mode
|
||||
col.separator()
|
||||
col.label(text='Current Object', text_ctxt='BBP_OT_legacy_align/draw')
|
||||
# it should be shown in horizon so we create a new sublayout
|
||||
row = col.row()
|
||||
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.label(text='Current Object (Active Object)', text_ctxt='BBP_OT_legacy_align/draw')
|
||||
col.prop(entry, "current_align_mode", expand = True)
|
||||
col.label(text='Target Objects (Selected Objects)', text_ctxt='BBP_OT_legacy_align/draw')
|
||||
col.prop(entry, "target_align_mode", expand = True)
|
||||
|
||||
# show apply button
|
||||
@@ -243,66 +206,44 @@ class BBP_OT_legacy_align(bpy.types.Operator):
|
||||
#region Core Functions
|
||||
|
||||
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():
|
||||
return False
|
||||
|
||||
# YYC MARK:
|
||||
# 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.
|
||||
# check current obj
|
||||
if bpy.context.active_object is None:
|
||||
return False
|
||||
|
||||
# YYC MARK:
|
||||
# Roughly check selected objects.
|
||||
# We do not need exclude active object from selected objects,
|
||||
# because active object may be moved when 3D Cursor is current object.
|
||||
if len(bpy.context.selected_objects) == 0:
|
||||
return False
|
||||
|
||||
return True
|
||||
# check target obj with filter of current obj
|
||||
length = len(bpy.context.selected_objects)
|
||||
if bpy.context.active_object in bpy.context.selected_objects:
|
||||
length -= 1
|
||||
return length != 0
|
||||
|
||||
def _prepare_objects() -> tuple[bpy.types.Object, mathutils.Vector, list[bpy.types.Object]]:
|
||||
# Fetch current object
|
||||
current_obj = typing.cast(bpy.types.Object, bpy.context.active_object)
|
||||
def _prepare_objects() -> tuple[bpy.types.Object, set[bpy.types.Object]]:
|
||||
# get current object
|
||||
current_obj: bpy.types.Object = bpy.context.active_object
|
||||
|
||||
# Fetch 3d cursor location
|
||||
current_cursor: mathutils.Vector = bpy.context.scene.cursor.location
|
||||
|
||||
# YYC MARK:
|
||||
# Fetch target objects and do NOT remove active object from it.
|
||||
# because active object will be moved when current instance is 3D Cursor.
|
||||
target_objs: list[bpy.types.Object] = bpy.context.selected_objects[:]
|
||||
# get target objects
|
||||
target_objs: set[bpy.types.Object] = set(bpy.context.selected_objects)
|
||||
# remove active one
|
||||
if current_obj in target_objs:
|
||||
target_objs.remove(current_obj)
|
||||
|
||||
# return value
|
||||
return (current_obj, current_cursor, target_objs)
|
||||
return (current_obj, target_objs)
|
||||
|
||||
def _align_objects(
|
||||
current_instance: CurrentInstance,
|
||||
current_obj: bpy.types.Object, current_cursor: mathutils.Vector, target_objs: list[bpy.types.Object],
|
||||
current_obj: bpy.types.Object, target_objs: set[bpy.types.Object],
|
||||
align_x: bool, align_y: bool, align_z: bool, current_mode: AlignMode, target_mode: AlignMode) -> None:
|
||||
# if no align, skip
|
||||
if not (align_x or align_y or align_z):
|
||||
return
|
||||
|
||||
# calc current object data
|
||||
current_obj_ref: mathutils.Vector
|
||||
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
|
||||
current_obj_ref: mathutils.Vector = _get_object_ref_point(current_obj, current_mode)
|
||||
|
||||
# process each target obj
|
||||
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
|
||||
target_obj_ref: mathutils.Vector = _get_object_ref_point(target_obj, target_mode)
|
||||
# build translation transform
|
||||
@@ -315,21 +256,21 @@ def _align_objects(
|
||||
# apply translation transform to left side (add into original matrix)
|
||||
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:
|
||||
ref_pos = mathutils.Vector((0, 0, 0))
|
||||
ref_pos: mathutils.Vector = mathutils.Vector((0, 0, 0))
|
||||
|
||||
# calc bounding box data
|
||||
corners: tuple[mathutils.Vector, ...] = tuple(obj.matrix_world @ mathutils.Vector(corner) for corner in obj.bound_box)
|
||||
bbox_min_corner = mathutils.Vector((
|
||||
min((vec.x for vec in corners)),
|
||||
min((vec.y for vec in corners)),
|
||||
min((vec.z for vec in corners)),
|
||||
))
|
||||
bbox_max_corner = mathutils.Vector((
|
||||
max((vec.x for vec in corners)),
|
||||
max((vec.y for vec in corners)),
|
||||
max((vec.z for vec in corners)),
|
||||
))
|
||||
corners: tuple[mathutils.Vector] = tuple(obj.matrix_world @ mathutils.Vector(corner) for corner in obj.bound_box)
|
||||
bbox_min_corner: mathutils.Vector = mathutils.Vector((0, 0, 0))
|
||||
bbox_min_corner.x = min((vec.x for vec in corners))
|
||||
bbox_min_corner.y = min((vec.y 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.x = max((vec.x for vec in corners))
|
||||
bbox_max_corner.y = max((vec.y for vec in corners))
|
||||
bbox_max_corner.z = max((vec.z for vec in corners))
|
||||
|
||||
# return value by given align mode
|
||||
match(mode):
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import bpy
|
||||
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):
|
||||
"""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):
|
||||
_rename_core(
|
||||
UTIL_naming_convention.VirtoolsGroupConvention.parse_from_object,
|
||||
UTIL_naming_convention.YYCToolchainConvention.set_to_object
|
||||
UTIL_naming_convension.VirtoolsGroupConvention.parse_from_object,
|
||||
UTIL_naming_convension.YYCToolchainConvention.set_to_object
|
||||
)
|
||||
return {'FINISHED'}
|
||||
|
||||
@@ -33,8 +33,8 @@ class BBP_OT_auto_grouping(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
_rename_core(
|
||||
UTIL_naming_convention.YYCToolchainConvention.parse_from_object,
|
||||
UTIL_naming_convention.VirtoolsGroupConvention.set_to_object
|
||||
UTIL_naming_convension.YYCToolchainConvention.parse_from_object,
|
||||
UTIL_naming_convension.VirtoolsGroupConvention.set_to_object
|
||||
)
|
||||
return {'FINISHED'}
|
||||
|
||||
@@ -51,26 +51,26 @@ class BBP_OT_convert_to_imengyu(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
_rename_core(
|
||||
UTIL_naming_convention.YYCToolchainConvention.parse_from_object,
|
||||
UTIL_naming_convention.ImengyuConvention.set_to_object
|
||||
UTIL_naming_convension.YYCToolchainConvention.parse_from_object,
|
||||
UTIL_naming_convension.ImengyuConvention.set_to_object
|
||||
)
|
||||
return {'FINISHED'}
|
||||
|
||||
def _rename_core(
|
||||
fct_get_info: typing.Callable[[bpy.types.Object, UTIL_naming_convention.RenameErrorReporter], UTIL_naming_convention.BallanceObjectInfo | None],
|
||||
ftc_set_info: typing.Callable[[bpy.types.Object, UTIL_naming_convention.BallanceObjectInfo, UTIL_naming_convention.RenameErrorReporter], bool]
|
||||
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_convension.BallanceObjectInfo, UTIL_naming_convension.RenameErrorReporter], bool]
|
||||
) -> None:
|
||||
# get selected objects. allow nested collection
|
||||
selected_objects: typing.Iterable[bpy.types.Object] = bpy.context.view_layer.active_layer_collection.collection.all_objects
|
||||
|
||||
# create reporter
|
||||
with UTIL_naming_convention.RenameErrorReporter() as reporter:
|
||||
with UTIL_naming_convension.RenameErrorReporter() as reporter:
|
||||
# iterate objects
|
||||
for obj in selected_objects:
|
||||
reporter.enter_object(obj)
|
||||
|
||||
# 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 valid, try assign it
|
||||
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.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,
|
||||
lambda x: str(x.value),
|
||||
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(
|
||||
os.path.dirname(__file__),
|
||||
"meshes",
|
||||
element_name + '.ph'
|
||||
element_name + '.bin'
|
||||
)
|
||||
|
||||
# open file and read
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import bpy
|
||||
import typing
|
||||
from dataclasses import dataclass
|
||||
from dataclasses import field as datafield
|
||||
from . import UTIL_functions
|
||||
|
||||
@dataclass
|
||||
class RawBallanceMapInfo():
|
||||
mSectorCount: int = datafield(default=1)
|
||||
cSectorCount: typing.ClassVar[int] = 1
|
||||
|
||||
mSectorCount: int
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.mSectorCount = kwargs.get("mSectorCount", RawBallanceMapInfo.cSectorCount)
|
||||
|
||||
def regulate(self):
|
||||
self.mSectorCount = UTIL_functions.clamp_int(self.mSectorCount, 1, 999)
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
import bpy
|
||||
import os, typing
|
||||
from dataclasses import dataclass
|
||||
from dataclasses import field as datafield
|
||||
from . import UTIL_naming_convention
|
||||
from . import UTIL_naming_convension
|
||||
|
||||
@dataclass
|
||||
class RawPreferences():
|
||||
mBallanceTextureFolder: str = datafield(default="")
|
||||
mNoComponentCollection: str = datafield(default="")
|
||||
cBallanceTextureFolder: typing.ClassVar[str] = ""
|
||||
cNoComponentCollection: typing.ClassVar[str] = ""
|
||||
|
||||
mBallanceTextureFolder: str
|
||||
mNoComponentCollection: str
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.mBallanceTextureFolder = kwargs.get("mBallanceTextureFolder", "")
|
||||
self.mNoComponentCollection = kwargs.get("mNoComponentCollection", "")
|
||||
|
||||
def has_valid_blc_tex_folder(self) -> bool:
|
||||
return os.path.isdir(self.mBallanceTextureFolder)
|
||||
|
||||
DEFAULT_RAW_PREFERENCES = RawPreferences()
|
||||
|
||||
class BBPPreferences(bpy.types.AddonPreferences):
|
||||
bl_idname = __package__
|
||||
|
||||
@@ -21,14 +23,14 @@ class BBPPreferences(bpy.types.AddonPreferences):
|
||||
name = "Ballance Texture Folder",
|
||||
description = "The path to folder which will be used by this plugin to get external Ballance texture.",
|
||||
subtype = 'DIR_PATH',
|
||||
default = DEFAULT_RAW_PREFERENCES.mBallanceTextureFolder,
|
||||
default = RawPreferences.cBallanceTextureFolder,
|
||||
translation_context = 'BBPPreferences/property'
|
||||
) # type: ignore
|
||||
|
||||
no_component_collection: bpy.props.StringProperty(
|
||||
name = "No Component Collection",
|
||||
description = "When importing, it is the name of collection where objects store will not be saved as component. When exporting, all forced no component objects will be stored in this name represented collection",
|
||||
default = DEFAULT_RAW_PREFERENCES.mNoComponentCollection,
|
||||
default = RawPreferences.cNoComponentCollection,
|
||||
translation_context = 'BBPPreferences/property'
|
||||
) # type: ignore
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ class BBP_PG_ptrprop_resolver(bpy.types.PropertyGroup):
|
||||
translation_context = 'BBP_PG_ptrprop_resolver/property'
|
||||
) # 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
|
||||
active_ioport_encodings: bpy.props.IntProperty() # type: ignore
|
||||
|
||||
@@ -195,16 +195,6 @@ class PropsVisitor():
|
||||
def get_ioport_encodings(self) -> tuple[str, ...]:
|
||||
encodings = get_ioport_encodings(self.__mAssocScene)
|
||||
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:
|
||||
target = get_ptrprop_resolver(self.__mAssocScene)
|
||||
row = layout.row()
|
||||
@@ -228,11 +218,24 @@ class PropsVisitor():
|
||||
col.separator()
|
||||
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:
|
||||
bpy.utils.register_class(BBP_PG_bmap_encoding)
|
||||
bpy.utils.register_class(BBP_UL_bmap_encoding)
|
||||
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_rm_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_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_UL_bmap_encoding)
|
||||
bpy.utils.unregister_class(BBP_PG_bmap_encoding)
|
||||
|
||||
@@ -1,404 +0,0 @@
|
||||
import bpy
|
||||
import typing, math, enum
|
||||
from dataclasses import dataclass
|
||||
from dataclasses import field as datafield
|
||||
from . import UTIL_functions, UTIL_virtools_types
|
||||
|
||||
@dataclass
|
||||
class RawVirtoolsCamera():
|
||||
|
||||
mProjectionType: UTIL_virtools_types.CK_CAMERA_PROJECTION = datafield(default=UTIL_virtools_types.CK_CAMERA_PROJECTION.CK_PERSPECTIVEPROJECTION)
|
||||
|
||||
mOrthographicZoom: float = datafield(default=1.0)
|
||||
|
||||
mFrontPlane: float = datafield(default=1.0)
|
||||
mBackPlane: float = datafield(default=4000.0)
|
||||
mFov: float = datafield(default=0.5)
|
||||
|
||||
mAspectRatio: tuple[int, int] = datafield(default_factory=lambda: (4, 3))
|
||||
|
||||
def regulate(self) -> None:
|
||||
# everything should be positive
|
||||
self.mOrthographicZoom = max(0.0, self.mOrthographicZoom)
|
||||
self.mFrontPlane = max(0.0, self.mFrontPlane)
|
||||
self.mBackPlane = max(0.0, self.mBackPlane)
|
||||
self.mFov = max(0.0, self.mFov)
|
||||
|
||||
# aspect ratio should be positive and at least 1
|
||||
(w, h) = self.mAspectRatio
|
||||
w = max(1, w)
|
||||
h = max(1, h)
|
||||
self.mAspectRatio = (w, h)
|
||||
|
||||
DEFAULT_RAW_VIRTOOLS_CAMERA = RawVirtoolsCamera()
|
||||
|
||||
#region Blender Enum Prop Helper
|
||||
|
||||
_g_Helper_CK_CAMERA_PROJECTION = UTIL_virtools_types.EnumPropHelper(UTIL_virtools_types.CK_CAMERA_PROJECTION)
|
||||
|
||||
#endregion
|
||||
|
||||
class BBP_PG_virtools_camera(bpy.types.PropertyGroup):
|
||||
projection_type: bpy.props.EnumProperty(
|
||||
name = "Type",
|
||||
description = "The type of this camera.",
|
||||
items = _g_Helper_CK_CAMERA_PROJECTION.generate_items(),
|
||||
default = _g_Helper_CK_CAMERA_PROJECTION.to_selection(DEFAULT_RAW_VIRTOOLS_CAMERA.mProjectionType),
|
||||
translation_context = 'BBP_PG_virtools_camera/property'
|
||||
) # type: ignore
|
||||
|
||||
orthographic_zoom: bpy.props.FloatProperty(
|
||||
name = "Orthographic Zoom",
|
||||
description = "Defines the orthographic zoom.",
|
||||
min = 0.0,
|
||||
soft_min = 0.0,
|
||||
soft_max = 0.5,
|
||||
step = 5,
|
||||
default = DEFAULT_RAW_VIRTOOLS_CAMERA.mOrthographicZoom,
|
||||
translation_context = 'BBP_PG_virtools_camera/property'
|
||||
) # type: ignore
|
||||
|
||||
front_plane: bpy.props.FloatProperty(
|
||||
name = "Front Plane",
|
||||
description = "Defines the front plane.",
|
||||
min = 0.0,
|
||||
soft_min = 0.0,
|
||||
soft_max = 5000.0,
|
||||
step = 100,
|
||||
default = DEFAULT_RAW_VIRTOOLS_CAMERA.mFrontPlane,
|
||||
translation_context = 'BBP_PG_virtools_camera/property'
|
||||
) # type: ignore
|
||||
|
||||
back_plane: bpy.props.FloatProperty(
|
||||
name = "Back Plane",
|
||||
description = "Defines the back plane.",
|
||||
min = 0.0,
|
||||
soft_min = 0.0,
|
||||
soft_max = 5000.0,
|
||||
step = 100,
|
||||
default = DEFAULT_RAW_VIRTOOLS_CAMERA.mBackPlane,
|
||||
translation_context = 'BBP_PG_virtools_camera/property'
|
||||
) # type: ignore
|
||||
|
||||
fov: bpy.props.FloatProperty(
|
||||
name = "Field of View",
|
||||
description = "Defines the field of view.",
|
||||
subtype = 'ANGLE',
|
||||
min = 0.0,
|
||||
max = math.radians(180.0),
|
||||
step = 100,
|
||||
precision = 100,
|
||||
default = DEFAULT_RAW_VIRTOOLS_CAMERA.mFov,
|
||||
translation_context = 'BBP_PG_virtools_camera/property'
|
||||
) # type: ignore
|
||||
|
||||
aspect_ratio_w: bpy.props.IntProperty(
|
||||
name = "Aspect Ratio Width",
|
||||
description = "Defines the width of aspect ratio.",
|
||||
min = 1,
|
||||
soft_min = 1,
|
||||
soft_max = 40,
|
||||
step = 1,
|
||||
default = DEFAULT_RAW_VIRTOOLS_CAMERA.mAspectRatio[0],
|
||||
translation_context = 'BBP_PG_virtools_camera/property'
|
||||
) # type: ignore
|
||||
|
||||
aspect_ratio_h: bpy.props.IntProperty(
|
||||
name = "Aspect Ratio Height",
|
||||
description = "Defines the height of aspect ratio.",
|
||||
min = 1,
|
||||
soft_min = 1,
|
||||
soft_max = 40,
|
||||
step = 1,
|
||||
default = DEFAULT_RAW_VIRTOOLS_CAMERA.mAspectRatio[1],
|
||||
translation_context = 'BBP_PG_virtools_camera/property'
|
||||
) # type: ignore
|
||||
|
||||
#region Getter Setter and Applyer
|
||||
|
||||
def get_virtools_camera(cam: bpy.types.Camera) -> BBP_PG_virtools_camera:
|
||||
return cam.virtools_camera
|
||||
|
||||
def get_raw_virtools_camera(cam: bpy.types.Camera) -> RawVirtoolsCamera:
|
||||
props: BBP_PG_virtools_camera = get_virtools_camera(cam)
|
||||
rawdata: RawVirtoolsCamera = RawVirtoolsCamera()
|
||||
|
||||
rawdata.mProjectionType = _g_Helper_CK_CAMERA_PROJECTION.get_selection(props.projection_type)
|
||||
|
||||
rawdata.mOrthographicZoom = props.orthographic_zoom
|
||||
|
||||
rawdata.mFrontPlane = props.front_plane
|
||||
rawdata.mBackPlane = props.back_plane
|
||||
rawdata.mFov = props.fov
|
||||
|
||||
rawdata.mAspectRatio = (props.aspect_ratio_w, props.aspect_ratio_h)
|
||||
|
||||
rawdata.regulate()
|
||||
return rawdata
|
||||
|
||||
def set_raw_virtools_camera(cam: bpy.types.Camera, rawdata: RawVirtoolsCamera) -> None:
|
||||
props: BBP_PG_virtools_camera = get_virtools_camera(cam)
|
||||
|
||||
props.projection_type = _g_Helper_CK_CAMERA_PROJECTION.to_selection(rawdata.mProjectionType)
|
||||
|
||||
props.orthographic_zoom = rawdata.mOrthographicZoom
|
||||
|
||||
props.front_plane = rawdata.mFrontPlane
|
||||
props.back_plane = rawdata.mBackPlane
|
||||
props.fov = rawdata.mFov
|
||||
|
||||
(props.aspect_ratio_w, props.aspect_ratio_h) = rawdata.mAspectRatio
|
||||
|
||||
def apply_to_blender_camera(cam: bpy.types.Camera) -> None:
|
||||
# get raw data first
|
||||
rawdata: RawVirtoolsCamera = get_raw_virtools_camera(cam)
|
||||
|
||||
# set camera type
|
||||
match(rawdata.mProjectionType):
|
||||
case UTIL_virtools_types.CK_CAMERA_PROJECTION.CK_PERSPECTIVEPROJECTION:
|
||||
cam.type = 'PERSP'
|
||||
case UTIL_virtools_types.CK_CAMERA_PROJECTION.CK_ORTHOGRAPHICPROJECTION:
|
||||
cam.type = 'ORTHO'
|
||||
|
||||
# set orthographic zoom
|
||||
cam.ortho_scale = rawdata.mOrthographicZoom
|
||||
|
||||
# front and back plane
|
||||
cam.clip_start = rawdata.mFrontPlane
|
||||
cam.clip_end = rawdata.mBackPlane
|
||||
|
||||
# fov
|
||||
cam.lens_unit = 'FOV'
|
||||
cam.angle = rawdata.mFov
|
||||
|
||||
def apply_to_blender_scene_resolution(cam: bpy.types.Camera) -> None:
|
||||
# get raw data first
|
||||
rawdata: RawVirtoolsCamera = get_raw_virtools_camera(cam)
|
||||
|
||||
# fetch width and height
|
||||
(w, h) = rawdata.mAspectRatio
|
||||
|
||||
# compute a proper resolution from this aspect ratio
|
||||
# calculate their lcm first
|
||||
hw_lcm = math.lcm(w, h)
|
||||
# get the first number which is greater than 1000 (1000 is a proper resolution size)
|
||||
# and can be integrally divided by this lcm.
|
||||
HW_MIN: int = 1000
|
||||
min_edge = ((HW_MIN // hw_lcm) + 1) * hw_lcm
|
||||
# calculate the final resolution
|
||||
if w < h:
|
||||
# width is shorter than height, set width as min edge
|
||||
width = min_edge
|
||||
height = width // w * h
|
||||
else:
|
||||
# opposite case
|
||||
height = min_edge
|
||||
width = height // h * w
|
||||
|
||||
# setup resolution
|
||||
render_settings = bpy.context.scene.render
|
||||
render_settings.resolution_x = width
|
||||
render_settings.resolution_y = height
|
||||
|
||||
#endregion
|
||||
|
||||
#region Aspect Ratio Preset
|
||||
|
||||
class AspectRatioPresetType(enum.IntEnum):
|
||||
Normal = enum.auto()
|
||||
Extended = enum.auto()
|
||||
Widescreen = enum.auto()
|
||||
Panoramic = enum.auto()
|
||||
|
||||
def to_aspect_ratio(self) -> tuple[int, int]:
|
||||
match self:
|
||||
case AspectRatioPresetType.Normal: return (4, 3)
|
||||
case AspectRatioPresetType.Extended: return (16, 9)
|
||||
case AspectRatioPresetType.Widescreen: return (7, 3)
|
||||
case AspectRatioPresetType.Panoramic: return (20, 7)
|
||||
|
||||
_g_AspectRatioPresetTypeDesc: dict[AspectRatioPresetType, tuple[str, str]] = {
|
||||
AspectRatioPresetType.Normal: ("Normal", "Aspect ratio: 4:3."),
|
||||
AspectRatioPresetType.Extended: ("Extended", "Aspect ratio: 16:9."),
|
||||
AspectRatioPresetType.Widescreen: ("Widescreen", "Aspect ratio: 7:3."),
|
||||
AspectRatioPresetType.Panoramic: ("Panoramic", "Aspect ratio: 20:7."),
|
||||
}
|
||||
|
||||
_g_Helper_AspectRatioPresetType = UTIL_functions.EnumPropHelper(
|
||||
AspectRatioPresetType,
|
||||
lambda x: str(x.value),
|
||||
lambda x: AspectRatioPresetType(int(x)),
|
||||
lambda x: _g_AspectRatioPresetTypeDesc[x][0],
|
||||
lambda x: _g_AspectRatioPresetTypeDesc[x][1],
|
||||
lambda _: ""
|
||||
)
|
||||
|
||||
def preset_virtools_camera_aspect_ratio(cam: bpy.types.Camera, preset_type: AspectRatioPresetType) -> None:
|
||||
# get raw data from it
|
||||
rawdata = get_raw_virtools_camera(cam)
|
||||
# modify its aspect ratio
|
||||
rawdata.mAspectRatio = preset_type.to_aspect_ratio()
|
||||
# rewrite it.
|
||||
set_raw_virtools_camera(cam, rawdata)
|
||||
|
||||
#endregion
|
||||
|
||||
#region Operators
|
||||
|
||||
class BBP_OT_apply_virtools_camera(bpy.types.Operator):
|
||||
"""Apply Virtools Camera to Blender Camera except Resolution."""
|
||||
bl_idname = "bbp.apply_virtools_camera"
|
||||
bl_label = "Apply to Blender Camera"
|
||||
bl_options = {'UNDO'}
|
||||
bl_translation_context = 'BBP_OT_apply_virtools_camera'
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.camera is not None
|
||||
|
||||
def execute(self, context):
|
||||
cam: bpy.types.Camera = context.camera
|
||||
apply_to_blender_camera(cam)
|
||||
return {'FINISHED'}
|
||||
|
||||
class BBP_OT_apply_virtools_camera_resolution(bpy.types.Operator):
|
||||
"""Apply Virtools Camera Resolution to Blender Scene."""
|
||||
bl_idname = "bbp.apply_virtools_camera_resolution"
|
||||
bl_label = "Apply to Blender Scene Resolution"
|
||||
bl_options = {'UNDO'}
|
||||
bl_translation_context = 'BBP_OT_apply_virtools_camera_resolution'
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.camera is not None
|
||||
|
||||
def execute(self, context):
|
||||
cam: bpy.types.Camera = context.camera
|
||||
apply_to_blender_scene_resolution(cam)
|
||||
return {'FINISHED'}
|
||||
|
||||
class BBP_OT_preset_virtools_camera_aspect_ratio(bpy.types.Operator):
|
||||
"""Preset Virtools Camera Aspect Ratio with Virtools Presets."""
|
||||
bl_idname = "bbp.preset_virtools_camera_aspect_ratio"
|
||||
bl_label = "Preset Virtools Camera Aspect Ratio"
|
||||
bl_options = {'UNDO'}
|
||||
bl_translation_context = 'BBP_OT_preset_virtools_camera_aspect_ratio'
|
||||
|
||||
preset_type: bpy.props.EnumProperty(
|
||||
name = "Preset",
|
||||
description = "The preset which you want to apply.",
|
||||
items = _g_Helper_AspectRatioPresetType.generate_items(),
|
||||
translation_context = 'BBP_OT_preset_virtools_camera_aspect_ratio/property'
|
||||
) # type: ignore
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.camera is not None
|
||||
|
||||
def invoke(self, context, event):
|
||||
wm = context.window_manager
|
||||
return wm.invoke_props_dialog(self)
|
||||
|
||||
def draw(self, context):
|
||||
self.layout.prop(self, "preset_type")
|
||||
|
||||
def execute(self, context):
|
||||
# get essential value
|
||||
cam: bpy.types.Camera = context.camera
|
||||
expected_preset: AspectRatioPresetType = _g_Helper_AspectRatioPresetType.get_selection(self.preset_type)
|
||||
|
||||
# apply preset to material
|
||||
preset_virtools_camera_aspect_ratio(cam, expected_preset)
|
||||
return {'FINISHED'}
|
||||
|
||||
#endregion
|
||||
|
||||
class BBP_PT_virtools_camera(bpy.types.Panel):
|
||||
"""Show Virtools Camera Properties"""
|
||||
bl_label = "Virtools Camera"
|
||||
bl_idname = "BBP_PT_virtools_camera"
|
||||
bl_space_type = 'PROPERTIES'
|
||||
bl_region_type = 'WINDOW'
|
||||
bl_context = "data" # idk why blender use `data` as the camera tab same as mesh.
|
||||
bl_translation_context = 'BBP_PT_virtools_camera'
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.camera is not None
|
||||
|
||||
def draw(self, context):
|
||||
# get layout and target
|
||||
layout = self.layout
|
||||
cam: bpy.types.Camera = context.camera
|
||||
props: BBP_PG_virtools_camera = get_virtools_camera(cam)
|
||||
rawdata: RawVirtoolsCamera = get_raw_virtools_camera(cam)
|
||||
|
||||
# draw operator
|
||||
row = layout.row()
|
||||
row.operator(
|
||||
BBP_OT_apply_virtools_camera.bl_idname, text='Apply', icon='NODETREE',
|
||||
text_ctxt='BBP_PT_virtools_camera/draw')
|
||||
row.operator(
|
||||
BBP_OT_apply_virtools_camera_resolution.bl_idname, text='Apply Resolution', icon='SCENE',
|
||||
text_ctxt='BBP_PT_virtools_camera/draw')
|
||||
|
||||
# draw data
|
||||
layout.separator()
|
||||
# show camera type first
|
||||
layout.prop(props, 'projection_type')
|
||||
# all camera has front and back plane
|
||||
layout.label(text='Clipping', text_ctxt='BBP_PT_virtools_camera/draw')
|
||||
sublayout = layout.column()
|
||||
sublayout.use_property_split = True
|
||||
sublayout.prop(props, 'front_plane')
|
||||
sublayout.prop(props, 'back_plane')
|
||||
|
||||
# only perspective camera has fov setting
|
||||
if rawdata.mProjectionType == UTIL_virtools_types.CK_CAMERA_PROJECTION.CK_PERSPECTIVEPROJECTION:
|
||||
layout.separator()
|
||||
layout.label(text='Perspective Parameters', text_ctxt='BBP_PT_virtools_camera/draw')
|
||||
sublayout = layout.column()
|
||||
sublayout.use_property_split = True
|
||||
sublayout.prop(props, 'fov')
|
||||
|
||||
# only orthographic camera has orthographic zoom setting
|
||||
if rawdata.mProjectionType == UTIL_virtools_types.CK_CAMERA_PROJECTION.CK_ORTHOGRAPHICPROJECTION:
|
||||
layout.separator()
|
||||
layout.label(text='Orthographic Parameters', text_ctxt='BBP_PT_virtools_camera/draw')
|
||||
sublayout = layout.column()
|
||||
sublayout.use_property_split = True
|
||||
sublayout.prop(props, 'orthographic_zoom')
|
||||
|
||||
# aspect ratio
|
||||
layout.separator()
|
||||
row = layout.row()
|
||||
row.label(text='Aspect Ratio', text_ctxt='BBP_PT_virtools_camera/draw')
|
||||
row.operator(BBP_OT_preset_virtools_camera_aspect_ratio.bl_idname, text='', icon = "PRESET")
|
||||
sublayout = layout.row()
|
||||
sublayout.use_property_split = False
|
||||
sublayout.prop(props, 'aspect_ratio_w', text = '', expand = True)
|
||||
sublayout.prop(props, 'aspect_ratio_h', text = '', expand = True)
|
||||
|
||||
# Register
|
||||
|
||||
def register() -> None:
|
||||
bpy.utils.register_class(BBP_PG_virtools_camera)
|
||||
bpy.utils.register_class(BBP_OT_apply_virtools_camera)
|
||||
bpy.utils.register_class(BBP_OT_apply_virtools_camera_resolution)
|
||||
bpy.utils.register_class(BBP_OT_preset_virtools_camera_aspect_ratio)
|
||||
bpy.utils.register_class(BBP_PT_virtools_camera)
|
||||
|
||||
# add into camera metadata
|
||||
bpy.types.Camera.virtools_camera = bpy.props.PointerProperty(type = BBP_PG_virtools_camera)
|
||||
|
||||
def unregister() -> None:
|
||||
# remove from metadata
|
||||
del bpy.types.Camera.virtools_camera
|
||||
|
||||
bpy.utils.unregister_class(BBP_PT_virtools_camera)
|
||||
bpy.utils.unregister_class(BBP_OT_preset_virtools_camera_aspect_ratio)
|
||||
bpy.utils.unregister_class(BBP_OT_apply_virtools_camera_resolution)
|
||||
bpy.utils.unregister_class(BBP_OT_apply_virtools_camera)
|
||||
bpy.utils.unregister_class(BBP_PG_virtools_camera)
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import bpy
|
||||
import typing, enum
|
||||
from . import UTIL_functions, UTIL_icons_manager, UTIL_blender_mesh
|
||||
from . import UTIL_functions, UTIL_icons_manager
|
||||
|
||||
#region Virtools Groups Define & Help Class
|
||||
|
||||
@@ -205,7 +205,7 @@ class VirtoolsGroupsPreset(enum.Enum):
|
||||
|
||||
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
|
||||
# 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
|
||||
else: return UTIL_icons_manager.get_empty_icon()
|
||||
# blender group name prop helper
|
||||
_g_EnumHelper_Group = UTIL_functions.EnumPropHelper(
|
||||
_g_EnumHelper_Group: UTIL_functions.EnumPropHelper = UTIL_functions.EnumPropHelper(
|
||||
VirtoolsGroupsPreset,
|
||||
lambda x: x.value, # member is string self
|
||||
lambda x: VirtoolsGroupsPreset(x), # convert directly because it is StrEnum.
|
||||
@@ -387,9 +387,9 @@ class BBP_PT_virtools_groups(bpy.types.Panel):
|
||||
target = typing.cast(bpy.types.Object, context.active_object)
|
||||
|
||||
# notify on non-mesh object
|
||||
if not UTIL_blender_mesh.TemporaryMesh.has_geometry(target):
|
||||
if target.type != 'MESH':
|
||||
layout.label(
|
||||
text='Virtools Group is invalid on non-mesh-like object!', icon='ERROR',
|
||||
text='Virtools Group is invalid on non-mesh object!', icon='ERROR',
|
||||
text_ctxt='BBP_PT_virtools_groups/draw')
|
||||
|
||||
# draw main body
|
||||
|
||||
@@ -1,26 +1,55 @@
|
||||
import bpy
|
||||
import bpy, mathutils
|
||||
from bpy.types import Context
|
||||
import typing, math
|
||||
from dataclasses import dataclass
|
||||
from dataclasses import field as datafield
|
||||
from . import UTIL_functions, UTIL_virtools_types
|
||||
|
||||
# Raw Data
|
||||
|
||||
@dataclass
|
||||
class RawVirtoolsLight():
|
||||
|
||||
mType: UTIL_virtools_types.VXLIGHT_TYPE = datafield(default=UTIL_virtools_types.VXLIGHT_TYPE.VX_LIGHTPOINT)
|
||||
mColor: UTIL_virtools_types.VxColor = datafield(default_factory=lambda: UTIL_virtools_types.VxColor(1.0, 1.0, 1.0, 1.0))
|
||||
# Class member
|
||||
|
||||
mConstantAttenuation: float = datafield(default=1.0)
|
||||
mLinearAttenuation: float = datafield(default=0.0)
|
||||
mQuadraticAttenuation: float = datafield(default=0.0)
|
||||
mType: UTIL_virtools_types.VXLIGHT_TYPE
|
||||
mColor: UTIL_virtools_types.VxColor
|
||||
|
||||
mRange: float = datafield(default=100.0)
|
||||
mConstantAttenuation: float
|
||||
mLinearAttenuation: float
|
||||
mQuadraticAttenuation: float
|
||||
|
||||
mHotSpot: float = datafield(default=math.radians(40))
|
||||
mFalloff: float = datafield(default=math.radians(45))
|
||||
mFalloffShape: float = datafield(default=1.0)
|
||||
mRange: float
|
||||
|
||||
mHotSpot: float
|
||||
mFalloff: float
|
||||
mFalloffShape: float
|
||||
|
||||
# Class member default value
|
||||
|
||||
cDefaultType: typing.ClassVar[UTIL_virtools_types.VXLIGHT_TYPE] = UTIL_virtools_types.VXLIGHT_TYPE.VX_LIGHTPOINT
|
||||
cDefaultColor: typing.ClassVar[UTIL_virtools_types.VxColor] = UTIL_virtools_types.VxColor(1.0, 1.0, 1.0, 1.0)
|
||||
|
||||
cDefaultConstantAttenuation: typing.ClassVar[float] = 1.0
|
||||
cDefaultLinearAttenuation: typing.ClassVar[float] = 0.0
|
||||
cDefaultQuadraticAttenuation: typing.ClassVar[float] = 0.0
|
||||
|
||||
cDefaultRange: typing.ClassVar[float] = 100.0
|
||||
|
||||
cDefaultHotSpot: typing.ClassVar[float] = math.radians(40)
|
||||
cDefaultFalloff: typing.ClassVar[float] = math.radians(45)
|
||||
cDefaultFalloffShape: typing.ClassVar[float] = 1.0
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
# assign default value for each component
|
||||
self.mType = kwargs.get('mType', RawVirtoolsLight.cDefaultType)
|
||||
self.mColor = kwargs.get('mColor', RawVirtoolsLight.cDefaultColor).clone()
|
||||
|
||||
self.mConstantAttenuation = kwargs.get('mConstantAttenuation', RawVirtoolsLight.cDefaultConstantAttenuation)
|
||||
self.mLinearAttenuation = kwargs.get('mLinearAttenuation', RawVirtoolsLight.cDefaultLinearAttenuation)
|
||||
self.mQuadraticAttenuation = kwargs.get('mQuadraticAttenuation', RawVirtoolsLight.cDefaultQuadraticAttenuation)
|
||||
|
||||
self.mRange = kwargs.get('mRange', RawVirtoolsLight.cDefaultRange)
|
||||
|
||||
self.mHotSpot = kwargs.get('mHotSpot', RawVirtoolsLight.cDefaultHotSpot)
|
||||
self.mFalloff = kwargs.get('mFalloff', RawVirtoolsLight.cDefaultFalloff)
|
||||
self.mFalloffShape = kwargs.get('mFalloffShape', RawVirtoolsLight.cDefaultFalloffShape)
|
||||
|
||||
def regulate(self) -> None:
|
||||
# regulate color and reset its alpha value
|
||||
@@ -42,18 +71,16 @@ class RawVirtoolsLight():
|
||||
if self.mFalloff < self.mHotSpot:
|
||||
self.mFalloff = self.mHotSpot
|
||||
|
||||
DEFAULT_RAW_VIRTOOLS_LIGHT = RawVirtoolsLight()
|
||||
|
||||
# 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):
|
||||
light_type: bpy.props.EnumProperty(
|
||||
name = "Type",
|
||||
description = "The type of this light",
|
||||
items = _g_Helper_VXLIGHT_TYPE.generate_items(),
|
||||
default = _g_Helper_VXLIGHT_TYPE.to_selection(DEFAULT_RAW_VIRTOOLS_LIGHT.mType),
|
||||
default = _g_Helper_VXLIGHT_TYPE.to_selection(RawVirtoolsLight.cDefaultType),
|
||||
translation_context = 'BBP_PG_virtools_light/property'
|
||||
) # type: ignore
|
||||
|
||||
@@ -64,7 +91,7 @@ class BBP_PG_virtools_light(bpy.types.PropertyGroup):
|
||||
min = 0.0,
|
||||
max = 1.0,
|
||||
size = 3,
|
||||
default = DEFAULT_RAW_VIRTOOLS_LIGHT.mColor.to_const_rgb(),
|
||||
default = RawVirtoolsLight.cDefaultColor.to_const_rgb(),
|
||||
translation_context = 'BBP_PG_virtools_light/property'
|
||||
) # type: ignore
|
||||
|
||||
@@ -74,7 +101,7 @@ class BBP_PG_virtools_light(bpy.types.PropertyGroup):
|
||||
min = 0.0,
|
||||
max = 10.0,
|
||||
step = 10,
|
||||
default = DEFAULT_RAW_VIRTOOLS_LIGHT.mConstantAttenuation,
|
||||
default = RawVirtoolsLight.cDefaultConstantAttenuation,
|
||||
translation_context = 'BBP_PG_virtools_light/property'
|
||||
) # type: ignore
|
||||
|
||||
@@ -84,7 +111,7 @@ class BBP_PG_virtools_light(bpy.types.PropertyGroup):
|
||||
min = 0.0,
|
||||
max = 10.0,
|
||||
step = 10,
|
||||
default = DEFAULT_RAW_VIRTOOLS_LIGHT.mLinearAttenuation,
|
||||
default = RawVirtoolsLight.cDefaultLinearAttenuation,
|
||||
translation_context = 'BBP_PG_virtools_light/property'
|
||||
) # type: ignore
|
||||
|
||||
@@ -94,7 +121,7 @@ class BBP_PG_virtools_light(bpy.types.PropertyGroup):
|
||||
min = 0.0,
|
||||
max = 10.0,
|
||||
step = 10,
|
||||
default = DEFAULT_RAW_VIRTOOLS_LIGHT.mQuadraticAttenuation,
|
||||
default = RawVirtoolsLight.cDefaultQuadraticAttenuation,
|
||||
translation_context = 'BBP_PG_virtools_light/property'
|
||||
) # type: ignore
|
||||
|
||||
@@ -104,7 +131,7 @@ class BBP_PG_virtools_light(bpy.types.PropertyGroup):
|
||||
min = 0.0,
|
||||
max = 200.0,
|
||||
step = 100,
|
||||
default = DEFAULT_RAW_VIRTOOLS_LIGHT.mRange,
|
||||
default = RawVirtoolsLight.cDefaultRange,
|
||||
translation_context = 'BBP_PG_virtools_light/property'
|
||||
) # type: ignore
|
||||
|
||||
@@ -114,7 +141,7 @@ class BBP_PG_virtools_light(bpy.types.PropertyGroup):
|
||||
min = 0.0,
|
||||
max = math.radians(180),
|
||||
subtype = 'ANGLE',
|
||||
default = DEFAULT_RAW_VIRTOOLS_LIGHT.mHotSpot,
|
||||
default = RawVirtoolsLight.cDefaultHotSpot,
|
||||
translation_context = 'BBP_PG_virtools_light/property'
|
||||
) # type: ignore
|
||||
|
||||
@@ -124,7 +151,7 @@ class BBP_PG_virtools_light(bpy.types.PropertyGroup):
|
||||
min = 0.0,
|
||||
max = math.radians(180),
|
||||
subtype = 'ANGLE',
|
||||
default = DEFAULT_RAW_VIRTOOLS_LIGHT.mFalloff,
|
||||
default = RawVirtoolsLight.cDefaultFalloff,
|
||||
translation_context = 'BBP_PG_virtools_light/property'
|
||||
) # type: ignore
|
||||
|
||||
@@ -134,7 +161,7 @@ class BBP_PG_virtools_light(bpy.types.PropertyGroup):
|
||||
min = 0.0,
|
||||
max = 10.0,
|
||||
step = 10,
|
||||
default = DEFAULT_RAW_VIRTOOLS_LIGHT.mFalloffShape,
|
||||
default = RawVirtoolsLight.cDefaultFalloffShape,
|
||||
translation_context = 'BBP_PG_virtools_light/property'
|
||||
) # type: ignore
|
||||
|
||||
|
||||
@@ -1,41 +1,98 @@
|
||||
import bpy
|
||||
import typing, enum, copy, os
|
||||
from dataclasses import dataclass
|
||||
from dataclasses import field as datafield
|
||||
from . import UTIL_virtools_types, UTIL_functions, UTIL_ballance_texture, UTIL_file_browser
|
||||
from . import PROP_virtools_texture, PROP_preferences
|
||||
|
||||
@dataclass
|
||||
class RawVirtoolsMaterial():
|
||||
|
||||
mDiffuse: UTIL_virtools_types.VxColor = datafield(default_factory=lambda: UTIL_virtools_types.VxColor(0.7, 0.7, 0.7, 1.0))
|
||||
mAmbient: UTIL_virtools_types.VxColor = datafield(default_factory=lambda: UTIL_virtools_types.VxColor(0.3, 0.3, 0.3, 1.0))
|
||||
mSpecular: UTIL_virtools_types.VxColor = datafield(default_factory=lambda: UTIL_virtools_types.VxColor(0.5, 0.5, 0.5, 1.0))
|
||||
mEmissive: UTIL_virtools_types.VxColor = datafield(default_factory=lambda: UTIL_virtools_types.VxColor(0.0, 0.0, 0.0, 1.0))
|
||||
mSpecularPower: float = datafield(default=0.0)
|
||||
# Instance Member Declarations
|
||||
|
||||
mTexture: bpy.types.Image | None = datafield(default=None)
|
||||
mTextureBorderColor: UTIL_virtools_types.VxColor = datafield(default_factory=lambda: UTIL_virtools_types.VxColor(0.0, 0.0, 0.0, 0.0))
|
||||
mDiffuse: UTIL_virtools_types.VxColor
|
||||
mAmbient: UTIL_virtools_types.VxColor
|
||||
mSpecular: UTIL_virtools_types.VxColor
|
||||
mEmissive: UTIL_virtools_types.VxColor
|
||||
mSpecularPower: float
|
||||
|
||||
mTextureBlendMode: UTIL_virtools_types.VXTEXTURE_BLENDMODE = datafield(default=UTIL_virtools_types.VXTEXTURE_BLENDMODE.VXTEXTUREBLEND_MODULATEALPHA)
|
||||
mTextureMinMode: UTIL_virtools_types.VXTEXTURE_FILTERMODE = datafield(default=UTIL_virtools_types.VXTEXTURE_FILTERMODE.VXTEXTUREFILTER_LINEAR)
|
||||
mTextureMagMode: UTIL_virtools_types.VXTEXTURE_FILTERMODE = datafield(default=UTIL_virtools_types.VXTEXTURE_FILTERMODE.VXTEXTUREFILTER_LINEAR)
|
||||
mTextureAddressMode: UTIL_virtools_types.VXTEXTURE_ADDRESSMODE = datafield(default=UTIL_virtools_types.VXTEXTURE_ADDRESSMODE.VXTEXTURE_ADDRESSWRAP)
|
||||
mTexture: bpy.types.Image | None
|
||||
mTextureBorderColor: UTIL_virtools_types.VxColor
|
||||
|
||||
mSourceBlend: UTIL_virtools_types.VXBLEND_MODE = datafield(default=UTIL_virtools_types.VXBLEND_MODE.VXBLEND_ONE)
|
||||
mDestBlend: UTIL_virtools_types.VXBLEND_MODE = datafield(default=UTIL_virtools_types.VXBLEND_MODE.VXBLEND_ZERO)
|
||||
mFillMode: UTIL_virtools_types.VXFILL_MODE = datafield(default=UTIL_virtools_types.VXFILL_MODE.VXFILL_SOLID)
|
||||
mShadeMode: UTIL_virtools_types.VXSHADE_MODE = datafield(default=UTIL_virtools_types.VXSHADE_MODE.VXSHADE_GOURAUD)
|
||||
mTextureBlendMode: UTIL_virtools_types.VXTEXTURE_BLENDMODE
|
||||
mTextureMinMode: UTIL_virtools_types.VXTEXTURE_FILTERMODE
|
||||
mTextureMagMode: UTIL_virtools_types.VXTEXTURE_FILTERMODE
|
||||
mTextureAddressMode: UTIL_virtools_types.VXTEXTURE_ADDRESSMODE
|
||||
|
||||
mEnableAlphaTest: bool = datafield(default=False)
|
||||
mEnableAlphaBlend: bool = datafield(default=False)
|
||||
mEnablePerspectiveCorrection: bool = datafield(default=True)
|
||||
mEnableZWrite: bool = datafield(default=True)
|
||||
mEnableTwoSided: bool = datafield(default=False)
|
||||
mSourceBlend: UTIL_virtools_types.VXBLEND_MODE
|
||||
mDestBlend: UTIL_virtools_types.VXBLEND_MODE
|
||||
mFillMode: UTIL_virtools_types.VXFILL_MODE
|
||||
mShadeMode: UTIL_virtools_types.VXSHADE_MODE
|
||||
|
||||
mAlphaRef: int = datafield(default=0)
|
||||
mAlphaFunc: UTIL_virtools_types.VXCMPFUNC = datafield(default=UTIL_virtools_types.VXCMPFUNC.VXCMP_ALWAYS)
|
||||
mZFunc: UTIL_virtools_types.VXCMPFUNC = datafield(default=UTIL_virtools_types.VXCMPFUNC.VXCMP_LESSEQUAL)
|
||||
mEnableAlphaTest: bool
|
||||
mEnableAlphaBlend: bool
|
||||
mEnablePerspectiveCorrection: bool
|
||||
mEnableZWrite: bool
|
||||
mEnableTwoSided: bool
|
||||
|
||||
mAlphaRef: int
|
||||
mAlphaFunc: UTIL_virtools_types.VXCMPFUNC
|
||||
mZFunc: UTIL_virtools_types.VXCMPFUNC
|
||||
|
||||
# Default Value Declarations
|
||||
|
||||
cDefaultDiffuse: typing.ClassVar[UTIL_virtools_types.VxColor] = UTIL_virtools_types.VxColor(0.7, 0.7, 0.7, 1.0)
|
||||
cDefaultAmbient: typing.ClassVar[UTIL_virtools_types.VxColor] = UTIL_virtools_types.VxColor(0.3, 0.3, 0.3, 1.0)
|
||||
cDefaultSpecular: typing.ClassVar[UTIL_virtools_types.VxColor] = UTIL_virtools_types.VxColor(0.5, 0.5, 0.5, 1.0)
|
||||
cDefaultEmissive: typing.ClassVar[UTIL_virtools_types.VxColor] = UTIL_virtools_types.VxColor(0.0, 0.0, 0.0, 1.0)
|
||||
cDefaultSpecularPower: typing.ClassVar[float] = 0.0
|
||||
|
||||
cDefaultTexture: typing.ClassVar[bpy.types.Image | None] = None
|
||||
cDefaultTextureBorderColor: typing.ClassVar[UTIL_virtools_types.VxColor] = UTIL_virtools_types.VxColor(0.0, 0.0, 0.0, 0.0)
|
||||
|
||||
cDefaultTextureBlendMode: typing.ClassVar[UTIL_virtools_types.VXTEXTURE_BLENDMODE]= UTIL_virtools_types.VXTEXTURE_BLENDMODE.VXTEXTUREBLEND_MODULATEALPHA
|
||||
cDefaultTextureMinMode: typing.ClassVar[UTIL_virtools_types.VXTEXTURE_FILTERMODE] = UTIL_virtools_types.VXTEXTURE_FILTERMODE.VXTEXTUREFILTER_LINEAR
|
||||
cDefaultTextureMagMode: typing.ClassVar[UTIL_virtools_types.VXTEXTURE_FILTERMODE] = UTIL_virtools_types.VXTEXTURE_FILTERMODE.VXTEXTUREFILTER_LINEAR
|
||||
cDefaultTextureAddressMode: typing.ClassVar[UTIL_virtools_types.VXTEXTURE_ADDRESSMODE] = UTIL_virtools_types.VXTEXTURE_ADDRESSMODE.VXTEXTURE_ADDRESSWRAP
|
||||
|
||||
cDefaultSourceBlend: typing.ClassVar[UTIL_virtools_types.VXBLEND_MODE] = UTIL_virtools_types.VXBLEND_MODE.VXBLEND_ONE
|
||||
cDefaultDestBlend: typing.ClassVar[UTIL_virtools_types.VXBLEND_MODE] = UTIL_virtools_types.VXBLEND_MODE.VXBLEND_ZERO
|
||||
cDefaultFillMode: typing.ClassVar[UTIL_virtools_types.VXFILL_MODE] = UTIL_virtools_types.VXFILL_MODE.VXFILL_SOLID
|
||||
cDefaultShadeMode: typing.ClassVar[UTIL_virtools_types.VXSHADE_MODE] = UTIL_virtools_types.VXSHADE_MODE.VXSHADE_GOURAUD
|
||||
|
||||
cDefaultEnableAlphaTest: typing.ClassVar[bool] = False
|
||||
cDefaultEnableAlphaBlend: typing.ClassVar[bool] = False
|
||||
cDefaultEnablePerspectiveCorrection: typing.ClassVar[bool] = True
|
||||
cDefaultEnableZWrite: typing.ClassVar[bool] = True
|
||||
cDefaultEnableTwoSided: typing.ClassVar[bool] = False
|
||||
|
||||
cDefaultAlphaRef: typing.ClassVar[int] = 0
|
||||
cDefaultAlphaFunc: typing.ClassVar[UTIL_virtools_types.VXCMPFUNC] = UTIL_virtools_types.VXCMPFUNC.VXCMP_ALWAYS
|
||||
cDefaultZFunc: typing.ClassVar[UTIL_virtools_types.VXCMPFUNC] = UTIL_virtools_types.VXCMPFUNC.VXCMP_LESSEQUAL
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
# assign default value for each component
|
||||
self.mDiffuse = kwargs.get('mDiffuse', RawVirtoolsMaterial.cDefaultDiffuse).clone()
|
||||
self.mAmbient = kwargs.get('mAmbient', RawVirtoolsMaterial.cDefaultAmbient).clone()
|
||||
self.mSpecular = kwargs.get('mSpecular', RawVirtoolsMaterial.cDefaultSpecular).clone()
|
||||
self.mSpecularPower = kwargs.get('mSpecularPower', RawVirtoolsMaterial.cDefaultSpecularPower)
|
||||
self.mEmissive = kwargs.get('mEmissive', RawVirtoolsMaterial.cDefaultEmissive).clone()
|
||||
self.mEnableTwoSided = kwargs.get('mEnableTwoSided', RawVirtoolsMaterial.cDefaultEnableTwoSided)
|
||||
self.mTexture = kwargs.get('mTexture', RawVirtoolsMaterial.cDefaultTexture)
|
||||
self.mTextureMinMode = kwargs.get('mTextureMinMode', RawVirtoolsMaterial.cDefaultTextureMinMode)
|
||||
self.mTextureMagMode = kwargs.get('mTextureMagMode', RawVirtoolsMaterial.cDefaultTextureMagMode)
|
||||
self.mSourceBlend = kwargs.get('mSourceBlend', RawVirtoolsMaterial.cDefaultSourceBlend)
|
||||
self.mDestBlend = kwargs.get('mDestBlend', RawVirtoolsMaterial.cDefaultDestBlend)
|
||||
self.mEnableAlphaBlend = kwargs.get('mEnableAlphaBlend', RawVirtoolsMaterial.cDefaultEnableAlphaBlend)
|
||||
self.mShadeMode = kwargs.get('mShadeMode', RawVirtoolsMaterial.cDefaultShadeMode)
|
||||
self.mFillMode = kwargs.get('mFillMode', RawVirtoolsMaterial.cDefaultFillMode)
|
||||
self.mEnableAlphaTest = kwargs.get('mEnableAlphaTest', RawVirtoolsMaterial.cDefaultEnableAlphaTest)
|
||||
self.mEnableZWrite = kwargs.get('mEnableZWrite', RawVirtoolsMaterial.cDefaultEnableZWrite)
|
||||
|
||||
self.mEnablePerspectiveCorrection = kwargs.get('mEnablePerspectiveCorrection', RawVirtoolsMaterial.cDefaultEnablePerspectiveCorrection)
|
||||
self.mTextureBlendMode = kwargs.get('mTextureBlendMode', RawVirtoolsMaterial.cDefaultTextureBlendMode)
|
||||
self.mTextureAddressMode = kwargs.get('mTextureAddressMode', RawVirtoolsMaterial.cDefaultTextureAddressMode)
|
||||
self.mZFunc = kwargs.get('mZFunc', RawVirtoolsMaterial.cDefaultZFunc)
|
||||
self.mAlphaFunc = kwargs.get('mAlphaFunc', RawVirtoolsMaterial.cDefaultAlphaFunc)
|
||||
self.mTextureBorderColor = kwargs.get('mTextureBorderColor', RawVirtoolsMaterial.cDefaultTextureBorderColor).clone()
|
||||
self.mAlphaRef = kwargs.get('mAlphaRef', RawVirtoolsMaterial.cDefaultAlphaRef)
|
||||
|
||||
def regulate(self) -> None:
|
||||
# regulate colors
|
||||
@@ -55,17 +112,15 @@ class RawVirtoolsMaterial():
|
||||
# specular power
|
||||
self.mSpecularPower = UTIL_functions.clamp_float(self.mSpecularPower, 0.0, 100.0)
|
||||
|
||||
DEFAULT_RAW_VIRTOOLS_MATERIAL = RawVirtoolsMaterial()
|
||||
|
||||
#region Blender Enum Prop Helper (Virtools type specified)
|
||||
|
||||
_g_Helper_VXTEXTURE_BLENDMODE = 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_ADDRESSMODE = UTIL_virtools_types.EnumPropHelper(UTIL_virtools_types.VXTEXTURE_ADDRESSMODE)
|
||||
_g_Helper_VXBLEND_MODE = UTIL_virtools_types.EnumPropHelper(UTIL_virtools_types.VXBLEND_MODE)
|
||||
_g_Helper_VXFILL_MODE = UTIL_virtools_types.EnumPropHelper(UTIL_virtools_types.VXFILL_MODE)
|
||||
_g_Helper_VXSHADE_MODE = UTIL_virtools_types.EnumPropHelper(UTIL_virtools_types.VXSHADE_MODE)
|
||||
_g_Helper_VXCMPFUNC = UTIL_virtools_types.EnumPropHelper(UTIL_virtools_types.VXCMPFUNC)
|
||||
_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.EnumPropHelper(UTIL_virtools_types.VXTEXTURE_FILTERMODE)
|
||||
_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.EnumPropHelper(UTIL_virtools_types.VXBLEND_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.EnumPropHelper(UTIL_virtools_types.VXSHADE_MODE)
|
||||
_g_Helper_VXCMPFUNC: UTIL_virtools_types.EnumPropHelper = UTIL_virtools_types.EnumPropHelper(UTIL_virtools_types.VXCMPFUNC)
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -77,7 +132,7 @@ class BBP_PG_virtools_material(bpy.types.PropertyGroup):
|
||||
min = 0.0,
|
||||
max = 1.0,
|
||||
size = 3,
|
||||
default = DEFAULT_RAW_VIRTOOLS_MATERIAL.mAmbient.to_const_rgb(),
|
||||
default = RawVirtoolsMaterial.cDefaultAmbient.to_const_rgb(),
|
||||
translation_context = 'BBP_PG_virtools_material/property'
|
||||
) # type: ignore
|
||||
|
||||
@@ -88,7 +143,7 @@ class BBP_PG_virtools_material(bpy.types.PropertyGroup):
|
||||
min = 0.0,
|
||||
max = 1.0,
|
||||
size = 4,
|
||||
default = DEFAULT_RAW_VIRTOOLS_MATERIAL.mDiffuse.to_const_rgba(),
|
||||
default = RawVirtoolsMaterial.cDefaultDiffuse.to_const_rgba(),
|
||||
translation_context = 'BBP_PG_virtools_material/property'
|
||||
) # type: ignore
|
||||
|
||||
@@ -99,7 +154,7 @@ class BBP_PG_virtools_material(bpy.types.PropertyGroup):
|
||||
min = 0.0,
|
||||
max = 1.0,
|
||||
size = 3,
|
||||
default = DEFAULT_RAW_VIRTOOLS_MATERIAL.mSpecular.to_const_rgb(),
|
||||
default = RawVirtoolsMaterial.cDefaultSpecular.to_const_rgb(),
|
||||
translation_context = 'BBP_PG_virtools_material/property'
|
||||
) # type: ignore
|
||||
|
||||
@@ -110,7 +165,7 @@ class BBP_PG_virtools_material(bpy.types.PropertyGroup):
|
||||
min = 0.0,
|
||||
max = 1.0,
|
||||
size = 3,
|
||||
default = DEFAULT_RAW_VIRTOOLS_MATERIAL.mEmissive.to_const_rgb(),
|
||||
default = RawVirtoolsMaterial.cDefaultEmissive.to_const_rgb(),
|
||||
translation_context = 'BBP_PG_virtools_material/property'
|
||||
) # type: ignore
|
||||
|
||||
@@ -119,7 +174,7 @@ class BBP_PG_virtools_material(bpy.types.PropertyGroup):
|
||||
description = "Specular highlight power",
|
||||
min = 0.0,
|
||||
max = 100.0,
|
||||
default = DEFAULT_RAW_VIRTOOLS_MATERIAL.mSpecularPower,
|
||||
default = RawVirtoolsMaterial.cDefaultSpecularPower,
|
||||
translation_context = 'BBP_PG_virtools_material/property'
|
||||
) # type: ignore
|
||||
|
||||
@@ -137,7 +192,7 @@ class BBP_PG_virtools_material(bpy.types.PropertyGroup):
|
||||
min = 0.0,
|
||||
max = 1.0,
|
||||
size = 4,
|
||||
default = DEFAULT_RAW_VIRTOOLS_MATERIAL.mTextureBorderColor.to_const_rgba(),
|
||||
default = RawVirtoolsMaterial.cDefaultTextureBorderColor.to_const_rgba(),
|
||||
translation_context = 'BBP_PG_virtools_material/property'
|
||||
) # type: ignore
|
||||
|
||||
@@ -145,7 +200,7 @@ class BBP_PG_virtools_material(bpy.types.PropertyGroup):
|
||||
name = "Texture Blend",
|
||||
description = "Texture blend mode",
|
||||
items = _g_Helper_VXTEXTURE_BLENDMODE.generate_items(),
|
||||
default = _g_Helper_VXTEXTURE_BLENDMODE.to_selection(DEFAULT_RAW_VIRTOOLS_MATERIAL.mTextureBlendMode),
|
||||
default = _g_Helper_VXTEXTURE_BLENDMODE.to_selection(RawVirtoolsMaterial.cDefaultTextureBlendMode),
|
||||
translation_context = 'BBP_PG_virtools_material/property'
|
||||
) # type: ignore
|
||||
|
||||
@@ -153,7 +208,7 @@ class BBP_PG_virtools_material(bpy.types.PropertyGroup):
|
||||
name = "Filter Min",
|
||||
description = "Texture filter mode when the texture is minified",
|
||||
items = _g_Helper_VXTEXTURE_FILTERMODE.generate_items(),
|
||||
default = _g_Helper_VXTEXTURE_FILTERMODE.to_selection(DEFAULT_RAW_VIRTOOLS_MATERIAL.mTextureMinMode),
|
||||
default = _g_Helper_VXTEXTURE_FILTERMODE.to_selection(RawVirtoolsMaterial.cDefaultTextureMinMode),
|
||||
translation_context = 'BBP_PG_virtools_material/property'
|
||||
) # type: ignore
|
||||
|
||||
@@ -161,7 +216,7 @@ class BBP_PG_virtools_material(bpy.types.PropertyGroup):
|
||||
name = "Filter Mag",
|
||||
description = "Texture filter mode when the texture is magnified",
|
||||
items = _g_Helper_VXTEXTURE_FILTERMODE.generate_items(),
|
||||
default = _g_Helper_VXTEXTURE_FILTERMODE.to_selection(DEFAULT_RAW_VIRTOOLS_MATERIAL.mTextureMagMode),
|
||||
default = _g_Helper_VXTEXTURE_FILTERMODE.to_selection(RawVirtoolsMaterial.cDefaultTextureMagMode),
|
||||
translation_context = 'BBP_PG_virtools_material/property'
|
||||
) # type: ignore
|
||||
|
||||
@@ -169,7 +224,7 @@ class BBP_PG_virtools_material(bpy.types.PropertyGroup):
|
||||
name = "Address Mode",
|
||||
description = "The address mode controls how the texture coordinates outside the range 0..1",
|
||||
items = _g_Helper_VXTEXTURE_ADDRESSMODE.generate_items(),
|
||||
default = _g_Helper_VXTEXTURE_ADDRESSMODE.to_selection(DEFAULT_RAW_VIRTOOLS_MATERIAL.mTextureAddressMode),
|
||||
default = _g_Helper_VXTEXTURE_ADDRESSMODE.to_selection(RawVirtoolsMaterial.cDefaultTextureAddressMode),
|
||||
translation_context = 'BBP_PG_virtools_material/property'
|
||||
) # type: ignore
|
||||
|
||||
@@ -177,7 +232,7 @@ class BBP_PG_virtools_material(bpy.types.PropertyGroup):
|
||||
name = "Source Blend",
|
||||
description = "Source blend factor",
|
||||
items = _g_Helper_VXBLEND_MODE.generate_items(),
|
||||
default = _g_Helper_VXBLEND_MODE.to_selection(DEFAULT_RAW_VIRTOOLS_MATERIAL.mSourceBlend),
|
||||
default = _g_Helper_VXBLEND_MODE.to_selection(RawVirtoolsMaterial.cDefaultSourceBlend),
|
||||
translation_context = 'BBP_PG_virtools_material/property'
|
||||
) # type: ignore
|
||||
|
||||
@@ -185,7 +240,7 @@ class BBP_PG_virtools_material(bpy.types.PropertyGroup):
|
||||
name = "Destination Blend",
|
||||
description = "Destination blend factor",
|
||||
items = _g_Helper_VXBLEND_MODE.generate_items(),
|
||||
default = _g_Helper_VXBLEND_MODE.to_selection(DEFAULT_RAW_VIRTOOLS_MATERIAL.mDestBlend),
|
||||
default = _g_Helper_VXBLEND_MODE.to_selection(RawVirtoolsMaterial.cDefaultDestBlend),
|
||||
translation_context = 'BBP_PG_virtools_material/property'
|
||||
) # type: ignore
|
||||
|
||||
@@ -193,7 +248,7 @@ class BBP_PG_virtools_material(bpy.types.PropertyGroup):
|
||||
name = "Fill Mode",
|
||||
description = "Fill mode",
|
||||
items = _g_Helper_VXFILL_MODE.generate_items(),
|
||||
default = _g_Helper_VXFILL_MODE.to_selection(DEFAULT_RAW_VIRTOOLS_MATERIAL.mFillMode),
|
||||
default = _g_Helper_VXFILL_MODE.to_selection(RawVirtoolsMaterial.cDefaultFillMode),
|
||||
translation_context = 'BBP_PG_virtools_material/property'
|
||||
) # type: ignore
|
||||
|
||||
@@ -201,38 +256,38 @@ class BBP_PG_virtools_material(bpy.types.PropertyGroup):
|
||||
name = "Shade Mode",
|
||||
description = "Shade mode",
|
||||
items = _g_Helper_VXSHADE_MODE.generate_items(),
|
||||
default = _g_Helper_VXSHADE_MODE.to_selection(DEFAULT_RAW_VIRTOOLS_MATERIAL.mShadeMode),
|
||||
default = _g_Helper_VXSHADE_MODE.to_selection(RawVirtoolsMaterial.cDefaultShadeMode),
|
||||
translation_context = 'BBP_PG_virtools_material/property'
|
||||
) # type: ignore
|
||||
|
||||
enable_alpha_test: bpy.props.BoolProperty(
|
||||
name = "Alpha Test",
|
||||
description = "Whether the alpha test is enabled",
|
||||
default = DEFAULT_RAW_VIRTOOLS_MATERIAL.mEnableAlphaTest,
|
||||
default = RawVirtoolsMaterial.cDefaultEnableAlphaTest,
|
||||
translation_context = 'BBP_PG_virtools_material/property'
|
||||
) # type: ignore
|
||||
enable_alpha_blend: bpy.props.BoolProperty(
|
||||
name = "Blend",
|
||||
description = "Whether alpha blending is enabled or not.",
|
||||
default = DEFAULT_RAW_VIRTOOLS_MATERIAL.mEnableAlphaBlend,
|
||||
default = RawVirtoolsMaterial.cDefaultEnableAlphaBlend,
|
||||
translation_context = 'BBP_PG_virtools_material/property'
|
||||
) # type: ignore
|
||||
enable_perspective_correction: bpy.props.BoolProperty(
|
||||
name = "Perspective Correction",
|
||||
description = "Whether texture perspective correction is enabled",
|
||||
default = DEFAULT_RAW_VIRTOOLS_MATERIAL.mEnablePerspectiveCorrection,
|
||||
default = RawVirtoolsMaterial.cDefaultEnablePerspectiveCorrection,
|
||||
translation_context = 'BBP_PG_virtools_material/property'
|
||||
) # type: ignore
|
||||
enable_z_write: bpy.props.BoolProperty(
|
||||
name = "Z-Buffer Write",
|
||||
description = "Whether writing in ZBuffer is enabled.",
|
||||
default = DEFAULT_RAW_VIRTOOLS_MATERIAL.mEnableZWrite,
|
||||
default = RawVirtoolsMaterial.cDefaultEnableZWrite,
|
||||
translation_context = 'BBP_PG_virtools_material/property'
|
||||
) # type: ignore
|
||||
enable_two_sided: bpy.props.BoolProperty(
|
||||
name = "Both Sided",
|
||||
description = "Whether the material is both sided or not",
|
||||
default = DEFAULT_RAW_VIRTOOLS_MATERIAL.mEnableTwoSided,
|
||||
default = RawVirtoolsMaterial.cDefaultEnableTwoSided,
|
||||
translation_context = 'BBP_PG_virtools_material/property'
|
||||
) # type: ignore
|
||||
|
||||
@@ -241,7 +296,7 @@ class BBP_PG_virtools_material(bpy.types.PropertyGroup):
|
||||
description = "Alpha referential value",
|
||||
min = 0,
|
||||
max = 255,
|
||||
default = DEFAULT_RAW_VIRTOOLS_MATERIAL.mAlphaRef,
|
||||
default = RawVirtoolsMaterial.cDefaultAlphaRef,
|
||||
translation_context = 'BBP_PG_virtools_material/property'
|
||||
) # type: ignore
|
||||
|
||||
@@ -249,7 +304,7 @@ class BBP_PG_virtools_material(bpy.types.PropertyGroup):
|
||||
name = "Alpha Test Function",
|
||||
description = "Alpha comparision function",
|
||||
items = _g_Helper_VXCMPFUNC.generate_items(),
|
||||
default = _g_Helper_VXCMPFUNC.to_selection(DEFAULT_RAW_VIRTOOLS_MATERIAL.mAlphaFunc),
|
||||
default = _g_Helper_VXCMPFUNC.to_selection(RawVirtoolsMaterial.cDefaultAlphaFunc),
|
||||
translation_context = 'BBP_PG_virtools_material/property'
|
||||
) # type: ignore
|
||||
|
||||
@@ -257,7 +312,7 @@ class BBP_PG_virtools_material(bpy.types.PropertyGroup):
|
||||
name = "Z Compare Function",
|
||||
description = "Z Comparison function",
|
||||
items = _g_Helper_VXCMPFUNC.generate_items(),
|
||||
default = _g_Helper_VXCMPFUNC.to_selection(DEFAULT_RAW_VIRTOOLS_MATERIAL.mZFunc),
|
||||
default = _g_Helper_VXCMPFUNC.to_selection(RawVirtoolsMaterial.cDefaultZFunc),
|
||||
translation_context = 'BBP_PG_virtools_material/property'
|
||||
) # type: ignore
|
||||
|
||||
@@ -503,7 +558,7 @@ def preset_virtools_material(mtl: bpy.types.Material, preset_type: MaterialPrese
|
||||
set_raw_virtools_material(mtl, preset_data)
|
||||
|
||||
# create preset enum blender helper
|
||||
_g_Helper_MtlPreset = UTIL_functions.EnumPropHelper(
|
||||
_g_Helper_MtlPreset: UTIL_functions.EnumPropHelper = UTIL_functions.EnumPropHelper(
|
||||
MaterialPresetType,
|
||||
lambda x: str(x.value),
|
||||
lambda x: MaterialPresetType(int(x)),
|
||||
@@ -517,13 +572,13 @@ _g_Helper_MtlPreset = UTIL_functions.EnumPropHelper(
|
||||
#region Fix Material
|
||||
|
||||
def fix_material(mtl: bpy.types.Material) -> bool:
|
||||
"""
|
||||
"""!
|
||||
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.
|
||||
:return: True if we do a fix, otherwise return False.
|
||||
@param mtl[in] The blender material need to be processed.
|
||||
@return True if we do a fix, otherwise return False.
|
||||
"""
|
||||
# prepare return value first
|
||||
ret: bool = False
|
||||
@@ -899,7 +954,6 @@ class BBP_OT_preset_virtools_material(bpy.types.Operator):
|
||||
name = "Preset",
|
||||
description = "The preset which you want to apply.",
|
||||
items = _g_Helper_MtlPreset.generate_items(),
|
||||
translation_context = 'BBP_OT_preset_virtools_material/property'
|
||||
) # type: ignore
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
import bpy
|
||||
import typing, enum
|
||||
from dataclasses import dataclass
|
||||
from dataclasses import field as datafield
|
||||
from . import UTIL_functions, UTIL_blender_mesh, UTIL_virtools_types
|
||||
from . import UTIL_functions, UTIL_virtools_types
|
||||
|
||||
# Raw Data
|
||||
|
||||
@dataclass
|
||||
class RawVirtoolsMesh():
|
||||
mLitMode: UTIL_virtools_types.VXMESH_LITMODE = datafield(default=UTIL_virtools_types.VXMESH_LITMODE.VX_LITMESH)
|
||||
|
||||
DEFAULT_RAW_VIRTOOLS_MESH = RawVirtoolsMesh()
|
||||
# Instance Member Declarations
|
||||
mLitMode: UTIL_virtools_types.VXMESH_LITMODE
|
||||
# Default Value Declarations
|
||||
cDefaultLitMode: typing.ClassVar[UTIL_virtools_types.VXMESH_LITMODE] = UTIL_virtools_types.VXMESH_LITMODE.VX_LITMESH
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
# assign default value for each component
|
||||
self.mLitMode = kwargs.get('mLitMode', RawVirtoolsMesh.cDefaultLitMode)
|
||||
|
||||
# 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
|
||||
|
||||
@@ -22,27 +24,25 @@ class BBP_PG_virtools_mesh(bpy.types.PropertyGroup):
|
||||
name = "Lit Mode",
|
||||
description = "Lighting mode of the mesh.",
|
||||
items = _g_Helper_VXMESH_LITMODE.generate_items(),
|
||||
default = _g_Helper_VXMESH_LITMODE.to_selection(DEFAULT_RAW_VIRTOOLS_MESH.mLitMode),
|
||||
default = _g_Helper_VXMESH_LITMODE.to_selection(RawVirtoolsMesh.cDefaultLitMode),
|
||||
translation_context = 'BBP_PG_virtools_mesh/property'
|
||||
) # type: ignore
|
||||
|
||||
# Getter Setter
|
||||
|
||||
CanToMesh = bpy.types.Mesh | bpy.types.Curve | bpy.types.SurfaceCurve | bpy.types.TextCurve | bpy.types.MetaBall
|
||||
def get_virtools_mesh(mesh: bpy.types.Mesh) -> BBP_PG_virtools_mesh:
|
||||
return mesh.virtools_mesh
|
||||
|
||||
def get_virtools_mesh(meshlike: CanToMesh) -> BBP_PG_virtools_mesh:
|
||||
return meshlike.virtools_mesh
|
||||
|
||||
def get_raw_virtools_mesh(meshlike: CanToMesh) -> RawVirtoolsMesh:
|
||||
props: BBP_PG_virtools_mesh = get_virtools_mesh(meshlike)
|
||||
def get_raw_virtools_mesh(mesh: bpy.types.Mesh) -> RawVirtoolsMesh:
|
||||
props: BBP_PG_virtools_mesh = get_virtools_mesh(mesh)
|
||||
rawdata: RawVirtoolsMesh = RawVirtoolsMesh()
|
||||
|
||||
rawdata.mLitMode = _g_Helper_VXMESH_LITMODE.get_selection(props.lit_mode)
|
||||
|
||||
return rawdata
|
||||
|
||||
def set_raw_virtools_mesh(meshlike: CanToMesh, rawdata: RawVirtoolsMesh) -> None:
|
||||
props: BBP_PG_virtools_mesh = get_virtools_mesh(meshlike)
|
||||
def set_raw_virtools_mesh(mesh: bpy.types.Mesh, rawdata: RawVirtoolsMesh) -> None:
|
||||
props: BBP_PG_virtools_mesh = get_virtools_mesh(mesh)
|
||||
|
||||
props.lit_mode = _g_Helper_VXMESH_LITMODE.to_selection(rawdata.mLitMode)
|
||||
|
||||
@@ -59,22 +59,12 @@ class BBP_PT_virtools_mesh(bpy.types.Panel):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if context.mesh is not None: return True
|
||||
if context.curve is not None: return True
|
||||
if context.meta_ball is not None: return True
|
||||
return False
|
||||
return context.mesh is not None
|
||||
|
||||
def draw(self, context):
|
||||
# get layout
|
||||
# get layout and target
|
||||
layout = self.layout
|
||||
# get target
|
||||
datablock: typing.Any
|
||||
if context.mesh is not None: datablock = context.mesh
|
||||
elif context.curve is not None: datablock = context.curve
|
||||
elif context.meta_ball is not None: datablock = context.meta_ball
|
||||
else: datablock = None
|
||||
# get mesh properties
|
||||
props: BBP_PG_virtools_mesh = get_virtools_mesh(datablock)
|
||||
props: BBP_PG_virtools_mesh = get_virtools_mesh(context.mesh)
|
||||
|
||||
# draw data
|
||||
layout.prop(props, 'lit_mode')
|
||||
@@ -85,21 +75,11 @@ def register() -> None:
|
||||
bpy.utils.register_class(BBP_PG_virtools_mesh)
|
||||
bpy.utils.register_class(BBP_PT_virtools_mesh)
|
||||
|
||||
# Add metadata into mesh-like data block.
|
||||
# according to TemporaryMesh, we need add it into:
|
||||
# mesh, curve, surface, font, and metaball.
|
||||
# add into mesh metadata
|
||||
bpy.types.Mesh.virtools_mesh = bpy.props.PointerProperty(type = BBP_PG_virtools_mesh)
|
||||
bpy.types.Curve.virtools_mesh = bpy.props.PointerProperty(type = BBP_PG_virtools_mesh)
|
||||
bpy.types.SurfaceCurve.virtools_mesh = bpy.props.PointerProperty(type = BBP_PG_virtools_mesh)
|
||||
bpy.types.TextCurve.virtools_mesh = bpy.props.PointerProperty(type = BBP_PG_virtools_mesh)
|
||||
bpy.types.MetaBall.virtools_mesh = bpy.props.PointerProperty(type = BBP_PG_virtools_mesh)
|
||||
|
||||
def unregister() -> None:
|
||||
# remove from metadata
|
||||
del bpy.types.MetaBall.virtools_mesh
|
||||
del bpy.types.TextCurve.virtools_mesh
|
||||
del bpy.types.SurfaceCurve.virtools_mesh
|
||||
del bpy.types.Curve.virtools_mesh
|
||||
del bpy.types.Mesh.virtools_mesh
|
||||
|
||||
bpy.utils.unregister_class(BBP_PT_virtools_mesh)
|
||||
|
||||
@@ -1,20 +1,27 @@
|
||||
import bpy
|
||||
import typing
|
||||
from dataclasses import dataclass
|
||||
from dataclasses import field as datafield
|
||||
from . import UTIL_virtools_types, UTIL_functions
|
||||
|
||||
@dataclass
|
||||
class RawVirtoolsTexture():
|
||||
|
||||
mSaveOptions: UTIL_virtools_types.CK_TEXTURE_SAVEOPTIONS = datafield(default=UTIL_virtools_types.CK_TEXTURE_SAVEOPTIONS.CKTEXTURE_RAWDATA)
|
||||
mVideoFormat: UTIL_virtools_types.VX_PIXELFORMAT = datafield(default=UTIL_virtools_types.VX_PIXELFORMAT._16_ARGB1555)
|
||||
# Instance Member Declarations
|
||||
|
||||
mSaveOptions: UTIL_virtools_types.CK_TEXTURE_SAVEOPTIONS
|
||||
mVideoFormat: UTIL_virtools_types.VX_PIXELFORMAT
|
||||
|
||||
DEFAULT_RAW_VIRTOOLS_TEXTURE = RawVirtoolsTexture()
|
||||
# Default Value Declarations
|
||||
|
||||
cDefaultSaveOptions: typing.ClassVar[UTIL_virtools_types.CK_TEXTURE_SAVEOPTIONS] = UTIL_virtools_types.CK_TEXTURE_SAVEOPTIONS.CKTEXTURE_RAWDATA
|
||||
cDefaultVideoFormat: typing.ClassVar[UTIL_virtools_types.VX_PIXELFORMAT] = UTIL_virtools_types.VX_PIXELFORMAT._16_ARGB1555
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
# assign default value for each component
|
||||
self.mSaveOptions = kwargs.get('mSaveOptions', RawVirtoolsTexture.cDefaultSaveOptions)
|
||||
self.mVideoFormat = kwargs.get('mVideoFormat', RawVirtoolsTexture.cDefaultVideoFormat)
|
||||
|
||||
# blender enum prop helper defines
|
||||
_g_Helper_CK_TEXTURE_SAVEOPTIONS = 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_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.EnumPropHelper(UTIL_virtools_types.VX_PIXELFORMAT)
|
||||
|
||||
class BBP_PG_virtools_texture(bpy.types.PropertyGroup):
|
||||
|
||||
@@ -22,7 +29,7 @@ class BBP_PG_virtools_texture(bpy.types.PropertyGroup):
|
||||
name = "Save Options",
|
||||
description = "When saving a composition textures or sprites can be kept as reference to external files or converted to a given format and saved inside the composition file.",
|
||||
items = _g_Helper_CK_TEXTURE_SAVEOPTIONS.generate_items(),
|
||||
default = _g_Helper_CK_TEXTURE_SAVEOPTIONS.to_selection(DEFAULT_RAW_VIRTOOLS_TEXTURE.mSaveOptions),
|
||||
default = _g_Helper_CK_TEXTURE_SAVEOPTIONS.to_selection(RawVirtoolsTexture.cDefaultSaveOptions),
|
||||
translation_context = 'BBP_PG_virtools_texture/property'
|
||||
) # type: ignore
|
||||
|
||||
@@ -30,7 +37,7 @@ class BBP_PG_virtools_texture(bpy.types.PropertyGroup):
|
||||
name = "Video Format",
|
||||
description = "The desired surface pixel format in video memory.",
|
||||
items = _g_Helper_VX_PIXELFORMAT.generate_items(),
|
||||
default = _g_Helper_VX_PIXELFORMAT.to_selection(DEFAULT_RAW_VIRTOOLS_TEXTURE.mVideoFormat),
|
||||
default = _g_Helper_VX_PIXELFORMAT.to_selection(RawVirtoolsTexture.cDefaultVideoFormat),
|
||||
translation_context = 'BBP_PG_virtools_texture/property'
|
||||
) # type: ignore
|
||||
|
||||
@@ -57,10 +64,12 @@ def set_raw_virtools_texture(img: bpy.types.Image, rawdata: RawVirtoolsTexture)
|
||||
|
||||
#region Virtools Texture Drawer
|
||||
|
||||
# YYC MARK:
|
||||
# Because Image do not have its unique properties window,
|
||||
# so we only can draw Virtools Texture properties in other window.
|
||||
# We provide various functions to help draw properties.
|
||||
"""!
|
||||
@remark
|
||||
Because Image do not have its unique properties window
|
||||
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):
|
||||
props: BBP_PG_virtools_texture = get_virtools_texture(img)
|
||||
|
||||
@@ -105,37 +105,22 @@ def _nest_custom_split_normal(nml_array: array.array) -> typing.Iterator[UTIL_vi
|
||||
class TemporaryMesh():
|
||||
"""
|
||||
Create a temporary mesh for convenient exporting.
|
||||
When exporting mesh, we need evaluate it first, then triangulate it.
|
||||
We create a temporary mesh to hold the evaluated and triangulated mesh result.
|
||||
When exporting mesh, we need triangulate it first.
|
||||
We create a temporary mesh to hold the triangulated mesh result.
|
||||
So that original object will not be affected and keep its original geometry.
|
||||
Please note passed bpy.types.Object must be an object which can be converted into mesh.
|
||||
You can use this class provided static method to check it.
|
||||
Please note passed bpy.types.Object must be Mesh Object.
|
||||
"""
|
||||
|
||||
__cAllowedObjectType: typing.ClassVar[set[str]] = {
|
||||
'MESH', 'CURVE', 'SURFACE', 'FONT', 'META'
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def has_geometry(obj: bpy.types.Object):
|
||||
"""
|
||||
Check whether given Blender object has geometry.
|
||||
If it has, it can safely utilize this class for visiting mesh.
|
||||
"""
|
||||
return obj.type in TemporaryMesh.__cAllowedObjectType
|
||||
|
||||
__mBindingObject: bpy.types.Object
|
||||
__mEvaluatedObject: bpy.types.Object
|
||||
__mTempMesh: bpy.types.Mesh
|
||||
|
||||
def __init__(self, binding_obj: bpy.types.Object):
|
||||
depsgraph = bpy.context.evaluated_depsgraph_get()
|
||||
self.__mBindingObject = binding_obj
|
||||
self.__mEvaluatedObject = self.__mBindingObject.evaluated_get(depsgraph)
|
||||
self.__mTempMesh = self.__mEvaluatedObject.to_mesh()
|
||||
self.__mTempMesh = None
|
||||
|
||||
if self.__mTempMesh is None:
|
||||
if self.__mBindingObject.data is None:
|
||||
raise UTIL_functions.BBPException('try getting mesh from an object without mesh.')
|
||||
self.__mTempMesh = self.__mBindingObject.to_mesh()
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
@@ -144,7 +129,6 @@ class TemporaryMesh():
|
||||
self.dispose()
|
||||
|
||||
def is_valid(self) -> bool:
|
||||
if self.__mBindingObject is None: return False
|
||||
if self.__mBindingObject is None: return False
|
||||
if self.__mTempMesh is None: return False
|
||||
return True
|
||||
@@ -152,8 +136,7 @@ class TemporaryMesh():
|
||||
def dispose(self) -> None:
|
||||
if self.is_valid():
|
||||
self.__mTempMesh = None
|
||||
self.__mEvaluatedObject.to_mesh_clear()
|
||||
self.__mEvaluatedObject = None
|
||||
self.__mBindingObject.to_mesh_clear()
|
||||
self.__mBindingObject = None
|
||||
|
||||
def get_temp_mesh(self) -> bpy.types.Mesh:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import bpy, mathutils
|
||||
import os, json, enum, typing, math
|
||||
from . import PROP_virtools_group, PROP_bme_material, UTIL_naming_convention
|
||||
from . import UTIL_functions, UTIL_icons_manager, UTIL_blender_mesh, UTIL_virtools_types
|
||||
from . import PROP_virtools_group, PROP_bme_material
|
||||
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
|
||||
# and drive this class' functions to work.
|
||||
@@ -24,7 +24,6 @@ TOKEN_IDENTIFIER: str = 'identifier'
|
||||
|
||||
TOKEN_SHOWCASE: str = 'showcase'
|
||||
TOKEN_SHOWCASE_TITLE: str = 'title'
|
||||
TOKEN_SHOWCASE_CATEGORY: str = 'category'
|
||||
TOKEN_SHOWCASE_ICON: str = 'icon'
|
||||
TOKEN_SHOWCASE_TYPE: str = 'type'
|
||||
TOKEN_SHOWCASE_CFGS: str = 'cfgs'
|
||||
@@ -65,10 +64,10 @@ TOKEN_INSTANCES_TRANSFORM: str = 'transform'
|
||||
|
||||
#region Prototype Loader
|
||||
|
||||
## The list storing BME prototype.
|
||||
_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] = {}
|
||||
"""The dict. Key is prototype identifier. Value is the index of prototype in prototype list."""
|
||||
|
||||
# the core loader
|
||||
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).
|
||||
diff = mathutils.Vector((x2, y2)) - mathutils.Vector((x1, y1))
|
||||
bld_angle = math.degrees(mathutils.Vector((1,0)).angle_signed(diff, 0))
|
||||
|
||||
|
||||
# flip it first
|
||||
bld_angle = -bld_angle
|
||||
# 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),
|
||||
'scale': lambda x, y, z: mathutils.Matrix.LocRotScale(None, None, (x, y, z)),
|
||||
'ident': lambda: mathutils.Matrix.Identity(4),
|
||||
|
||||
|
||||
# my misc custom functions
|
||||
'distance': _env_fct_distance,
|
||||
'angle': _env_fct_angle,
|
||||
@@ -188,38 +187,15 @@ class PrototypeShowcaseCfgDescriptor():
|
||||
def get_default(self) -> typing.Any:
|
||||
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.
|
||||
"""
|
||||
|
||||
showcase_identifiers: tuple[str, ...]
|
||||
showcase_categories: dict[str, tuple[str, ...]]
|
||||
|
||||
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
|
||||
super().__init__(
|
||||
UTIL_functions.EnumPropHelper.__init__(
|
||||
self,
|
||||
self.get_bme_identifiers(),
|
||||
lambda x: x,
|
||||
lambda x: x,
|
||||
@@ -227,20 +203,17 @@ class EnumPropHelper(UTIL_functions.EnumPropHelper[str]):
|
||||
lambda _: '',
|
||||
lambda x: self.get_bme_showcase_icon(x)
|
||||
)
|
||||
|
||||
|
||||
def get_bme_identifiers(self) -> tuple[str, ...]:
|
||||
"""
|
||||
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:
|
||||
"""
|
||||
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
|
||||
# get obj info first
|
||||
obj_info: UTIL_naming_convention.BallanceObjectInfo
|
||||
obj_info: UTIL_naming_convension.BallanceObjectInfo
|
||||
match(PrototypeShowcaseTypes(proto[TOKEN_SHOWCASE][TOKEN_SHOWCASE_TYPE])):
|
||||
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:
|
||||
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:
|
||||
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:
|
||||
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
|
||||
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')
|
||||
# create object by name
|
||||
obj: bpy.types.Object = bpy.data.objects.new(obj_name, mesh)
|
||||
# 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 obj
|
||||
@@ -354,14 +327,14 @@ def create_bme_struct(
|
||||
# create mtl slot remap to help following mesh adding
|
||||
# because mesh writer do not accept string format mtl slot visiting,
|
||||
# it only accept int based mtl slot index.
|
||||
#
|
||||
#
|
||||
# Also we build face used mtl slot index at the same time.
|
||||
# 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.
|
||||
# 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.
|
||||
# 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.
|
||||
# we rely on this to implement following features.
|
||||
mtl_remap: dict[str, int] = {}
|
||||
@@ -379,7 +352,7 @@ def create_bme_struct(
|
||||
# if existing, no need to add into remap
|
||||
# but we need get its index from remap
|
||||
prebuild_face_mtl_idx[face_idx] = mtl_remap.get(mtl_name, 0)
|
||||
|
||||
|
||||
# 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
|
||||
# by given vertices.
|
||||
@@ -394,7 +367,7 @@ def create_bme_struct(
|
||||
cache_bv = typing.cast(mathutils.Vector, transform @ cache_bv)
|
||||
# get result
|
||||
prebuild_vec_data.append((cache_bv.x, cache_bv.y, cache_bv.z))
|
||||
|
||||
|
||||
# Check whether given transform is mirror matrix
|
||||
# because mirror matrix will reverse triangle indice order.
|
||||
# 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
|
||||
|
||||
class BBPException(Exception):
|
||||
""" The exception thrown by Ballance Blender Plugin"""
|
||||
"""
|
||||
The exception thrown by Ballance Blender Plugin
|
||||
"""
|
||||
pass
|
||||
|
||||
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 min_val: The allowed minium value (inclusive).
|
||||
:param max_val: The allowed maxium value (inclusive).
|
||||
:return: Clamped value.
|
||||
@param v[in] The value need to be clamp.
|
||||
@param min_val[in] The allowed minium value, including self.
|
||||
@param max_val[in] The allowed maxium value, including self.
|
||||
@return Clamped value.
|
||||
"""
|
||||
if (max_val < min_val): raise BBPException("Invalid range of clamp_float().")
|
||||
|
||||
|
||||
if (v < min_val): return min_val
|
||||
elif (v > max_val): return max_val
|
||||
else: return v
|
||||
|
||||
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 min_val: The allowed minium value (inclusive).
|
||||
:param max_val: The allowed maxium value (inclusive).
|
||||
:return: Clamped value.
|
||||
@param v[in] The value need to be clamp.
|
||||
@param min_val[in] The allowed minium value, including self.
|
||||
@param max_val[in] The allowed maxium value, including self.
|
||||
@return Clamped value.
|
||||
"""
|
||||
if (max_val < min_val): raise BBPException("Invalid range of clamp_int().")
|
||||
|
||||
|
||||
if (v < min_val): return min_val
|
||||
elif (v > max_val): return max_val
|
||||
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.
|
||||
|
||||
:param message: The text this message box displayed. Each item in this param will show as a single line.
|
||||
:param title: Message box title text.
|
||||
:param icon: The icon this message box displayed.
|
||||
@param message[in] The text this message box displayed. Each item in this param will show as a single line.
|
||||
@param title[in] Message box title text.
|
||||
@param icon[in] The icon this message box displayed.
|
||||
"""
|
||||
def draw(self, context: bpy.types.Context):
|
||||
layout = self.layout
|
||||
for item in message:
|
||||
layout.label(text=item, translate=False)
|
||||
|
||||
|
||||
bpy.context.window_manager.popup_menu(draw, title = title, icon = icon)
|
||||
|
||||
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
|
||||
collection = view_layer.active_layer_collection.collection
|
||||
collection.objects.link(obj)
|
||||
|
||||
def move_to_cursor(obj: bpy.types.Object):
|
||||
"""
|
||||
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:
|
||||
# use obj.matrix_world to move, not obj.location because this bug:
|
||||
# https://blender.stackexchange.com/questions/27667/incorrect-matrix-world-after-transformation
|
||||
# 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,
|
||||
# because it involve too much objects and cost too much time.
|
||||
|
||||
# the update of matrix_world after setting location is not immediately.
|
||||
# and calling update() function for view_layer for the translation of each object is not suit for too much objects.
|
||||
|
||||
# obj.location = bpy.context.scene.cursor.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):
|
||||
"""
|
||||
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)
|
||||
move_to_cursor(obj)
|
||||
|
||||
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
|
||||
bpy.ops.object.select_all(action = 'DESELECT')
|
||||
# if no objects, return
|
||||
if len(objs) == 0: return
|
||||
|
||||
|
||||
# set selection for each object
|
||||
for obj in objs:
|
||||
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]
|
||||
|
||||
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
|
||||
obj = bpy.context.active_object
|
||||
|
||||
|
||||
# if there is no active object, we think it is in object mode
|
||||
if obj is None: return True
|
||||
|
||||
|
||||
# simply check active object mode
|
||||
return obj.mode == 'OBJECT'
|
||||
|
||||
#region Blender Enum Property Helper
|
||||
|
||||
_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]):
|
||||
class EnumPropHelper():
|
||||
"""
|
||||
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.
|
||||
"""
|
||||
|
||||
# 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]
|
||||
__mFctToStr: _TFctToStr[_TRawEnum]
|
||||
__mFctFromStr: _TFctFromStr[_TRawEnum]
|
||||
__mFctName: _TFctName[_TRawEnum]
|
||||
__mFctDesc: _TFctDesc[_TRawEnum]
|
||||
__mFctIcon: _TFctIcon[_TRawEnum]
|
||||
|
||||
def __init__(self, collections: typing.Iterable[_TRawEnum],
|
||||
fct_to_str: _TFctToStr[_TRawEnum], fct_from_str: _TFctFromStr[_TRawEnum],
|
||||
fct_name: _TFctName[_TRawEnum], fct_desc: _TFctDesc[_TRawEnum],
|
||||
fct_icon: _TFctIcon[_TRawEnum]):
|
||||
# define some type hint
|
||||
_TFctToStr = typing.Callable[[typing.Any], str]
|
||||
_TFctFromStr = typing.Callable[[str], typing.Any]
|
||||
_TFctName = typing.Callable[[typing.Any], str]
|
||||
_TFctDesc = typing.Callable[[typing.Any], str]
|
||||
_TFctIcon = typing.Callable[[typing.Any], str | int]
|
||||
|
||||
# define class member
|
||||
|
||||
__mCollections: typing.Iterable[typing.Any]
|
||||
__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.
|
||||
It can be `enum.Enum` or a simple list/tuple.
|
||||
:param fct_to_str: 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 simple `lambda x: str(x.value)`
|
||||
:param fct_from_str: A function pointer getting data collection member from its string format.
|
||||
This class promise that given string must can be parsed.
|
||||
For `enum.IntEnum`, it can be simple `lambda x: TEnum(int(x))`
|
||||
:param fct_name: A function pointer converting data collection member to its display name which shown in Blender.
|
||||
: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.
|
||||
@param collections_ [in] The collection all available enum property entries contained.
|
||||
It can be enum.Enum or a simple list/tuple/dict.
|
||||
@param fct_to_str [in] A function pointer converting data collection member to its string format.
|
||||
For enum.IntEnum, it can be simply `lambda x: str(x.value)`
|
||||
@param fct_from_str [in] A function pointer getting data collection member from its string format.
|
||||
For enum.IntEnum, it can be simply `lambda x: TEnum(int(x))`
|
||||
@param fct_name [in] A function pointer converting data collection member to its display name.
|
||||
@param fct_desc [in] Same as `fct_name` but return description instead. Return empty string, not None if no description.
|
||||
@param fct_icon [in] Same as `fct_name` but return the used icon instead. Return empty string if no icon.
|
||||
"""
|
||||
# assign member
|
||||
self.__mCollections = collections
|
||||
self.__mCollections = collections_
|
||||
self.__mFctToStr = fct_to_str
|
||||
self.__mFctFromStr = fct_from_str
|
||||
self.__mFctName = fct_name
|
||||
self.__mFctDesc = fct_desc
|
||||
self.__mFctIcon = fct_icon
|
||||
|
||||
|
||||
def generate_items(self) -> tuple[tuple[str, str, str, int | str, int], ...]:
|
||||
"""
|
||||
Generate a tuple which can be applied to Blender EnumProperty's "items".
|
||||
@@ -187,29 +152,27 @@ class EnumPropHelper(typing.Generic[_TRawEnum]):
|
||||
return tuple(
|
||||
(
|
||||
self.__mFctToStr(member), # call to_str as its token.
|
||||
self.__mFctName(member),
|
||||
self.__mFctDesc(member),
|
||||
self.__mFctIcon(member),
|
||||
self.__mFctName(member),
|
||||
self.__mFctDesc(member),
|
||||
self.__mFctIcon(member),
|
||||
idx # use hardcode index, not the collection member self.
|
||||
) 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.
|
||||
"""
|
||||
# call from_str fct ptr
|
||||
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.
|
||||
"""
|
||||
# call to_str fct ptr
|
||||
return self.__mFctToStr(val)
|
||||
|
||||
#endregion
|
||||
|
||||
#region Blender Collection Visitor
|
||||
|
||||
_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.
|
||||
So I create a wrapper for my personal use to reduce type hint errors raised by my linter.
|
||||
"""
|
||||
|
||||
|
||||
__mSrcProp: bpy.types.CollectionProperty
|
||||
|
||||
|
||||
def __init__(self, src_prop: bpy.types.CollectionProperty):
|
||||
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()
|
||||
|
||||
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)
|
||||
|
||||
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.
|
||||
:param to_index: The target index where the item should be moved.
|
||||
def move(self, from_index: int, to_index: int) -> None:
|
||||
"""!
|
||||
@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)
|
||||
|
||||
|
||||
def clear(self) -> None:
|
||||
"""
|
||||
Clears all items from the collection.
|
||||
"""!
|
||||
@brief Clears all items from the collection.
|
||||
"""
|
||||
self.__mSrcProp.clear()
|
||||
|
||||
|
||||
def __len__(self) -> int:
|
||||
return self.__mSrcProp.__len__()
|
||||
def __getitem__(self, index: int | str) -> _TPropertyGroup:
|
||||
@@ -278,50 +238,32 @@ _TMutexObject = typing.TypeVar('_TMutexObject')
|
||||
|
||||
class TinyMutex(typing.Generic[_TMutexObject]):
|
||||
"""
|
||||
In this plugin, some classes 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.
|
||||
In this plugin, some class have "with" context feature.
|
||||
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.
|
||||
|
||||
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.
|
||||
So it doesn't matter that we do not use lock before operating something.
|
||||
"""
|
||||
|
||||
|
||||
__mProtectedObjects: set[_TMutexObject]
|
||||
|
||||
|
||||
def __init__(self):
|
||||
self.__mProtectedObjects = set()
|
||||
|
||||
def lock(self, obj: _TMutexObject) -> None:
|
||||
"""
|
||||
Lock given object.
|
||||
|
||||
:raise BBPException: Raised if given object has been locked.
|
||||
:param obj: The resource to be locked.
|
||||
"""
|
||||
def lock(self, obj: _TMutexObject) -> None:
|
||||
if obj in self.__mProtectedObjects:
|
||||
raise BBPException('It is not allowed that operate multiple "with" contexts on a single object.')
|
||||
self.__mProtectedObjects.add(obj)
|
||||
|
||||
def try_lock(self, obj: _TMutexObject) -> bool:
|
||||
"""
|
||||
Try lock given object.
|
||||
|
||||
:param obj: The resource to be locked.
|
||||
:return: True if we successfully lock it, otherwise false.
|
||||
"""
|
||||
def try_lock(self, obj: _TMutexObject) -> bool:
|
||||
if obj in self.__mProtectedObjects:
|
||||
return False
|
||||
self.__mProtectedObjects.add(obj)
|
||||
return True
|
||||
|
||||
def unlock(self, obj: _TMutexObject) -> None:
|
||||
"""
|
||||
Unlock given object.
|
||||
|
||||
:raise BBPException: Raised if given object is not locked.
|
||||
:param obj: The resource to be unlocked.
|
||||
"""
|
||||
def unlock(self, obj: _TMutexObject) -> None:
|
||||
if obj not in self.__mProtectedObjects:
|
||||
raise BBPException('It is not allowed that unlock an non-existent object.')
|
||||
self.__mProtectedObjects.remove(obj)
|
||||
|
||||
@@ -3,13 +3,11 @@ import enum, typing
|
||||
from . import UTIL_virtools_types, UTIL_functions
|
||||
from . import PROP_ptrprop_resolver, PROP_ballance_map_info
|
||||
|
||||
# INTENT:
|
||||
# Some importer or exporter may share same properties.
|
||||
# So we create some shared class and user just need inherit them
|
||||
# and call general getter to get user selected data.
|
||||
# Also provide draw function thus caller do not need draw the params themselves.
|
||||
|
||||
#region Import Params
|
||||
## Intent
|
||||
# Some importer or exporter may share same properties.
|
||||
# So we create some shared class and user just need inherit them
|
||||
# and call general getter to get user selected data.
|
||||
# Also provide draw function thus caller do not need draw the params themselves.
|
||||
|
||||
class ConflictStrategy(enum.IntEnum):
|
||||
Rename = enum.auto()
|
||||
@@ -18,7 +16,7 @@ _g_ConflictStrategyDesc: dict[ConflictStrategy, tuple[str, str]] = {
|
||||
ConflictStrategy.Rename: ('Rename', 'Rename the new one'),
|
||||
ConflictStrategy.Current: ('Use Current', 'Use current one'),
|
||||
}
|
||||
_g_EnumHelper_ConflictStrategy = UTIL_functions.EnumPropHelper(
|
||||
_g_EnumHelper_ConflictStrategy: UTIL_functions.EnumPropHelper = UTIL_functions.EnumPropHelper(
|
||||
ConflictStrategy,
|
||||
lambda x: str(x.value),
|
||||
lambda x: ConflictStrategy(int(x)),
|
||||
@@ -27,6 +25,39 @@ _g_EnumHelper_ConflictStrategy = UTIL_functions.EnumPropHelper(
|
||||
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():
|
||||
"""
|
||||
This class frequently used when importing objects.
|
||||
@@ -37,7 +68,6 @@ class ConflictResolver():
|
||||
|
||||
__mObjectStrategy: ConflictStrategy
|
||||
__mLightStrategy: ConflictStrategy
|
||||
__mCameraStrategy: ConflictStrategy
|
||||
__mMeshStrategy: ConflictStrategy
|
||||
__mMaterialStrategy: ConflictStrategy
|
||||
__mTextureStrategy: ConflictStrategy
|
||||
@@ -45,13 +75,11 @@ class ConflictResolver():
|
||||
def __init__(self,
|
||||
obj_strategy: ConflictStrategy,
|
||||
light_strategy: ConflictStrategy,
|
||||
camera_strategy: ConflictStrategy,
|
||||
mesh_strategy: ConflictStrategy,
|
||||
mtl_strategy: ConflictStrategy,
|
||||
tex_strategy: ConflictStrategy):
|
||||
self.__mObjectStrategy = obj_strategy
|
||||
self.__mLightStrategy = light_strategy
|
||||
self.__mCameraStrategy = camera_strategy
|
||||
self.__mMeshStrategy = mesh_strategy
|
||||
self.__mMaterialStrategy = mtl_strategy
|
||||
self.__mTextureStrategy = tex_strategy
|
||||
@@ -91,22 +119,6 @@ class ConflictResolver():
|
||||
new_obj: bpy.types.Object = bpy.data.objects.new(name, new_light)
|
||||
return (new_obj, new_light, True)
|
||||
|
||||
def create_camera(self, name: str) -> tuple[bpy.types.Object, bpy.types.Camera, bool]:
|
||||
"""
|
||||
Create camera data block and associated 3d object.
|
||||
|
||||
Same execution pattern with light creation.
|
||||
"""
|
||||
if self.__mCameraStrategy == ConflictStrategy.Current:
|
||||
old_obj: bpy.types.Object | None = bpy.data.objects.get(name, None)
|
||||
if old_obj is not None and old_obj.type == 'CAMERA':
|
||||
return (old_obj, typing.cast(bpy.types.Camera, old_obj.data), False)
|
||||
# create new object.
|
||||
# if object or camera name is conflict, rename it directly without considering conflict strategy
|
||||
new_camera: bpy.types.Camera = bpy.data.cameras.new(name)
|
||||
new_obj: bpy.types.Object = bpy.data.objects.new(name, new_camera)
|
||||
return (new_obj, new_camera, True)
|
||||
|
||||
def create_mesh(self, name: str) -> tuple[bpy.types.Mesh, bool]:
|
||||
if self.__mMeshStrategy == ConflictStrategy.Current:
|
||||
old: bpy.types.Mesh | None = bpy.data.meshes.get(name, None)
|
||||
@@ -139,6 +151,8 @@ class ConflictResolver():
|
||||
tex.name = name
|
||||
return (tex, True)
|
||||
|
||||
#endregion
|
||||
|
||||
class ImportParams():
|
||||
texture_conflict_strategy: bpy.props.EnumProperty(
|
||||
name = "Texture Name Conflict",
|
||||
@@ -172,14 +186,6 @@ class ImportParams():
|
||||
translation_context = 'BBP/UTIL_ioport_shared.ImportParams/property'
|
||||
) # type: ignore
|
||||
|
||||
camera_conflict_strategy: bpy.props.EnumProperty(
|
||||
name = "Camera Name Conflict",
|
||||
items = _g_EnumHelper_ConflictStrategy.generate_items(),
|
||||
description = "Define how to process camera name conflict",
|
||||
default = _g_EnumHelper_ConflictStrategy.to_selection(ConflictStrategy.Rename),
|
||||
translation_context = 'BBP/UTIL_ioport_shared.ImportParams/property'
|
||||
) # type: ignore
|
||||
|
||||
object_conflict_strategy: bpy.props.EnumProperty(
|
||||
name = "Object Name Conflict",
|
||||
items = _g_EnumHelper_ConflictStrategy.generate_items(),
|
||||
@@ -200,13 +206,11 @@ class ImportParams():
|
||||
grid = body.grid_flow(row_major=False, columns=2)
|
||||
grid.label(text='Object', icon='CUBE', text_ctxt='BBP/UTIL_ioport_shared.ImportParams/draw')
|
||||
grid.label(text='Light', icon='LIGHT', text_ctxt='BBP/UTIL_ioport_shared.ImportParams/draw')
|
||||
grid.label(text='Camera', icon='CAMERA_DATA', text_ctxt='BBP/UTIL_ioport_shared.ImportParams/draw')
|
||||
grid.label(text='Mesh', icon='MESH_DATA', text_ctxt='BBP/UTIL_ioport_shared.ImportParams/draw')
|
||||
grid.label(text='Material', icon='MATERIAL', text_ctxt='BBP/UTIL_ioport_shared.ImportParams/draw')
|
||||
grid.label(text='Texture', icon='TEXTURE', text_ctxt='BBP/UTIL_ioport_shared.ImportParams/draw')
|
||||
grid.prop(self, 'object_conflict_strategy', text='')
|
||||
grid.prop(self, 'light_conflict_strategy', text='')
|
||||
grid.prop(self, 'camera_conflict_strategy', text='')
|
||||
grid.prop(self, 'mesh_conflict_strategy', text='')
|
||||
grid.prop(self, 'material_conflict_strategy', text='')
|
||||
grid.prop(self, 'texture_conflict_strategy', text='')
|
||||
@@ -223,9 +227,6 @@ class ImportParams():
|
||||
def general_get_light_conflict_strategy(self) -> ConflictStrategy:
|
||||
return _g_EnumHelper_ConflictStrategy.get_selection(self.light_conflict_strategy)
|
||||
|
||||
def general_get_camera_conflict_strategy(self) -> ConflictStrategy:
|
||||
return _g_EnumHelper_ConflictStrategy.get_selection(self.camera_conflict_strategy)
|
||||
|
||||
def general_get_object_conflict_strategy(self) -> ConflictStrategy:
|
||||
return _g_EnumHelper_ConflictStrategy.get_selection(self.object_conflict_strategy)
|
||||
|
||||
@@ -233,73 +234,18 @@ class ImportParams():
|
||||
return ConflictResolver(
|
||||
self.general_get_object_conflict_strategy(),
|
||||
self.general_get_light_conflict_strategy(),
|
||||
self.general_get_camera_conflict_strategy(),
|
||||
self.general_get_mesh_conflict_strategy(),
|
||||
self.general_get_material_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():
|
||||
export_mode: bpy.props.EnumProperty(
|
||||
name = "Export Mode",
|
||||
description = "Define which 3D objects should be exported",
|
||||
items = _g_EnumHelper_ExportMode.generate_items(),
|
||||
default = _g_EnumHelper_ExportMode.to_selection(ExportMode.BldColl),
|
||||
items = (
|
||||
('COLLECTION', "Collection", "Export a collection", 'OUTLINER_COLLECTION', 0),
|
||||
('OBJECT', "Object", "Export an object", 'OBJECT_DATA', 1),
|
||||
),
|
||||
translation_context = 'BBP/UTIL_ioport_shared.ExportParams/property'
|
||||
) # type: ignore
|
||||
|
||||
@@ -310,48 +256,35 @@ class ExportParams():
|
||||
header.label(text='Export Parameters', text_ctxt='BBP/UTIL_ioport_shared.ExportParams/draw')
|
||||
if body is None: return
|
||||
|
||||
# make prop expand horizontaly, not vertical.
|
||||
horizon_body = body.row()
|
||||
# draw switch
|
||||
body.prop(self, "export_mode", expand=True)
|
||||
horizon_body.prop(self, "export_mode", expand=True)
|
||||
|
||||
# draw picker
|
||||
export_mode = _g_EnumHelper_ExportMode.get_selection(self.export_mode)
|
||||
ptrprops = PROP_ptrprop_resolver.PropsVisitor(context.scene)
|
||||
match export_mode:
|
||||
case ExportMode.BldColl:
|
||||
ptrprops.draw_export_collection(body)
|
||||
case ExportMode.BldObj:
|
||||
ptrprops.draw_export_object(body)
|
||||
case ExportMode.BldSelObjs:
|
||||
pass # Draw nothing
|
||||
case ExportMode.BldAllObjs:
|
||||
pass # Draw nothing
|
||||
if self.export_mode == 'COLLECTION':
|
||||
ptrprops.draw_export_collection(body)
|
||||
elif self.export_mode == 'OBJECT':
|
||||
ptrprops.draw_export_object(body)
|
||||
|
||||
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.
|
||||
"""
|
||||
export_mode = _g_EnumHelper_ExportMode.get_selection(self.export_mode)
|
||||
ptrprops = PROP_ptrprop_resolver.PropsVisitor(context.scene)
|
||||
match export_mode:
|
||||
case ExportMode.BldColl:
|
||||
col: bpy.types.Collection = ptrprops.get_export_collection()
|
||||
if col is None: return None
|
||||
else: return tuple(col.all_objects)
|
||||
case ExportMode.BldObj:
|
||||
obj: bpy.types.Object = ptrprops.get_export_object()
|
||||
if obj is None: return None
|
||||
else: return (obj, )
|
||||
case ExportMode.BldSelObjs:
|
||||
return tuple(context.selected_objects)
|
||||
case ExportMode.BldAllObjs:
|
||||
return tuple(bpy.data.objects)
|
||||
|
||||
#endregion
|
||||
|
||||
#region Virtools Params
|
||||
if self.export_mode == 'COLLECTION':
|
||||
col: bpy.types.Collection = ptrprops.get_export_collection()
|
||||
if col is None: return None
|
||||
else:
|
||||
return tuple(col.all_objects)
|
||||
else:
|
||||
obj: bpy.types.Object = ptrprops.get_export_object()
|
||||
if obj is None: return None
|
||||
else: return (obj, )
|
||||
|
||||
# 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():
|
||||
texture_save_opt: bpy.props.EnumProperty(
|
||||
@@ -377,14 +310,6 @@ class VirtoolsParams():
|
||||
translation_context = 'BBP/UTIL_ioport_shared.VirtoolsParams/property'
|
||||
) # 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:
|
||||
header: bpy.types.UILayout
|
||||
body: bpy.types.UILayout
|
||||
@@ -409,6 +334,7 @@ class VirtoolsParams():
|
||||
if self.use_compress:
|
||||
body.prop(self, 'compress_level')
|
||||
|
||||
|
||||
def general_get_vt_encodings(self, context: bpy.types.Context) -> tuple[str, ...]:
|
||||
# get from ptrprop resolver then filter empty item
|
||||
ptrprops = PROP_ptrprop_resolver.PropsVisitor(context.scene)
|
||||
@@ -423,10 +349,6 @@ class VirtoolsParams():
|
||||
def general_get_compress_level(self) -> int:
|
||||
return self.compress_level
|
||||
|
||||
#endregion
|
||||
|
||||
#region Ballance Params
|
||||
|
||||
class BallanceParams():
|
||||
successive_sector: bpy.props.BoolProperty(
|
||||
name="Successive Sector",
|
||||
@@ -465,5 +387,3 @@ class BallanceParams():
|
||||
map_info: PROP_ballance_map_info.RawBallanceMapInfo
|
||||
map_info = PROP_ballance_map_info.get_raw_ballance_map_info(bpy.context.scene)
|
||||
return map_info.mSectorCount
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -72,7 +72,7 @@ class RenameErrorReporter():
|
||||
print('============')
|
||||
print(bpy.app.translations.pgettext_rpt(
|
||||
'Rename Report',
|
||||
'BBP/UTIL_naming_convention.RenameErrorReporter'
|
||||
'BBP/UTIL_naming_convension.RenameErrorReporter'
|
||||
))
|
||||
print('------------')
|
||||
# return self as context
|
||||
@@ -82,7 +82,7 @@ class RenameErrorReporter():
|
||||
# print console report tail
|
||||
print('------------')
|
||||
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('============')
|
||||
# reset variables
|
||||
@@ -107,10 +107,10 @@ class RenameErrorReporter():
|
||||
tr_text: str
|
||||
new_name: str = obj.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))
|
||||
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))
|
||||
|
||||
# output error list with indent
|
||||
@@ -125,11 +125,11 @@ class RenameErrorReporter():
|
||||
def __errtype_to_string(err_v: _RenameErrorType) -> str:
|
||||
match(err_v):
|
||||
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:
|
||||
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:
|
||||
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.")
|
||||
@staticmethod
|
||||
def __erritem_to_string(item: _RenameErrorItem) -> str:
|
||||
@@ -292,7 +292,7 @@ class VirtoolsGroupConvention():
|
||||
)
|
||||
|
||||
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)
|
||||
return None
|
||||
|
||||
@@ -337,12 +337,12 @@ class VirtoolsGroupConvention():
|
||||
return VirtoolsGroupConvention.__get_pcpr_from_name(obj.name, reporter)
|
||||
case _:
|
||||
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)
|
||||
return None
|
||||
elif len(inter_gps) != 0:
|
||||
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)
|
||||
return None
|
||||
|
||||
@@ -356,7 +356,7 @@ class VirtoolsGroupConvention():
|
||||
if gotten_sector is None:
|
||||
# fail to get sector
|
||||
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)
|
||||
return None
|
||||
return BallanceObjectInfo.create_from_component(
|
||||
@@ -366,7 +366,7 @@ class VirtoolsGroupConvention():
|
||||
elif len(inter_gps) != 0:
|
||||
# must be a weird grouping, report it
|
||||
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)
|
||||
return None
|
||||
|
||||
@@ -384,7 +384,7 @@ class VirtoolsGroupConvention():
|
||||
return BallanceObjectInfo.create_from_others(BallanceObjectType.WOOD)
|
||||
else:
|
||||
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)
|
||||
return BallanceObjectInfo.create_from_others(BallanceObjectType.FLOOR)
|
||||
elif gp.contain_group('Phys_FloorStopper'):
|
||||
@@ -394,7 +394,7 @@ class VirtoolsGroupConvention():
|
||||
|
||||
# no matched
|
||||
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)
|
||||
return None
|
||||
|
||||
@@ -445,7 +445,7 @@ class VirtoolsGroupConvention():
|
||||
|
||||
case _:
|
||||
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)
|
||||
return False
|
||||
|
||||
@@ -499,7 +499,7 @@ class YYCToolchainConvention():
|
||||
return BallanceObjectInfo.create_from_others(BallanceObjectType.SKYLAYER)
|
||||
|
||||
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)
|
||||
return None
|
||||
|
||||
@@ -543,7 +543,7 @@ class YYCToolchainConvention():
|
||||
|
||||
case _:
|
||||
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)
|
||||
return None
|
||||
|
||||
@@ -607,7 +607,7 @@ class ImengyuConvention():
|
||||
return BallanceObjectInfo.create_from_others(BallanceObjectType.SKYLAYER)
|
||||
|
||||
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)
|
||||
return None
|
||||
|
||||
@@ -653,7 +653,7 @@ class ImengyuConvention():
|
||||
|
||||
case _:
|
||||
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)
|
||||
return None
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import bpy, bmesh, mathutils, math
|
||||
import typing
|
||||
from . import UTIL_functions, UTIL_naming_convention
|
||||
from . import UTIL_functions, UTIL_naming_convension
|
||||
from . import PROP_bme_material
|
||||
|
||||
#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 info first
|
||||
rail_info: UTIL_naming_convention.BallanceObjectInfo = UTIL_naming_convention.BallanceObjectInfo.create_from_others(
|
||||
UTIL_naming_convention.BallanceObjectType.RAIL
|
||||
rail_info: UTIL_naming_convension.BallanceObjectInfo = UTIL_naming_convension.BallanceObjectInfo.create_from_others(
|
||||
UTIL_naming_convension.BallanceObjectType.RAIL
|
||||
)
|
||||
# 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')
|
||||
# create object by name
|
||||
obj: bpy.types.Object = bpy.data.objects.new(rail_name, mesh)
|
||||
# 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
|
||||
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`.
|
||||
# * 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.
|
||||
CTX_BBP: str = 'BBP'
|
||||
|
||||
# The universal translation context prefix for BME module in BBP_NG plugin.
|
||||
CTX_BBP_BME: str = f'{CTX_BBP}/BME'
|
||||
CTX_BBP_BME_CATEGORY: str = f'{CTX_BBP_BME}/Category'
|
||||
CTX_BBP_BME_PROTOTYPE: str = f'{CTX_BBP_BME}/Proto'
|
||||
def build_prototype_showcase_category_context() -> str:
|
||||
"""
|
||||
Build the context for getting the translation for BME prototype showcase category.
|
||||
@return The context for getting translation.
|
||||
"""
|
||||
return CTX_BBP_BME_CATEGORY
|
||||
def build_prototype_showcase_title_context(identifier: str) -> str:
|
||||
CTX_BBP_BME: str = CTX_BBP + '/BME'
|
||||
def build_prototype_showcase_context(identifier: str) -> str:
|
||||
"""
|
||||
Build the context for getting the translation for BME prototype showcase title.
|
||||
@param[in] identifier The identifier of this prototype.
|
||||
@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:
|
||||
"""
|
||||
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.
|
||||
@return The context for getting translation.
|
||||
"""
|
||||
return f'{CTX_BBP_BME_PROTOTYPE}/{identifier}/[{cfg_index}]'
|
||||
return CTX_BBP_BME + f'/{identifier}/[{cfg_index}]'
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import bpy, mathutils
|
||||
import typing, math
|
||||
from dataclasses import dataclass
|
||||
from . import UTIL_functions
|
||||
|
||||
# extract all declarations in pybmap
|
||||
from .pybmap.virtools_types import *
|
||||
# extract all declarations in PyBMap
|
||||
from .PyBMap.virtools_types import *
|
||||
# and add some patches for them
|
||||
# mainly patch them with functions exchanging data with blender
|
||||
# and the convertion between differnet coordinate system.
|
||||
@@ -79,7 +78,7 @@ def vxmatrix_to_blender(self: VxMatrix) -> mathutils.Matrix:
|
||||
## Hints about Light Matrix
|
||||
# There is a slight difference between Virtools and Blender.
|
||||
# In blender, the default direction of all directional light (spot and sun) are Down (-Z).
|
||||
# However, in Virtools, the default direction of all directional light (spot and directional) are Forward (+Z).
|
||||
# Hoewver, in Virtools, the default direction of all directional light (spot and directional) are Forward (+Z).
|
||||
#
|
||||
# As brief view, in Blender coordinate system, you can see that we got Blender default light direction
|
||||
# from Virtools default light direction by rotating it around X-axis with -90 degree
|
||||
@@ -109,188 +108,152 @@ def bldmatrix_restore_light_obj(data: mathutils.Matrix) -> mathutils.Matrix:
|
||||
# so we simply right multiple it.
|
||||
return data @ mathutils.Matrix.Rotation(math.radians(-90), 4, 'X')
|
||||
|
||||
## Hints about Camera Matrix
|
||||
# Just like light, camera is also different between Virtools and Blender.
|
||||
# In Blender, the default camera orientation is looking at -Z and +Y up.
|
||||
# Oppositely, Virtools camera is looking at +Z and +Y up.
|
||||
#
|
||||
# These direction is based on their own coordinate system respectively.
|
||||
# Accidently this difference is same like light.
|
||||
# So we can simply copy light strategy in there.
|
||||
|
||||
def bldmatrix_patch_camera_obj(data: mathutils.Matrix) -> mathutils.Matrix:
|
||||
"""
|
||||
Add patch for camera world matrix to correct its direction.
|
||||
This function is usually used when importing camera.
|
||||
"""
|
||||
# same operation like light matrix patch
|
||||
return data @ mathutils.Matrix.Rotation(math.radians(90), 4, 'X')
|
||||
|
||||
def bldmatrix_restore_camera_obj(data: mathutils.Matrix) -> mathutils.Matrix:
|
||||
"""
|
||||
The reverse operation of bldmatrix_patch_camera_mat().
|
||||
This function is usually used when exporting camera.
|
||||
"""
|
||||
# same operation like light matrix patch
|
||||
return data @ mathutils.Matrix.Rotation(math.radians(-90), 4, 'X')
|
||||
|
||||
#endregion
|
||||
|
||||
#region Blender EnumProperty Creation
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class EnumDocstring():
|
||||
display_name: str
|
||||
"""The name of this enum entry."""
|
||||
description: str
|
||||
"""The description of this enum entry."""
|
||||
class EnumAnnotation():
|
||||
mDisplayName: str
|
||||
mDescription: str
|
||||
def __init__(self, display_name: str, description: str):
|
||||
self.mDisplayName = display_name
|
||||
self.mDescription = description
|
||||
|
||||
_g_Docstring: dict[type, dict[int, EnumDocstring]] = {
|
||||
_g_Annotation: dict[type, dict[int, EnumAnnotation]] = {
|
||||
VXTEXTURE_BLENDMODE: {
|
||||
VXTEXTURE_BLENDMODE.VXTEXTUREBLEND_DECAL.value: EnumDocstring("Decal", "Texture replace any material information "),
|
||||
VXTEXTURE_BLENDMODE.VXTEXTUREBLEND_MODULATE.value: EnumDocstring("Modulate", "Texture and material are combine. Alpha information of the texture replace material alpha component. "),
|
||||
VXTEXTURE_BLENDMODE.VXTEXTUREBLEND_DECALALPHA.value: EnumDocstring("Decal Alpha", "Alpha information in the texture specify how material and texture are combined. Alpha information of the texture replace material alpha component. "),
|
||||
VXTEXTURE_BLENDMODE.VXTEXTUREBLEND_MODULATEALPHA.value: EnumDocstring("Modulate Alpha", "Alpha information in the texture specify how material and texture are combined "),
|
||||
VXTEXTURE_BLENDMODE.VXTEXTUREBLEND_DECALMASK.value: EnumDocstring("Decal Mask", ""),
|
||||
VXTEXTURE_BLENDMODE.VXTEXTUREBLEND_MODULATEMASK.value: EnumDocstring("Modulate Mask", ""),
|
||||
VXTEXTURE_BLENDMODE.VXTEXTUREBLEND_COPY.value: EnumDocstring("Copy", "Equivalent to DECAL "),
|
||||
VXTEXTURE_BLENDMODE.VXTEXTUREBLEND_ADD.value: EnumDocstring("Add", ""),
|
||||
VXTEXTURE_BLENDMODE.VXTEXTUREBLEND_DOTPRODUCT3.value: EnumDocstring("Dot Product 3", "Perform a Dot Product 3 between texture (normal map) and a referential vector given in VXRENDERSTATE_TEXTUREFACTOR. "),
|
||||
VXTEXTURE_BLENDMODE.VXTEXTUREBLEND_MAX.value: EnumDocstring("Max", ""),
|
||||
VXTEXTURE_BLENDMODE.VXTEXTUREBLEND_DECAL.value: EnumAnnotation("Decal", "Texture replace any material information "),
|
||||
VXTEXTURE_BLENDMODE.VXTEXTUREBLEND_MODULATE.value: EnumAnnotation("Modulate", "Texture and material are combine. Alpha information of the texture replace material alpha component. "),
|
||||
VXTEXTURE_BLENDMODE.VXTEXTUREBLEND_DECALALPHA.value: EnumAnnotation("Decal Alpha", "Alpha information in the texture specify how material and texture are combined. Alpha information of the texture replace material alpha component. "),
|
||||
VXTEXTURE_BLENDMODE.VXTEXTUREBLEND_MODULATEALPHA.value: EnumAnnotation("Modulate Alpha", "Alpha information in the texture specify how material and texture are combined "),
|
||||
VXTEXTURE_BLENDMODE.VXTEXTUREBLEND_DECALMASK.value: EnumAnnotation("Decal Mask", ""),
|
||||
VXTEXTURE_BLENDMODE.VXTEXTUREBLEND_MODULATEMASK.value: EnumAnnotation("Modulate Mask", ""),
|
||||
VXTEXTURE_BLENDMODE.VXTEXTUREBLEND_COPY.value: EnumAnnotation("Copy", "Equivalent to DECAL "),
|
||||
VXTEXTURE_BLENDMODE.VXTEXTUREBLEND_ADD.value: EnumAnnotation("Add", ""),
|
||||
VXTEXTURE_BLENDMODE.VXTEXTUREBLEND_DOTPRODUCT3.value: EnumAnnotation("Dot Product 3", "Perform a Dot Product 3 between texture (normal map) and a referential vector given in VXRENDERSTATE_TEXTUREFACTOR. "),
|
||||
VXTEXTURE_BLENDMODE.VXTEXTUREBLEND_MAX.value: EnumAnnotation("Max", ""),
|
||||
},
|
||||
VXTEXTURE_FILTERMODE: {
|
||||
VXTEXTURE_FILTERMODE.VXTEXTUREFILTER_NEAREST.value: EnumDocstring("Nearest", "No Filter "),
|
||||
VXTEXTURE_FILTERMODE.VXTEXTUREFILTER_LINEAR.value: EnumDocstring("Linear", "Bilinear Interpolation "),
|
||||
VXTEXTURE_FILTERMODE.VXTEXTUREFILTER_MIPNEAREST.value: EnumDocstring("Mip Nearest", "Mip mapping "),
|
||||
VXTEXTURE_FILTERMODE.VXTEXTUREFILTER_MIPLINEAR.value: EnumDocstring("Mip Linear", "Mip Mapping with Bilinear interpolation "),
|
||||
VXTEXTURE_FILTERMODE.VXTEXTUREFILTER_LINEARMIPNEAREST.value: EnumDocstring("Linear Mip Nearest", "Mip Mapping with Bilinear interpolation between mipmap levels. "),
|
||||
VXTEXTURE_FILTERMODE.VXTEXTUREFILTER_LINEARMIPLINEAR.value: EnumDocstring("Linear Mip Linear", "Trilinear Filtering "),
|
||||
VXTEXTURE_FILTERMODE.VXTEXTUREFILTER_ANISOTROPIC.value: EnumDocstring("Anisotropic", "Anisotropic filtering "),
|
||||
VXTEXTURE_FILTERMODE.VXTEXTUREFILTER_NEAREST.value: EnumAnnotation("Nearest", "No Filter "),
|
||||
VXTEXTURE_FILTERMODE.VXTEXTUREFILTER_LINEAR.value: EnumAnnotation("Linear", "Bilinear Interpolation "),
|
||||
VXTEXTURE_FILTERMODE.VXTEXTUREFILTER_MIPNEAREST.value: EnumAnnotation("Mip Nearest", "Mip mapping "),
|
||||
VXTEXTURE_FILTERMODE.VXTEXTUREFILTER_MIPLINEAR.value: EnumAnnotation("Mip Linear", "Mip Mapping with Bilinear interpolation "),
|
||||
VXTEXTURE_FILTERMODE.VXTEXTUREFILTER_LINEARMIPNEAREST.value: EnumAnnotation("Linear Mip Nearest", "Mip Mapping with Bilinear interpolation between mipmap levels. "),
|
||||
VXTEXTURE_FILTERMODE.VXTEXTUREFILTER_LINEARMIPLINEAR.value: EnumAnnotation("Linear Mip Linear", "Trilinear Filtering "),
|
||||
VXTEXTURE_FILTERMODE.VXTEXTUREFILTER_ANISOTROPIC.value: EnumAnnotation("Anisotropic", "Anisotropic filtering "),
|
||||
},
|
||||
VXBLEND_MODE: {
|
||||
VXBLEND_MODE.VXBLEND_ZERO.value: EnumDocstring("Zero", "Blend factor is (0, 0, 0, 0). "),
|
||||
VXBLEND_MODE.VXBLEND_ONE.value: EnumDocstring("One", "Blend factor is (1, 1, 1, 1). "),
|
||||
VXBLEND_MODE.VXBLEND_SRCCOLOR.value: EnumDocstring("Src Color", "Blend factor is (Rs, Gs, Bs, As). "),
|
||||
VXBLEND_MODE.VXBLEND_INVSRCCOLOR.value: EnumDocstring("Inv Src Color", "Blend factor is (1-Rs, 1-Gs, 1-Bs, 1-As). "),
|
||||
VXBLEND_MODE.VXBLEND_SRCALPHA.value: EnumDocstring("Src Alpha", "Blend factor is (As, As, As, As). "),
|
||||
VXBLEND_MODE.VXBLEND_INVSRCALPHA.value: EnumDocstring("Inv Src Alpha", "Blend factor is (1-As, 1-As, 1-As, 1-As). "),
|
||||
VXBLEND_MODE.VXBLEND_DESTALPHA.value: EnumDocstring("Dest Alpha", "Blend factor is (Ad, Ad, Ad, Ad). "),
|
||||
VXBLEND_MODE.VXBLEND_INVDESTALPHA.value: EnumDocstring("Inv Dest Alpha", "Blend factor is (1-Ad, 1-Ad, 1-Ad, 1-Ad). "),
|
||||
VXBLEND_MODE.VXBLEND_DESTCOLOR.value: EnumDocstring("Dest Color", "Blend factor is (Rd, Gd, Bd, Ad). "),
|
||||
VXBLEND_MODE.VXBLEND_INVDESTCOLOR.value: EnumDocstring("Inv Dest Color", "Blend factor is (1-Rd, 1-Gd, 1-Bd, 1-Ad). "),
|
||||
VXBLEND_MODE.VXBLEND_SRCALPHASAT.value: EnumDocstring("Src Alpha Sat", "Blend factor is (f, f, f, 1); f = min(As, 1-Ad). "),
|
||||
#VXBLEND_MODE.VXBLEND_BOTHSRCALPHA.value: EnumDocstring("Both Src Alpha", "Source blend factor is (As, As, As, As) and destination blend factor is (1-As, 1-As, 1-As, 1-As) "),
|
||||
#VXBLEND_MODE.VXBLEND_BOTHINVSRCALPHA.value: EnumDocstring("Both Inv Src Alpha", "Source blend factor is (1-As, 1-As, 1-As, 1-As) and destination blend factor is (As, As, As, As) "),
|
||||
VXBLEND_MODE.VXBLEND_ZERO.value: EnumAnnotation("Zero", "Blend factor is (0, 0, 0, 0). "),
|
||||
VXBLEND_MODE.VXBLEND_ONE.value: EnumAnnotation("One", "Blend factor is (1, 1, 1, 1). "),
|
||||
VXBLEND_MODE.VXBLEND_SRCCOLOR.value: EnumAnnotation("Src Color", "Blend factor is (Rs, Gs, Bs, As). "),
|
||||
VXBLEND_MODE.VXBLEND_INVSRCCOLOR.value: EnumAnnotation("Inv Src Color", "Blend factor is (1-Rs, 1-Gs, 1-Bs, 1-As). "),
|
||||
VXBLEND_MODE.VXBLEND_SRCALPHA.value: EnumAnnotation("Src Alpha", "Blend factor is (As, As, As, As). "),
|
||||
VXBLEND_MODE.VXBLEND_INVSRCALPHA.value: EnumAnnotation("Inv Src Alpha", "Blend factor is (1-As, 1-As, 1-As, 1-As). "),
|
||||
VXBLEND_MODE.VXBLEND_DESTALPHA.value: EnumAnnotation("Dest Alpha", "Blend factor is (Ad, Ad, Ad, Ad). "),
|
||||
VXBLEND_MODE.VXBLEND_INVDESTALPHA.value: EnumAnnotation("Inv Dest Alpha", "Blend factor is (1-Ad, 1-Ad, 1-Ad, 1-Ad). "),
|
||||
VXBLEND_MODE.VXBLEND_DESTCOLOR.value: EnumAnnotation("Dest Color", "Blend factor is (Rd, Gd, Bd, Ad). "),
|
||||
VXBLEND_MODE.VXBLEND_INVDESTCOLOR.value: EnumAnnotation("Inv Dest Color", "Blend factor is (1-Rd, 1-Gd, 1-Bd, 1-Ad). "),
|
||||
VXBLEND_MODE.VXBLEND_SRCALPHASAT.value: EnumAnnotation("Src Alpha Sat", "Blend factor is (f, f, f, 1); f = min(As, 1-Ad). "),
|
||||
#VXBLEND_MODE.VXBLEND_BOTHSRCALPHA.value: EnumAnnotation("Both Src Alpha", "Source blend factor is (As, As, As, As) and destination blend factor is (1-As, 1-As, 1-As, 1-As) "),
|
||||
#VXBLEND_MODE.VXBLEND_BOTHINVSRCALPHA.value: EnumAnnotation("Both Inv Src Alpha", "Source blend factor is (1-As, 1-As, 1-As, 1-As) and destination blend factor is (As, As, As, As) "),
|
||||
},
|
||||
VXTEXTURE_ADDRESSMODE: {
|
||||
VXTEXTURE_ADDRESSMODE.VXTEXTURE_ADDRESSWRAP.value: EnumDocstring("Wrap", "Default mesh wrap mode is used (see CKMesh::SetWrapMode) "),
|
||||
VXTEXTURE_ADDRESSMODE.VXTEXTURE_ADDRESSMIRROR.value: EnumDocstring("Mirror", "Texture coordinates outside the range [0..1] are flipped evenly. "),
|
||||
VXTEXTURE_ADDRESSMODE.VXTEXTURE_ADDRESSCLAMP.value: EnumDocstring("Clamp", "Texture coordinates greater than 1.0 are set to 1.0, and values less than 0.0 are set to 0.0. "),
|
||||
VXTEXTURE_ADDRESSMODE.VXTEXTURE_ADDRESSBORDER.value: EnumDocstring("Border", "When texture coordinates are greater than 1.0 or less than 0.0 texture is set to a color defined in CKMaterial::SetTextureBorderColor. "),
|
||||
VXTEXTURE_ADDRESSMODE.VXTEXTURE_ADDRESSMIRRORONCE.value: EnumDocstring("Mirror Once", " "),
|
||||
VXTEXTURE_ADDRESSMODE.VXTEXTURE_ADDRESSWRAP.value: EnumAnnotation("Wrap", "Default mesh wrap mode is used (see CKMesh::SetWrapMode) "),
|
||||
VXTEXTURE_ADDRESSMODE.VXTEXTURE_ADDRESSMIRROR.value: EnumAnnotation("Mirror", "Texture coordinates outside the range [0..1] are flipped evenly. "),
|
||||
VXTEXTURE_ADDRESSMODE.VXTEXTURE_ADDRESSCLAMP.value: EnumAnnotation("Clamp", "Texture coordinates greater than 1.0 are set to 1.0, and values less than 0.0 are set to 0.0. "),
|
||||
VXTEXTURE_ADDRESSMODE.VXTEXTURE_ADDRESSBORDER.value: EnumAnnotation("Border", "When texture coordinates are greater than 1.0 or less than 0.0 texture is set to a color defined in CKMaterial::SetTextureBorderColor. "),
|
||||
VXTEXTURE_ADDRESSMODE.VXTEXTURE_ADDRESSMIRRORONCE.value: EnumAnnotation("Mirror Once", " "),
|
||||
},
|
||||
VXFILL_MODE: {
|
||||
VXFILL_MODE.VXFILL_POINT.value: EnumDocstring("Point", "Vertices rendering "),
|
||||
VXFILL_MODE.VXFILL_WIREFRAME.value: EnumDocstring("Wireframe", "Edges rendering "),
|
||||
VXFILL_MODE.VXFILL_SOLID.value: EnumDocstring("Solid", "Face rendering "),
|
||||
VXFILL_MODE.VXFILL_POINT.value: EnumAnnotation("Point", "Vertices rendering "),
|
||||
VXFILL_MODE.VXFILL_WIREFRAME.value: EnumAnnotation("Wireframe", "Edges rendering "),
|
||||
VXFILL_MODE.VXFILL_SOLID.value: EnumAnnotation("Solid", "Face rendering "),
|
||||
},
|
||||
VXSHADE_MODE: {
|
||||
VXSHADE_MODE.VXSHADE_FLAT.value: EnumDocstring("Flat", "Flat Shading "),
|
||||
VXSHADE_MODE.VXSHADE_GOURAUD.value: EnumDocstring("Gouraud", "Gouraud Shading "),
|
||||
VXSHADE_MODE.VXSHADE_PHONG.value: EnumDocstring("Phong", "Phong Shading (Not yet supported by most implementation) "),
|
||||
VXSHADE_MODE.VXSHADE_FLAT.value: EnumAnnotation("Flat", "Flat Shading "),
|
||||
VXSHADE_MODE.VXSHADE_GOURAUD.value: EnumAnnotation("Gouraud", "Gouraud Shading "),
|
||||
VXSHADE_MODE.VXSHADE_PHONG.value: EnumAnnotation("Phong", "Phong Shading (Not yet supported by most implementation) "),
|
||||
},
|
||||
VXCMPFUNC: {
|
||||
VXCMPFUNC.VXCMP_NEVER.value: EnumDocstring("Never", "Always fail the test. "),
|
||||
VXCMPFUNC.VXCMP_LESS.value: EnumDocstring("Less", "Accept if value if less than current value. "),
|
||||
VXCMPFUNC.VXCMP_EQUAL.value: EnumDocstring("Equal", "Accept if value if equal than current value. "),
|
||||
VXCMPFUNC.VXCMP_LESSEQUAL.value: EnumDocstring("Less Equal", "Accept if value if less or equal than current value. "),
|
||||
VXCMPFUNC.VXCMP_GREATER.value: EnumDocstring("Greater", "Accept if value if greater than current value. "),
|
||||
VXCMPFUNC.VXCMP_NOTEQUAL.value: EnumDocstring("Not Equal", "Accept if value if different than current value. "),
|
||||
VXCMPFUNC.VXCMP_GREATEREQUAL.value: EnumDocstring("Greater Equal", "Accept if value if greater or equal current value. "),
|
||||
VXCMPFUNC.VXCMP_ALWAYS.value: EnumDocstring("Always", "Always accept the test. "),
|
||||
VXCMPFUNC.VXCMP_NEVER.value: EnumAnnotation("Never", "Always fail the test. "),
|
||||
VXCMPFUNC.VXCMP_LESS.value: EnumAnnotation("Less", "Accept if value if less than current value. "),
|
||||
VXCMPFUNC.VXCMP_EQUAL.value: EnumAnnotation("Equal", "Accept if value if equal than current value. "),
|
||||
VXCMPFUNC.VXCMP_LESSEQUAL.value: EnumAnnotation("Less Equal", "Accept if value if less or equal than current value. "),
|
||||
VXCMPFUNC.VXCMP_GREATER.value: EnumAnnotation("Greater", "Accept if value if greater than current value. "),
|
||||
VXCMPFUNC.VXCMP_NOTEQUAL.value: EnumAnnotation("Not Equal", "Accept if value if different than current value. "),
|
||||
VXCMPFUNC.VXCMP_GREATEREQUAL.value: EnumAnnotation("Greater Equal", "Accept if value if greater or equal current value. "),
|
||||
VXCMPFUNC.VXCMP_ALWAYS.value: EnumAnnotation("Always", "Always accept the test. "),
|
||||
},
|
||||
CK_TEXTURE_SAVEOPTIONS: {
|
||||
CK_TEXTURE_SAVEOPTIONS.CKTEXTURE_RAWDATA.value: EnumDocstring("Raw Data", "Save raw data inside file. The bitmap is saved in a raw 32 bit per pixel format. "),
|
||||
CK_TEXTURE_SAVEOPTIONS.CKTEXTURE_EXTERNAL.value: EnumDocstring("External", "Store only the file name for the texture. The bitmap file must be present in the bitmap paths when loading the composition. "),
|
||||
CK_TEXTURE_SAVEOPTIONS.CKTEXTURE_IMAGEFORMAT.value: EnumDocstring("Image Format", "Save using format specified. The bitmap data will be converted to the specified format by the correspondant bitmap plugin and saved inside file. "),
|
||||
CK_TEXTURE_SAVEOPTIONS.CKTEXTURE_USEGLOBAL.value: EnumDocstring("Use Global", "Use Global settings, that is the settings given with CKContext::SetGlobalImagesSaveOptions. (Not valid when using CKContext::SetImagesSaveOptions). "),
|
||||
CK_TEXTURE_SAVEOPTIONS.CKTEXTURE_INCLUDEORIGINALFILE.value: EnumDocstring("Include Original File", "Insert original image file inside CMO file. The bitmap file that was used originally for the texture or sprite will be append to the composition file and extracted when the file is loaded. "),
|
||||
CK_TEXTURE_SAVEOPTIONS.CKTEXTURE_RAWDATA.value: EnumAnnotation("Raw Data", "Save raw data inside file. The bitmap is saved in a raw 32 bit per pixel format. "),
|
||||
CK_TEXTURE_SAVEOPTIONS.CKTEXTURE_EXTERNAL.value: EnumAnnotation("External", "Store only the file name for the texture. The bitmap file must be present in the bitmap paths when loading the composition. "),
|
||||
CK_TEXTURE_SAVEOPTIONS.CKTEXTURE_IMAGEFORMAT.value: EnumAnnotation("Image Format", "Save using format specified. The bitmap data will be converted to the specified format by the correspondant bitmap plugin and saved inside file. "),
|
||||
CK_TEXTURE_SAVEOPTIONS.CKTEXTURE_USEGLOBAL.value: EnumAnnotation("Use Global", "Use Global settings, that is the settings given with CKContext::SetGlobalImagesSaveOptions. (Not valid when using CKContext::SetImagesSaveOptions). "),
|
||||
CK_TEXTURE_SAVEOPTIONS.CKTEXTURE_INCLUDEORIGINALFILE.value: EnumAnnotation("Include Original File", "Insert original image file inside CMO file. The bitmap file that was used originally for the texture or sprite will be append to the composition file and extracted when the file is loaded. "),
|
||||
},
|
||||
VX_PIXELFORMAT: {
|
||||
VX_PIXELFORMAT._32_ARGB8888.value: EnumDocstring("32 Bits ARGB8888", "32-bit ARGB pixel format with alpha "),
|
||||
VX_PIXELFORMAT._32_RGB888.value: EnumDocstring("32 Bits RGB888", "32-bit RGB pixel format without alpha "),
|
||||
VX_PIXELFORMAT._24_RGB888.value: EnumDocstring("24 Bits RGB888", "24-bit RGB pixel format "),
|
||||
VX_PIXELFORMAT._16_RGB565.value: EnumDocstring("16 Bits RGB565", "16-bit RGB pixel format "),
|
||||
VX_PIXELFORMAT._16_RGB555.value: EnumDocstring("16 Bits RGB555", "16-bit RGB pixel format (5 bits per color) "),
|
||||
VX_PIXELFORMAT._16_ARGB1555.value: EnumDocstring("16 Bits ARGB1555", "16-bit ARGB pixel format (5 bits per color + 1 bit for alpha) "),
|
||||
VX_PIXELFORMAT._16_ARGB4444.value: EnumDocstring("16 Bits ARGB4444", "16-bit ARGB pixel format (4 bits per color) "),
|
||||
VX_PIXELFORMAT._8_RGB332.value: EnumDocstring("8 Bits RGB332", "8-bit RGB pixel format "),
|
||||
VX_PIXELFORMAT._8_ARGB2222.value: EnumDocstring("8 Bits ARGB2222", "8-bit ARGB pixel format "),
|
||||
VX_PIXELFORMAT._32_ABGR8888.value: EnumDocstring("32 Bits ABGR8888", "32-bit ABGR pixel format "),
|
||||
VX_PIXELFORMAT._32_RGBA8888.value: EnumDocstring("32 Bits RGBA8888", "32-bit RGBA pixel format "),
|
||||
VX_PIXELFORMAT._32_BGRA8888.value: EnumDocstring("32 Bits BGRA8888", "32-bit BGRA pixel format "),
|
||||
VX_PIXELFORMAT._32_BGR888.value: EnumDocstring("32 Bits BGR888", "32-bit BGR pixel format "),
|
||||
VX_PIXELFORMAT._24_BGR888.value: EnumDocstring("24 Bits BGR888", "24-bit BGR pixel format "),
|
||||
VX_PIXELFORMAT._16_BGR565.value: EnumDocstring("16 Bits BGR565", "16-bit BGR pixel format "),
|
||||
VX_PIXELFORMAT._16_BGR555.value: EnumDocstring("16 Bits BGR555", "16-bit BGR pixel format (5 bits per color) "),
|
||||
VX_PIXELFORMAT._16_ABGR1555.value: EnumDocstring("16 Bits ABGR1555", "16-bit ABGR pixel format (5 bits per color + 1 bit for alpha) "),
|
||||
VX_PIXELFORMAT._16_ABGR4444.value: EnumDocstring("16 Bits ABGR4444", "16-bit ABGR pixel format (4 bits per color) "),
|
||||
VX_PIXELFORMAT._DXT1.value: EnumDocstring("DXT1", "S3/DirectX Texture Compression 1 "),
|
||||
VX_PIXELFORMAT._DXT2.value: EnumDocstring("DXT2", "S3/DirectX Texture Compression 2 "),
|
||||
VX_PIXELFORMAT._DXT3.value: EnumDocstring("DXT3", "S3/DirectX Texture Compression 3 "),
|
||||
VX_PIXELFORMAT._DXT4.value: EnumDocstring("DXT4", "S3/DirectX Texture Compression 4 "),
|
||||
VX_PIXELFORMAT._DXT5.value: EnumDocstring("DXT5", "S3/DirectX Texture Compression 5 "),
|
||||
VX_PIXELFORMAT._16_V8U8.value: EnumDocstring("16 Bits V8U8", "16-bit Bump Map format format (8 bits per color) "),
|
||||
VX_PIXELFORMAT._32_V16U16.value: EnumDocstring("32 Bits V16U16", "32-bit Bump Map format format (16 bits per color) "),
|
||||
VX_PIXELFORMAT._16_L6V5U5.value: EnumDocstring("16 Bits L6V5U5", "16-bit Bump Map format format with luminance "),
|
||||
VX_PIXELFORMAT._32_X8L8V8U8.value: EnumDocstring("32 Bits X8L8V8U8", "32-bit Bump Map format format with luminance "),
|
||||
VX_PIXELFORMAT._8_ABGR8888_CLUT.value: EnumDocstring("8 Bits ABGR8888 CLUT", "8 bits indexed CLUT (ABGR) "),
|
||||
VX_PIXELFORMAT._8_ARGB8888_CLUT.value: EnumDocstring("8 Bits ARGB8888 CLUT", "8 bits indexed CLUT (ARGB) "),
|
||||
VX_PIXELFORMAT._4_ABGR8888_CLUT.value: EnumDocstring("4 Bits ABGR8888 CLUT", "4 bits indexed CLUT (ABGR) "),
|
||||
VX_PIXELFORMAT._4_ARGB8888_CLUT.value: EnumDocstring("4 Bits ARGB8888 CLUT", "4 bits indexed CLUT (ARGB) "),
|
||||
VX_PIXELFORMAT._32_ARGB8888.value: EnumAnnotation("32 Bits ARGB8888", "32-bit ARGB pixel format with alpha "),
|
||||
VX_PIXELFORMAT._32_RGB888.value: EnumAnnotation("32 Bits RGB888", "32-bit RGB pixel format without alpha "),
|
||||
VX_PIXELFORMAT._24_RGB888.value: EnumAnnotation("24 Bits RGB888", "24-bit RGB pixel format "),
|
||||
VX_PIXELFORMAT._16_RGB565.value: EnumAnnotation("16 Bits RGB565", "16-bit RGB pixel format "),
|
||||
VX_PIXELFORMAT._16_RGB555.value: EnumAnnotation("16 Bits RGB555", "16-bit RGB pixel format (5 bits per color) "),
|
||||
VX_PIXELFORMAT._16_ARGB1555.value: EnumAnnotation("16 Bits ARGB1555", "16-bit ARGB pixel format (5 bits per color + 1 bit for alpha) "),
|
||||
VX_PIXELFORMAT._16_ARGB4444.value: EnumAnnotation("16 Bits ARGB4444", "16-bit ARGB pixel format (4 bits per color) "),
|
||||
VX_PIXELFORMAT._8_RGB332.value: EnumAnnotation("8 Bits RGB332", "8-bit RGB pixel format "),
|
||||
VX_PIXELFORMAT._8_ARGB2222.value: EnumAnnotation("8 Bits ARGB2222", "8-bit ARGB pixel format "),
|
||||
VX_PIXELFORMAT._32_ABGR8888.value: EnumAnnotation("32 Bits ABGR8888", "32-bit ABGR pixel format "),
|
||||
VX_PIXELFORMAT._32_RGBA8888.value: EnumAnnotation("32 Bits RGBA8888", "32-bit RGBA pixel format "),
|
||||
VX_PIXELFORMAT._32_BGRA8888.value: EnumAnnotation("32 Bits BGRA8888", "32-bit BGRA pixel format "),
|
||||
VX_PIXELFORMAT._32_BGR888.value: EnumAnnotation("32 Bits BGR888", "32-bit BGR pixel format "),
|
||||
VX_PIXELFORMAT._24_BGR888.value: EnumAnnotation("24 Bits BGR888", "24-bit BGR pixel format "),
|
||||
VX_PIXELFORMAT._16_BGR565.value: EnumAnnotation("16 Bits BGR565", "16-bit BGR pixel format "),
|
||||
VX_PIXELFORMAT._16_BGR555.value: EnumAnnotation("16 Bits BGR555", "16-bit BGR pixel format (5 bits per color) "),
|
||||
VX_PIXELFORMAT._16_ABGR1555.value: EnumAnnotation("16 Bits ABGR1555", "16-bit ABGR pixel format (5 bits per color + 1 bit for alpha) "),
|
||||
VX_PIXELFORMAT._16_ABGR4444.value: EnumAnnotation("16 Bits ABGR4444", "16-bit ABGR pixel format (4 bits per color) "),
|
||||
VX_PIXELFORMAT._DXT1.value: EnumAnnotation("DXT1", "S3/DirectX Texture Compression 1 "),
|
||||
VX_PIXELFORMAT._DXT2.value: EnumAnnotation("DXT2", "S3/DirectX Texture Compression 2 "),
|
||||
VX_PIXELFORMAT._DXT3.value: EnumAnnotation("DXT3", "S3/DirectX Texture Compression 3 "),
|
||||
VX_PIXELFORMAT._DXT4.value: EnumAnnotation("DXT4", "S3/DirectX Texture Compression 4 "),
|
||||
VX_PIXELFORMAT._DXT5.value: EnumAnnotation("DXT5", "S3/DirectX Texture Compression 5 "),
|
||||
VX_PIXELFORMAT._16_V8U8.value: EnumAnnotation("16 Bits V8U8", "16-bit Bump Map format format (8 bits per color) "),
|
||||
VX_PIXELFORMAT._32_V16U16.value: EnumAnnotation("32 Bits V16U16", "32-bit Bump Map format format (16 bits per color) "),
|
||||
VX_PIXELFORMAT._16_L6V5U5.value: EnumAnnotation("16 Bits L6V5U5", "16-bit Bump Map format format with luminance "),
|
||||
VX_PIXELFORMAT._32_X8L8V8U8.value: EnumAnnotation("32 Bits X8L8V8U8", "32-bit Bump Map format format with luminance "),
|
||||
VX_PIXELFORMAT._8_ABGR8888_CLUT.value: EnumAnnotation("8 Bits ABGR8888 CLUT", "8 bits indexed CLUT (ABGR) "),
|
||||
VX_PIXELFORMAT._8_ARGB8888_CLUT.value: EnumAnnotation("8 Bits ARGB8888 CLUT", "8 bits indexed CLUT (ARGB) "),
|
||||
VX_PIXELFORMAT._4_ABGR8888_CLUT.value: EnumAnnotation("4 Bits ABGR8888 CLUT", "4 bits indexed CLUT (ABGR) "),
|
||||
VX_PIXELFORMAT._4_ARGB8888_CLUT.value: EnumAnnotation("4 Bits ARGB8888 CLUT", "4 bits indexed CLUT (ARGB) "),
|
||||
},
|
||||
VXLIGHT_TYPE: {
|
||||
VXLIGHT_TYPE.VX_LIGHTPOINT.value: EnumDocstring("Point", "The Light is a point of light "),
|
||||
VXLIGHT_TYPE.VX_LIGHTSPOT.value: EnumDocstring("Spot", "The light is a spotlight "),
|
||||
VXLIGHT_TYPE.VX_LIGHTDIREC.value: EnumDocstring("Directional", "The light is directional light : Lights comes from an infinite point so only direction of light can be given "),
|
||||
#VXLIGHT_TYPE.VX_LIGHTPARA.value: EnumDocstring("Lightpara", "Obsolete, do not use "),
|
||||
VXLIGHT_TYPE.VX_LIGHTPOINT.value: EnumAnnotation("Point", "The Light is a point of light "),
|
||||
VXLIGHT_TYPE.VX_LIGHTSPOT.value: EnumAnnotation("Spot", "The light is a spotlight "),
|
||||
VXLIGHT_TYPE.VX_LIGHTDIREC.value: EnumAnnotation("Directional", "The light is directional light : Lights comes from an infinite point so only direction of light can be given "),
|
||||
#VXLIGHT_TYPE.VX_LIGHTPARA.value: EnumAnnotation("Lightpara", "Obsolete, do not use "),
|
||||
},
|
||||
VXMESH_LITMODE: {
|
||||
VXMESH_LITMODE.VX_PRELITMESH.value: EnumDocstring("Prelit", "Lighting use color information store with vertices "),
|
||||
VXMESH_LITMODE.VX_LITMESH.value: EnumDocstring("Lit", "Lighting is done by renderer using normals and face material information. "),
|
||||
},
|
||||
CK_CAMERA_PROJECTION: {
|
||||
CK_CAMERA_PROJECTION.CK_PERSPECTIVEPROJECTION.value: EnumDocstring("Perspective Projection", ""),
|
||||
CK_CAMERA_PROJECTION.CK_ORTHOGRAPHICPROJECTION.value: EnumDocstring("Orthographic Projection", ""),
|
||||
VXMESH_LITMODE.VX_PRELITMESH.value: EnumAnnotation("Prelit", "Lighting use color information store with vertices "),
|
||||
VXMESH_LITMODE.VX_LITMESH.value: EnumAnnotation("Lit", "Lighting is done by renderer using normals and face material information. "),
|
||||
}
|
||||
}
|
||||
|
||||
_TRawEnum = typing.TypeVar('_TRawEnum', bound = enum.Enum)
|
||||
|
||||
class EnumPropHelper(UTIL_functions.EnumPropHelper[_TRawEnum]):
|
||||
class EnumPropHelper(UTIL_functions.EnumPropHelper):
|
||||
"""
|
||||
Virtools type specified Blender EnumProp helper.
|
||||
"""
|
||||
__mDocstringDict: dict[int, EnumDocstring]
|
||||
__mEnumTy: type[_TRawEnum]
|
||||
__mAnnotationDict: dict[int, EnumAnnotation]
|
||||
__mEnumTy: type[enum.Enum]
|
||||
|
||||
def __init__(self, ty: type[_TRawEnum]):
|
||||
# set enum type and docstring ref first
|
||||
def __init__(self, ty: type[enum.Enum]):
|
||||
# set enum type and annotation ref first
|
||||
self.__mEnumTy = ty
|
||||
self.__mDocstringDict = _g_Docstring[ty]
|
||||
|
||||
# YYC MARK:
|
||||
# It seems that Pylance has bad generic analyse ability in there.
|
||||
# It can not deduce the correct generic type in lambda.
|
||||
# I gave up.
|
||||
|
||||
# Init parent data
|
||||
super().__init__(
|
||||
self.__mEnumTy, # enum.Enum its self is iterable
|
||||
self.__mAnnotationDict = _g_Annotation[ty]
|
||||
# init parent data
|
||||
UTIL_functions.EnumPropHelper.__init__(
|
||||
self,
|
||||
self.__mEnumTy, # enum.Enum it self is iterable
|
||||
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.__mDocstringDict[x.value].display_name,
|
||||
lambda x: self.__mDocstringDict[x.value].description,
|
||||
lambda x: self.__mAnnotationDict[x.value].mDisplayName,
|
||||
lambda x: self.__mAnnotationDict[x.value].mDescription,
|
||||
lambda _: ''
|
||||
)
|
||||
|
||||
@@ -302,12 +265,12 @@ def virtools_name_regulator(name: str | None) -> str:
|
||||
if name: return name
|
||||
else: return bpy.app.translations.pgettext_data('annoymous', 'BME/UTIL_virtools_types.virtools_name_regulator()')
|
||||
|
||||
# YYC MARK:
|
||||
# There are default encodings for pybmap. We support Western European and Simplified Chinese in default.
|
||||
# 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.
|
||||
# Use universal encoding name (like Python).
|
||||
g_PybmapDefaultEncodings: tuple[str, ...] = (
|
||||
## Default Encoding for PyBMap
|
||||
# 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.
|
||||
# So no need set it according to different platform.
|
||||
# Use universal encoding name (like Python).
|
||||
g_PyBMapDefaultEncodings: tuple[str, ...] = (
|
||||
'cp1252',
|
||||
'gbk'
|
||||
)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#region Import and Reload
|
||||
#region Reload and Import
|
||||
|
||||
# import core lib
|
||||
import bpy
|
||||
import typing, enum
|
||||
import typing, collections
|
||||
|
||||
# reload if needed
|
||||
# TODO: finish reload feature if needed.
|
||||
@@ -10,6 +10,8 @@ import typing, enum
|
||||
if "bpy" in locals():
|
||||
import importlib
|
||||
|
||||
#endregion
|
||||
|
||||
# we must load icons manager first
|
||||
# and register it
|
||||
from . import UTIL_icons_manager
|
||||
@@ -17,151 +19,17 @@ UTIL_icons_manager.register()
|
||||
|
||||
# then load other modules
|
||||
from . import UTIL_translation
|
||||
from . import PROP_preferences, PROP_ptrprop_resolver, PROP_virtools_material, PROP_virtools_texture, PROP_virtools_mesh, PROP_virtools_light, PROP_virtools_camera, PROP_virtools_group
|
||||
from . import PROP_preferences, PROP_ptrprop_resolver, PROP_virtools_material, PROP_virtools_texture, PROP_virtools_mesh, PROP_virtools_light, PROP_virtools_group
|
||||
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_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_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
|
||||
|
||||
#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
|
||||
# ===== Menu Defines =====
|
||||
|
||||
class BBP_MT_View3DMenu(bpy.types.Menu):
|
||||
"""Ballance 3D related operators"""
|
||||
@@ -170,7 +38,7 @@ class BBP_MT_View3DMenu(bpy.types.Menu):
|
||||
bl_translation_context = 'BBP_MT_View3DMenu'
|
||||
|
||||
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.operator(OP_UV_flatten_uv.BBP_OT_flatten_uv.bl_idname)
|
||||
layout.operator(OP_UV_rail_uv.BBP_OT_rail_uv.bl_idname)
|
||||
@@ -178,20 +46,77 @@ class BBP_MT_View3DMenu(bpy.types.Menu):
|
||||
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.separator()
|
||||
layout.label(text='Camera', icon='CAMERA_DATA', text_ctxt='BBP_MT_View3DMenu/draw')
|
||||
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.operator(OP_OBJECT_virtools_group.BBP_OT_select_object_by_virtools_group.bl_idname)
|
||||
layout.separator()
|
||||
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:
|
||||
layout: bpy.types.UILayout = self.layout
|
||||
@@ -229,17 +154,16 @@ def menu_drawer_add(self, context) -> None:
|
||||
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_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:
|
||||
layout: bpy.types.UILayout = self.layout
|
||||
layout.separator()
|
||||
|
||||
# YYC MARK:
|
||||
# Because outline context change operator context into EXEC_*,
|
||||
# NOTE: because outline context may change operator context
|
||||
# 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',
|
||||
# so that all operators can pop up normally.
|
||||
# thus we create a sub layout and set its operator context as 'INVOKE_DEFAULT'
|
||||
# thus, all operators can pop up normally.
|
||||
col = layout.column()
|
||||
col.operator_context = 'INVOKE_DEFAULT'
|
||||
|
||||
@@ -264,8 +188,7 @@ def menu_drawer_naming_convention(self, context) -> None:
|
||||
layout: bpy.types.UILayout = self.layout
|
||||
layout.separator()
|
||||
|
||||
# YYC MARK:
|
||||
# Same reason for changing operator context introduced in `menu_drawer_grouping()`
|
||||
# same reason in `menu_drawer_grouping()``
|
||||
col = layout.column()
|
||||
col.operator_context = 'INVOKE_DEFAULT'
|
||||
|
||||
@@ -276,43 +199,37 @@ def menu_drawer_naming_convention(self, context) -> None:
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
#region Register and Unregister.
|
||||
|
||||
g_BldClasses: tuple[typing.Any, ...] = (
|
||||
BBP_MT_View3DMenu,
|
||||
BBP_MT_AddBmeMenu,
|
||||
BBP_MT_AddRailMenu,
|
||||
BBP_MT_AddComponentMenu,
|
||||
|
||||
BBP_PT_SidebarAddBmePanel,
|
||||
BBP_PT_SidebarAddRailPanel,
|
||||
BBP_PT_SidebarAddComponentPanel,
|
||||
BBP_MT_AddComponentsMenu
|
||||
)
|
||||
|
||||
class MenuEntry():
|
||||
mContainerMenu: bpy.types.Menu
|
||||
mIsAppend: bool
|
||||
mMenuDrawer: TFctMenuDrawer
|
||||
def __init__(self, cont: bpy.types.Menu, is_append: bool, menu_func: TFctMenuDrawer):
|
||||
mMenuDrawer: MenuDrawer_t
|
||||
def __init__(self, cont: bpy.types.Menu, is_append: bool, menu_func: MenuDrawer_t):
|
||||
self.mContainerMenu = cont
|
||||
self.mIsAppend = is_append
|
||||
self.mMenuDrawer = menu_func
|
||||
|
||||
g_BldMenus: tuple[MenuEntry, ...] = (
|
||||
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_export, True, menu_drawer_export),
|
||||
MenuEntry(bpy.types.VIEW3D_MT_add, True, menu_drawer_add),
|
||||
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_export, True, menu_drawer_export),
|
||||
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)
|
||||
MenuEntry(bpy.types.VIEW3D_MT_object_context_menu, True, menu_drawer_grouping),
|
||||
MenuEntry(bpy.types.OUTLINER_MT_object, True, menu_drawer_grouping),
|
||||
# register double (for 2 menus)
|
||||
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_collection, True, menu_drawer_naming_convention),
|
||||
MenuEntry(bpy.types.OUTLINER_MT_collection, True, menu_drawer_naming_convention),
|
||||
)
|
||||
|
||||
def register() -> None:
|
||||
@@ -326,7 +243,6 @@ def register() -> None:
|
||||
PROP_virtools_texture.register()
|
||||
PROP_virtools_mesh.register()
|
||||
PROP_virtools_light.register()
|
||||
PROP_virtools_camera.register()
|
||||
PROP_virtools_group.register()
|
||||
PROP_ballance_element.register()
|
||||
PROP_bme_material.register()
|
||||
@@ -340,7 +256,7 @@ def register() -> None:
|
||||
OP_UV_rail_uv.register()
|
||||
OP_UV_flatten_uv.register()
|
||||
|
||||
OP_MTL_fix_materials.register()
|
||||
OP_MTL_fix_material.register()
|
||||
|
||||
OP_ADDS_component.register()
|
||||
OP_ADDS_bme.register()
|
||||
@@ -350,7 +266,6 @@ def register() -> None:
|
||||
OP_OBJECT_virtools_group.register()
|
||||
OP_OBJECT_snoop_group_then_to_mesh.register()
|
||||
OP_OBJECT_naming_convention.register()
|
||||
OP_OBJECT_game_view.register()
|
||||
|
||||
# register other classes
|
||||
for cls in g_BldClasses:
|
||||
@@ -373,7 +288,6 @@ def unregister() -> None:
|
||||
bpy.utils.unregister_class(cls)
|
||||
|
||||
# unregister modules
|
||||
OP_OBJECT_game_view.unregister()
|
||||
OP_OBJECT_naming_convention.unregister()
|
||||
OP_OBJECT_snoop_group_then_to_mesh.unregister()
|
||||
OP_OBJECT_virtools_group.unregister()
|
||||
@@ -383,7 +297,7 @@ def unregister() -> None:
|
||||
OP_ADDS_bme.unregister()
|
||||
OP_ADDS_component.unregister()
|
||||
|
||||
OP_MTL_fix_materials.unregister()
|
||||
OP_MTL_fix_material.unregister()
|
||||
|
||||
OP_UV_flatten_uv.unregister()
|
||||
OP_UV_rail_uv.unregister()
|
||||
@@ -397,7 +311,6 @@ def unregister() -> None:
|
||||
PROP_bme_material.unregister()
|
||||
PROP_ballance_element.unregister()
|
||||
PROP_virtools_group.unregister()
|
||||
PROP_virtools_camera.unregister()
|
||||
PROP_virtools_light.unregister()
|
||||
PROP_virtools_mesh.unregister()
|
||||
PROP_virtools_texture.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
|
||||
|
||||
schema_version = "1.0.0"
|
||||
@@ -6,7 +6,7 @@ schema_version = "1.0.0"
|
||||
# Example of manifest file for a Blender extension
|
||||
# Change the values according to your extension
|
||||
id = "bbp_ng"
|
||||
version = "4.4.0"
|
||||
version = "4.2.1"
|
||||
name = "Ballance Blender Plugin"
|
||||
tagline = "The specialized add-on served for creating game map of Ballance"
|
||||
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
|
||||
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.
|
||||
# # This can be omitted and defined later on the extensions platform if an issue is found.
|
||||
# blender_version_max = "5.1.0"
|
||||
@@ -71,8 +71,12 @@ files = "Import/export Virtools file from/to disk"
|
||||
[build]
|
||||
paths_exclude_pattern = [
|
||||
"__pycache__/", # Python runtime cache
|
||||
".gitignore", # Git Ignore File
|
||||
".style.yapf", # Python code style
|
||||
"*.gitkeep", # Git directory keeper
|
||||
".gitignore", # Git Ignore File
|
||||
"*.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 |