BallanceBlenderHelper/bbp_ng/UTIL_bme.py
2023-12-17 12:01:57 +08:00

229 lines
6.8 KiB
Python

import bpy, mathutils
import os, json, enum, typing, math
from . import PROP_virtools_group, PROP_bme_material
from . import UTIL_functions, UTIL_icons_manager, UTIL_blender_mesh
## NOTE: Outside caller should use BME struct's unique indetifier to visit each prototype
# and drive this class' functions to work.
#region Prototype Visitor
class PrototypeShowcaseCfgsTypes(enum.Enum):
Integer = 'int'
Float = 'float'
Boolean = 'bool'
Face = 'face'
class PrototypeShowcaseTypes(enum.Enum):
No = 'none'
Floor = 'floor'
Rail = 'rail'
Wood = 'wood'
TOKEN_IDENTIFIER: str = 'identifier'
TOKEN_SHOWCASE: str = 'showcase'
TOKEN_SHOWCASE_TITLE: str = 'title'
TOKEN_SHOWCASE_ICON: str = 'icon'
TOKEN_SHOWCASE_TYPE: str = 'type'
TOKEN_SHOWCASE_CFGS: str = 'cfgs'
TOKEN_SHOWCASE_CFGS_FIELD: str = 'field'
TOKEN_SHOWCASE_CFGS_TYPE: str = 'type'
TOKEN_SHOWCASE_CFGS_TITLE: str = 'title'
TOKEN_SHOWCASE_CFGS_DESC: str = 'desc'
TOKEN_SHOWCASE_CFGS_DEFAULT: str = 'default'
TOKEN_PARAMS: str = 'params'
TOKEN_PARAMS_FIELD: str = 'field'
TOKEN_PARAMS_DATA: str = 'data'
TOKEN_VARS: str = 'vars'
TOKEN_VARS_FIELD: str = 'field'
TOKEN_VARS_DATA: str = 'data'
TOKEN_VERTICES: str = 'vertices'
TOKEN_VERTICES_SKIP: str = 'skip'
TOKEN_VERTICES_DATA: str = 'data'
TOKEN_FACES: str = 'faces'
TOKEN_FACES_SKIP: str = 'skip'
TOKEN_FACES_TEXTURE: str = 'texture'
TOKEN_FACES_INDICES: str = 'indices'
TOKEN_FACES_UVS: str = 'uvs'
TOKEN_FACES_NORMALS: str = 'normals'
TOKEN_INSTANCES: str = 'instances'
TOKEN_INSTANCES_IDENTIFIER: str = 'identifier'
TOKEN_INSTANCES_SKIP: str = 'skip'
TOKEN_INSTANCES_PARAMS: str = 'params'
TOKEN_INSTANCES_TRANSFORM: str = 'transform'
#endregion
#region Prototype Loader
## The list storing BME prototype.
_g_BMEPrototypes: list[dict[str, typing.Any]] = []
## The dict. Key is prototype identifier. value is the index of prototype in prototype list.
_g_BMEPrototypeIndexMap: dict[str, int] = {}
# the core loader
for walk_root, walk_dirs, walk_files in os.walk(os.path.join(os.path.dirname(__file__), 'json')):
for relfile in walk_files:
if not relfile.endswith('.json'): continue
with open(os.path.join(walk_root, relfile), 'r', encoding = 'utf-8') as fp:
proto: dict[str, typing.Any]
for proto in json.load(fp):
# insert index to map
_g_BMEPrototypeIndexMap[proto[TOKEN_IDENTIFIER]] = len(_g_BMEPrototypes)
# add into list
_g_BMEPrototypes.append(proto)
def _get_prototype_by_identifier(ident: str) -> dict[str, typing.Any]:
return _g_BMEPrototypes[_g_BMEPrototypeIndexMap[ident]]
#endregion
#region Programmable Field Calc
_g_ProgFieldGlobals: dict[str, typing.Any] = {
# constant
'pi': math.pi,
'tau': math.tau,
# math functions
'sin': math.sin,
'cos': math.cos,
'tan': math.tan,
'asin': math.asin,
'acos': math.acos,
'atan': math.atan,
'pow': math.pow,
'sqrt': math.sqrt,
'fabs': math.fabs,
'degrees': math.degrees,
'radians': math.radians,
# builtin functions
'abs': abs,
'int': int,
'float': float,
'str': str,
'bool': bool,
# my custom matrix functions
'move': lambda x, y, z: mathutils.Matrix.Translation((x, y, z)),
'rot': lambda x, y, z: mathutils.Matrix.LocRotScale(None, mathutils.Euler((math.radians(x), math.radians(y), math.radians(z)), 'XYZ'), None),
'ident': lambda: mathutils.Matrix.Identity(4),
}
def _eval_showcase_cfgs_default(strl: str) -> typing.Any:
return eval(strl, _g_ProgFieldGlobals, None)
def _eval_params(strl: str, cfgs_data: dict[str, typing.Any]) -> typing.Any:
return eval(strl, _g_ProgFieldGlobals, cfgs_data)
def _eval_vars(strl: str, params_data: dict[str, typing.Any]) -> typing.Any:
return eval(strl, _g_ProgFieldGlobals, params_data)
def _eval_others(strl, str, params_vars_data: dict[str, typing.Any]) -> typing.Any:
return eval(strl, _g_ProgFieldGlobals, params_vars_data)
#endregion
#region Prototype Helper
class PrototypeShowcaseCfgDescriptor():
__mRawCfg: dict[str, str]
def __init__(self, raw_cfg: dict[str, str]):
self.__mRawCfg = raw_cfg
def get_field(self) -> str:
return self.__mRawCfg[TOKEN_SHOWCASE_CFGS_FIELD]
def get_type(self) -> PrototypeShowcaseCfgsTypes:
return PrototypeShowcaseCfgsTypes(self.__mRawCfg[TOKEN_SHOWCASE_CFGS_TYPE])
def get_title(self) -> str:
return self.__mRawCfg[TOKEN_SHOWCASE_CFGS_TITLE]
def get_desc(self) -> str:
return self.__mRawCfg[TOKEN_SHOWCASE_CFGS_DESC]
def get_default(self) -> typing.Any:
return _eval_showcase_cfgs_default(self.__mRawCfg[TOKEN_SHOWCASE_CFGS_DEFAULT])
class EnumPropHelper(UTIL_functions.EnumPropHelper):
"""
The BME specialized Blender EnumProperty helper.
"""
def __init__(self):
# init parent class
UTIL_functions.EnumPropHelper.__init__(
self,
self.get_bme_identifiers(),
lambda x: x,
lambda x: x,
lambda x: self.get_bme_showcase_title(x),
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.
Template prototype is not included.
"""
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_showcase_title(self, ident: str) -> str:
"""
Get BME display title by prototype identifier.
"""
# get prototype first
proto: dict[str, typing.Any] = _get_prototype_by_identifier(ident)
# visit title field
return proto[TOKEN_SHOWCASE][TOKEN_SHOWCASE_TITLE]
def get_bme_showcase_icon(self, ident: str) -> int:
"""
Get BME icon by prototype's identifier
"""
# get prototype specified icon name
proto: dict[str, typing.Any] = _get_prototype_by_identifier(ident)
icon_name: str = proto[TOKEN_SHOWCASE][TOKEN_SHOWCASE_ICON]
# get icon from icon manager
cache: int | None = UTIL_icons_manager.get_bme_icon(icon_name)
if cache is None: return UTIL_icons_manager.get_empty_icon()
else: return cache
def get_bme_showcase_cfgs(self, ident: str) -> typing.Iterator[PrototypeShowcaseCfgDescriptor]:
# get prototype first
proto: dict[str, typing.Any] = _get_prototype_by_identifier(ident)
# use map to batch create descriptor
return map(lambda x: PrototypeShowcaseCfgDescriptor(x), proto[TOKEN_SHOWCASE][TOKEN_SHOWCASE_CFGS])
#endregion
#region Core Creator
def get_bme_struct_cfgs():
pass
def create_bme_struct_wrapper():
pass
def create_bme_struct():
pass
#endregion