8 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
17 changed files with 1103 additions and 639 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)"
} }
] ]
} }
] ]

View File

@@ -1,9 +1,10 @@
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):
@@ -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,11 +155,14 @@ 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)
@@ -174,8 +184,13 @@ def _prepare_virtools_3dobjects(
match(obj3d.type): match(obj3d.type):
case 'CAMERA': case 'CAMERA':
# camera object # camera object
# TODO if obj3d not in camera_cret_set:
pass # add into set
camera_cret_set.add(obj3d)
# create virtools instance
vtcamera: bmap.BMTargetCamera = writer.create_target_camera()
# add into result list
camera_crets.append((obj3d, typing.cast(bpy.types.Camera, obj3d.data), vtcamera))
case 'LIGHT': case 'LIGHT':
# light object # light object
if obj3d not in light_cret_set: if obj3d not in light_cret_set:
@@ -191,14 +206,14 @@ def _prepare_virtools_3dobjects(
# 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] = {}
@@ -246,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')
@@ -288,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')
@@ -338,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')
@@ -453,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')
@@ -522,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,7 +3,7 @@ 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):
@@ -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

@@ -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,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 dataclasses import dataclass
from dataclasses import field as datafield
from . import UTIL_functions, UTIL_blender_mesh, UTIL_virtools_types 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,7 +22,7 @@ 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

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

@@ -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

@@ -79,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
@@ -109,6 +109,31 @@ 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
@@ -235,8 +260,8 @@ _g_Docstring: dict[type, dict[int, EnumDocstring]] = {
VXMESH_LITMODE.VX_LITMESH.value: EnumDocstring("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_CAMERA_PROJECTION.CK_PERSPECTIVEPROJECTION.value: EnumDocstring("Perspectiveprojection", ""), CK_CAMERA_PROJECTION.CK_PERSPECTIVEPROJECTION.value: EnumDocstring("Perspective Projection", ""),
CK_CAMERA_PROJECTION.CK_ORTHOGRAPHICPROJECTION.value: EnumDocstring("Orthographicprojection", ""), CK_CAMERA_PROJECTION.CK_ORTHOGRAPHICPROJECTION.value: EnumDocstring("Orthographic Projection", ""),
} }
} }

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>"