11 Commits

Author SHA1 Message Date
2e76ce7862 feat: bump up version 2026-03-20 14:55:38 +08:00
eb8984c341 refactor: use python built in dataclass to improve our raw data structs. 2026-03-20 14:50:05 +08:00
77e4dcdb69 fix: rename some types in module 2026-03-20 14:16:44 +08:00
44d3b1fc99 feat: finish virtools camera work
- add operator for applying camera aspect ratio to blender scene.
- add camera aspect ratio preset in virtools camera panel.
- update game camera operators. use virtools camera instead of directly modifying camera properties.
2026-03-20 13:49:00 +08:00
70fa3b5f07 feat: support virtools camera import and export 2026-03-13 15:47:24 +08:00
fbee0ccce5 doc: update README for notes 2026-03-06 16:14:47 +08:00
684567777f feat: add virtools camera support 2026-03-06 16:10:05 +08:00
5e94d7db85 fix: change the default length of flat and sink street from 2.5 to 5
- this feature is requested by Lee623
2026-03-06 14:40:50 +08:00
8a7e3306a7 feat: allow exporting object in virtools file which has geometry.
- allow exporting object without apply modifier.
- allow exporting any objects which can be mesh (has geometry).
- due to this change, add virtools mesh properties for metaball, font, curve, surface.
- due to this change, remove the virtools group warning for metaball, font, curve and surface.
2026-03-05 21:45:36 +08:00
68fbffad54 feat: update pybmap to the latest version 2026-03-05 12:45:25 +08:00
dd6e557f39 refactor: rename PyBMap to pybmap due to the change of libcmo21 2026-03-04 21:56:11 +08:00
23 changed files with 1308 additions and 795 deletions

View File

@@ -5,3 +5,9 @@
BBP NG, abbr **B**allance **B**lender **P**lugin **N**ext **G**eneration. 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 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.

View File

@@ -5,3 +5,8 @@
BBP NG又名**B**allance **B**lender **P**lugin **N**ext **G**eneration下一代Ballance Blender插件 BBP NG又名**B**allance **B**lender **P**lugin **N**ext **G**eneration下一代Ballance Blender插件
有关此插件的介绍安装编译汇报错误等请参阅本项目的GitHub Page页面https://yyc12345.github.io/BallanceBlenderHelper 有关此插件的介绍安装编译汇报错误等请参阅本项目的GitHub Page页面https://yyc12345.github.io/BallanceBlenderHelper
## 注意事项
该项目的开发目前已暂停,并且由于我兴趣的丧失和时间的不足,可能会永远暂停。
在该项目剩余的生命周期中,只会修复易于解决的致命错误。

View File

@@ -1,324 +1,324 @@
[ [
{ {
"identifier": "floor_normal_straight", "identifier": "floor_normal_straight",
"showcase": { "showcase": {
"title": "Normal Floor", "title": "Normal Floor",
"category": "Floors", "category": "Floors",
"icon": "NormalFloor", "icon": "NormalFloor",
"type": "floor", "type": "floor",
"cfgs": [ "cfgs": [
{ {
"field": "height_", "field": "height_",
"type": "float", "type": "float",
"title": "Height", "title": "Height",
"desc": "The height of block.", "desc": "The height of block.",
"default": "5.0" "default": "5.0"
}, },
{ {
"field": "length_", "field": "length_",
"type": "float", "type": "float",
"title": "Length", "title": "Length",
"desc": "The length of block.", "desc": "The length of block.",
"default": "2.5" "default": "5.0"
}, },
{ {
"field": "face_", "field": "face_",
"type": "face", "type": "face",
"title": "Face", "title": "Face",
"desc": "Whether has some faces.", "desc": "Whether has some faces.",
"default": "(True, False, False, False, True, True)" "default": "(True, False, False, False, True, True)"
} }
] ]
}, },
"params": [ "params": [
{ {
"field": "height", "field": "height",
"data": "height_" "data": "height_"
}, },
{ {
"field": "length", "field": "length",
"data": "length_" "data": "length_"
}, },
{ {
"field": "face", "field": "face",
"data": "face_" "data": "face_"
} }
], ],
"skip": "length == 0", "skip": "length == 0",
"vars": [ "vars": [
{ {
"field": "length_uv", "field": "length_uv",
"data": "length / 5.0" "data": "length / 5.0"
} }
], ],
"vertices": [ "vertices": [
{ {
"skip": "not face[0]", "skip": "not face[0]",
"data": "(0, 0, 0)" "data": "(0, 0, 0)"
}, },
{ {
"skip": "not face[0]", "skip": "not face[0]",
"data": "(length, 0, 0)" "data": "(length, 0, 0)"
}, },
{ {
"skip": "not face[0]", "skip": "not face[0]",
"data": "(length, 5, 0)" "data": "(length, 5, 0)"
}, },
{ {
"skip": "not face[0]", "skip": "not face[0]",
"data": "(0, 5, 0)" "data": "(0, 5, 0)"
} }
], ],
"faces": [ "faces": [
{ {
"skip": "not face[0]", "skip": "not face[0]",
"texture": "\"FloorTopFlat\"", "texture": "\"FloorTopFlat\"",
"indices": [0, 1, 2, 3], "indices": [0, 1, 2, 3],
"uvs": [ "uvs": [
"(0, 0)", "(0, 0)",
"(0, length_uv)", "(0, length_uv)",
"(1, length_uv)", "(1, length_uv)",
"(1, 0)" "(1, 0)"
], ],
"normals": null "normals": null
} }
], ],
"instances": [ "instances": [
{ {
"identifier": "raw_floor_side", "identifier": "raw_floor_side",
"skip": "not face[2]", "skip": "not face[2]",
"params": { "params": {
"height": "height", "height": "height",
"length": "5", "length": "5",
"is_left_sink": "False", "is_left_sink": "False",
"is_right_sink": "False" "is_right_sink": "False"
}, },
"transform": "rot(0, 0, 90) @ scale(1, -1, 1)" "transform": "rot(0, 0, 90) @ scale(1, -1, 1)"
}, },
{ {
"identifier": "raw_floor_side", "identifier": "raw_floor_side",
"skip": "not face[3]", "skip": "not face[3]",
"params": { "params": {
"height": "height", "height": "height",
"length": "5", "length": "5",
"is_left_sink": "False", "is_left_sink": "False",
"is_right_sink": "False" "is_right_sink": "False"
}, },
"transform": "move(length, 0, 0) @ rot(0, 0, 90)" "transform": "move(length, 0, 0) @ rot(0, 0, 90)"
}, },
{ {
"identifier": "raw_floor_side", "identifier": "raw_floor_side",
"skip": "not face[4]", "skip": "not face[4]",
"params": { "params": {
"height": "height", "height": "height",
"length": "length", "length": "length",
"is_left_sink": "False", "is_left_sink": "False",
"is_right_sink": "False" "is_right_sink": "False"
}, },
"transform": "ident()" "transform": "ident()"
}, },
{ {
"identifier": "raw_floor_side", "identifier": "raw_floor_side",
"skip": "not face[5]", "skip": "not face[5]",
"params": { "params": {
"height": "height", "height": "height",
"length": "length", "length": "length",
"is_left_sink": "False", "is_left_sink": "False",
"is_right_sink": "False" "is_right_sink": "False"
}, },
"transform": "move(0, 5, 0) @ scale(1, -1, 1)" "transform": "move(0, 5, 0) @ scale(1, -1, 1)"
}, },
{ {
"identifier": "floor_rectangle_bottom", "identifier": "floor_rectangle_bottom",
"skip": "not face[1]", "skip": "not face[1]",
"params": { "params": {
"length": "length", "length": "length",
"width": "5" "width": "5"
}, },
"transform": "move(0, 0, -height)" "transform": "move(0, 0, -height)"
} }
] ]
}, },
{ {
"identifier": "floor_sink_straight", "identifier": "floor_sink_straight",
"showcase": { "showcase": {
"title": "Sink Floor", "title": "Sink Floor",
"category": "Floors", "category": "Floors",
"icon": "SinkFloor", "icon": "SinkFloor",
"type": "floor", "type": "floor",
"cfgs": [ "cfgs": [
{ {
"field": "height_", "field": "height_",
"type": "float", "type": "float",
"title": "Height", "title": "Height",
"desc": "The height of block.", "desc": "The height of block.",
"default": "5.0" "default": "5.0"
}, },
{ {
"field": "length_", "field": "length_",
"type": "float", "type": "float",
"title": "Length", "title": "Length",
"desc": "The length of block.", "desc": "The length of block.",
"default": "2.5" "default": "5.0"
}, },
{ {
"field": "face_", "field": "face_",
"type": "face", "type": "face",
"title": "Face", "title": "Face",
"desc": "Whether has some faces.", "desc": "Whether has some faces.",
"default": "(True, False, False, False, True, True)" "default": "(True, False, False, False, True, True)"
} }
] ]
}, },
"params": [ "params": [
{ {
"field": "height", "field": "height",
"data": "height_" "data": "height_"
}, },
{ {
"field": "length", "field": "length",
"data": "length_" "data": "length_"
}, },
{ {
"field": "face", "field": "face",
"data": "face_" "data": "face_"
} }
], ],
"skip": "length == 0", "skip": "length == 0",
"vars": [ "vars": [
{ {
"field": "length_uv", "field": "length_uv",
"data": "length / 5.0" "data": "length / 5.0"
} }
], ],
"vertices": [ "vertices": [
{ {
"skip": "not face[0]", "skip": "not face[0]",
"data": "(0, 0, 0)" "data": "(0, 0, 0)"
}, },
{ {
"skip": "not face[0]", "skip": "not face[0]",
"data": "(length, 0, 0)" "data": "(length, 0, 0)"
}, },
{ {
"skip": "not face[0]", "skip": "not face[0]",
"data": "(length, 2.5, -0.7)" "data": "(length, 2.5, -0.7)"
}, },
{ {
"skip": "not face[0]", "skip": "not face[0]",
"data": "(0, 2.5, -0.7)" "data": "(0, 2.5, -0.7)"
}, },
{ {
"skip": "not face[0]", "skip": "not face[0]",
"data": "(length, 5, 0)" "data": "(length, 5, 0)"
}, },
{ {
"skip": "not face[0]", "skip": "not face[0]",
"data": "(0, 5, 0)" "data": "(0, 5, 0)"
} }
], ],
"faces": [ "faces": [
{ {
"skip": "not face[0]", "skip": "not face[0]",
"texture": "\"FloorTopProfil\"", "texture": "\"FloorTopProfil\"",
"indices": [0, 1, 2, 3], "indices": [0, 1, 2, 3],
"uvs": [ "uvs": [
"(0, 0)", "(0, 0)",
"(0, length_uv)", "(0, length_uv)",
"(0.5, length_uv)", "(0.5, length_uv)",
"(0.5, 0)" "(0.5, 0)"
], ],
"normals": null "normals": null
}, },
{ {
"skip": "not face[0]", "skip": "not face[0]",
"texture": "\"FloorTopProfil\"", "texture": "\"FloorTopProfil\"",
"indices": [3, 2, 4, 5], "indices": [3, 2, 4, 5],
"uvs": [ "uvs": [
"(0.5, 0)", "(0.5, 0)",
"(0.5, length_uv)", "(0.5, length_uv)",
"(1, length_uv)", "(1, length_uv)",
"(1, 0)" "(1, 0)"
], ],
"normals": null "normals": null
} }
], ],
"instances": [ "instances": [
{ {
"identifier": "raw_floor_side", "identifier": "raw_floor_side",
"skip": "not face[2]", "skip": "not face[2]",
"params": { "params": {
"height": "height", "height": "height",
"length": "2.5", "length": "2.5",
"is_left_sink": "False", "is_left_sink": "False",
"is_right_sink": "True" "is_right_sink": "True"
}, },
"transform": "rot(0, 0, 90) @ scale(1, -1, 1)" "transform": "rot(0, 0, 90) @ scale(1, -1, 1)"
}, },
{ {
"identifier": "raw_floor_side", "identifier": "raw_floor_side",
"skip": "not face[2]", "skip": "not face[2]",
"params": { "params": {
"height": "height", "height": "height",
"length": "2.5", "length": "2.5",
"is_left_sink": "True", "is_left_sink": "True",
"is_right_sink": "False" "is_right_sink": "False"
}, },
"transform": "move(0, 2.5, 0) @ rot(0, 0, 90) @ scale(1, -1, 1)" "transform": "move(0, 2.5, 0) @ rot(0, 0, 90) @ scale(1, -1, 1)"
}, },
{ {
"identifier": "raw_floor_side", "identifier": "raw_floor_side",
"skip": "not face[3]", "skip": "not face[3]",
"params": { "params": {
"height": "height", "height": "height",
"length": "2.5", "length": "2.5",
"is_left_sink": "False", "is_left_sink": "False",
"is_right_sink": "True" "is_right_sink": "True"
}, },
"transform": "move(length, 0, 0) @ rot(0, 0, 90)" "transform": "move(length, 0, 0) @ rot(0, 0, 90)"
}, },
{ {
"identifier": "raw_floor_side", "identifier": "raw_floor_side",
"skip": "not face[3]", "skip": "not face[3]",
"params": { "params": {
"height": "height", "height": "height",
"length": "2.5", "length": "2.5",
"is_left_sink": "True", "is_left_sink": "True",
"is_right_sink": "False" "is_right_sink": "False"
}, },
"transform": "move(length, 2.5, 0) @ rot(0, 0, 90)" "transform": "move(length, 2.5, 0) @ rot(0, 0, 90)"
}, },
{ {
"identifier": "raw_floor_side", "identifier": "raw_floor_side",
"skip": "not face[4]", "skip": "not face[4]",
"params": { "params": {
"height": "height", "height": "height",
"length": "length", "length": "length",
"is_left_sink": "False", "is_left_sink": "False",
"is_right_sink": "False" "is_right_sink": "False"
}, },
"transform": "ident()" "transform": "ident()"
}, },
{ {
"identifier": "raw_floor_side", "identifier": "raw_floor_side",
"skip": "not face[5]", "skip": "not face[5]",
"params": { "params": {
"height": "height", "height": "height",
"length": "length", "length": "length",
"is_left_sink": "False", "is_left_sink": "False",
"is_right_sink": "False" "is_right_sink": "False"
}, },
"transform": "move(0, 5, 0) @ scale(1, -1, 1)" "transform": "move(0, 5, 0) @ scale(1, -1, 1)"
}, },
{ {
"identifier": "floor_rectangle_bottom", "identifier": "floor_rectangle_bottom",
"skip": "not face[1]", "skip": "not face[1]",
"params": { "params": {
"length": "length", "length": "length",
"width": "5" "width": "5"
}, },
"transform": "move(0, 0, -height)" "transform": "move(0, 0, -height)"
} }
] ]
} }
] ]

4
bbp_ng/.gitignore vendored
View File

@@ -1,7 +1,7 @@
## ===== Personal ===== ## ===== Personal =====
# Do not include PyBMap in this repository. # Do not include pybmap in this repository.
# Order user build and fetch it manually. # Order user build and fetch it manually.
PyBMap/ pybmap/
# Disable generated assets but keep the directory hierarchy. # Disable generated assets but keep the directory hierarchy.
icons/* icons/*

View File

@@ -1,10 +1,11 @@
import bpy, mathutils import bpy, mathutils
from bpy_extras.wm_utils.progress_report import ProgressReport from bpy_extras.wm_utils.progress_report import ProgressReport
import tempfile, os, typing import tempfile, os, typing
from dataclasses import dataclass
from . import PROP_preferences, UTIL_ioport_shared, UTIL_naming_convention 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 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 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 .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): 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""" """Export Virtools File"""
@@ -81,11 +82,18 @@ class BBP_OT_export_virtools(bpy.types.Operator, UTIL_file_browser.ExportVirtool
self.draw_virtools_params(context, layout, False) self.draw_virtools_params(context, layout, False)
self.draw_ballance_params(layout, False) self.draw_ballance_params(layout, False)
_TObj3dPair = tuple[bpy.types.Object, bmap.BM3dObject] _Object3dPair = tuple[bpy.types.Object, bmap.BM3dObject]
_TLightPair = tuple[bpy.types.Object, bpy.types.Light, bmap.BMTargetLight] _LightPair = tuple[bpy.types.Object, bpy.types.Light, bmap.BMTargetLight]
_TMeshPair = tuple[bpy.types.Object, bpy.types.Mesh, bmap.BMMesh] _CameraPair = tuple[bpy.types.Object, bpy.types.Camera, bmap.BMTargetCamera]
_TMaterialPair = tuple[bpy.types.Material, bmap.BMMaterial] _MeshPair = tuple[bpy.types.Object, bpy.types.Mesh, bmap.BMMesh]
_TTexturePair = tuple[bpy.types.Image, bmap.BMTexture] _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, ...]
def _export_virtools( def _export_virtools(
file_name_: str, file_name_: str,
@@ -112,22 +120,21 @@ def _export_virtools(
# prepare progress reporter # prepare progress reporter
with ProgressReport(wm = bpy.context.window_manager) as progress: with ProgressReport(wm = bpy.context.window_manager) as progress:
# prepare 3dobject and light # prepare 3dobject, light and camera
obj3d_crets: tuple[_TObj3dPair, ...] prep_crets = _prepare_virtools_3dobjects(writer, progress, export_objects)
light_crets: tuple[_TLightPair, ...]
(obj3d_crets, light_crets) = _prepare_virtools_3dobjects(writer, progress, export_objects)
# export group according to prepared 3dobject # export group according to prepared 3dobject
_export_virtools_groups(writer, progress, successive_sector_, successive_sector_count_, obj3d_crets) _export_virtools_groups(writer, progress, successive_sector_, successive_sector_count_, prep_crets.obj3d_crets)
# export prepared light # export prepared light and camera
_export_virtools_light(writer, progress, light_crets) _export_virtools_light(writer, progress, prep_crets.light_crets)
_export_virtools_camera(writer, progress, prep_crets.camera_crets)
# export prepared 3dobject # export prepared 3dobject
mesh_crets: tuple[_TMeshPair, ...] = _export_virtools_3dobjects( mesh_crets: tuple[_MeshPair, ...] = _export_virtools_3dobjects(
writer, progress, obj3d_crets) writer, progress, prep_crets.obj3d_crets)
# export mesh # export mesh
material_crets: tuple[_TMaterialPair, ...] = _export_virtools_meshes( material_crets: tuple[_MaterialPair, ...] = _export_virtools_meshes(
writer, progress, mesh_crets) writer, progress, mesh_crets)
# export material # export material
texture_crets: tuple[_TTexturePair, ...] = _export_virtools_materials( texture_crets: tuple[_TexturePair, ...] = _export_virtools_materials(
writer, progress, material_crets) writer, progress, material_crets)
# export texture # export texture
_export_virtools_textures(writer, progress, vt_temp_folder, texture_crets) _export_virtools_textures(writer, progress, vt_temp_folder, texture_crets)
@@ -140,7 +147,7 @@ def _prepare_virtools_3dobjects(
writer: bmap.BMFileWriter, writer: bmap.BMFileWriter,
progress: ProgressReport, progress: ProgressReport,
export_objects: tuple[bpy.types.Object, ...] export_objects: tuple[bpy.types.Object, ...]
) -> tuple[tuple[_TObj3dPair, ...], tuple[_TLightPair, ...]]: ) -> _PreparedCrets:
# this function only create equvalent entries in virtools engine and do not export anything # 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 # because _export_virtools_3dobjects() and _export_virtools_groups() are need use the return value of this function
# #
@@ -148,52 +155,65 @@ def _prepare_virtools_3dobjects(
# we also need extract exported lights and create equvalent entries in virtools for them. # we also need extract exported lights and create equvalent entries in virtools for them.
# create 3dobject hashset and result # create 3dobject hashset and result
obj3d_crets: list[_TObj3dPair] = [] obj3d_crets: list[_Object3dPair] = []
obj3d_cret_set: set[bpy.types.Object] = set() obj3d_cret_set: set[bpy.types.Object] = set()
# create light hashset and result # create light hashset and result
light_crets: list[_TLightPair] = [] light_crets: list[_LightPair] = []
light_cret_set: set[bpy.types.Object] = set() 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 # start saving
tr_text: str = bpy.app.translations.pgettext_rpt('Creating 3dObjects and Lights', 'BBP_OT_export_virtools/execute') 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) progress.enter_substeps(len(export_objects), tr_text)
# iterate exported object list # iterate exported object list
for obj3d in export_objects: for obj3d in export_objects:
# only accept mesh object and light object # only accept mesh-like object, camera and light object
# all of other objects will be discard. # all of other objects will be discard.
match(obj3d.type): if UTIL_blender_mesh.TemporaryMesh.has_geometry(obj3d):
case 'MESH': # mesh-like object
# mesh object if obj3d not in obj3d_cret_set:
if obj3d not in obj3d_cret_set: # add into set
# add into set obj3d_cret_set.add(obj3d)
obj3d_cret_set.add(obj3d) # create virtools instance
# create virtools instance vtobj3d: bmap.BM3dObject = writer.create_3dobject()
vtobj3d: bmap.BM3dObject = writer.create_3dobject() # add into result list
# add into result list obj3d_crets.append((obj3d, vtobj3d))
obj3d_crets.append((obj3d, vtobj3d)) else:
case 'LIGHT': match(obj3d.type):
# light object case 'CAMERA':
if obj3d not in light_cret_set: # camera object
# add into set if obj3d not in camera_cret_set:
light_cret_set.add(obj3d) # add into set
# create virtools instance camera_cret_set.add(obj3d)
vtlight: bmap.BMTargetLight = writer.create_target_light() # create virtools instance
# add into result list vtcamera: bmap.BMTargetCamera = writer.create_target_camera()
light_crets.append((obj3d, typing.cast(bpy.types.Light, obj3d.data), vtlight)) # 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))
# step progress no matter whether create new one # step progress no matter whether create new one
progress.step() progress.step()
# leave progress and return # leave progress and return
progress.leave_substeps() progress.leave_substeps()
return (tuple(obj3d_crets), tuple(light_crets)) return _PreparedCrets(tuple(obj3d_crets), tuple(light_crets), tuple(camera_crets))
def _export_virtools_groups( def _export_virtools_groups(
writer: bmap.BMFileWriter, writer: bmap.BMFileWriter,
progress: ProgressReport, progress: ProgressReport,
successive_sector: bool, successive_sector: bool,
successive_sector_count: int, successive_sector_count: int,
obj3d_crets: tuple[_TObj3dPair, ...] obj3d_crets: tuple[_Object3dPair, ...]
) -> None: ) -> None:
# create virtools group # create virtools group
group_cret_map: dict[str, bmap.BMGroup] = {} group_cret_map: dict[str, bmap.BMGroup] = {}
@@ -241,7 +261,7 @@ def _export_virtools_groups(
def _export_virtools_light( def _export_virtools_light(
writer: bmap.BMFileWriter, writer: bmap.BMFileWriter,
progress: ProgressReport, progress: ProgressReport,
light_crets: tuple[_TLightPair, ...] light_crets: tuple[_LightPair, ...]
) -> None: ) -> None:
# start saving # start saving
tr_text: str = bpy.app.translations.pgettext_rpt('Saving Lights', 'BBP_OT_export_virtools/execute') tr_text: str = bpy.app.translations.pgettext_rpt('Saving Lights', 'BBP_OT_export_virtools/execute')
@@ -283,13 +303,56 @@ def _export_virtools_light(
# leave progress and return # leave progress and return
progress.leave_substeps() 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( def _export_virtools_3dobjects(
writer: bmap.BMFileWriter, writer: bmap.BMFileWriter,
progress: ProgressReport, progress: ProgressReport,
obj3d_crets: tuple[_TObj3dPair, ...] obj3d_crets: tuple[_Object3dPair, ...]
) -> tuple[_TMeshPair, ...]: ) -> tuple[_MeshPair, ...]:
# create virtools mesh # create virtools mesh
mesh_crets: list[_TMeshPair] = [] mesh_crets: list[_MeshPair] = []
mesh_cret_map: dict[bpy.types.Mesh, bmap.BMMesh] = {} mesh_cret_map: dict[bpy.types.Mesh, bmap.BMMesh] = {}
# start saving # start saving
tr_text: str = bpy.app.translations.pgettext_rpt('Saving 3dObjects', 'BBP_OT_export_virtools/execute') tr_text: str = bpy.app.translations.pgettext_rpt('Saving 3dObjects', 'BBP_OT_export_virtools/execute')
@@ -333,10 +396,10 @@ def _export_virtools_3dobjects(
def _export_virtools_meshes( def _export_virtools_meshes(
writer: bmap.BMFileWriter, writer: bmap.BMFileWriter,
progress: ProgressReport, progress: ProgressReport,
mesh_crets: tuple[_TMeshPair, ...] mesh_crets: tuple[_MeshPair, ...]
) -> tuple[_TMaterialPair, ...]: ) -> tuple[_MaterialPair, ...]:
# create virtools mesh # create virtools mesh
material_crets: list[_TMaterialPair] = [] material_crets: list[_MaterialPair] = []
material_cret_map: dict[bpy.types.Material, bmap.BMMaterial] = {} material_cret_map: dict[bpy.types.Material, bmap.BMMaterial] = {}
# start saving # start saving
tr_text: str = bpy.app.translations.pgettext_rpt('Saving Meshes', 'BBP_OT_export_virtools/execute') tr_text: str = bpy.app.translations.pgettext_rpt('Saving Meshes', 'BBP_OT_export_virtools/execute')
@@ -448,10 +511,10 @@ def _export_virtools_meshes(
def _export_virtools_materials( def _export_virtools_materials(
writer: bmap.BMFileWriter, writer: bmap.BMFileWriter,
progress: ProgressReport, progress: ProgressReport,
material_crets: tuple[_TMaterialPair, ...] material_crets: tuple[_MaterialPair, ...]
) -> tuple[_TTexturePair, ...]: ) -> tuple[_TexturePair, ...]:
# create virtools mesh # create virtools mesh
texture_crets: list[_TTexturePair] = [] texture_crets: list[_TexturePair] = []
texture_cret_map: dict[bpy.types.Image, bmap.BMTexture] = {} texture_cret_map: dict[bpy.types.Image, bmap.BMTexture] = {}
# start saving # start saving
tr_text: str = bpy.app.translations.pgettext_rpt('Saving Materials', 'BBP_OT_export_virtools/execute') tr_text: str = bpy.app.translations.pgettext_rpt('Saving Materials', 'BBP_OT_export_virtools/execute')
@@ -517,7 +580,7 @@ def _export_virtools_textures(
writer: bmap.BMFileWriter, writer: bmap.BMFileWriter,
progress: ProgressReport, progress: ProgressReport,
vt_temp_folder: str, vt_temp_folder: str,
texture_crets: tuple[_TTexturePair, ...] texture_crets: tuple[_TexturePair, ...]
) -> None: ) -> None:
# start saving # start saving
tr_text: str = bpy.app.translations.pgettext_rpt('Saving Textures', 'BBP_OT_export_virtools/execute') tr_text: str = bpy.app.translations.pgettext_rpt('Saving Textures', 'BBP_OT_export_virtools/execute')

View File

@@ -3,8 +3,8 @@ from bpy_extras.wm_utils.progress_report import ProgressReport
import tempfile, os, typing import tempfile, os, typing
from . import PROP_preferences, UTIL_ioport_shared, UTIL_naming_convention from . import PROP_preferences, UTIL_ioport_shared, UTIL_naming_convention
from . import UTIL_virtools_types, UTIL_functions, UTIL_file_browser, UTIL_blender_mesh, UTIL_ballance_texture from . import UTIL_virtools_types, UTIL_functions, UTIL_file_browser, UTIL_blender_mesh, UTIL_ballance_texture
from . import PROP_virtools_group, PROP_virtools_material, PROP_virtools_mesh, PROP_virtools_texture, PROP_virtools_light, PROP_ballance_map_info from . import PROP_virtools_group, PROP_virtools_material, PROP_virtools_mesh, PROP_virtools_texture, PROP_virtools_light, PROP_virtools_camera, PROP_ballance_map_info
from .PyBMap import bmap_wrapper as bmap 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): 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""" """Import Virtools File"""
@@ -80,8 +80,9 @@ def _import_virtools(file_name_: str, encodings_: tuple[str, ...], resolver: UTI
# import 3dobjects # import 3dobjects
obj3d_cret_map: dict[bmap.BM3dObject, bpy.types.Object] = _import_virtools_3dobjects( obj3d_cret_map: dict[bmap.BM3dObject, bpy.types.Object] = _import_virtools_3dobjects(
reader, progress, resolver, mesh_cret_map) reader, progress, resolver, mesh_cret_map)
# import light # import light and camera
_import_virtools_lights(reader, progress, resolver) _import_virtools_lights(reader, progress, resolver)
_import_virtools_cameras(reader, progress, resolver)
# import groups # import groups
_import_virtools_groups(reader, progress, obj3d_cret_map) _import_virtools_groups(reader, progress, obj3d_cret_map)
@@ -423,6 +424,53 @@ def _import_virtools_lights(
# leave progress # leave progress
progress.leave_substeps() 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( def _import_virtools_groups(
reader: bmap.BMFileReader, reader: bmap.BMFileReader,
progress: ProgressReport, progress: ProgressReport,

View File

@@ -1,34 +1,15 @@
import bpy, mathutils import bpy, mathutils
import typing, enum, math import typing, enum, math
from . import UTIL_functions from . import UTIL_functions, PROP_virtools_camera
# TODO: #region Enum Defines
# This file should have fully refactor after we finish Virtools Camera import and export,
# because this module is highly rely on it. Current implementation is a compromise.
# There is a list of things to be done:
# - Remove BBP_OT_game_resolution operator, because Virtools Camera will have similar function in panel.
# - Update BBP_OT_game_cameraoperator with Virtools Camera.
#region Game Resolution
class ResolutionKind(enum.IntEnum): class ResolutionKind(enum.IntEnum):
Normal = enum.auto() Normal = enum.auto()
Extended = enum.auto() WideScreen = enum.auto()
Widescreen = enum.auto()
Panoramic = enum.auto()
def to_resolution(self) -> tuple[int, int]:
match self:
case ResolutionKind.Normal: return (1024, 768)
case ResolutionKind.Extended: return (1280, 720)
case ResolutionKind.Widescreen: return (1400, 600)
case ResolutionKind.Panoramic: return (2000, 700)
_g_ResolutionKindDesc: dict[ResolutionKind, tuple[str, str]] = { _g_ResolutionKindDesc: dict[ResolutionKind, tuple[str, str]] = {
ResolutionKind.Normal: ("Normal", "Aspect ratio: 4:3."), ResolutionKind.Normal: ("Normal", "Vanilla Ballance Resolution"),
ResolutionKind.Extended: ("Extended", "Aspect ratio: 16:9."), ResolutionKind.WideScreen: ("Wide Screen", "Ballance Resolution with Wide Screen Fix"),
ResolutionKind.Widescreen: ("Widescreen", "Aspect ratio: 7:3."),
ResolutionKind.Panoramic: ("Panoramic", "Aspect ratio: 20:7."),
} }
_g_EnumHelper_ResolutionKind = UTIL_functions.EnumPropHelper( _g_EnumHelper_ResolutionKind = UTIL_functions.EnumPropHelper(
ResolutionKind, ResolutionKind,
@@ -39,45 +20,6 @@ _g_EnumHelper_ResolutionKind = UTIL_functions.EnumPropHelper(
lambda _: "" lambda _: ""
) )
class BBP_OT_game_resolution(bpy.types.Operator):
"""Set Blender render resolution to Ballance game"""
bl_idname = "bbp.game_resolution"
bl_label = "Game Resolution"
bl_options = {'REGISTER', 'UNDO'}
bl_translation_context = 'BBP_OT_game_resolution'
resolution_kind: bpy.props.EnumProperty(
name = "Resolution Kind",
description = "The type of preset resolution.",
items = _g_EnumHelper_ResolutionKind.generate_items(),
default = _g_EnumHelper_ResolutionKind.to_selection(ResolutionKind.Normal),
translation_context = 'BBP_OT_game_resolution/property'
) # type: ignore
def invoke(self, context, event):
return self.execute(context)
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.prop(self, 'resolution_kind')
def execute(self, context):
# fetch resolution
resolution_kind = _g_EnumHelper_ResolutionKind.get_selection(self.resolution_kind)
resolution = resolution_kind.to_resolution()
# setup resolution
render_settings = bpy.context.scene.render
render_settings.resolution_x = resolution[0]
render_settings.resolution_y = resolution[1]
return {'FINISHED'}
#endregion
#region Game Camera
#region Enum Defines
class TargetKind(enum.IntEnum): class TargetKind(enum.IntEnum):
Cursor = enum.auto() Cursor = enum.auto()
ActiveObject = enum.auto() ActiveObject = enum.auto()
@@ -281,6 +223,21 @@ class BBP_OT_game_camera(bpy.types.Operator):
translation_context = 'BBP_OT_game_camera/property' translation_context = 'BBP_OT_game_camera/property'
) # type: ignore ) # 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 @classmethod
def poll(cls, context): def poll(cls, context):
# find camera object # find camera object
@@ -333,6 +290,12 @@ class BBP_OT_game_camera(bpy.types.Operator):
layout.label(text='Perspective', text_ctxt='BBP_OT_game_camera/draw') layout.label(text='Perspective', text_ctxt='BBP_OT_game_camera/draw')
layout.row().prop(self, 'perspective_kind', expand=True) 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): def execute(self, context):
# fetch angle # fetch angle
angle: float angle: float
@@ -347,11 +310,12 @@ class BBP_OT_game_camera(bpy.types.Operator):
camera_obj = typing.cast(bpy.types.Object, _find_camera_obj()) camera_obj = typing.cast(bpy.types.Object, _find_camera_obj())
target_kind = _g_EnumHelper_TargetKind.get_selection(self.target_kind) target_kind = _g_EnumHelper_TargetKind.get_selection(self.target_kind)
perspective_kind = _g_EnumHelper_PerspectiveKind.get_selection(self.perspective_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 # setup its transform and properties
glob_trans = _fetch_glob_translation(camera_obj, target_kind) glob_trans = _fetch_glob_translation(camera_obj, target_kind)
_setup_camera_transform(camera_obj, angle, perspective_kind, glob_trans) _setup_camera_transform(camera_obj, angle, perspective_kind, glob_trans)
_setup_camera_properties(camera_obj) _setup_camera_properties(camera_obj, resolution_kind)
# return # return
return {'FINISHED'} return {'FINISHED'}
@@ -451,24 +415,39 @@ def _setup_camera_transform(camobj: bpy.types.Object, angle: float, perspective:
glob_trans_mat = mathutils.Matrix.Translation(glob_trans) glob_trans_mat = mathutils.Matrix.Translation(glob_trans)
camobj.matrix_world = glob_trans_mat @ trans_mat @ rot_mat camobj.matrix_world = glob_trans_mat @ trans_mat @ rot_mat
def _setup_camera_properties(camobj: bpy.types.Object) -> None: def _setup_camera_properties(camobj: bpy.types.Object, resolution_kind: ResolutionKind | None) -> None:
# fetch camera # fetch camera and its raw data
camera = typing.cast(bpy.types.Camera, camobj.data) camera = typing.cast(bpy.types.Camera, camobj.data)
rawdata = PROP_virtools_camera.get_raw_virtools_camera(camera)
# set clipping # set clipping
camera.clip_start = 4 rawdata.mFrontPlane = 4
camera.clip_end = 1200 rawdata.mBackPlane = 1200
# set FOV # set FOV and aspect ratio according to presented resolution kind
camera.lens_unit = 'FOV' if resolution_kind is not None:
camera.angle = math.radians(58) match resolution_kind:
case ResolutionKind.Normal:
#endregion 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: def register() -> None:
bpy.utils.register_class(BBP_OT_game_resolution)
bpy.utils.register_class(BBP_OT_game_camera) bpy.utils.register_class(BBP_OT_game_camera)
def unregister() -> None: def unregister() -> None:
bpy.utils.unregister_class(BBP_OT_game_camera) bpy.utils.unregister_class(BBP_OT_game_camera)
bpy.utils.unregister_class(BBP_OT_game_resolution)

View File

@@ -1,14 +1,12 @@
import bpy import bpy
import typing import typing
from dataclasses import dataclass
from dataclasses import field as datafield
from . import UTIL_functions from . import UTIL_functions
@dataclass
class RawBallanceMapInfo(): class RawBallanceMapInfo():
cSectorCount: typing.ClassVar[int] = 1 mSectorCount: int = datafield(default=1)
mSectorCount: int
def __init__(self, **kwargs):
self.mSectorCount = kwargs.get("mSectorCount", RawBallanceMapInfo.cSectorCount)
def regulate(self): def regulate(self):
self.mSectorCount = UTIL_functions.clamp_int(self.mSectorCount, 1, 999) self.mSectorCount = UTIL_functions.clamp_int(self.mSectorCount, 1, 999)

View File

@@ -1,21 +1,19 @@
import bpy import bpy
import os, typing import os, typing
from dataclasses import dataclass
from dataclasses import field as datafield
from . import UTIL_naming_convention from . import UTIL_naming_convention
@dataclass
class RawPreferences(): class RawPreferences():
cBallanceTextureFolder: typing.ClassVar[str] = "" mBallanceTextureFolder: str = datafield(default="")
cNoComponentCollection: typing.ClassVar[str] = "" mNoComponentCollection: str = datafield(default="")
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: def has_valid_blc_tex_folder(self) -> bool:
return os.path.isdir(self.mBallanceTextureFolder) return os.path.isdir(self.mBallanceTextureFolder)
DEFAULT_RAW_PREFERENCES = RawPreferences()
class BBPPreferences(bpy.types.AddonPreferences): class BBPPreferences(bpy.types.AddonPreferences):
bl_idname = __package__ bl_idname = __package__
@@ -23,14 +21,14 @@ class BBPPreferences(bpy.types.AddonPreferences):
name = "Ballance Texture Folder", name = "Ballance Texture Folder",
description = "The path to folder which will be used by this plugin to get external Ballance texture.", description = "The path to folder which will be used by this plugin to get external Ballance texture.",
subtype = 'DIR_PATH', subtype = 'DIR_PATH',
default = RawPreferences.cBallanceTextureFolder, default = DEFAULT_RAW_PREFERENCES.mBallanceTextureFolder,
translation_context = 'BBPPreferences/property' translation_context = 'BBPPreferences/property'
) # type: ignore ) # type: ignore
no_component_collection: bpy.props.StringProperty( no_component_collection: bpy.props.StringProperty(
name = "No Component Collection", 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", 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 = RawPreferences.cNoComponentCollection, default = DEFAULT_RAW_PREFERENCES.mNoComponentCollection,
translation_context = 'BBPPreferences/property' translation_context = 'BBPPreferences/property'
) # type: ignore ) # type: ignore

View File

@@ -202,7 +202,7 @@ class PropsVisitor():
""" """
encodings = get_ioport_encodings(self.__mAssocScene) encodings = get_ioport_encodings(self.__mAssocScene)
encodings.clear() encodings.clear()
for default_enc in UTIL_virtools_types.g_PyBMapDefaultEncodings: for default_enc in UTIL_virtools_types.g_PybmapDefaultEncodings:
item = encodings.add() item = encodings.add()
item.encoding = default_enc item.encoding = default_enc
def draw_ioport_encodings(self, layout: bpy.types.UILayout) -> None: def draw_ioport_encodings(self, layout: bpy.types.UILayout) -> None:

View File

@@ -0,0 +1,404 @@
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)

View File

@@ -1,6 +1,6 @@
import bpy import bpy
import typing, enum import typing, enum
from . import UTIL_functions, UTIL_icons_manager from . import UTIL_functions, UTIL_icons_manager, UTIL_blender_mesh
#region Virtools Groups Define & Help Class #region Virtools Groups Define & Help Class
@@ -387,9 +387,9 @@ class BBP_PT_virtools_groups(bpy.types.Panel):
target = typing.cast(bpy.types.Object, context.active_object) target = typing.cast(bpy.types.Object, context.active_object)
# notify on non-mesh object # notify on non-mesh object
if target.type != 'MESH': if not UTIL_blender_mesh.TemporaryMesh.has_geometry(target):
layout.label( layout.label(
text='Virtools Group is invalid on non-mesh object!', icon='ERROR', text='Virtools Group is invalid on non-mesh-like object!', icon='ERROR',
text_ctxt='BBP_PT_virtools_groups/draw') text_ctxt='BBP_PT_virtools_groups/draw')
# draw main body # draw main body

View File

@@ -1,55 +1,26 @@
import bpy, mathutils import bpy
from bpy.types import Context
import typing, math import typing, math
from dataclasses import dataclass
from dataclasses import field as datafield
from . import UTIL_functions, UTIL_virtools_types from . import UTIL_functions, UTIL_virtools_types
# Raw Data # Raw Data
@dataclass
class RawVirtoolsLight(): class RawVirtoolsLight():
# Class member
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))
mType: UTIL_virtools_types.VXLIGHT_TYPE mConstantAttenuation: float = datafield(default=1.0)
mColor: UTIL_virtools_types.VxColor mLinearAttenuation: float = datafield(default=0.0)
mQuadraticAttenuation: float = datafield(default=0.0)
mConstantAttenuation: float mRange: float = datafield(default=100.0)
mLinearAttenuation: float
mQuadraticAttenuation: float
mRange: float mHotSpot: float = datafield(default=math.radians(40))
mFalloff: float = datafield(default=math.radians(45))
mHotSpot: float mFalloffShape: float = datafield(default=1.0)
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: def regulate(self) -> None:
# regulate color and reset its alpha value # regulate color and reset its alpha value
@@ -71,6 +42,8 @@ class RawVirtoolsLight():
if self.mFalloff < self.mHotSpot: if self.mFalloff < self.mHotSpot:
self.mFalloff = self.mHotSpot self.mFalloff = self.mHotSpot
DEFAULT_RAW_VIRTOOLS_LIGHT = RawVirtoolsLight()
# Blender Property Group # Blender Property Group
_g_Helper_VXLIGHT_TYPE = UTIL_virtools_types.EnumPropHelper(UTIL_virtools_types.VXLIGHT_TYPE) _g_Helper_VXLIGHT_TYPE = UTIL_virtools_types.EnumPropHelper(UTIL_virtools_types.VXLIGHT_TYPE)
@@ -80,7 +53,7 @@ class BBP_PG_virtools_light(bpy.types.PropertyGroup):
name = "Type", name = "Type",
description = "The type of this light", description = "The type of this light",
items = _g_Helper_VXLIGHT_TYPE.generate_items(), items = _g_Helper_VXLIGHT_TYPE.generate_items(),
default = _g_Helper_VXLIGHT_TYPE.to_selection(RawVirtoolsLight.cDefaultType), default = _g_Helper_VXLIGHT_TYPE.to_selection(DEFAULT_RAW_VIRTOOLS_LIGHT.mType),
translation_context = 'BBP_PG_virtools_light/property' translation_context = 'BBP_PG_virtools_light/property'
) # type: ignore ) # type: ignore
@@ -91,7 +64,7 @@ class BBP_PG_virtools_light(bpy.types.PropertyGroup):
min = 0.0, min = 0.0,
max = 1.0, max = 1.0,
size = 3, size = 3,
default = RawVirtoolsLight.cDefaultColor.to_const_rgb(), default = DEFAULT_RAW_VIRTOOLS_LIGHT.mColor.to_const_rgb(),
translation_context = 'BBP_PG_virtools_light/property' translation_context = 'BBP_PG_virtools_light/property'
) # type: ignore ) # type: ignore
@@ -101,7 +74,7 @@ class BBP_PG_virtools_light(bpy.types.PropertyGroup):
min = 0.0, min = 0.0,
max = 10.0, max = 10.0,
step = 10, step = 10,
default = RawVirtoolsLight.cDefaultConstantAttenuation, default = DEFAULT_RAW_VIRTOOLS_LIGHT.mConstantAttenuation,
translation_context = 'BBP_PG_virtools_light/property' translation_context = 'BBP_PG_virtools_light/property'
) # type: ignore ) # type: ignore
@@ -111,7 +84,7 @@ class BBP_PG_virtools_light(bpy.types.PropertyGroup):
min = 0.0, min = 0.0,
max = 10.0, max = 10.0,
step = 10, step = 10,
default = RawVirtoolsLight.cDefaultLinearAttenuation, default = DEFAULT_RAW_VIRTOOLS_LIGHT.mLinearAttenuation,
translation_context = 'BBP_PG_virtools_light/property' translation_context = 'BBP_PG_virtools_light/property'
) # type: ignore ) # type: ignore
@@ -121,7 +94,7 @@ class BBP_PG_virtools_light(bpy.types.PropertyGroup):
min = 0.0, min = 0.0,
max = 10.0, max = 10.0,
step = 10, step = 10,
default = RawVirtoolsLight.cDefaultQuadraticAttenuation, default = DEFAULT_RAW_VIRTOOLS_LIGHT.mQuadraticAttenuation,
translation_context = 'BBP_PG_virtools_light/property' translation_context = 'BBP_PG_virtools_light/property'
) # type: ignore ) # type: ignore
@@ -131,7 +104,7 @@ class BBP_PG_virtools_light(bpy.types.PropertyGroup):
min = 0.0, min = 0.0,
max = 200.0, max = 200.0,
step = 100, step = 100,
default = RawVirtoolsLight.cDefaultRange, default = DEFAULT_RAW_VIRTOOLS_LIGHT.mRange,
translation_context = 'BBP_PG_virtools_light/property' translation_context = 'BBP_PG_virtools_light/property'
) # type: ignore ) # type: ignore
@@ -141,7 +114,7 @@ class BBP_PG_virtools_light(bpy.types.PropertyGroup):
min = 0.0, min = 0.0,
max = math.radians(180), max = math.radians(180),
subtype = 'ANGLE', subtype = 'ANGLE',
default = RawVirtoolsLight.cDefaultHotSpot, default = DEFAULT_RAW_VIRTOOLS_LIGHT.mHotSpot,
translation_context = 'BBP_PG_virtools_light/property' translation_context = 'BBP_PG_virtools_light/property'
) # type: ignore ) # type: ignore
@@ -151,7 +124,7 @@ class BBP_PG_virtools_light(bpy.types.PropertyGroup):
min = 0.0, min = 0.0,
max = math.radians(180), max = math.radians(180),
subtype = 'ANGLE', subtype = 'ANGLE',
default = RawVirtoolsLight.cDefaultFalloff, default = DEFAULT_RAW_VIRTOOLS_LIGHT.mFalloff,
translation_context = 'BBP_PG_virtools_light/property' translation_context = 'BBP_PG_virtools_light/property'
) # type: ignore ) # type: ignore
@@ -161,7 +134,7 @@ class BBP_PG_virtools_light(bpy.types.PropertyGroup):
min = 0.0, min = 0.0,
max = 10.0, max = 10.0,
step = 10, step = 10,
default = RawVirtoolsLight.cDefaultFalloffShape, default = DEFAULT_RAW_VIRTOOLS_LIGHT.mFalloffShape,
translation_context = 'BBP_PG_virtools_light/property' translation_context = 'BBP_PG_virtools_light/property'
) # type: ignore ) # type: ignore

View File

@@ -1,98 +1,41 @@
import bpy import bpy
import typing, enum, copy, os 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 UTIL_virtools_types, UTIL_functions, UTIL_ballance_texture, UTIL_file_browser
from . import PROP_virtools_texture, PROP_preferences from . import PROP_virtools_texture, PROP_preferences
@dataclass
class RawVirtoolsMaterial(): class RawVirtoolsMaterial():
# Instance Member Declarations 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)
mDiffuse: UTIL_virtools_types.VxColor mTexture: bpy.types.Image | None = datafield(default=None)
mAmbient: UTIL_virtools_types.VxColor mTextureBorderColor: UTIL_virtools_types.VxColor = datafield(default_factory=lambda: UTIL_virtools_types.VxColor(0.0, 0.0, 0.0, 0.0))
mSpecular: UTIL_virtools_types.VxColor
mEmissive: UTIL_virtools_types.VxColor
mSpecularPower: float
mTexture: bpy.types.Image | None mTextureBlendMode: UTIL_virtools_types.VXTEXTURE_BLENDMODE = datafield(default=UTIL_virtools_types.VXTEXTURE_BLENDMODE.VXTEXTUREBLEND_MODULATEALPHA)
mTextureBorderColor: UTIL_virtools_types.VxColor 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)
mTextureBlendMode: UTIL_virtools_types.VXTEXTURE_BLENDMODE mSourceBlend: UTIL_virtools_types.VXBLEND_MODE = datafield(default=UTIL_virtools_types.VXBLEND_MODE.VXBLEND_ONE)
mTextureMinMode: UTIL_virtools_types.VXTEXTURE_FILTERMODE mDestBlend: UTIL_virtools_types.VXBLEND_MODE = datafield(default=UTIL_virtools_types.VXBLEND_MODE.VXBLEND_ZERO)
mTextureMagMode: UTIL_virtools_types.VXTEXTURE_FILTERMODE mFillMode: UTIL_virtools_types.VXFILL_MODE = datafield(default=UTIL_virtools_types.VXFILL_MODE.VXFILL_SOLID)
mTextureAddressMode: UTIL_virtools_types.VXTEXTURE_ADDRESSMODE mShadeMode: UTIL_virtools_types.VXSHADE_MODE = datafield(default=UTIL_virtools_types.VXSHADE_MODE.VXSHADE_GOURAUD)
mSourceBlend: UTIL_virtools_types.VXBLEND_MODE mEnableAlphaTest: bool = datafield(default=False)
mDestBlend: UTIL_virtools_types.VXBLEND_MODE mEnableAlphaBlend: bool = datafield(default=False)
mFillMode: UTIL_virtools_types.VXFILL_MODE mEnablePerspectiveCorrection: bool = datafield(default=True)
mShadeMode: UTIL_virtools_types.VXSHADE_MODE mEnableZWrite: bool = datafield(default=True)
mEnableTwoSided: bool = datafield(default=False)
mEnableAlphaTest: bool mAlphaRef: int = datafield(default=0)
mEnableAlphaBlend: bool mAlphaFunc: UTIL_virtools_types.VXCMPFUNC = datafield(default=UTIL_virtools_types.VXCMPFUNC.VXCMP_ALWAYS)
mEnablePerspectiveCorrection: bool mZFunc: UTIL_virtools_types.VXCMPFUNC = datafield(default=UTIL_virtools_types.VXCMPFUNC.VXCMP_LESSEQUAL)
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: def regulate(self) -> None:
# regulate colors # regulate colors
@@ -112,6 +55,8 @@ class RawVirtoolsMaterial():
# specular power # specular power
self.mSpecularPower = UTIL_functions.clamp_float(self.mSpecularPower, 0.0, 100.0) 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) #region Blender Enum Prop Helper (Virtools type specified)
_g_Helper_VXTEXTURE_BLENDMODE = UTIL_virtools_types.EnumPropHelper(UTIL_virtools_types.VXTEXTURE_BLENDMODE) _g_Helper_VXTEXTURE_BLENDMODE = UTIL_virtools_types.EnumPropHelper(UTIL_virtools_types.VXTEXTURE_BLENDMODE)
@@ -132,7 +77,7 @@ class BBP_PG_virtools_material(bpy.types.PropertyGroup):
min = 0.0, min = 0.0,
max = 1.0, max = 1.0,
size = 3, size = 3,
default = RawVirtoolsMaterial.cDefaultAmbient.to_const_rgb(), default = DEFAULT_RAW_VIRTOOLS_MATERIAL.mAmbient.to_const_rgb(),
translation_context = 'BBP_PG_virtools_material/property' translation_context = 'BBP_PG_virtools_material/property'
) # type: ignore ) # type: ignore
@@ -143,7 +88,7 @@ class BBP_PG_virtools_material(bpy.types.PropertyGroup):
min = 0.0, min = 0.0,
max = 1.0, max = 1.0,
size = 4, size = 4,
default = RawVirtoolsMaterial.cDefaultDiffuse.to_const_rgba(), default = DEFAULT_RAW_VIRTOOLS_MATERIAL.mDiffuse.to_const_rgba(),
translation_context = 'BBP_PG_virtools_material/property' translation_context = 'BBP_PG_virtools_material/property'
) # type: ignore ) # type: ignore
@@ -154,7 +99,7 @@ class BBP_PG_virtools_material(bpy.types.PropertyGroup):
min = 0.0, min = 0.0,
max = 1.0, max = 1.0,
size = 3, size = 3,
default = RawVirtoolsMaterial.cDefaultSpecular.to_const_rgb(), default = DEFAULT_RAW_VIRTOOLS_MATERIAL.mSpecular.to_const_rgb(),
translation_context = 'BBP_PG_virtools_material/property' translation_context = 'BBP_PG_virtools_material/property'
) # type: ignore ) # type: ignore
@@ -165,7 +110,7 @@ class BBP_PG_virtools_material(bpy.types.PropertyGroup):
min = 0.0, min = 0.0,
max = 1.0, max = 1.0,
size = 3, size = 3,
default = RawVirtoolsMaterial.cDefaultEmissive.to_const_rgb(), default = DEFAULT_RAW_VIRTOOLS_MATERIAL.mEmissive.to_const_rgb(),
translation_context = 'BBP_PG_virtools_material/property' translation_context = 'BBP_PG_virtools_material/property'
) # type: ignore ) # type: ignore
@@ -174,7 +119,7 @@ class BBP_PG_virtools_material(bpy.types.PropertyGroup):
description = "Specular highlight power", description = "Specular highlight power",
min = 0.0, min = 0.0,
max = 100.0, max = 100.0,
default = RawVirtoolsMaterial.cDefaultSpecularPower, default = DEFAULT_RAW_VIRTOOLS_MATERIAL.mSpecularPower,
translation_context = 'BBP_PG_virtools_material/property' translation_context = 'BBP_PG_virtools_material/property'
) # type: ignore ) # type: ignore
@@ -192,7 +137,7 @@ class BBP_PG_virtools_material(bpy.types.PropertyGroup):
min = 0.0, min = 0.0,
max = 1.0, max = 1.0,
size = 4, size = 4,
default = RawVirtoolsMaterial.cDefaultTextureBorderColor.to_const_rgba(), default = DEFAULT_RAW_VIRTOOLS_MATERIAL.mTextureBorderColor.to_const_rgba(),
translation_context = 'BBP_PG_virtools_material/property' translation_context = 'BBP_PG_virtools_material/property'
) # type: ignore ) # type: ignore
@@ -200,7 +145,7 @@ class BBP_PG_virtools_material(bpy.types.PropertyGroup):
name = "Texture Blend", name = "Texture Blend",
description = "Texture blend mode", description = "Texture blend mode",
items = _g_Helper_VXTEXTURE_BLENDMODE.generate_items(), items = _g_Helper_VXTEXTURE_BLENDMODE.generate_items(),
default = _g_Helper_VXTEXTURE_BLENDMODE.to_selection(RawVirtoolsMaterial.cDefaultTextureBlendMode), default = _g_Helper_VXTEXTURE_BLENDMODE.to_selection(DEFAULT_RAW_VIRTOOLS_MATERIAL.mTextureBlendMode),
translation_context = 'BBP_PG_virtools_material/property' translation_context = 'BBP_PG_virtools_material/property'
) # type: ignore ) # type: ignore
@@ -208,7 +153,7 @@ class BBP_PG_virtools_material(bpy.types.PropertyGroup):
name = "Filter Min", name = "Filter Min",
description = "Texture filter mode when the texture is minified", description = "Texture filter mode when the texture is minified",
items = _g_Helper_VXTEXTURE_FILTERMODE.generate_items(), items = _g_Helper_VXTEXTURE_FILTERMODE.generate_items(),
default = _g_Helper_VXTEXTURE_FILTERMODE.to_selection(RawVirtoolsMaterial.cDefaultTextureMinMode), default = _g_Helper_VXTEXTURE_FILTERMODE.to_selection(DEFAULT_RAW_VIRTOOLS_MATERIAL.mTextureMinMode),
translation_context = 'BBP_PG_virtools_material/property' translation_context = 'BBP_PG_virtools_material/property'
) # type: ignore ) # type: ignore
@@ -216,7 +161,7 @@ class BBP_PG_virtools_material(bpy.types.PropertyGroup):
name = "Filter Mag", name = "Filter Mag",
description = "Texture filter mode when the texture is magnified", description = "Texture filter mode when the texture is magnified",
items = _g_Helper_VXTEXTURE_FILTERMODE.generate_items(), items = _g_Helper_VXTEXTURE_FILTERMODE.generate_items(),
default = _g_Helper_VXTEXTURE_FILTERMODE.to_selection(RawVirtoolsMaterial.cDefaultTextureMagMode), default = _g_Helper_VXTEXTURE_FILTERMODE.to_selection(DEFAULT_RAW_VIRTOOLS_MATERIAL.mTextureMagMode),
translation_context = 'BBP_PG_virtools_material/property' translation_context = 'BBP_PG_virtools_material/property'
) # type: ignore ) # type: ignore
@@ -224,7 +169,7 @@ class BBP_PG_virtools_material(bpy.types.PropertyGroup):
name = "Address Mode", name = "Address Mode",
description = "The address mode controls how the texture coordinates outside the range 0..1", description = "The address mode controls how the texture coordinates outside the range 0..1",
items = _g_Helper_VXTEXTURE_ADDRESSMODE.generate_items(), items = _g_Helper_VXTEXTURE_ADDRESSMODE.generate_items(),
default = _g_Helper_VXTEXTURE_ADDRESSMODE.to_selection(RawVirtoolsMaterial.cDefaultTextureAddressMode), default = _g_Helper_VXTEXTURE_ADDRESSMODE.to_selection(DEFAULT_RAW_VIRTOOLS_MATERIAL.mTextureAddressMode),
translation_context = 'BBP_PG_virtools_material/property' translation_context = 'BBP_PG_virtools_material/property'
) # type: ignore ) # type: ignore
@@ -232,7 +177,7 @@ class BBP_PG_virtools_material(bpy.types.PropertyGroup):
name = "Source Blend", name = "Source Blend",
description = "Source blend factor", description = "Source blend factor",
items = _g_Helper_VXBLEND_MODE.generate_items(), items = _g_Helper_VXBLEND_MODE.generate_items(),
default = _g_Helper_VXBLEND_MODE.to_selection(RawVirtoolsMaterial.cDefaultSourceBlend), default = _g_Helper_VXBLEND_MODE.to_selection(DEFAULT_RAW_VIRTOOLS_MATERIAL.mSourceBlend),
translation_context = 'BBP_PG_virtools_material/property' translation_context = 'BBP_PG_virtools_material/property'
) # type: ignore ) # type: ignore
@@ -240,7 +185,7 @@ class BBP_PG_virtools_material(bpy.types.PropertyGroup):
name = "Destination Blend", name = "Destination Blend",
description = "Destination blend factor", description = "Destination blend factor",
items = _g_Helper_VXBLEND_MODE.generate_items(), items = _g_Helper_VXBLEND_MODE.generate_items(),
default = _g_Helper_VXBLEND_MODE.to_selection(RawVirtoolsMaterial.cDefaultDestBlend), default = _g_Helper_VXBLEND_MODE.to_selection(DEFAULT_RAW_VIRTOOLS_MATERIAL.mDestBlend),
translation_context = 'BBP_PG_virtools_material/property' translation_context = 'BBP_PG_virtools_material/property'
) # type: ignore ) # type: ignore
@@ -248,7 +193,7 @@ class BBP_PG_virtools_material(bpy.types.PropertyGroup):
name = "Fill Mode", name = "Fill Mode",
description = "Fill mode", description = "Fill mode",
items = _g_Helper_VXFILL_MODE.generate_items(), items = _g_Helper_VXFILL_MODE.generate_items(),
default = _g_Helper_VXFILL_MODE.to_selection(RawVirtoolsMaterial.cDefaultFillMode), default = _g_Helper_VXFILL_MODE.to_selection(DEFAULT_RAW_VIRTOOLS_MATERIAL.mFillMode),
translation_context = 'BBP_PG_virtools_material/property' translation_context = 'BBP_PG_virtools_material/property'
) # type: ignore ) # type: ignore
@@ -256,38 +201,38 @@ class BBP_PG_virtools_material(bpy.types.PropertyGroup):
name = "Shade Mode", name = "Shade Mode",
description = "Shade mode", description = "Shade mode",
items = _g_Helper_VXSHADE_MODE.generate_items(), items = _g_Helper_VXSHADE_MODE.generate_items(),
default = _g_Helper_VXSHADE_MODE.to_selection(RawVirtoolsMaterial.cDefaultShadeMode), default = _g_Helper_VXSHADE_MODE.to_selection(DEFAULT_RAW_VIRTOOLS_MATERIAL.mShadeMode),
translation_context = 'BBP_PG_virtools_material/property' translation_context = 'BBP_PG_virtools_material/property'
) # type: ignore ) # type: ignore
enable_alpha_test: bpy.props.BoolProperty( enable_alpha_test: bpy.props.BoolProperty(
name = "Alpha Test", name = "Alpha Test",
description = "Whether the alpha test is enabled", description = "Whether the alpha test is enabled",
default = RawVirtoolsMaterial.cDefaultEnableAlphaTest, default = DEFAULT_RAW_VIRTOOLS_MATERIAL.mEnableAlphaTest,
translation_context = 'BBP_PG_virtools_material/property' translation_context = 'BBP_PG_virtools_material/property'
) # type: ignore ) # type: ignore
enable_alpha_blend: bpy.props.BoolProperty( enable_alpha_blend: bpy.props.BoolProperty(
name = "Blend", name = "Blend",
description = "Whether alpha blending is enabled or not.", description = "Whether alpha blending is enabled or not.",
default = RawVirtoolsMaterial.cDefaultEnableAlphaBlend, default = DEFAULT_RAW_VIRTOOLS_MATERIAL.mEnableAlphaBlend,
translation_context = 'BBP_PG_virtools_material/property' translation_context = 'BBP_PG_virtools_material/property'
) # type: ignore ) # type: ignore
enable_perspective_correction: bpy.props.BoolProperty( enable_perspective_correction: bpy.props.BoolProperty(
name = "Perspective Correction", name = "Perspective Correction",
description = "Whether texture perspective correction is enabled", description = "Whether texture perspective correction is enabled",
default = RawVirtoolsMaterial.cDefaultEnablePerspectiveCorrection, default = DEFAULT_RAW_VIRTOOLS_MATERIAL.mEnablePerspectiveCorrection,
translation_context = 'BBP_PG_virtools_material/property' translation_context = 'BBP_PG_virtools_material/property'
) # type: ignore ) # type: ignore
enable_z_write: bpy.props.BoolProperty( enable_z_write: bpy.props.BoolProperty(
name = "Z-Buffer Write", name = "Z-Buffer Write",
description = "Whether writing in ZBuffer is enabled.", description = "Whether writing in ZBuffer is enabled.",
default = RawVirtoolsMaterial.cDefaultEnableZWrite, default = DEFAULT_RAW_VIRTOOLS_MATERIAL.mEnableZWrite,
translation_context = 'BBP_PG_virtools_material/property' translation_context = 'BBP_PG_virtools_material/property'
) # type: ignore ) # type: ignore
enable_two_sided: bpy.props.BoolProperty( enable_two_sided: bpy.props.BoolProperty(
name = "Both Sided", name = "Both Sided",
description = "Whether the material is both sided or not", description = "Whether the material is both sided or not",
default = RawVirtoolsMaterial.cDefaultEnableTwoSided, default = DEFAULT_RAW_VIRTOOLS_MATERIAL.mEnableTwoSided,
translation_context = 'BBP_PG_virtools_material/property' translation_context = 'BBP_PG_virtools_material/property'
) # type: ignore ) # type: ignore
@@ -296,7 +241,7 @@ class BBP_PG_virtools_material(bpy.types.PropertyGroup):
description = "Alpha referential value", description = "Alpha referential value",
min = 0, min = 0,
max = 255, max = 255,
default = RawVirtoolsMaterial.cDefaultAlphaRef, default = DEFAULT_RAW_VIRTOOLS_MATERIAL.mAlphaRef,
translation_context = 'BBP_PG_virtools_material/property' translation_context = 'BBP_PG_virtools_material/property'
) # type: ignore ) # type: ignore
@@ -304,7 +249,7 @@ class BBP_PG_virtools_material(bpy.types.PropertyGroup):
name = "Alpha Test Function", name = "Alpha Test Function",
description = "Alpha comparision function", description = "Alpha comparision function",
items = _g_Helper_VXCMPFUNC.generate_items(), items = _g_Helper_VXCMPFUNC.generate_items(),
default = _g_Helper_VXCMPFUNC.to_selection(RawVirtoolsMaterial.cDefaultAlphaFunc), default = _g_Helper_VXCMPFUNC.to_selection(DEFAULT_RAW_VIRTOOLS_MATERIAL.mAlphaFunc),
translation_context = 'BBP_PG_virtools_material/property' translation_context = 'BBP_PG_virtools_material/property'
) # type: ignore ) # type: ignore
@@ -312,7 +257,7 @@ class BBP_PG_virtools_material(bpy.types.PropertyGroup):
name = "Z Compare Function", name = "Z Compare Function",
description = "Z Comparison function", description = "Z Comparison function",
items = _g_Helper_VXCMPFUNC.generate_items(), items = _g_Helper_VXCMPFUNC.generate_items(),
default = _g_Helper_VXCMPFUNC.to_selection(RawVirtoolsMaterial.cDefaultZFunc), default = _g_Helper_VXCMPFUNC.to_selection(DEFAULT_RAW_VIRTOOLS_MATERIAL.mZFunc),
translation_context = 'BBP_PG_virtools_material/property' translation_context = 'BBP_PG_virtools_material/property'
) # type: ignore ) # type: ignore

View File

@@ -1,18 +1,16 @@
import bpy import bpy
import typing, enum import typing, enum
from . import UTIL_functions, UTIL_virtools_types from dataclasses import dataclass
from dataclasses import field as datafield
from . import UTIL_functions, UTIL_blender_mesh, UTIL_virtools_types
# Raw Data # Raw Data
@dataclass
class RawVirtoolsMesh(): class RawVirtoolsMesh():
# Instance Member Declarations mLitMode: UTIL_virtools_types.VXMESH_LITMODE = datafield(default=UTIL_virtools_types.VXMESH_LITMODE.VX_LITMESH)
mLitMode: UTIL_virtools_types.VXMESH_LITMODE
# Default Value Declarations DEFAULT_RAW_VIRTOOLS_MESH = RawVirtoolsMesh()
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 # 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.VXMESH_LITMODE)
@@ -24,25 +22,27 @@ class BBP_PG_virtools_mesh(bpy.types.PropertyGroup):
name = "Lit Mode", name = "Lit Mode",
description = "Lighting mode of the mesh.", description = "Lighting mode of the mesh.",
items = _g_Helper_VXMESH_LITMODE.generate_items(), items = _g_Helper_VXMESH_LITMODE.generate_items(),
default = _g_Helper_VXMESH_LITMODE.to_selection(RawVirtoolsMesh.cDefaultLitMode), default = _g_Helper_VXMESH_LITMODE.to_selection(DEFAULT_RAW_VIRTOOLS_MESH.mLitMode),
translation_context = 'BBP_PG_virtools_mesh/property' translation_context = 'BBP_PG_virtools_mesh/property'
) # type: ignore ) # type: ignore
# Getter Setter # Getter Setter
def get_virtools_mesh(mesh: bpy.types.Mesh) -> BBP_PG_virtools_mesh: CanToMesh = bpy.types.Mesh | bpy.types.Curve | bpy.types.SurfaceCurve | bpy.types.TextCurve | bpy.types.MetaBall
return mesh.virtools_mesh
def get_raw_virtools_mesh(mesh: bpy.types.Mesh) -> RawVirtoolsMesh: def get_virtools_mesh(meshlike: CanToMesh) -> BBP_PG_virtools_mesh:
props: BBP_PG_virtools_mesh = get_virtools_mesh(mesh) return meshlike.virtools_mesh
def get_raw_virtools_mesh(meshlike: CanToMesh) -> RawVirtoolsMesh:
props: BBP_PG_virtools_mesh = get_virtools_mesh(meshlike)
rawdata: RawVirtoolsMesh = RawVirtoolsMesh() rawdata: RawVirtoolsMesh = RawVirtoolsMesh()
rawdata.mLitMode = _g_Helper_VXMESH_LITMODE.get_selection(props.lit_mode) rawdata.mLitMode = _g_Helper_VXMESH_LITMODE.get_selection(props.lit_mode)
return rawdata return rawdata
def set_raw_virtools_mesh(mesh: bpy.types.Mesh, rawdata: RawVirtoolsMesh) -> None: def set_raw_virtools_mesh(meshlike: CanToMesh, rawdata: RawVirtoolsMesh) -> None:
props: BBP_PG_virtools_mesh = get_virtools_mesh(mesh) props: BBP_PG_virtools_mesh = get_virtools_mesh(meshlike)
props.lit_mode = _g_Helper_VXMESH_LITMODE.to_selection(rawdata.mLitMode) props.lit_mode = _g_Helper_VXMESH_LITMODE.to_selection(rawdata.mLitMode)
@@ -59,12 +59,22 @@ class BBP_PT_virtools_mesh(bpy.types.Panel):
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
return context.mesh is not None 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
def draw(self, context): def draw(self, context):
# get layout and target # get layout
layout = self.layout layout = self.layout
props: BBP_PG_virtools_mesh = get_virtools_mesh(context.mesh) # 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)
# draw data # draw data
layout.prop(props, 'lit_mode') layout.prop(props, 'lit_mode')
@@ -75,11 +85,21 @@ def register() -> None:
bpy.utils.register_class(BBP_PG_virtools_mesh) bpy.utils.register_class(BBP_PG_virtools_mesh)
bpy.utils.register_class(BBP_PT_virtools_mesh) bpy.utils.register_class(BBP_PT_virtools_mesh)
# add into mesh metadata # Add metadata into mesh-like data block.
# according to TemporaryMesh, we need add it into:
# mesh, curve, surface, font, and metaball.
bpy.types.Mesh.virtools_mesh = bpy.props.PointerProperty(type = BBP_PG_virtools_mesh) 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: def unregister() -> None:
# remove from metadata # 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 del bpy.types.Mesh.virtools_mesh
bpy.utils.unregister_class(BBP_PT_virtools_mesh) bpy.utils.unregister_class(BBP_PT_virtools_mesh)

View File

@@ -1,24 +1,17 @@
import bpy import bpy
import typing import typing
from dataclasses import dataclass
from dataclasses import field as datafield
from . import UTIL_virtools_types, UTIL_functions from . import UTIL_virtools_types, UTIL_functions
@dataclass
class RawVirtoolsTexture(): class RawVirtoolsTexture():
# Instance Member Declarations 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)
mSaveOptions: UTIL_virtools_types.CK_TEXTURE_SAVEOPTIONS
mVideoFormat: UTIL_virtools_types.VX_PIXELFORMAT
# Default Value Declarations DEFAULT_RAW_VIRTOOLS_TEXTURE = RawVirtoolsTexture()
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 # blender enum prop helper defines
_g_Helper_CK_TEXTURE_SAVEOPTIONS = UTIL_virtools_types.EnumPropHelper(UTIL_virtools_types.CK_TEXTURE_SAVEOPTIONS) _g_Helper_CK_TEXTURE_SAVEOPTIONS = UTIL_virtools_types.EnumPropHelper(UTIL_virtools_types.CK_TEXTURE_SAVEOPTIONS)
_g_Helper_VX_PIXELFORMAT = UTIL_virtools_types.EnumPropHelper(UTIL_virtools_types.VX_PIXELFORMAT) _g_Helper_VX_PIXELFORMAT = UTIL_virtools_types.EnumPropHelper(UTIL_virtools_types.VX_PIXELFORMAT)
@@ -29,7 +22,7 @@ class BBP_PG_virtools_texture(bpy.types.PropertyGroup):
name = "Save Options", 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.", 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(), items = _g_Helper_CK_TEXTURE_SAVEOPTIONS.generate_items(),
default = _g_Helper_CK_TEXTURE_SAVEOPTIONS.to_selection(RawVirtoolsTexture.cDefaultSaveOptions), default = _g_Helper_CK_TEXTURE_SAVEOPTIONS.to_selection(DEFAULT_RAW_VIRTOOLS_TEXTURE.mSaveOptions),
translation_context = 'BBP_PG_virtools_texture/property' translation_context = 'BBP_PG_virtools_texture/property'
) # type: ignore ) # type: ignore
@@ -37,7 +30,7 @@ class BBP_PG_virtools_texture(bpy.types.PropertyGroup):
name = "Video Format", name = "Video Format",
description = "The desired surface pixel format in video memory.", description = "The desired surface pixel format in video memory.",
items = _g_Helper_VX_PIXELFORMAT.generate_items(), items = _g_Helper_VX_PIXELFORMAT.generate_items(),
default = _g_Helper_VX_PIXELFORMAT.to_selection(RawVirtoolsTexture.cDefaultVideoFormat), default = _g_Helper_VX_PIXELFORMAT.to_selection(DEFAULT_RAW_VIRTOOLS_TEXTURE.mVideoFormat),
translation_context = 'BBP_PG_virtools_texture/property' translation_context = 'BBP_PG_virtools_texture/property'
) # type: ignore ) # type: ignore

View File

@@ -105,22 +105,37 @@ def _nest_custom_split_normal(nml_array: array.array) -> typing.Iterator[UTIL_vi
class TemporaryMesh(): class TemporaryMesh():
""" """
Create a temporary mesh for convenient exporting. Create a temporary mesh for convenient exporting.
When exporting mesh, we need triangulate it first. When exporting mesh, we need evaluate it first, then triangulate it.
We create a temporary mesh to hold the triangulated mesh result. We create a temporary mesh to hold the evaluated and triangulated mesh result.
So that original object will not be affected and keep its original geometry. So that original object will not be affected and keep its original geometry.
Please note passed bpy.types.Object must be Mesh Object. 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.
""" """
__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 __mBindingObject: bpy.types.Object
__mEvaluatedObject: bpy.types.Object
__mTempMesh: bpy.types.Mesh __mTempMesh: bpy.types.Mesh
def __init__(self, binding_obj: bpy.types.Object): def __init__(self, binding_obj: bpy.types.Object):
depsgraph = bpy.context.evaluated_depsgraph_get()
self.__mBindingObject = binding_obj self.__mBindingObject = binding_obj
self.__mTempMesh = None self.__mEvaluatedObject = self.__mBindingObject.evaluated_get(depsgraph)
self.__mTempMesh = self.__mEvaluatedObject.to_mesh()
if self.__mBindingObject.data is None: if self.__mTempMesh is None:
raise UTIL_functions.BBPException('try getting mesh from an object without mesh.') raise UTIL_functions.BBPException('try getting mesh from an object without mesh.')
self.__mTempMesh = self.__mBindingObject.to_mesh()
def __enter__(self): def __enter__(self):
return self return self
@@ -129,6 +144,7 @@ class TemporaryMesh():
self.dispose() self.dispose()
def is_valid(self) -> bool: def is_valid(self) -> bool:
if self.__mBindingObject is None: return False
if self.__mBindingObject is None: return False if self.__mBindingObject is None: return False
if self.__mTempMesh is None: return False if self.__mTempMesh is None: return False
return True return True
@@ -136,7 +152,8 @@ class TemporaryMesh():
def dispose(self) -> None: def dispose(self) -> None:
if self.is_valid(): if self.is_valid():
self.__mTempMesh = None self.__mTempMesh = None
self.__mBindingObject.to_mesh_clear() self.__mEvaluatedObject.to_mesh_clear()
self.__mEvaluatedObject = None
self.__mBindingObject = None self.__mBindingObject = None
def get_temp_mesh(self) -> bpy.types.Mesh: def get_temp_mesh(self) -> bpy.types.Mesh:

View File

@@ -37,6 +37,7 @@ class ConflictResolver():
__mObjectStrategy: ConflictStrategy __mObjectStrategy: ConflictStrategy
__mLightStrategy: ConflictStrategy __mLightStrategy: ConflictStrategy
__mCameraStrategy: ConflictStrategy
__mMeshStrategy: ConflictStrategy __mMeshStrategy: ConflictStrategy
__mMaterialStrategy: ConflictStrategy __mMaterialStrategy: ConflictStrategy
__mTextureStrategy: ConflictStrategy __mTextureStrategy: ConflictStrategy
@@ -44,11 +45,13 @@ class ConflictResolver():
def __init__(self, def __init__(self,
obj_strategy: ConflictStrategy, obj_strategy: ConflictStrategy,
light_strategy: ConflictStrategy, light_strategy: ConflictStrategy,
camera_strategy: ConflictStrategy,
mesh_strategy: ConflictStrategy, mesh_strategy: ConflictStrategy,
mtl_strategy: ConflictStrategy, mtl_strategy: ConflictStrategy,
tex_strategy: ConflictStrategy): tex_strategy: ConflictStrategy):
self.__mObjectStrategy = obj_strategy self.__mObjectStrategy = obj_strategy
self.__mLightStrategy = light_strategy self.__mLightStrategy = light_strategy
self.__mCameraStrategy = camera_strategy
self.__mMeshStrategy = mesh_strategy self.__mMeshStrategy = mesh_strategy
self.__mMaterialStrategy = mtl_strategy self.__mMaterialStrategy = mtl_strategy
self.__mTextureStrategy = tex_strategy self.__mTextureStrategy = tex_strategy
@@ -88,6 +91,22 @@ class ConflictResolver():
new_obj: bpy.types.Object = bpy.data.objects.new(name, new_light) new_obj: bpy.types.Object = bpy.data.objects.new(name, new_light)
return (new_obj, new_light, True) 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]: def create_mesh(self, name: str) -> tuple[bpy.types.Mesh, bool]:
if self.__mMeshStrategy == ConflictStrategy.Current: if self.__mMeshStrategy == ConflictStrategy.Current:
old: bpy.types.Mesh | None = bpy.data.meshes.get(name, None) old: bpy.types.Mesh | None = bpy.data.meshes.get(name, None)
@@ -153,6 +172,14 @@ class ImportParams():
translation_context = 'BBP/UTIL_ioport_shared.ImportParams/property' translation_context = 'BBP/UTIL_ioport_shared.ImportParams/property'
) # type: ignore ) # 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( object_conflict_strategy: bpy.props.EnumProperty(
name = "Object Name Conflict", name = "Object Name Conflict",
items = _g_EnumHelper_ConflictStrategy.generate_items(), items = _g_EnumHelper_ConflictStrategy.generate_items(),
@@ -173,11 +200,13 @@ class ImportParams():
grid = body.grid_flow(row_major=False, columns=2) 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='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='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='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='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.label(text='Texture', icon='TEXTURE', text_ctxt='BBP/UTIL_ioport_shared.ImportParams/draw')
grid.prop(self, 'object_conflict_strategy', text='') grid.prop(self, 'object_conflict_strategy', text='')
grid.prop(self, 'light_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, 'mesh_conflict_strategy', text='')
grid.prop(self, 'material_conflict_strategy', text='') grid.prop(self, 'material_conflict_strategy', text='')
grid.prop(self, 'texture_conflict_strategy', text='') grid.prop(self, 'texture_conflict_strategy', text='')
@@ -194,6 +223,9 @@ class ImportParams():
def general_get_light_conflict_strategy(self) -> ConflictStrategy: def general_get_light_conflict_strategy(self) -> ConflictStrategy:
return _g_EnumHelper_ConflictStrategy.get_selection(self.light_conflict_strategy) 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: def general_get_object_conflict_strategy(self) -> ConflictStrategy:
return _g_EnumHelper_ConflictStrategy.get_selection(self.object_conflict_strategy) return _g_EnumHelper_ConflictStrategy.get_selection(self.object_conflict_strategy)
@@ -201,6 +233,7 @@ class ImportParams():
return ConflictResolver( return ConflictResolver(
self.general_get_object_conflict_strategy(), self.general_get_object_conflict_strategy(),
self.general_get_light_conflict_strategy(), self.general_get_light_conflict_strategy(),
self.general_get_camera_conflict_strategy(),
self.general_get_mesh_conflict_strategy(), self.general_get_mesh_conflict_strategy(),
self.general_get_material_conflict_strategy(), self.general_get_material_conflict_strategy(),
self.general_get_texture_conflict_strategy() self.general_get_texture_conflict_strategy()

View File

@@ -1,9 +1,10 @@
import bpy, mathutils import bpy, mathutils
import typing, math import typing, math
from dataclasses import dataclass
from . import UTIL_functions from . import UTIL_functions
# extract all declarations in PyBMap # extract all declarations in pybmap
from .PyBMap.virtools_types import * from .pybmap.virtools_types import *
# and add some patches for them # and add some patches for them
# mainly patch them with functions exchanging data with blender # mainly patch them with functions exchanging data with blender
# and the convertion between differnet coordinate system. # and the convertion between differnet coordinate system.
@@ -78,7 +79,7 @@ def vxmatrix_to_blender(self: VxMatrix) -> mathutils.Matrix:
## Hints about Light Matrix ## Hints about Light Matrix
# There is a slight difference between Virtools and Blender. # There is a slight difference between Virtools and Blender.
# In blender, the default direction of all directional light (spot and sun) are Down (-Z). # In blender, the default direction of all directional light (spot and sun) are Down (-Z).
# Hoewver, in Virtools, the default direction of all directional light (spot and directional) are Forward (+Z). # However, 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 # 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 # from Virtools default light direction by rotating it around X-axis with -90 degree
@@ -108,130 +109,159 @@ def bldmatrix_restore_light_obj(data: mathutils.Matrix) -> mathutils.Matrix:
# so we simply right multiple it. # so we simply right multiple it.
return data @ mathutils.Matrix.Rotation(math.radians(-90), 4, 'X') 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 #endregion
#region Blender EnumProperty Creation #region Blender EnumProperty Creation
class EnumAnnotation(): @dataclass(frozen=True)
mDisplayName: str class EnumDocstring():
mDescription: str display_name: str
def __init__(self, display_name: str, description: str): """The name of this enum entry."""
self.mDisplayName = display_name description: str
self.mDescription = description """The description of this enum entry."""
_g_Annotation: dict[type, dict[int, EnumAnnotation]] = { _g_Docstring: dict[type, dict[int, EnumDocstring]] = {
VXTEXTURE_BLENDMODE: { VXTEXTURE_BLENDMODE: {
VXTEXTURE_BLENDMODE.VXTEXTUREBLEND_DECAL.value: EnumAnnotation("Decal", "Texture replace any material information "), VXTEXTURE_BLENDMODE.VXTEXTUREBLEND_DECAL.value: EnumDocstring("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_MODULATE.value: EnumDocstring("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_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: EnumAnnotation("Modulate Alpha", "Alpha information in the texture specify how material and texture are combined "), 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: EnumAnnotation("Decal Mask", ""), VXTEXTURE_BLENDMODE.VXTEXTUREBLEND_DECALMASK.value: EnumDocstring("Decal Mask", ""),
VXTEXTURE_BLENDMODE.VXTEXTUREBLEND_MODULATEMASK.value: EnumAnnotation("Modulate Mask", ""), VXTEXTURE_BLENDMODE.VXTEXTUREBLEND_MODULATEMASK.value: EnumDocstring("Modulate Mask", ""),
VXTEXTURE_BLENDMODE.VXTEXTUREBLEND_COPY.value: EnumAnnotation("Copy", "Equivalent to DECAL "), VXTEXTURE_BLENDMODE.VXTEXTUREBLEND_COPY.value: EnumDocstring("Copy", "Equivalent to DECAL "),
VXTEXTURE_BLENDMODE.VXTEXTUREBLEND_ADD.value: EnumAnnotation("Add", ""), VXTEXTURE_BLENDMODE.VXTEXTUREBLEND_ADD.value: EnumDocstring("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_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: EnumAnnotation("Max", ""), VXTEXTURE_BLENDMODE.VXTEXTUREBLEND_MAX.value: EnumDocstring("Max", ""),
}, },
VXTEXTURE_FILTERMODE: { VXTEXTURE_FILTERMODE: {
VXTEXTURE_FILTERMODE.VXTEXTUREFILTER_NEAREST.value: EnumAnnotation("Nearest", "No Filter "), VXTEXTURE_FILTERMODE.VXTEXTUREFILTER_NEAREST.value: EnumDocstring("Nearest", "No Filter "),
VXTEXTURE_FILTERMODE.VXTEXTUREFILTER_LINEAR.value: EnumAnnotation("Linear", "Bilinear Interpolation "), VXTEXTURE_FILTERMODE.VXTEXTUREFILTER_LINEAR.value: EnumDocstring("Linear", "Bilinear Interpolation "),
VXTEXTURE_FILTERMODE.VXTEXTUREFILTER_MIPNEAREST.value: EnumAnnotation("Mip Nearest", "Mip mapping "), VXTEXTURE_FILTERMODE.VXTEXTUREFILTER_MIPNEAREST.value: EnumDocstring("Mip Nearest", "Mip mapping "),
VXTEXTURE_FILTERMODE.VXTEXTUREFILTER_MIPLINEAR.value: EnumAnnotation("Mip Linear", "Mip Mapping with Bilinear interpolation "), VXTEXTURE_FILTERMODE.VXTEXTUREFILTER_MIPLINEAR.value: EnumDocstring("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_LINEARMIPNEAREST.value: EnumDocstring("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_LINEARMIPLINEAR.value: EnumDocstring("Linear Mip Linear", "Trilinear Filtering "),
VXTEXTURE_FILTERMODE.VXTEXTUREFILTER_ANISOTROPIC.value: EnumAnnotation("Anisotropic", "Anisotropic filtering "), VXTEXTURE_FILTERMODE.VXTEXTUREFILTER_ANISOTROPIC.value: EnumDocstring("Anisotropic", "Anisotropic filtering "),
}, },
VXBLEND_MODE: { VXBLEND_MODE: {
VXBLEND_MODE.VXBLEND_ZERO.value: EnumAnnotation("Zero", "Blend factor is (0, 0, 0, 0). "), VXBLEND_MODE.VXBLEND_ZERO.value: EnumDocstring("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_ONE.value: EnumDocstring("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_SRCCOLOR.value: EnumDocstring("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_INVSRCCOLOR.value: EnumDocstring("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_SRCALPHA.value: EnumDocstring("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_INVSRCALPHA.value: EnumDocstring("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_DESTALPHA.value: EnumDocstring("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_INVDESTALPHA.value: EnumDocstring("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_DESTCOLOR.value: EnumDocstring("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_INVDESTCOLOR.value: EnumDocstring("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_SRCALPHASAT.value: EnumDocstring("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_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: 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) "), #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) "),
}, },
VXTEXTURE_ADDRESSMODE: { VXTEXTURE_ADDRESSMODE: {
VXTEXTURE_ADDRESSMODE.VXTEXTURE_ADDRESSWRAP.value: EnumAnnotation("Wrap", "Default mesh wrap mode is used (see CKMesh::SetWrapMode) "), VXTEXTURE_ADDRESSMODE.VXTEXTURE_ADDRESSWRAP.value: EnumDocstring("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_ADDRESSMIRROR.value: EnumDocstring("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_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: 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_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: EnumAnnotation("Mirror Once", " "), VXTEXTURE_ADDRESSMODE.VXTEXTURE_ADDRESSMIRRORONCE.value: EnumDocstring("Mirror Once", " "),
}, },
VXFILL_MODE: { VXFILL_MODE: {
VXFILL_MODE.VXFILL_POINT.value: EnumAnnotation("Point", "Vertices rendering "), VXFILL_MODE.VXFILL_POINT.value: EnumDocstring("Point", "Vertices rendering "),
VXFILL_MODE.VXFILL_WIREFRAME.value: EnumAnnotation("Wireframe", "Edges rendering "), VXFILL_MODE.VXFILL_WIREFRAME.value: EnumDocstring("Wireframe", "Edges rendering "),
VXFILL_MODE.VXFILL_SOLID.value: EnumAnnotation("Solid", "Face rendering "), VXFILL_MODE.VXFILL_SOLID.value: EnumDocstring("Solid", "Face rendering "),
}, },
VXSHADE_MODE: { VXSHADE_MODE: {
VXSHADE_MODE.VXSHADE_FLAT.value: EnumAnnotation("Flat", "Flat Shading "), VXSHADE_MODE.VXSHADE_FLAT.value: EnumDocstring("Flat", "Flat Shading "),
VXSHADE_MODE.VXSHADE_GOURAUD.value: EnumAnnotation("Gouraud", "Gouraud Shading "), VXSHADE_MODE.VXSHADE_GOURAUD.value: EnumDocstring("Gouraud", "Gouraud Shading "),
VXSHADE_MODE.VXSHADE_PHONG.value: EnumAnnotation("Phong", "Phong Shading (Not yet supported by most implementation) "), VXSHADE_MODE.VXSHADE_PHONG.value: EnumDocstring("Phong", "Phong Shading (Not yet supported by most implementation) "),
}, },
VXCMPFUNC: { VXCMPFUNC: {
VXCMPFUNC.VXCMP_NEVER.value: EnumAnnotation("Never", "Always fail the test. "), VXCMPFUNC.VXCMP_NEVER.value: EnumDocstring("Never", "Always fail the test. "),
VXCMPFUNC.VXCMP_LESS.value: EnumAnnotation("Less", "Accept if value if less than current value. "), VXCMPFUNC.VXCMP_LESS.value: EnumDocstring("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_EQUAL.value: EnumDocstring("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_LESSEQUAL.value: EnumDocstring("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_GREATER.value: EnumDocstring("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_NOTEQUAL.value: EnumDocstring("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_GREATEREQUAL.value: EnumDocstring("Greater Equal", "Accept if value if greater or equal current value. "),
VXCMPFUNC.VXCMP_ALWAYS.value: EnumAnnotation("Always", "Always accept the test. "), VXCMPFUNC.VXCMP_ALWAYS.value: EnumDocstring("Always", "Always accept the test. "),
}, },
CK_TEXTURE_SAVEOPTIONS: { CK_TEXTURE_SAVEOPTIONS: {
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_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: 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_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: 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_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: EnumAnnotation("Use Global", "Use Global settings, that is the settings given with CKContext::SetGlobalImagesSaveOptions. (Not valid when using CKContext::SetImagesSaveOptions). "), 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: 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. "), 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. "),
}, },
VX_PIXELFORMAT: { VX_PIXELFORMAT: {
VX_PIXELFORMAT._32_ARGB8888.value: EnumAnnotation("32 Bits ARGB8888", "32-bit ARGB pixel format with alpha "), VX_PIXELFORMAT._32_ARGB8888.value: EnumDocstring("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._32_RGB888.value: EnumDocstring("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._24_RGB888.value: EnumDocstring("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_RGB565.value: EnumDocstring("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_RGB555.value: EnumDocstring("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_ARGB1555.value: EnumDocstring("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._16_ARGB4444.value: EnumDocstring("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_RGB332.value: EnumDocstring("8 Bits RGB332", "8-bit RGB pixel format "),
VX_PIXELFORMAT._8_ARGB2222.value: EnumAnnotation("8 Bits ARGB2222", "8-bit ARGB pixel format "), VX_PIXELFORMAT._8_ARGB2222.value: EnumDocstring("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_ABGR8888.value: EnumDocstring("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_RGBA8888.value: EnumDocstring("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_BGRA8888.value: EnumDocstring("32 Bits BGRA8888", "32-bit BGRA pixel format "),
VX_PIXELFORMAT._32_BGR888.value: EnumAnnotation("32 Bits BGR888", "32-bit BGR pixel format "), VX_PIXELFORMAT._32_BGR888.value: EnumDocstring("32 Bits BGR888", "32-bit BGR pixel format "),
VX_PIXELFORMAT._24_BGR888.value: EnumAnnotation("24 Bits BGR888", "24-bit BGR pixel format "), VX_PIXELFORMAT._24_BGR888.value: EnumDocstring("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_BGR565.value: EnumDocstring("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_BGR555.value: EnumDocstring("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_ABGR1555.value: EnumDocstring("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._16_ABGR4444.value: EnumDocstring("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._DXT1.value: EnumDocstring("DXT1", "S3/DirectX Texture Compression 1 "),
VX_PIXELFORMAT._DXT2.value: EnumAnnotation("DXT2", "S3/DirectX Texture Compression 2 "), VX_PIXELFORMAT._DXT2.value: EnumDocstring("DXT2", "S3/DirectX Texture Compression 2 "),
VX_PIXELFORMAT._DXT3.value: EnumAnnotation("DXT3", "S3/DirectX Texture Compression 3 "), VX_PIXELFORMAT._DXT3.value: EnumDocstring("DXT3", "S3/DirectX Texture Compression 3 "),
VX_PIXELFORMAT._DXT4.value: EnumAnnotation("DXT4", "S3/DirectX Texture Compression 4 "), VX_PIXELFORMAT._DXT4.value: EnumDocstring("DXT4", "S3/DirectX Texture Compression 4 "),
VX_PIXELFORMAT._DXT5.value: EnumAnnotation("DXT5", "S3/DirectX Texture Compression 5 "), VX_PIXELFORMAT._DXT5.value: EnumDocstring("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._16_V8U8.value: EnumDocstring("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._32_V16U16.value: EnumDocstring("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._16_L6V5U5.value: EnumDocstring("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._32_X8L8V8U8.value: EnumDocstring("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_ABGR8888_CLUT.value: EnumDocstring("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._8_ARGB8888_CLUT.value: EnumDocstring("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_ABGR8888_CLUT.value: EnumDocstring("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) "), VX_PIXELFORMAT._4_ARGB8888_CLUT.value: EnumDocstring("4 Bits ARGB8888 CLUT", "4 bits indexed CLUT (ARGB) "),
}, },
VXLIGHT_TYPE: { VXLIGHT_TYPE: {
VXLIGHT_TYPE.VX_LIGHTPOINT.value: EnumAnnotation("Point", "The Light is a point of light "), VXLIGHT_TYPE.VX_LIGHTPOINT.value: EnumDocstring("Point", "The Light is a point of light "),
VXLIGHT_TYPE.VX_LIGHTSPOT.value: EnumAnnotation("Spot", "The light is a spotlight "), VXLIGHT_TYPE.VX_LIGHTSPOT.value: EnumDocstring("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_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: EnumAnnotation("Lightpara", "Obsolete, do not use "), #VXLIGHT_TYPE.VX_LIGHTPARA.value: EnumDocstring("Lightpara", "Obsolete, do not use "),
}, },
VXMESH_LITMODE: { VXMESH_LITMODE: {
VXMESH_LITMODE.VX_PRELITMESH.value: EnumAnnotation("Prelit", "Lighting use color information store with vertices "), VXMESH_LITMODE.VX_PRELITMESH.value: EnumDocstring("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. "), 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", ""),
} }
} }
@@ -241,13 +271,13 @@ class EnumPropHelper(UTIL_functions.EnumPropHelper[_TRawEnum]):
""" """
Virtools type specified Blender EnumProp helper. Virtools type specified Blender EnumProp helper.
""" """
__mAnnotationDict: dict[int, EnumAnnotation] __mDocstringDict: dict[int, EnumDocstring]
__mEnumTy: type[_TRawEnum] __mEnumTy: type[_TRawEnum]
def __init__(self, ty: type[_TRawEnum]): def __init__(self, ty: type[_TRawEnum]):
# set enum type and annotation ref first # set enum type and docstring ref first
self.__mEnumTy = ty self.__mEnumTy = ty
self.__mAnnotationDict = _g_Annotation[ty] self.__mDocstringDict = _g_Docstring[ty]
# YYC MARK: # YYC MARK:
# It seems that Pylance has bad generic analyse ability in there. # It seems that Pylance has bad generic analyse ability in there.
@@ -259,8 +289,8 @@ class EnumPropHelper(UTIL_functions.EnumPropHelper[_TRawEnum]):
self.__mEnumTy, # enum.Enum its self is iterable self.__mEnumTy, # enum.Enum its self is iterable
lambda x: str(x.value), # convert enum.Enum's value to string lambda x: str(x.value), # convert enum.Enum's value to string
lambda x: self.__mEnumTy(int(x)), # use stored enum type and int() to get enum member lambda x: self.__mEnumTy(int(x)), # use stored enum type and int() to get enum member
lambda x: self.__mAnnotationDict[x.value].mDisplayName, lambda x: self.__mDocstringDict[x.value].display_name,
lambda x: self.__mAnnotationDict[x.value].mDescription, lambda x: self.__mDocstringDict[x.value].description,
lambda _: '' lambda _: ''
) )
@@ -273,11 +303,11 @@ def virtools_name_regulator(name: str | None) -> str:
else: return bpy.app.translations.pgettext_data('annoymous', 'BME/UTIL_virtools_types.virtools_name_regulator()') else: return bpy.app.translations.pgettext_data('annoymous', 'BME/UTIL_virtools_types.virtools_name_regulator()')
# YYC MARK: # YYC MARK:
# There are default encodings for PyBMap. We support Western European and Simplified Chinese in default. # 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. # Since LibCmo 0.2, the encoding name of LibCmo become universal encoding which is platfoorm independent.
# So no need set it according to different platform. # So no need set it according to different platform.
# Use universal encoding name (like Python). # Use universal encoding name (like Python).
g_PyBMapDefaultEncodings: tuple[str, ...] = ( g_PybmapDefaultEncodings: tuple[str, ...] = (
'cp1252', 'cp1252',
'gbk' 'gbk'
) )

View File

@@ -17,7 +17,7 @@ UTIL_icons_manager.register()
# then load other modules # then load other modules
from . import UTIL_translation 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_group 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_ballance_element, PROP_bme_material, PROP_ballance_map_info from . import PROP_ballance_element, PROP_bme_material, PROP_ballance_map_info
from . import OP_IMPORT_bmfile, OP_EXPORT_bmfile, OP_IMPORT_virtools, OP_EXPORT_virtools from . import OP_IMPORT_bmfile, OP_EXPORT_bmfile, OP_IMPORT_virtools, OP_EXPORT_virtools
from . import OP_UV_flatten_uv, OP_UV_rail_uv from . import OP_UV_flatten_uv, OP_UV_rail_uv
@@ -179,7 +179,6 @@ class BBP_MT_View3DMenu(bpy.types.Menu):
layout.operator(OP_OBJECT_legacy_align.BBP_OT_legacy_align.bl_idname) layout.operator(OP_OBJECT_legacy_align.BBP_OT_legacy_align.bl_idname)
layout.separator() layout.separator()
layout.label(text='Camera', icon='CAMERA_DATA', text_ctxt='BBP_MT_View3DMenu/draw') layout.label(text='Camera', icon='CAMERA_DATA', text_ctxt='BBP_MT_View3DMenu/draw')
layout.operator(OP_OBJECT_game_view.BBP_OT_game_resolution.bl_idname)
layout.operator(OP_OBJECT_game_view.BBP_OT_game_camera.bl_idname) layout.operator(OP_OBJECT_game_view.BBP_OT_game_camera.bl_idname)
layout.separator() layout.separator()
layout.label(text='Select', icon='SELECT_SET', text_ctxt='BBP_MT_View3DMenu/draw') layout.label(text='Select', icon='SELECT_SET', text_ctxt='BBP_MT_View3DMenu/draw')
@@ -327,6 +326,7 @@ def register() -> None:
PROP_virtools_texture.register() PROP_virtools_texture.register()
PROP_virtools_mesh.register() PROP_virtools_mesh.register()
PROP_virtools_light.register() PROP_virtools_light.register()
PROP_virtools_camera.register()
PROP_virtools_group.register() PROP_virtools_group.register()
PROP_ballance_element.register() PROP_ballance_element.register()
PROP_bme_material.register() PROP_bme_material.register()
@@ -397,6 +397,7 @@ def unregister() -> None:
PROP_bme_material.unregister() PROP_bme_material.unregister()
PROP_ballance_element.unregister() PROP_ballance_element.unregister()
PROP_virtools_group.unregister() PROP_virtools_group.unregister()
PROP_virtools_camera.unregister()
PROP_virtools_light.unregister() PROP_virtools_light.unregister()
PROP_virtools_mesh.unregister() PROP_virtools_mesh.unregister()
PROP_virtools_texture.unregister() PROP_virtools_texture.unregister()

View File

@@ -6,7 +6,7 @@ schema_version = "1.0.0"
# Example of manifest file for a Blender extension # Example of manifest file for a Blender extension
# Change the values according to your extension # Change the values according to your extension
id = "bbp_ng" id = "bbp_ng"
version = "4.3.1" version = "4.4.0"
name = "Ballance Blender Plugin" name = "Ballance Blender Plugin"
tagline = "The specialized add-on served for creating game map of Ballance" tagline = "The specialized add-on served for creating game map of Ballance"
maintainer = "yyc12345 <yyc12321@outlook.com>" maintainer = "yyc12345 <yyc12321@outlook.com>"

View File

@@ -4,13 +4,13 @@ This page will guide you in compiling the plugin as well as distributing it.
## Compiling LibCmo with BMap ## Compiling LibCmo with BMap
BBP's Virtools file native import/export functionality relies on BMap and its Python binding PyBMap. In order to distribute the plugin, we need to first compile BMap and its predecessor LibCmo, and before doing so, you need to check the version of BMap you need. Because BBP doesn't always use the latest version of BMap, e.g. if you're compiling an older version of BBP, it's obviously not possible to rely on the latest version of BMap. BMap is constantly being upgraded, and the functionality it provides is constantly changing, and different versions of BMap are incompatible. BBP usually states the version of BMap it uses at the time of release, but if BBP doesn't point it out, you may need to look for the most recent version of BMap that compiles with the version of BBP at the time of its release. BBP's Virtools file native import/export functionality relies on BMap and its Python binding pybmap. In order to distribute the plugin, we need to first compile BMap and its predecessor LibCmo, and before doing so, you need to check the version of BMap you need. Because BBP doesn't always use the latest version of BMap, e.g. if you're compiling an older version of BBP, it's obviously not possible to rely on the latest version of BMap. BMap is constantly being upgraded, and the functionality it provides is constantly changing, and different versions of BMap are incompatible. BBP usually states the version of BMap it uses at the time of release, but if BBP doesn't point it out, you may need to look for the most recent version of BMap that compiles with the version of BBP at the time of its release.
After specifying the version, you need to visit [LibCmo GitHub repository](https://github.com/yyc12345/libcmo21). Then clone the project and use the Git command to go to the corresponding version (or just download the source code of the corresponding version). Then follow LibCmo's compilation manual to compile to get BMap. on Windows, you'll usually get the files `BMap.dll` and `BMap.pdb`. On Linux, it will be `BMap.so`. After specifying the version, you need to visit [LibCmo GitHub repository](https://github.com/yyc12345/libcmo21). Then clone the project and use the Git command to go to the corresponding version (or just download the source code of the corresponding version). Then follow LibCmo's compilation manual to compile to get BMap. on Windows, you'll usually get the files `BMap.dll` and `BMap.pdb`. On Linux, it will be `BMap.so`.
Then we need to configure PyBMap, which comes with LibCmo. Please follow the manual of PyBMap to combine the compiled binary BMap library with PyBMap. That is to complete the PyBMap configuration. Then we need to configure pybmap, which comes with LibCmo. Please follow the manual of pybmap to combine the compiled binary BMap library with pybmap. That is to complete the pybmap configuration.
Then we need to copy the configured PyBMap to our project under `bbp_ng/PyBMap` to complete this step. Then we need to copy the configured pybmap to our project under `bbp_ng/pybmap` to complete this step.
## Generate Resources ## Generate Resources

View File

@@ -4,13 +4,13 @@
## 编译LibCmo与BMap ## 编译LibCmo与BMap
BBP的Virtools文件原生导入导出功能依赖BMap以及其Python绑定PyBMap来实现。为了分发插件我们需要首先编译BMap及其前置LibCmo。而在编译前你需要先确认你需要的BMap版本。因为BBP并不总是使用最新的BMap例如你正在编译一个旧版的BBP其显然不可能依赖最新的BMap。BMap也在不断升级中其提供的功能也在不断变化不同版本的BMap是不兼容的。BBP通常会在发布时写明其所用的BMap版本如果BBP没有指出你可能需要寻找与BBP发布时最近的BMap版本来编译。 BBP的Virtools文件原生导入导出功能依赖BMap以及其Python绑定pybmap来实现。为了分发插件我们需要首先编译BMap及其前置LibCmo。而在编译前你需要先确认你需要的BMap版本。因为BBP并不总是使用最新的BMap例如你正在编译一个旧版的BBP其显然不可能依赖最新的BMap。BMap也在不断升级中其提供的功能也在不断变化不同版本的BMap是不兼容的。BBP通常会在发布时写明其所用的BMap版本如果BBP没有指出你可能需要寻找与BBP发布时最近的BMap版本来编译。
在明确版本后,你需要访问[LibCmo位于GitHub的存储库](https://github.com/yyc12345/libcmo21)。然后克隆项目使用Git命令转到对应版本或者直接下载对应版本的源码。然后按照LibCmo的编译手册编译得到BMap。在Windows上你通常会得到`BMap.dll``BMap.pdb`这两个文件。而在Linux上则会是`BMap.so` 在明确版本后,你需要访问[LibCmo位于GitHub的存储库](https://github.com/yyc12345/libcmo21)。然后克隆项目使用Git命令转到对应版本或者直接下载对应版本的源码。然后按照LibCmo的编译手册编译得到BMap。在Windows上你通常会得到`BMap.dll``BMap.pdb`这两个文件。而在Linux上则会是`BMap.so`
然后我们需要配置PyBMap。PyBMap是随LibCmo一起提供的。请按照PyBMap的手册将编译得到的二进制BMap库PyBMap结合在一起。即完成PyBMap配置。 然后我们需要配置pybmap。pybmap是随LibCmo一起提供的。请按照pybmap的手册将编译得到的二进制BMap库pybmap结合在一起。即完成pybmap配置。
然后我们需要将配置好的PyBMap拷贝到本项目的根目录下即可完成此步即存在`bbp_ng/PyBMap`文件夹,为配置好的PyBMap 然后我们需要将配置好的pybmap拷贝到本项目的根目录下即可完成此步即存在`bbp_ng/pybmap`文件夹,为配置好的pybmap
## 生成资源 ## 生成资源