2023-12-16 18:12:00 +08:00
|
|
|
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
|
|
|
|
|
2023-12-16 22:27:31 +08:00
|
|
|
## NOTE: Outside caller should use BME struct's unique indetifier to visit each prototype
|
|
|
|
# and drive this class' functions to work.
|
|
|
|
|
2023-12-16 18:12:00 +08:00
|
|
|
#region Prototype Visitor
|
|
|
|
|
|
|
|
class PrototypeShowcaseCfgsTypes(enum.Enum):
|
|
|
|
Integer = 'int'
|
|
|
|
Float = 'float'
|
2023-12-17 12:01:57 +08:00
|
|
|
Boolean = 'bool'
|
2023-12-16 18:12:00 +08:00
|
|
|
Face = 'face'
|
|
|
|
|
2023-12-16 22:27:31 +08:00
|
|
|
class PrototypeShowcaseTypes(enum.Enum):
|
|
|
|
No = 'none'
|
|
|
|
Floor = 'floor'
|
|
|
|
Rail = 'rail'
|
|
|
|
Wood = 'wood'
|
|
|
|
|
2023-12-16 18:12:00 +08:00
|
|
|
TOKEN_IDENTIFIER: str = 'identifier'
|
|
|
|
|
|
|
|
TOKEN_SHOWCASE: str = 'showcase'
|
|
|
|
TOKEN_SHOWCASE_TITLE: str = 'title'
|
|
|
|
TOKEN_SHOWCASE_ICON: str = 'icon'
|
2023-12-16 22:27:31 +08:00
|
|
|
TOKEN_SHOWCASE_TYPE: str = 'type'
|
2023-12-16 18:12:00 +08:00
|
|
|
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.
|
2023-12-17 12:01:57 +08:00
|
|
|
_g_BMEPrototypes: list[dict[str, typing.Any]] = []
|
2023-12-16 18:12:00 +08:00
|
|
|
## 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
|
2023-12-16 22:27:31 +08:00
|
|
|
with open(os.path.join(walk_root, relfile), 'r', encoding = 'utf-8') as fp:
|
2023-12-16 18:12:00 +08:00
|
|
|
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)
|
|
|
|
|
2023-12-17 12:01:57 +08:00
|
|
|
def _get_prototype_by_identifier(ident: str) -> dict[str, typing.Any]:
|
|
|
|
return _g_BMEPrototypes[_g_BMEPrototypeIndexMap[ident]]
|
2023-12-16 22:27:31 +08:00
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
2023-12-16 18:12:00 +08:00
|
|
|
#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
|
|
|
|
|
2023-12-17 12:01:57 +08:00
|
|
|
#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
|
|
|
|
|
2023-12-16 18:12:00 +08:00
|
|
|
#region Core Creator
|
|
|
|
|
|
|
|
def get_bme_struct_cfgs():
|
|
|
|
pass
|
|
|
|
|
|
|
|
def create_bme_struct_wrapper():
|
|
|
|
pass
|
|
|
|
|
|
|
|
def create_bme_struct():
|
|
|
|
pass
|
|
|
|
|
|
|
|
#endregion
|