19 Commits

Author SHA1 Message Date
e884332163 i18n: update translation 2026-03-24 20:17:58 +08:00
15013c4d59 doc: update en doc by translation 2026-03-24 16:31:03 +08:00
3b79b8a0b1 doc: update zh-cn doc 2026-03-24 16:12:07 +08:00
2e76ce7862 feat: bump up version 2026-03-20 14:55:38 +08:00
eb8984c341 refactor: use python built in dataclass to improve our raw data structs. 2026-03-20 14:50:05 +08:00
77e4dcdb69 fix: rename some types in module 2026-03-20 14:16:44 +08:00
44d3b1fc99 feat: finish virtools camera work
- add operator for applying camera aspect ratio to blender scene.
- add camera aspect ratio preset in virtools camera panel.
- update game camera operators. use virtools camera instead of directly modifying camera properties.
2026-03-20 13:49:00 +08:00
70fa3b5f07 feat: support virtools camera import and export 2026-03-13 15:47:24 +08:00
fbee0ccce5 doc: update README for notes 2026-03-06 16:14:47 +08:00
684567777f feat: add virtools camera support 2026-03-06 16:10:05 +08:00
5e94d7db85 fix: change the default length of flat and sink street from 2.5 to 5
- this feature is requested by Lee623
2026-03-06 14:40:50 +08:00
8a7e3306a7 feat: allow exporting object in virtools file which has geometry.
- allow exporting object without apply modifier.
- allow exporting any objects which can be mesh (has geometry).
- due to this change, add virtools mesh properties for metaball, font, curve, surface.
- due to this change, remove the virtools group warning for metaball, font, curve and surface.
2026-03-05 21:45:36 +08:00
68fbffad54 feat: update pybmap to the latest version 2026-03-05 12:45:25 +08:00
dd6e557f39 refactor: rename PyBMap to pybmap due to the change of libcmo21 2026-03-04 21:56:11 +08:00
e4a4eae88e fix: fix fatal error when exporting virtools file
- fix fatal error that user only can export virtools file into existing file, rather than any new file.
2025-09-23 21:45:56 +08:00
f920cdcaf5 i18n: fix translation 2025-09-04 10:01:41 +08:00
40cad381af i18n: update translation 2025-09-03 09:50:56 +08:00
0d20a1fe44 doc: update english doc 2025-09-02 13:51:51 +08:00
599e9a6ef0 doc: sync zh-cn doc to en doc 2025-09-01 13:59:31 +08:00
44 changed files with 2419 additions and 1223 deletions

View File

@@ -5,3 +5,9 @@
BBP NG, abbr **B**allance **B**lender **P**lugin **N**ext **G**eneration.
For an introduction to this plugin, installing it, compiling it, reporting bugs, etc., see the GitHub Page of this project: https://yyc12345.github.io/BallanceBlenderHelper
## Notes
This project is now suspended and may be still suspended eternally
due to the lost interest and the lack of time of mine.
In the rest of life time of this project, only easy-to-be-resolved critical bugs will be fixed.

View File

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

View File

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

4
bbp_ng/.gitignore vendored
View File

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

View File

@@ -1,10 +1,11 @@
import bpy, mathutils
from bpy_extras.wm_utils.progress_report import ProgressReport
import tempfile, os, typing
from dataclasses import dataclass
from . import PROP_preferences, UTIL_ioport_shared, UTIL_naming_convention
from . import UTIL_virtools_types, UTIL_functions, UTIL_file_browser, UTIL_blender_mesh, UTIL_ballance_texture
from . import PROP_virtools_group, PROP_virtools_material, PROP_virtools_mesh, PROP_virtools_texture, PROP_virtools_light
from .PyBMap import bmap_wrapper as bmap
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
class BBP_OT_export_virtools(bpy.types.Operator, UTIL_file_browser.ExportVirtoolsFile, UTIL_ioport_shared.ExportParams, UTIL_ioport_shared.VirtoolsParams, UTIL_ioport_shared.BallanceParams):
"""Export Virtools File"""
@@ -44,9 +45,18 @@ class BBP_OT_export_virtools(bpy.types.Operator, UTIL_file_browser.ExportVirtool
self.report({'ERROR'}, 'You must specify at least one encoding for file saving (e.g. cp1252, gbk)!')
return {'CANCELLED'}
# check file name
# check file name, but has slightly difference with importer.
# when exporting, file can be inexisting in file system, so we can't check it directly.
filename = self.general_get_filename()
if not os.path.isfile(filename):
bad_filename: bool = False
# for filename, at first, it should not be empty surely.
if len(filename) == 0:
bad_filename = True
# then, if it is existing, it should be a file, otherwise everything is okey.
if os.path.exists(filename) and (not os.path.isfile(filename)):
bad_filename = True
# now, show message and exit if we have bad file name
if bad_filename:
self.report({'ERROR'}, 'No file was selected!')
return {'CANCELLED'}
@@ -72,11 +82,18 @@ class BBP_OT_export_virtools(bpy.types.Operator, UTIL_file_browser.ExportVirtool
self.draw_virtools_params(context, layout, False)
self.draw_ballance_params(layout, False)
_TObj3dPair = tuple[bpy.types.Object, bmap.BM3dObject]
_TLightPair = tuple[bpy.types.Object, bpy.types.Light, bmap.BMTargetLight]
_TMeshPair = tuple[bpy.types.Object, bpy.types.Mesh, bmap.BMMesh]
_TMaterialPair = tuple[bpy.types.Material, bmap.BMMaterial]
_TTexturePair = tuple[bpy.types.Image, bmap.BMTexture]
_Object3dPair = tuple[bpy.types.Object, bmap.BM3dObject]
_LightPair = tuple[bpy.types.Object, bpy.types.Light, bmap.BMTargetLight]
_CameraPair = tuple[bpy.types.Object, bpy.types.Camera, bmap.BMTargetCamera]
_MeshPair = tuple[bpy.types.Object, bpy.types.Mesh, bmap.BMMesh]
_MaterialPair = tuple[bpy.types.Material, bmap.BMMaterial]
_TexturePair = tuple[bpy.types.Image, bmap.BMTexture]
@dataclass
class _PreparedCrets:
obj3d_crets: tuple[_Object3dPair, ...]
light_crets: tuple[_LightPair, ...]
camera_crets: tuple[_CameraPair, ...]
def _export_virtools(
file_name_: str,
@@ -103,22 +120,21 @@ def _export_virtools(
# prepare progress reporter
with ProgressReport(wm = bpy.context.window_manager) as progress:
# prepare 3dobject and light
obj3d_crets: tuple[_TObj3dPair, ...]
light_crets: tuple[_TLightPair, ...]
(obj3d_crets, light_crets) = _prepare_virtools_3dobjects(writer, progress, export_objects)
# prepare 3dobject, light and camera
prep_crets = _prepare_virtools_3dobjects(writer, progress, export_objects)
# export group according to prepared 3dobject
_export_virtools_groups(writer, progress, successive_sector_, successive_sector_count_, obj3d_crets)
# export prepared light
_export_virtools_light(writer, progress, light_crets)
_export_virtools_groups(writer, progress, successive_sector_, successive_sector_count_, prep_crets.obj3d_crets)
# export prepared light and camera
_export_virtools_light(writer, progress, prep_crets.light_crets)
_export_virtools_camera(writer, progress, prep_crets.camera_crets)
# export prepared 3dobject
mesh_crets: tuple[_TMeshPair, ...] = _export_virtools_3dobjects(
writer, progress, obj3d_crets)
mesh_crets: tuple[_MeshPair, ...] = _export_virtools_3dobjects(
writer, progress, prep_crets.obj3d_crets)
# export mesh
material_crets: tuple[_TMaterialPair, ...] = _export_virtools_meshes(
material_crets: tuple[_MaterialPair, ...] = _export_virtools_meshes(
writer, progress, mesh_crets)
# export material
texture_crets: tuple[_TTexturePair, ...] = _export_virtools_materials(
texture_crets: tuple[_TexturePair, ...] = _export_virtools_materials(
writer, progress, material_crets)
# export texture
_export_virtools_textures(writer, progress, vt_temp_folder, texture_crets)
@@ -131,7 +147,7 @@ def _prepare_virtools_3dobjects(
writer: bmap.BMFileWriter,
progress: ProgressReport,
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
# because _export_virtools_3dobjects() and _export_virtools_groups() are need use the return value of this function
#
@@ -139,52 +155,65 @@ def _prepare_virtools_3dobjects(
# we also need extract exported lights and create equvalent entries in virtools for them.
# create 3dobject hashset and result
obj3d_crets: list[_TObj3dPair] = []
obj3d_crets: list[_Object3dPair] = []
obj3d_cret_set: set[bpy.types.Object] = set()
# create light hashset and result
light_crets: list[_TLightPair] = []
light_crets: list[_LightPair] = []
light_cret_set: set[bpy.types.Object] = set()
# create camera hashset and result
camera_crets: list[_CameraPair] = []
camera_cret_set: set[bpy.types.Object] = set()
# start saving
tr_text: str = bpy.app.translations.pgettext_rpt('Creating 3dObjects and Lights', 'BBP_OT_export_virtools/execute')
progress.enter_substeps(len(export_objects), tr_text)
# iterate exported object list
for obj3d in export_objects:
# only accept mesh object and light object
# only accept mesh-like object, camera and light object
# all of other objects will be discard.
match(obj3d.type):
case 'MESH':
# mesh object
if obj3d not in obj3d_cret_set:
# add into set
obj3d_cret_set.add(obj3d)
# create virtools instance
vtobj3d: bmap.BM3dObject = writer.create_3dobject()
# add into result list
obj3d_crets.append((obj3d, vtobj3d))
case 'LIGHT':
# light object
if obj3d not in light_cret_set:
# add into set
light_cret_set.add(obj3d)
# create virtools instance
vtlight: bmap.BMTargetLight = writer.create_target_light()
# add into result list
light_crets.append((obj3d, typing.cast(bpy.types.Light, obj3d.data), vtlight))
if UTIL_blender_mesh.TemporaryMesh.has_geometry(obj3d):
# mesh-like object
if obj3d not in obj3d_cret_set:
# add into set
obj3d_cret_set.add(obj3d)
# create virtools instance
vtobj3d: bmap.BM3dObject = writer.create_3dobject()
# add into result list
obj3d_crets.append((obj3d, vtobj3d))
else:
match(obj3d.type):
case 'CAMERA':
# camera object
if obj3d not in camera_cret_set:
# add into set
camera_cret_set.add(obj3d)
# create virtools instance
vtcamera: bmap.BMTargetCamera = writer.create_target_camera()
# add into result list
camera_crets.append((obj3d, typing.cast(bpy.types.Camera, obj3d.data), vtcamera))
case 'LIGHT':
# light object
if obj3d not in light_cret_set:
# add into set
light_cret_set.add(obj3d)
# create virtools instance
vtlight: bmap.BMTargetLight = writer.create_target_light()
# add into result list
light_crets.append((obj3d, typing.cast(bpy.types.Light, obj3d.data), vtlight))
# step progress no matter whether create new one
progress.step()
# leave progress and return
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(
writer: bmap.BMFileWriter,
progress: ProgressReport,
successive_sector: bool,
successive_sector_count: int,
obj3d_crets: tuple[_TObj3dPair, ...]
obj3d_crets: tuple[_Object3dPair, ...]
) -> None:
# create virtools group
group_cret_map: dict[str, bmap.BMGroup] = {}
@@ -232,7 +261,7 @@ def _export_virtools_groups(
def _export_virtools_light(
writer: bmap.BMFileWriter,
progress: ProgressReport,
light_crets: tuple[_TLightPair, ...]
light_crets: tuple[_LightPair, ...]
) -> None:
# start saving
tr_text: str = bpy.app.translations.pgettext_rpt('Saving Lights', 'BBP_OT_export_virtools/execute')
@@ -274,13 +303,56 @@ def _export_virtools_light(
# leave progress and return
progress.leave_substeps()
def _export_virtools_camera(
writer: bmap.BMFileWriter,
progress: ProgressReport,
camera_crets: tuple[_CameraPair, ...]
) -> None:
# start saving
tr_text: str = bpy.app.translations.pgettext_rpt('Saving Cameras', 'BBP_OT_export_virtools/execute')
progress.enter_substeps(len(camera_crets), tr_text)
for obj3d, camera, vtcamera in camera_crets:
# set name
vtcamera.set_name(obj3d.name)
# setup 3d entity parts
# set world matrix
vtmat: UTIL_virtools_types.VxMatrix = UTIL_virtools_types.VxMatrix()
bldmat: mathutils.Matrix = UTIL_virtools_types.bldmatrix_restore_camera_obj(obj3d.matrix_world)
UTIL_virtools_types.vxmatrix_from_blender(vtmat, bldmat)
UTIL_virtools_types.vxmatrix_conv_co(vtmat)
vtcamera.set_world_matrix(vtmat)
# set visibility
vtcamera.set_visibility(not obj3d.hide_get())
# setup camera data
rawcamera: PROP_virtools_camera.RawVirtoolsCamera = PROP_virtools_camera.get_raw_virtools_camera(camera)
vtcamera.set_projection_type(rawcamera.mProjectionType)
vtcamera.set_orthographic_zoom(rawcamera.mOrthographicZoom)
vtcamera.set_front_plane(rawcamera.mFrontPlane)
vtcamera.set_back_plane(rawcamera.mBackPlane)
vtcamera.set_fov(rawcamera.mFov)
(w, h) = rawcamera.mAspectRatio
vtcamera.set_aspect_ratio(w, h)
# step
progress.step()
# leave progress and return
progress.leave_substeps()
def _export_virtools_3dobjects(
writer: bmap.BMFileWriter,
progress: ProgressReport,
obj3d_crets: tuple[_TObj3dPair, ...]
) -> tuple[_TMeshPair, ...]:
obj3d_crets: tuple[_Object3dPair, ...]
) -> tuple[_MeshPair, ...]:
# create virtools mesh
mesh_crets: list[_TMeshPair] = []
mesh_crets: list[_MeshPair] = []
mesh_cret_map: dict[bpy.types.Mesh, bmap.BMMesh] = {}
# start saving
tr_text: str = bpy.app.translations.pgettext_rpt('Saving 3dObjects', 'BBP_OT_export_virtools/execute')
@@ -324,10 +396,10 @@ def _export_virtools_3dobjects(
def _export_virtools_meshes(
writer: bmap.BMFileWriter,
progress: ProgressReport,
mesh_crets: tuple[_TMeshPair, ...]
) -> tuple[_TMaterialPair, ...]:
mesh_crets: tuple[_MeshPair, ...]
) -> tuple[_MaterialPair, ...]:
# create virtools mesh
material_crets: list[_TMaterialPair] = []
material_crets: list[_MaterialPair] = []
material_cret_map: dict[bpy.types.Material, bmap.BMMaterial] = {}
# start saving
tr_text: str = bpy.app.translations.pgettext_rpt('Saving Meshes', 'BBP_OT_export_virtools/execute')
@@ -439,10 +511,10 @@ def _export_virtools_meshes(
def _export_virtools_materials(
writer: bmap.BMFileWriter,
progress: ProgressReport,
material_crets: tuple[_TMaterialPair, ...]
) -> tuple[_TTexturePair, ...]:
material_crets: tuple[_MaterialPair, ...]
) -> tuple[_TexturePair, ...]:
# create virtools mesh
texture_crets: list[_TTexturePair] = []
texture_crets: list[_TexturePair] = []
texture_cret_map: dict[bpy.types.Image, bmap.BMTexture] = {}
# start saving
tr_text: str = bpy.app.translations.pgettext_rpt('Saving Materials', 'BBP_OT_export_virtools/execute')
@@ -508,7 +580,7 @@ def _export_virtools_textures(
writer: bmap.BMFileWriter,
progress: ProgressReport,
vt_temp_folder: str,
texture_crets: tuple[_TTexturePair, ...]
texture_crets: tuple[_TexturePair, ...]
) -> None:
# start saving
tr_text: str = bpy.app.translations.pgettext_rpt('Saving Textures', 'BBP_OT_export_virtools/execute')

View File

@@ -3,8 +3,8 @@ from bpy_extras.wm_utils.progress_report import ProgressReport
import tempfile, os, typing
from . import PROP_preferences, UTIL_ioport_shared, UTIL_naming_convention
from . import UTIL_virtools_types, UTIL_functions, UTIL_file_browser, UTIL_blender_mesh, UTIL_ballance_texture
from . import PROP_virtools_group, PROP_virtools_material, PROP_virtools_mesh, PROP_virtools_texture, PROP_virtools_light, PROP_ballance_map_info
from .PyBMap import bmap_wrapper as bmap
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
class BBP_OT_import_virtools(bpy.types.Operator, UTIL_file_browser.ImportVirtoolsFile, UTIL_ioport_shared.ImportParams, UTIL_ioport_shared.VirtoolsParams, UTIL_ioport_shared.BallanceParams):
"""Import Virtools File"""
@@ -80,8 +80,9 @@ def _import_virtools(file_name_: str, encodings_: tuple[str, ...], resolver: UTI
# import 3dobjects
obj3d_cret_map: dict[bmap.BM3dObject, bpy.types.Object] = _import_virtools_3dobjects(
reader, progress, resolver, mesh_cret_map)
# import light
# import light and camera
_import_virtools_lights(reader, progress, resolver)
_import_virtools_cameras(reader, progress, resolver)
# import groups
_import_virtools_groups(reader, progress, obj3d_cret_map)
@@ -423,6 +424,53 @@ def _import_virtools_lights(
# leave progress
progress.leave_substeps()
def _import_virtools_cameras(
reader: bmap.BMFileReader,
progress: ProgressReport,
resolver: UTIL_ioport_shared.ConflictResolver
) -> None:
# prepare progress
tr_text: str = bpy.app.translations.pgettext_rpt('Loading Cameras', 'BBP_OT_import_virtools/execute')
progress.enter_substeps(reader.get_target_camera_count(), tr_text)
# same creation notes like light
for vtcamera in reader.get_target_cameras():
# create camera data block and 3d object together
(camera_3dobj, camera, init_camera) = resolver.create_camera(
UTIL_virtools_types.virtools_name_regulator(vtcamera.get_name())
)
if init_camera:
# setup camera data block
rawcamera: PROP_virtools_camera.RawVirtoolsCamera = PROP_virtools_camera.RawVirtoolsCamera()
rawcamera.mProjectionType = vtcamera.get_projection_type()
rawcamera.mOrthographicZoom = vtcamera.get_orthographic_zoom()
rawcamera.mFrontPlane = vtcamera.get_front_plane()
rawcamera.mBackPlane = vtcamera.get_back_plane()
rawcamera.mFov = vtcamera.get_fov()
rawcamera.mAspectRatio = vtcamera.get_aspect_ratio()
PROP_virtools_camera.set_raw_virtools_camera(camera, rawcamera)
PROP_virtools_camera.apply_to_blender_camera(camera)
# setup camera associated 3d object
# add into scene
UTIL_functions.add_into_scene(camera_3dobj)
# set world matrix
vtmat: UTIL_virtools_types.VxMatrix = vtcamera.get_world_matrix()
UTIL_virtools_types.vxmatrix_conv_co(vtmat)
bldmat: mathutils.Matrix = UTIL_virtools_types.vxmatrix_to_blender(vtmat)
camera_3dobj.matrix_world = UTIL_virtools_types.bldmatrix_patch_camera_obj(bldmat)
# set visibility
camera_3dobj.hide_set(not vtcamera.get_visibility())
# leave progress
progress.leave_substeps()
def _import_virtools_groups(
reader: bmap.BMFileReader,
progress: ProgressReport,

View File

@@ -1,34 +1,15 @@
import bpy, mathutils
import typing, enum, math
from . import UTIL_functions
from . import UTIL_functions, PROP_virtools_camera
# TODO:
# 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
#region Enum Defines
class ResolutionKind(enum.IntEnum):
Normal = enum.auto()
Extended = 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)
WideScreen = enum.auto()
_g_ResolutionKindDesc: dict[ResolutionKind, tuple[str, str]] = {
ResolutionKind.Normal: ("Normal", "Aspect ratio: 4:3."),
ResolutionKind.Extended: ("Extended", "Aspect ratio: 16:9."),
ResolutionKind.Widescreen: ("Widescreen", "Aspect ratio: 7:3."),
ResolutionKind.Panoramic: ("Panoramic", "Aspect ratio: 20:7."),
ResolutionKind.Normal: ("Normal", "Vanilla Ballance Resolution"),
ResolutionKind.WideScreen: ("Wide Screen", "Ballance Resolution with Wide Screen Fix"),
}
_g_EnumHelper_ResolutionKind = UTIL_functions.EnumPropHelper(
ResolutionKind,
@@ -39,45 +20,6 @@ _g_EnumHelper_ResolutionKind = UTIL_functions.EnumPropHelper(
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):
Cursor = enum.auto()
ActiveObject = enum.auto()
@@ -191,6 +133,9 @@ class BBP_OT_game_camera(bpy.types.Operator):
) # type: ignore
rotation_kind: bpy.props.EnumProperty(
# YYC MAKR:
# This property is not shown on UI layout,
# but it should be translated because it is not PURE assistant property.
name = "Rotation Angle Kind",
description = "",
items = _g_EnumHelper_RotationKind.generate_items(),
@@ -198,9 +143,9 @@ class BBP_OT_game_camera(bpy.types.Operator):
translation_context = 'BBP_OT_game_camera/property'
) # type: ignore
preset_rotation_angle: bpy.props.EnumProperty(
# I18N: Property not showen should not have name and desc.
# name = "Preset Rotation Angle",
# description = "",
name = "Preset Rotation Angle",
description = "",
translation_context = 'BBP_OT_game_camera/property',
options = {'HIDDEN'},
items = _g_EnumHelper_RotationAngle.generate_items(),
default = _g_EnumHelper_RotationAngle.to_selection(RotationAngle.Deg0),
@@ -278,6 +223,21 @@ class BBP_OT_game_camera(bpy.types.Operator):
translation_context = 'BBP_OT_game_camera/property'
) # type: ignore
modify_resolution: bpy.props.BoolProperty(
name = 'Modify Resolution',
description = 'Whether modify the resolution of camera.',
default = False,
translation_context = 'BBP_OT_game_camera/property'
) # type: ignore
resolution_kind: bpy.props.EnumProperty(
name = "Resolution Kind",
description = "The type of preset resolution.",
items = _g_EnumHelper_ResolutionKind.generate_items(),
default = _g_EnumHelper_ResolutionKind.to_selection(ResolutionKind.Normal),
translation_context = 'BBP_OT_game_camera/property'
) # type: ignore
@classmethod
def poll(cls, context):
# find camera object
@@ -330,6 +290,12 @@ class BBP_OT_game_camera(bpy.types.Operator):
layout.label(text='Perspective', text_ctxt='BBP_OT_game_camera/draw')
layout.row().prop(self, 'perspective_kind', expand=True)
# Show resolution kind
layout.separator()
layout.prop(self, 'modify_resolution', text='Resolution', text_ctxt='BBP_OT_game_camera/draw')
if self.modify_resolution:
layout.row().prop(self, 'resolution_kind', expand=True)
def execute(self, context):
# fetch angle
angle: float
@@ -344,11 +310,12 @@ class BBP_OT_game_camera(bpy.types.Operator):
camera_obj = typing.cast(bpy.types.Object, _find_camera_obj())
target_kind = _g_EnumHelper_TargetKind.get_selection(self.target_kind)
perspective_kind = _g_EnumHelper_PerspectiveKind.get_selection(self.perspective_kind)
resolution_kind = _g_EnumHelper_ResolutionKind.get_selection(self.resolution_kind)
# setup its transform and properties
glob_trans = _fetch_glob_translation(camera_obj, target_kind)
_setup_camera_transform(camera_obj, angle, perspective_kind, glob_trans)
_setup_camera_properties(camera_obj)
_setup_camera_properties(camera_obj, resolution_kind)
# return
return {'FINISHED'}
@@ -448,24 +415,39 @@ def _setup_camera_transform(camobj: bpy.types.Object, angle: float, perspective:
glob_trans_mat = mathutils.Matrix.Translation(glob_trans)
camobj.matrix_world = glob_trans_mat @ trans_mat @ rot_mat
def _setup_camera_properties(camobj: bpy.types.Object) -> None:
# fetch camera
def _setup_camera_properties(camobj: bpy.types.Object, resolution_kind: ResolutionKind | None) -> None:
# fetch camera and its raw data
camera = typing.cast(bpy.types.Camera, camobj.data)
rawdata = PROP_virtools_camera.get_raw_virtools_camera(camera)
# set clipping
camera.clip_start = 4
camera.clip_end = 1200
# set FOV
camera.lens_unit = 'FOV'
camera.angle = math.radians(58)
#endregion
rawdata.mFrontPlane = 4
rawdata.mBackPlane = 1200
# set FOV and aspect ratio according to presented resolution kind
if resolution_kind is not None:
match resolution_kind:
case ResolutionKind.Normal:
rawdata.mFov = math.radians(58)
rawdata.mAspectRatio = (4, 3)
case ResolutionKind.WideScreen:
# prepare input arguments
aspect_ratio = (16, 9)
fov = math.radians(58)
# FOV correction reference:
# https://github.com/doyaGu/BallanceModLoaderPlus/blob/c4ab4386fd834af69a960c156fca97237b2fd4c5/src/RenderHook.cpp#L46
aspect = aspect_ratio[0] / aspect_ratio[1]
rawdata.mFov = math.atan2(math.tan(fov * 0.5) * 0.75 * aspect, 1.0) * 2.0
rawdata.mAspectRatio = aspect_ratio
# rewrite it back
PROP_virtools_camera.set_raw_virtools_camera(camera, rawdata)
# and apply it into camera and blender scene
PROP_virtools_camera.apply_to_blender_camera(camera)
PROP_virtools_camera.apply_to_blender_scene_resolution(camera)
def register() -> None:
bpy.utils.register_class(BBP_OT_game_resolution)
bpy.utils.register_class(BBP_OT_game_camera)
def unregister() -> None:
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 typing
from dataclasses import dataclass
from dataclasses import field as datafield
from . import UTIL_functions
@dataclass
class RawBallanceMapInfo():
cSectorCount: typing.ClassVar[int] = 1
mSectorCount: int
def __init__(self, **kwargs):
self.mSectorCount = kwargs.get("mSectorCount", RawBallanceMapInfo.cSectorCount)
mSectorCount: int = datafield(default=1)
def regulate(self):
self.mSectorCount = UTIL_functions.clamp_int(self.mSectorCount, 1, 999)

View File

@@ -1,21 +1,19 @@
import bpy
import os, typing
from dataclasses import dataclass
from dataclasses import field as datafield
from . import UTIL_naming_convention
@dataclass
class RawPreferences():
cBallanceTextureFolder: typing.ClassVar[str] = ""
cNoComponentCollection: typing.ClassVar[str] = ""
mBallanceTextureFolder: str
mNoComponentCollection: str
def __init__(self, **kwargs):
self.mBallanceTextureFolder = kwargs.get("mBallanceTextureFolder", "")
self.mNoComponentCollection = kwargs.get("mNoComponentCollection", "")
mBallanceTextureFolder: str = datafield(default="")
mNoComponentCollection: str = datafield(default="")
def has_valid_blc_tex_folder(self) -> bool:
return os.path.isdir(self.mBallanceTextureFolder)
DEFAULT_RAW_PREFERENCES = RawPreferences()
class BBPPreferences(bpy.types.AddonPreferences):
bl_idname = __package__
@@ -23,14 +21,14 @@ class BBPPreferences(bpy.types.AddonPreferences):
name = "Ballance Texture Folder",
description = "The path to folder which will be used by this plugin to get external Ballance texture.",
subtype = 'DIR_PATH',
default = RawPreferences.cBallanceTextureFolder,
default = DEFAULT_RAW_PREFERENCES.mBallanceTextureFolder,
translation_context = 'BBPPreferences/property'
) # type: ignore
no_component_collection: bpy.props.StringProperty(
name = "No Component Collection",
description = "When importing, it is the name of collection where objects store will not be saved as component. When exporting, all forced no component objects will be stored in this name represented collection",
default = RawPreferences.cNoComponentCollection,
default = DEFAULT_RAW_PREFERENCES.mNoComponentCollection,
translation_context = 'BBPPreferences/property'
) # type: ignore

View File

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

View File

@@ -0,0 +1,404 @@
import bpy
import typing, math, enum
from dataclasses import dataclass
from dataclasses import field as datafield
from . import UTIL_functions, UTIL_virtools_types
@dataclass
class RawVirtoolsCamera():
mProjectionType: UTIL_virtools_types.CK_CAMERA_PROJECTION = datafield(default=UTIL_virtools_types.CK_CAMERA_PROJECTION.CK_PERSPECTIVEPROJECTION)
mOrthographicZoom: float = datafield(default=1.0)
mFrontPlane: float = datafield(default=1.0)
mBackPlane: float = datafield(default=4000.0)
mFov: float = datafield(default=0.5)
mAspectRatio: tuple[int, int] = datafield(default_factory=lambda: (4, 3))
def regulate(self) -> None:
# everything should be positive
self.mOrthographicZoom = max(0.0, self.mOrthographicZoom)
self.mFrontPlane = max(0.0, self.mFrontPlane)
self.mBackPlane = max(0.0, self.mBackPlane)
self.mFov = max(0.0, self.mFov)
# aspect ratio should be positive and at least 1
(w, h) = self.mAspectRatio
w = max(1, w)
h = max(1, h)
self.mAspectRatio = (w, h)
DEFAULT_RAW_VIRTOOLS_CAMERA = RawVirtoolsCamera()
#region Blender Enum Prop Helper
_g_Helper_CK_CAMERA_PROJECTION = UTIL_virtools_types.EnumPropHelper(UTIL_virtools_types.CK_CAMERA_PROJECTION)
#endregion
class BBP_PG_virtools_camera(bpy.types.PropertyGroup):
projection_type: bpy.props.EnumProperty(
name = "Type",
description = "The type of this camera.",
items = _g_Helper_CK_CAMERA_PROJECTION.generate_items(),
default = _g_Helper_CK_CAMERA_PROJECTION.to_selection(DEFAULT_RAW_VIRTOOLS_CAMERA.mProjectionType),
translation_context = 'BBP_PG_virtools_camera/property'
) # type: ignore
orthographic_zoom: bpy.props.FloatProperty(
name = "Orthographic Zoom",
description = "Defines the orthographic zoom.",
min = 0.0,
soft_min = 0.0,
soft_max = 0.5,
step = 5,
default = DEFAULT_RAW_VIRTOOLS_CAMERA.mOrthographicZoom,
translation_context = 'BBP_PG_virtools_camera/property'
) # type: ignore
front_plane: bpy.props.FloatProperty(
name = "Front Plane",
description = "Defines the front plane.",
min = 0.0,
soft_min = 0.0,
soft_max = 5000.0,
step = 100,
default = DEFAULT_RAW_VIRTOOLS_CAMERA.mFrontPlane,
translation_context = 'BBP_PG_virtools_camera/property'
) # type: ignore
back_plane: bpy.props.FloatProperty(
name = "Back Plane",
description = "Defines the back plane.",
min = 0.0,
soft_min = 0.0,
soft_max = 5000.0,
step = 100,
default = DEFAULT_RAW_VIRTOOLS_CAMERA.mBackPlane,
translation_context = 'BBP_PG_virtools_camera/property'
) # type: ignore
fov: bpy.props.FloatProperty(
name = "Field of View",
description = "Defines the field of view.",
subtype = 'ANGLE',
min = 0.0,
max = math.radians(180.0),
step = 100,
precision = 100,
default = DEFAULT_RAW_VIRTOOLS_CAMERA.mFov,
translation_context = 'BBP_PG_virtools_camera/property'
) # type: ignore
aspect_ratio_w: bpy.props.IntProperty(
name = "Aspect Ratio Width",
description = "Defines the width of aspect ratio.",
min = 1,
soft_min = 1,
soft_max = 40,
step = 1,
default = DEFAULT_RAW_VIRTOOLS_CAMERA.mAspectRatio[0],
translation_context = 'BBP_PG_virtools_camera/property'
) # type: ignore
aspect_ratio_h: bpy.props.IntProperty(
name = "Aspect Ratio Height",
description = "Defines the height of aspect ratio.",
min = 1,
soft_min = 1,
soft_max = 40,
step = 1,
default = DEFAULT_RAW_VIRTOOLS_CAMERA.mAspectRatio[1],
translation_context = 'BBP_PG_virtools_camera/property'
) # type: ignore
#region Getter Setter and Applyer
def get_virtools_camera(cam: bpy.types.Camera) -> BBP_PG_virtools_camera:
return cam.virtools_camera
def get_raw_virtools_camera(cam: bpy.types.Camera) -> RawVirtoolsCamera:
props: BBP_PG_virtools_camera = get_virtools_camera(cam)
rawdata: RawVirtoolsCamera = RawVirtoolsCamera()
rawdata.mProjectionType = _g_Helper_CK_CAMERA_PROJECTION.get_selection(props.projection_type)
rawdata.mOrthographicZoom = props.orthographic_zoom
rawdata.mFrontPlane = props.front_plane
rawdata.mBackPlane = props.back_plane
rawdata.mFov = props.fov
rawdata.mAspectRatio = (props.aspect_ratio_w, props.aspect_ratio_h)
rawdata.regulate()
return rawdata
def set_raw_virtools_camera(cam: bpy.types.Camera, rawdata: RawVirtoolsCamera) -> None:
props: BBP_PG_virtools_camera = get_virtools_camera(cam)
props.projection_type = _g_Helper_CK_CAMERA_PROJECTION.to_selection(rawdata.mProjectionType)
props.orthographic_zoom = rawdata.mOrthographicZoom
props.front_plane = rawdata.mFrontPlane
props.back_plane = rawdata.mBackPlane
props.fov = rawdata.mFov
(props.aspect_ratio_w, props.aspect_ratio_h) = rawdata.mAspectRatio
def apply_to_blender_camera(cam: bpy.types.Camera) -> None:
# get raw data first
rawdata: RawVirtoolsCamera = get_raw_virtools_camera(cam)
# set camera type
match(rawdata.mProjectionType):
case UTIL_virtools_types.CK_CAMERA_PROJECTION.CK_PERSPECTIVEPROJECTION:
cam.type = 'PERSP'
case UTIL_virtools_types.CK_CAMERA_PROJECTION.CK_ORTHOGRAPHICPROJECTION:
cam.type = 'ORTHO'
# set orthographic zoom
cam.ortho_scale = rawdata.mOrthographicZoom
# front and back plane
cam.clip_start = rawdata.mFrontPlane
cam.clip_end = rawdata.mBackPlane
# fov
cam.lens_unit = 'FOV'
cam.angle = rawdata.mFov
def apply_to_blender_scene_resolution(cam: bpy.types.Camera) -> None:
# get raw data first
rawdata: RawVirtoolsCamera = get_raw_virtools_camera(cam)
# fetch width and height
(w, h) = rawdata.mAspectRatio
# compute a proper resolution from this aspect ratio
# calculate their lcm first
hw_lcm = math.lcm(w, h)
# get the first number which is greater than 1000 (1000 is a proper resolution size)
# and can be integrally divided by this lcm.
HW_MIN: int = 1000
min_edge = ((HW_MIN // hw_lcm) + 1) * hw_lcm
# calculate the final resolution
if w < h:
# width is shorter than height, set width as min edge
width = min_edge
height = width // w * h
else:
# opposite case
height = min_edge
width = height // h * w
# setup resolution
render_settings = bpy.context.scene.render
render_settings.resolution_x = width
render_settings.resolution_y = height
#endregion
#region Aspect Ratio Preset
class AspectRatioPresetType(enum.IntEnum):
Normal = enum.auto()
Extended = enum.auto()
Widescreen = enum.auto()
Panoramic = enum.auto()
def to_aspect_ratio(self) -> tuple[int, int]:
match self:
case AspectRatioPresetType.Normal: return (4, 3)
case AspectRatioPresetType.Extended: return (16, 9)
case AspectRatioPresetType.Widescreen: return (7, 3)
case AspectRatioPresetType.Panoramic: return (20, 7)
_g_AspectRatioPresetTypeDesc: dict[AspectRatioPresetType, tuple[str, str]] = {
AspectRatioPresetType.Normal: ("Normal", "Aspect ratio: 4:3."),
AspectRatioPresetType.Extended: ("Extended", "Aspect ratio: 16:9."),
AspectRatioPresetType.Widescreen: ("Widescreen", "Aspect ratio: 7:3."),
AspectRatioPresetType.Panoramic: ("Panoramic", "Aspect ratio: 20:7."),
}
_g_Helper_AspectRatioPresetType = UTIL_functions.EnumPropHelper(
AspectRatioPresetType,
lambda x: str(x.value),
lambda x: AspectRatioPresetType(int(x)),
lambda x: _g_AspectRatioPresetTypeDesc[x][0],
lambda x: _g_AspectRatioPresetTypeDesc[x][1],
lambda _: ""
)
def preset_virtools_camera_aspect_ratio(cam: bpy.types.Camera, preset_type: AspectRatioPresetType) -> None:
# get raw data from it
rawdata = get_raw_virtools_camera(cam)
# modify its aspect ratio
rawdata.mAspectRatio = preset_type.to_aspect_ratio()
# rewrite it.
set_raw_virtools_camera(cam, rawdata)
#endregion
#region Operators
class BBP_OT_apply_virtools_camera(bpy.types.Operator):
"""Apply Virtools Camera to Blender Camera except Resolution."""
bl_idname = "bbp.apply_virtools_camera"
bl_label = "Apply to Blender Camera"
bl_options = {'UNDO'}
bl_translation_context = 'BBP_OT_apply_virtools_camera'
@classmethod
def poll(cls, context):
return context.camera is not None
def execute(self, context):
cam: bpy.types.Camera = context.camera
apply_to_blender_camera(cam)
return {'FINISHED'}
class BBP_OT_apply_virtools_camera_resolution(bpy.types.Operator):
"""Apply Virtools Camera Resolution to Blender Scene."""
bl_idname = "bbp.apply_virtools_camera_resolution"
bl_label = "Apply to Blender Scene Resolution"
bl_options = {'UNDO'}
bl_translation_context = 'BBP_OT_apply_virtools_camera_resolution'
@classmethod
def poll(cls, context):
return context.camera is not None
def execute(self, context):
cam: bpy.types.Camera = context.camera
apply_to_blender_scene_resolution(cam)
return {'FINISHED'}
class BBP_OT_preset_virtools_camera_aspect_ratio(bpy.types.Operator):
"""Preset Virtools Camera Aspect Ratio with Virtools Presets."""
bl_idname = "bbp.preset_virtools_camera_aspect_ratio"
bl_label = "Preset Virtools Camera Aspect Ratio"
bl_options = {'UNDO'}
bl_translation_context = 'BBP_OT_preset_virtools_camera_aspect_ratio'
preset_type: bpy.props.EnumProperty(
name = "Preset",
description = "The preset which you want to apply.",
items = _g_Helper_AspectRatioPresetType.generate_items(),
translation_context = 'BBP_OT_preset_virtools_camera_aspect_ratio/property'
) # type: ignore
@classmethod
def poll(cls, context):
return context.camera is not None
def invoke(self, context, event):
wm = context.window_manager
return wm.invoke_props_dialog(self)
def draw(self, context):
self.layout.prop(self, "preset_type")
def execute(self, context):
# get essential value
cam: bpy.types.Camera = context.camera
expected_preset: AspectRatioPresetType = _g_Helper_AspectRatioPresetType.get_selection(self.preset_type)
# apply preset to material
preset_virtools_camera_aspect_ratio(cam, expected_preset)
return {'FINISHED'}
#endregion
class BBP_PT_virtools_camera(bpy.types.Panel):
"""Show Virtools Camera Properties"""
bl_label = "Virtools Camera"
bl_idname = "BBP_PT_virtools_camera"
bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW'
bl_context = "data" # idk why blender use `data` as the camera tab same as mesh.
bl_translation_context = 'BBP_PT_virtools_camera'
@classmethod
def poll(cls, context):
return context.camera is not None
def draw(self, context):
# get layout and target
layout = self.layout
cam: bpy.types.Camera = context.camera
props: BBP_PG_virtools_camera = get_virtools_camera(cam)
rawdata: RawVirtoolsCamera = get_raw_virtools_camera(cam)
# draw operator
row = layout.row()
row.operator(
BBP_OT_apply_virtools_camera.bl_idname, text='Apply', icon='NODETREE',
text_ctxt='BBP_PT_virtools_camera/draw')
row.operator(
BBP_OT_apply_virtools_camera_resolution.bl_idname, text='Apply Resolution', icon='SCENE',
text_ctxt='BBP_PT_virtools_camera/draw')
# draw data
layout.separator()
# show camera type first
layout.prop(props, 'projection_type')
# all camera has front and back plane
layout.label(text='Clipping', text_ctxt='BBP_PT_virtools_camera/draw')
sublayout = layout.column()
sublayout.use_property_split = True
sublayout.prop(props, 'front_plane')
sublayout.prop(props, 'back_plane')
# only perspective camera has fov setting
if rawdata.mProjectionType == UTIL_virtools_types.CK_CAMERA_PROJECTION.CK_PERSPECTIVEPROJECTION:
layout.separator()
layout.label(text='Perspective Parameters', text_ctxt='BBP_PT_virtools_camera/draw')
sublayout = layout.column()
sublayout.use_property_split = True
sublayout.prop(props, 'fov')
# only orthographic camera has orthographic zoom setting
if rawdata.mProjectionType == UTIL_virtools_types.CK_CAMERA_PROJECTION.CK_ORTHOGRAPHICPROJECTION:
layout.separator()
layout.label(text='Orthographic Parameters', text_ctxt='BBP_PT_virtools_camera/draw')
sublayout = layout.column()
sublayout.use_property_split = True
sublayout.prop(props, 'orthographic_zoom')
# aspect ratio
layout.separator()
row = layout.row()
row.label(text='Aspect Ratio', text_ctxt='BBP_PT_virtools_camera/draw')
row.operator(BBP_OT_preset_virtools_camera_aspect_ratio.bl_idname, text='', icon = "PRESET")
sublayout = layout.row()
sublayout.use_property_split = False
sublayout.prop(props, 'aspect_ratio_w', text = '', expand = True)
sublayout.prop(props, 'aspect_ratio_h', text = '', expand = True)
# Register
def register() -> None:
bpy.utils.register_class(BBP_PG_virtools_camera)
bpy.utils.register_class(BBP_OT_apply_virtools_camera)
bpy.utils.register_class(BBP_OT_apply_virtools_camera_resolution)
bpy.utils.register_class(BBP_OT_preset_virtools_camera_aspect_ratio)
bpy.utils.register_class(BBP_PT_virtools_camera)
# add into camera metadata
bpy.types.Camera.virtools_camera = bpy.props.PointerProperty(type = BBP_PG_virtools_camera)
def unregister() -> None:
# remove from metadata
del bpy.types.Camera.virtools_camera
bpy.utils.unregister_class(BBP_PT_virtools_camera)
bpy.utils.unregister_class(BBP_OT_preset_virtools_camera_aspect_ratio)
bpy.utils.unregister_class(BBP_OT_apply_virtools_camera_resolution)
bpy.utils.unregister_class(BBP_OT_apply_virtools_camera)
bpy.utils.unregister_class(BBP_PG_virtools_camera)

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,24 +1,17 @@
import bpy
import typing
from dataclasses import dataclass
from dataclasses import field as datafield
from . import UTIL_virtools_types, UTIL_functions
@dataclass
class RawVirtoolsTexture():
# Instance Member Declarations
mSaveOptions: UTIL_virtools_types.CK_TEXTURE_SAVEOPTIONS
mVideoFormat: UTIL_virtools_types.VX_PIXELFORMAT
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)
# Default Value Declarations
cDefaultSaveOptions: typing.ClassVar[UTIL_virtools_types.CK_TEXTURE_SAVEOPTIONS] = UTIL_virtools_types.CK_TEXTURE_SAVEOPTIONS.CKTEXTURE_RAWDATA
cDefaultVideoFormat: typing.ClassVar[UTIL_virtools_types.VX_PIXELFORMAT] = UTIL_virtools_types.VX_PIXELFORMAT._16_ARGB1555
DEFAULT_RAW_VIRTOOLS_TEXTURE = RawVirtoolsTexture()
def __init__(self, **kwargs):
# assign default value for each component
self.mSaveOptions = kwargs.get('mSaveOptions', RawVirtoolsTexture.cDefaultSaveOptions)
self.mVideoFormat = kwargs.get('mVideoFormat', RawVirtoolsTexture.cDefaultVideoFormat)
# blender enum prop helper defines
_g_Helper_CK_TEXTURE_SAVEOPTIONS = UTIL_virtools_types.EnumPropHelper(UTIL_virtools_types.CK_TEXTURE_SAVEOPTIONS)
_g_Helper_VX_PIXELFORMAT = UTIL_virtools_types.EnumPropHelper(UTIL_virtools_types.VX_PIXELFORMAT)
@@ -29,7 +22,7 @@ class BBP_PG_virtools_texture(bpy.types.PropertyGroup):
name = "Save Options",
description = "When saving a composition textures or sprites can be kept as reference to external files or converted to a given format and saved inside the composition file.",
items = _g_Helper_CK_TEXTURE_SAVEOPTIONS.generate_items(),
default = _g_Helper_CK_TEXTURE_SAVEOPTIONS.to_selection(RawVirtoolsTexture.cDefaultSaveOptions),
default = _g_Helper_CK_TEXTURE_SAVEOPTIONS.to_selection(DEFAULT_RAW_VIRTOOLS_TEXTURE.mSaveOptions),
translation_context = 'BBP_PG_virtools_texture/property'
) # type: ignore
@@ -37,7 +30,7 @@ class BBP_PG_virtools_texture(bpy.types.PropertyGroup):
name = "Video Format",
description = "The desired surface pixel format in video memory.",
items = _g_Helper_VX_PIXELFORMAT.generate_items(),
default = _g_Helper_VX_PIXELFORMAT.to_selection(RawVirtoolsTexture.cDefaultVideoFormat),
default = _g_Helper_VX_PIXELFORMAT.to_selection(DEFAULT_RAW_VIRTOOLS_TEXTURE.mVideoFormat),
translation_context = 'BBP_PG_virtools_texture/property'
) # type: ignore

View File

@@ -105,22 +105,37 @@ def _nest_custom_split_normal(nml_array: array.array) -> typing.Iterator[UTIL_vi
class TemporaryMesh():
"""
Create a temporary mesh for convenient exporting.
When exporting mesh, we need triangulate it first.
We create a temporary mesh to hold the triangulated mesh result.
When exporting mesh, we need evaluate it first, then triangulate it.
We create a temporary mesh to hold the evaluated and triangulated mesh result.
So that original object will not be affected and keep its original geometry.
Please note passed bpy.types.Object must be Mesh Object.
Please note passed bpy.types.Object must be an object which can be converted into mesh.
You can use this class provided static method to check it.
"""
__cAllowedObjectType: typing.ClassVar[set[str]] = {
'MESH', 'CURVE', 'SURFACE', 'FONT', 'META'
}
@staticmethod
def has_geometry(obj: bpy.types.Object):
"""
Check whether given Blender object has geometry.
If it has, it can safely utilize this class for visiting mesh.
"""
return obj.type in TemporaryMesh.__cAllowedObjectType
__mBindingObject: bpy.types.Object
__mEvaluatedObject: bpy.types.Object
__mTempMesh: bpy.types.Mesh
def __init__(self, binding_obj: bpy.types.Object):
depsgraph = bpy.context.evaluated_depsgraph_get()
self.__mBindingObject = binding_obj
self.__mTempMesh = None
self.__mEvaluatedObject = self.__mBindingObject.evaluated_get(depsgraph)
self.__mTempMesh = self.__mEvaluatedObject.to_mesh()
if self.__mBindingObject.data is None:
if self.__mTempMesh is None:
raise UTIL_functions.BBPException('try getting mesh from an object without mesh.')
self.__mTempMesh = self.__mBindingObject.to_mesh()
def __enter__(self):
return self
@@ -129,6 +144,7 @@ class TemporaryMesh():
self.dispose()
def is_valid(self) -> bool:
if self.__mBindingObject is None: return False
if self.__mBindingObject is None: return False
if self.__mTempMesh is None: return False
return True
@@ -136,7 +152,8 @@ class TemporaryMesh():
def dispose(self) -> None:
if self.is_valid():
self.__mTempMesh = None
self.__mBindingObject.to_mesh_clear()
self.__mEvaluatedObject.to_mesh_clear()
self.__mEvaluatedObject = None
self.__mBindingObject = None
def get_temp_mesh(self) -> bpy.types.Mesh:

View File

@@ -37,6 +37,7 @@ class ConflictResolver():
__mObjectStrategy: ConflictStrategy
__mLightStrategy: ConflictStrategy
__mCameraStrategy: ConflictStrategy
__mMeshStrategy: ConflictStrategy
__mMaterialStrategy: ConflictStrategy
__mTextureStrategy: ConflictStrategy
@@ -44,11 +45,13 @@ class ConflictResolver():
def __init__(self,
obj_strategy: ConflictStrategy,
light_strategy: ConflictStrategy,
camera_strategy: ConflictStrategy,
mesh_strategy: ConflictStrategy,
mtl_strategy: ConflictStrategy,
tex_strategy: ConflictStrategy):
self.__mObjectStrategy = obj_strategy
self.__mLightStrategy = light_strategy
self.__mCameraStrategy = camera_strategy
self.__mMeshStrategy = mesh_strategy
self.__mMaterialStrategy = mtl_strategy
self.__mTextureStrategy = tex_strategy
@@ -88,6 +91,22 @@ class ConflictResolver():
new_obj: bpy.types.Object = bpy.data.objects.new(name, new_light)
return (new_obj, new_light, True)
def create_camera(self, name: str) -> tuple[bpy.types.Object, bpy.types.Camera, bool]:
"""
Create camera data block and associated 3d object.
Same execution pattern with light creation.
"""
if self.__mCameraStrategy == ConflictStrategy.Current:
old_obj: bpy.types.Object | None = bpy.data.objects.get(name, None)
if old_obj is not None and old_obj.type == 'CAMERA':
return (old_obj, typing.cast(bpy.types.Camera, old_obj.data), False)
# create new object.
# if object or camera name is conflict, rename it directly without considering conflict strategy
new_camera: bpy.types.Camera = bpy.data.cameras.new(name)
new_obj: bpy.types.Object = bpy.data.objects.new(name, new_camera)
return (new_obj, new_camera, True)
def create_mesh(self, name: str) -> tuple[bpy.types.Mesh, bool]:
if self.__mMeshStrategy == ConflictStrategy.Current:
old: bpy.types.Mesh | None = bpy.data.meshes.get(name, None)
@@ -153,6 +172,14 @@ class ImportParams():
translation_context = 'BBP/UTIL_ioport_shared.ImportParams/property'
) # type: ignore
camera_conflict_strategy: bpy.props.EnumProperty(
name = "Camera Name Conflict",
items = _g_EnumHelper_ConflictStrategy.generate_items(),
description = "Define how to process camera name conflict",
default = _g_EnumHelper_ConflictStrategy.to_selection(ConflictStrategy.Rename),
translation_context = 'BBP/UTIL_ioport_shared.ImportParams/property'
) # type: ignore
object_conflict_strategy: bpy.props.EnumProperty(
name = "Object Name Conflict",
items = _g_EnumHelper_ConflictStrategy.generate_items(),
@@ -173,11 +200,13 @@ class ImportParams():
grid = body.grid_flow(row_major=False, columns=2)
grid.label(text='Object', icon='CUBE', text_ctxt='BBP/UTIL_ioport_shared.ImportParams/draw')
grid.label(text='Light', icon='LIGHT', text_ctxt='BBP/UTIL_ioport_shared.ImportParams/draw')
grid.label(text='Camera', icon='CAMERA_DATA', text_ctxt='BBP/UTIL_ioport_shared.ImportParams/draw')
grid.label(text='Mesh', icon='MESH_DATA', text_ctxt='BBP/UTIL_ioport_shared.ImportParams/draw')
grid.label(text='Material', icon='MATERIAL', text_ctxt='BBP/UTIL_ioport_shared.ImportParams/draw')
grid.label(text='Texture', icon='TEXTURE', text_ctxt='BBP/UTIL_ioport_shared.ImportParams/draw')
grid.prop(self, 'object_conflict_strategy', text='')
grid.prop(self, 'light_conflict_strategy', text='')
grid.prop(self, 'camera_conflict_strategy', text='')
grid.prop(self, 'mesh_conflict_strategy', text='')
grid.prop(self, 'material_conflict_strategy', text='')
grid.prop(self, 'texture_conflict_strategy', text='')
@@ -194,6 +223,9 @@ class ImportParams():
def general_get_light_conflict_strategy(self) -> ConflictStrategy:
return _g_EnumHelper_ConflictStrategy.get_selection(self.light_conflict_strategy)
def general_get_camera_conflict_strategy(self) -> ConflictStrategy:
return _g_EnumHelper_ConflictStrategy.get_selection(self.camera_conflict_strategy)
def general_get_object_conflict_strategy(self) -> ConflictStrategy:
return _g_EnumHelper_ConflictStrategy.get_selection(self.object_conflict_strategy)
@@ -201,6 +233,7 @@ class ImportParams():
return ConflictResolver(
self.general_get_object_conflict_strategy(),
self.general_get_light_conflict_strategy(),
self.general_get_camera_conflict_strategy(),
self.general_get_mesh_conflict_strategy(),
self.general_get_material_conflict_strategy(),
self.general_get_texture_conflict_strategy()

View File

@@ -1,9 +1,10 @@
import bpy, mathutils
import typing, math
from dataclasses import dataclass
from . import UTIL_functions
# extract all declarations in PyBMap
from .PyBMap.virtools_types import *
# extract all declarations in pybmap
from .pybmap.virtools_types import *
# and add some patches for them
# mainly patch them with functions exchanging data with blender
# and the convertion between differnet coordinate system.
@@ -78,7 +79,7 @@ def vxmatrix_to_blender(self: VxMatrix) -> mathutils.Matrix:
## Hints about Light Matrix
# There is a slight difference between Virtools and Blender.
# In blender, the default direction of all directional light (spot and sun) are Down (-Z).
# 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
# from Virtools default light direction by rotating it around X-axis with -90 degree
@@ -108,130 +109,159 @@ def bldmatrix_restore_light_obj(data: mathutils.Matrix) -> mathutils.Matrix:
# so we simply right multiple it.
return data @ mathutils.Matrix.Rotation(math.radians(-90), 4, 'X')
## Hints about Camera Matrix
# Just like light, camera is also different between Virtools and Blender.
# In Blender, the default camera orientation is looking at -Z and +Y up.
# Oppositely, Virtools camera is looking at +Z and +Y up.
#
# These direction is based on their own coordinate system respectively.
# Accidently this difference is same like light.
# So we can simply copy light strategy in there.
def bldmatrix_patch_camera_obj(data: mathutils.Matrix) -> mathutils.Matrix:
"""
Add patch for camera world matrix to correct its direction.
This function is usually used when importing camera.
"""
# same operation like light matrix patch
return data @ mathutils.Matrix.Rotation(math.radians(90), 4, 'X')
def bldmatrix_restore_camera_obj(data: mathutils.Matrix) -> mathutils.Matrix:
"""
The reverse operation of bldmatrix_patch_camera_mat().
This function is usually used when exporting camera.
"""
# same operation like light matrix patch
return data @ mathutils.Matrix.Rotation(math.radians(-90), 4, 'X')
#endregion
#region Blender EnumProperty Creation
class EnumAnnotation():
mDisplayName: str
mDescription: str
def __init__(self, display_name: str, description: str):
self.mDisplayName = display_name
self.mDescription = description
@dataclass(frozen=True)
class EnumDocstring():
display_name: str
"""The name of this enum entry."""
description: str
"""The description of this enum entry."""
_g_Annotation: dict[type, dict[int, EnumAnnotation]] = {
_g_Docstring: dict[type, dict[int, EnumDocstring]] = {
VXTEXTURE_BLENDMODE: {
VXTEXTURE_BLENDMODE.VXTEXTUREBLEND_DECAL.value: EnumAnnotation("Decal", "Texture replace any material information "),
VXTEXTURE_BLENDMODE.VXTEXTUREBLEND_MODULATE.value: EnumAnnotation("Modulate", "Texture and material are combine. Alpha information of the texture replace material alpha component. "),
VXTEXTURE_BLENDMODE.VXTEXTUREBLEND_DECALALPHA.value: EnumAnnotation("Decal Alpha", "Alpha information in the texture specify how material and texture are combined. Alpha information of the texture replace material alpha component. "),
VXTEXTURE_BLENDMODE.VXTEXTUREBLEND_MODULATEALPHA.value: EnumAnnotation("Modulate Alpha", "Alpha information in the texture specify how material and texture are combined "),
VXTEXTURE_BLENDMODE.VXTEXTUREBLEND_DECALMASK.value: EnumAnnotation("Decal Mask", ""),
VXTEXTURE_BLENDMODE.VXTEXTUREBLEND_MODULATEMASK.value: EnumAnnotation("Modulate Mask", ""),
VXTEXTURE_BLENDMODE.VXTEXTUREBLEND_COPY.value: EnumAnnotation("Copy", "Equivalent to DECAL "),
VXTEXTURE_BLENDMODE.VXTEXTUREBLEND_ADD.value: EnumAnnotation("Add", ""),
VXTEXTURE_BLENDMODE.VXTEXTUREBLEND_DOTPRODUCT3.value: EnumAnnotation("Dot Product 3", "Perform a Dot Product 3 between texture (normal map) and a referential vector given in VXRENDERSTATE_TEXTUREFACTOR. "),
VXTEXTURE_BLENDMODE.VXTEXTUREBLEND_MAX.value: EnumAnnotation("Max", ""),
VXTEXTURE_BLENDMODE.VXTEXTUREBLEND_DECAL.value: EnumDocstring("Decal", "Texture replace any material information "),
VXTEXTURE_BLENDMODE.VXTEXTUREBLEND_MODULATE.value: EnumDocstring("Modulate", "Texture and material are combine. Alpha information of the texture replace material alpha component. "),
VXTEXTURE_BLENDMODE.VXTEXTUREBLEND_DECALALPHA.value: EnumDocstring("Decal Alpha", "Alpha information in the texture specify how material and texture are combined. Alpha information of the texture replace material alpha component. "),
VXTEXTURE_BLENDMODE.VXTEXTUREBLEND_MODULATEALPHA.value: EnumDocstring("Modulate Alpha", "Alpha information in the texture specify how material and texture are combined "),
VXTEXTURE_BLENDMODE.VXTEXTUREBLEND_DECALMASK.value: EnumDocstring("Decal Mask", ""),
VXTEXTURE_BLENDMODE.VXTEXTUREBLEND_MODULATEMASK.value: EnumDocstring("Modulate Mask", ""),
VXTEXTURE_BLENDMODE.VXTEXTUREBLEND_COPY.value: EnumDocstring("Copy", "Equivalent to DECAL "),
VXTEXTURE_BLENDMODE.VXTEXTUREBLEND_ADD.value: EnumDocstring("Add", ""),
VXTEXTURE_BLENDMODE.VXTEXTUREBLEND_DOTPRODUCT3.value: EnumDocstring("Dot Product 3", "Perform a Dot Product 3 between texture (normal map) and a referential vector given in VXRENDERSTATE_TEXTUREFACTOR. "),
VXTEXTURE_BLENDMODE.VXTEXTUREBLEND_MAX.value: EnumDocstring("Max", ""),
},
VXTEXTURE_FILTERMODE: {
VXTEXTURE_FILTERMODE.VXTEXTUREFILTER_NEAREST.value: EnumAnnotation("Nearest", "No Filter "),
VXTEXTURE_FILTERMODE.VXTEXTUREFILTER_LINEAR.value: EnumAnnotation("Linear", "Bilinear Interpolation "),
VXTEXTURE_FILTERMODE.VXTEXTUREFILTER_MIPNEAREST.value: EnumAnnotation("Mip Nearest", "Mip mapping "),
VXTEXTURE_FILTERMODE.VXTEXTUREFILTER_MIPLINEAR.value: EnumAnnotation("Mip Linear", "Mip Mapping with Bilinear interpolation "),
VXTEXTURE_FILTERMODE.VXTEXTUREFILTER_LINEARMIPNEAREST.value: EnumAnnotation("Linear Mip Nearest", "Mip Mapping with Bilinear interpolation between mipmap levels. "),
VXTEXTURE_FILTERMODE.VXTEXTUREFILTER_LINEARMIPLINEAR.value: EnumAnnotation("Linear Mip Linear", "Trilinear Filtering "),
VXTEXTURE_FILTERMODE.VXTEXTUREFILTER_ANISOTROPIC.value: EnumAnnotation("Anisotropic", "Anisotropic filtering "),
VXTEXTURE_FILTERMODE.VXTEXTUREFILTER_NEAREST.value: EnumDocstring("Nearest", "No Filter "),
VXTEXTURE_FILTERMODE.VXTEXTUREFILTER_LINEAR.value: EnumDocstring("Linear", "Bilinear Interpolation "),
VXTEXTURE_FILTERMODE.VXTEXTUREFILTER_MIPNEAREST.value: EnumDocstring("Mip Nearest", "Mip mapping "),
VXTEXTURE_FILTERMODE.VXTEXTUREFILTER_MIPLINEAR.value: EnumDocstring("Mip Linear", "Mip Mapping with Bilinear interpolation "),
VXTEXTURE_FILTERMODE.VXTEXTUREFILTER_LINEARMIPNEAREST.value: EnumDocstring("Linear Mip Nearest", "Mip Mapping with Bilinear interpolation between mipmap levels. "),
VXTEXTURE_FILTERMODE.VXTEXTUREFILTER_LINEARMIPLINEAR.value: EnumDocstring("Linear Mip Linear", "Trilinear Filtering "),
VXTEXTURE_FILTERMODE.VXTEXTUREFILTER_ANISOTROPIC.value: EnumDocstring("Anisotropic", "Anisotropic filtering "),
},
VXBLEND_MODE: {
VXBLEND_MODE.VXBLEND_ZERO.value: EnumAnnotation("Zero", "Blend factor is (0, 0, 0, 0). "),
VXBLEND_MODE.VXBLEND_ONE.value: EnumAnnotation("One", "Blend factor is (1, 1, 1, 1). "),
VXBLEND_MODE.VXBLEND_SRCCOLOR.value: EnumAnnotation("Src Color", "Blend factor is (Rs, Gs, Bs, As). "),
VXBLEND_MODE.VXBLEND_INVSRCCOLOR.value: EnumAnnotation("Inv Src Color", "Blend factor is (1-Rs, 1-Gs, 1-Bs, 1-As). "),
VXBLEND_MODE.VXBLEND_SRCALPHA.value: EnumAnnotation("Src Alpha", "Blend factor is (As, As, As, As). "),
VXBLEND_MODE.VXBLEND_INVSRCALPHA.value: EnumAnnotation("Inv Src Alpha", "Blend factor is (1-As, 1-As, 1-As, 1-As). "),
VXBLEND_MODE.VXBLEND_DESTALPHA.value: EnumAnnotation("Dest Alpha", "Blend factor is (Ad, Ad, Ad, Ad). "),
VXBLEND_MODE.VXBLEND_INVDESTALPHA.value: EnumAnnotation("Inv Dest Alpha", "Blend factor is (1-Ad, 1-Ad, 1-Ad, 1-Ad). "),
VXBLEND_MODE.VXBLEND_DESTCOLOR.value: EnumAnnotation("Dest Color", "Blend factor is (Rd, Gd, Bd, Ad). "),
VXBLEND_MODE.VXBLEND_INVDESTCOLOR.value: EnumAnnotation("Inv Dest Color", "Blend factor is (1-Rd, 1-Gd, 1-Bd, 1-Ad). "),
VXBLEND_MODE.VXBLEND_SRCALPHASAT.value: EnumAnnotation("Src Alpha Sat", "Blend factor is (f, f, f, 1); f = min(As, 1-Ad). "),
#VXBLEND_MODE.VXBLEND_BOTHSRCALPHA.value: EnumAnnotation("Both Src Alpha", "Source blend factor is (As, As, As, As) and destination blend factor is (1-As, 1-As, 1-As, 1-As) "),
#VXBLEND_MODE.VXBLEND_BOTHINVSRCALPHA.value: EnumAnnotation("Both Inv Src Alpha", "Source blend factor is (1-As, 1-As, 1-As, 1-As) and destination blend factor is (As, As, As, As) "),
VXBLEND_MODE.VXBLEND_ZERO.value: EnumDocstring("Zero", "Blend factor is (0, 0, 0, 0). "),
VXBLEND_MODE.VXBLEND_ONE.value: EnumDocstring("One", "Blend factor is (1, 1, 1, 1). "),
VXBLEND_MODE.VXBLEND_SRCCOLOR.value: EnumDocstring("Src Color", "Blend factor is (Rs, Gs, Bs, As). "),
VXBLEND_MODE.VXBLEND_INVSRCCOLOR.value: EnumDocstring("Inv Src Color", "Blend factor is (1-Rs, 1-Gs, 1-Bs, 1-As). "),
VXBLEND_MODE.VXBLEND_SRCALPHA.value: EnumDocstring("Src Alpha", "Blend factor is (As, As, As, As). "),
VXBLEND_MODE.VXBLEND_INVSRCALPHA.value: EnumDocstring("Inv Src Alpha", "Blend factor is (1-As, 1-As, 1-As, 1-As). "),
VXBLEND_MODE.VXBLEND_DESTALPHA.value: EnumDocstring("Dest Alpha", "Blend factor is (Ad, Ad, Ad, Ad). "),
VXBLEND_MODE.VXBLEND_INVDESTALPHA.value: EnumDocstring("Inv Dest Alpha", "Blend factor is (1-Ad, 1-Ad, 1-Ad, 1-Ad). "),
VXBLEND_MODE.VXBLEND_DESTCOLOR.value: EnumDocstring("Dest Color", "Blend factor is (Rd, Gd, Bd, Ad). "),
VXBLEND_MODE.VXBLEND_INVDESTCOLOR.value: EnumDocstring("Inv Dest Color", "Blend factor is (1-Rd, 1-Gd, 1-Bd, 1-Ad). "),
VXBLEND_MODE.VXBLEND_SRCALPHASAT.value: EnumDocstring("Src Alpha Sat", "Blend factor is (f, f, f, 1); f = min(As, 1-Ad). "),
#VXBLEND_MODE.VXBLEND_BOTHSRCALPHA.value: EnumDocstring("Both Src Alpha", "Source blend factor is (As, As, As, As) and destination blend factor is (1-As, 1-As, 1-As, 1-As) "),
#VXBLEND_MODE.VXBLEND_BOTHINVSRCALPHA.value: EnumDocstring("Both Inv Src Alpha", "Source blend factor is (1-As, 1-As, 1-As, 1-As) and destination blend factor is (As, As, As, As) "),
},
VXTEXTURE_ADDRESSMODE: {
VXTEXTURE_ADDRESSMODE.VXTEXTURE_ADDRESSWRAP.value: EnumAnnotation("Wrap", "Default mesh wrap mode is used (see CKMesh::SetWrapMode) "),
VXTEXTURE_ADDRESSMODE.VXTEXTURE_ADDRESSMIRROR.value: EnumAnnotation("Mirror", "Texture coordinates outside the range [0..1] are flipped evenly. "),
VXTEXTURE_ADDRESSMODE.VXTEXTURE_ADDRESSCLAMP.value: EnumAnnotation("Clamp", "Texture coordinates greater than 1.0 are set to 1.0, and values less than 0.0 are set to 0.0. "),
VXTEXTURE_ADDRESSMODE.VXTEXTURE_ADDRESSBORDER.value: EnumAnnotation("Border", "When texture coordinates are greater than 1.0 or less than 0.0 texture is set to a color defined in CKMaterial::SetTextureBorderColor. "),
VXTEXTURE_ADDRESSMODE.VXTEXTURE_ADDRESSMIRRORONCE.value: EnumAnnotation("Mirror Once", " "),
VXTEXTURE_ADDRESSMODE.VXTEXTURE_ADDRESSWRAP.value: EnumDocstring("Wrap", "Default mesh wrap mode is used (see CKMesh::SetWrapMode) "),
VXTEXTURE_ADDRESSMODE.VXTEXTURE_ADDRESSMIRROR.value: EnumDocstring("Mirror", "Texture coordinates outside the range [0..1] are flipped evenly. "),
VXTEXTURE_ADDRESSMODE.VXTEXTURE_ADDRESSCLAMP.value: EnumDocstring("Clamp", "Texture coordinates greater than 1.0 are set to 1.0, and values less than 0.0 are set to 0.0. "),
VXTEXTURE_ADDRESSMODE.VXTEXTURE_ADDRESSBORDER.value: EnumDocstring("Border", "When texture coordinates are greater than 1.0 or less than 0.0 texture is set to a color defined in CKMaterial::SetTextureBorderColor. "),
VXTEXTURE_ADDRESSMODE.VXTEXTURE_ADDRESSMIRRORONCE.value: EnumDocstring("Mirror Once", " "),
},
VXFILL_MODE: {
VXFILL_MODE.VXFILL_POINT.value: EnumAnnotation("Point", "Vertices rendering "),
VXFILL_MODE.VXFILL_WIREFRAME.value: EnumAnnotation("Wireframe", "Edges rendering "),
VXFILL_MODE.VXFILL_SOLID.value: EnumAnnotation("Solid", "Face rendering "),
VXFILL_MODE.VXFILL_POINT.value: EnumDocstring("Point", "Vertices rendering "),
VXFILL_MODE.VXFILL_WIREFRAME.value: EnumDocstring("Wireframe", "Edges rendering "),
VXFILL_MODE.VXFILL_SOLID.value: EnumDocstring("Solid", "Face rendering "),
},
VXSHADE_MODE: {
VXSHADE_MODE.VXSHADE_FLAT.value: EnumAnnotation("Flat", "Flat Shading "),
VXSHADE_MODE.VXSHADE_GOURAUD.value: EnumAnnotation("Gouraud", "Gouraud Shading "),
VXSHADE_MODE.VXSHADE_PHONG.value: EnumAnnotation("Phong", "Phong Shading (Not yet supported by most implementation) "),
VXSHADE_MODE.VXSHADE_FLAT.value: EnumDocstring("Flat", "Flat Shading "),
VXSHADE_MODE.VXSHADE_GOURAUD.value: EnumDocstring("Gouraud", "Gouraud Shading "),
VXSHADE_MODE.VXSHADE_PHONG.value: EnumDocstring("Phong", "Phong Shading (Not yet supported by most implementation) "),
},
VXCMPFUNC: {
VXCMPFUNC.VXCMP_NEVER.value: EnumAnnotation("Never", "Always fail the test. "),
VXCMPFUNC.VXCMP_LESS.value: EnumAnnotation("Less", "Accept if value if less than current value. "),
VXCMPFUNC.VXCMP_EQUAL.value: EnumAnnotation("Equal", "Accept if value if equal than current value. "),
VXCMPFUNC.VXCMP_LESSEQUAL.value: EnumAnnotation("Less Equal", "Accept if value if less or equal than current value. "),
VXCMPFUNC.VXCMP_GREATER.value: EnumAnnotation("Greater", "Accept if value if greater than current value. "),
VXCMPFUNC.VXCMP_NOTEQUAL.value: EnumAnnotation("Not Equal", "Accept if value if different than current value. "),
VXCMPFUNC.VXCMP_GREATEREQUAL.value: EnumAnnotation("Greater Equal", "Accept if value if greater or equal current value. "),
VXCMPFUNC.VXCMP_ALWAYS.value: EnumAnnotation("Always", "Always accept the test. "),
VXCMPFUNC.VXCMP_NEVER.value: EnumDocstring("Never", "Always fail the test. "),
VXCMPFUNC.VXCMP_LESS.value: EnumDocstring("Less", "Accept if value if less than current value. "),
VXCMPFUNC.VXCMP_EQUAL.value: EnumDocstring("Equal", "Accept if value if equal than current value. "),
VXCMPFUNC.VXCMP_LESSEQUAL.value: EnumDocstring("Less Equal", "Accept if value if less or equal than current value. "),
VXCMPFUNC.VXCMP_GREATER.value: EnumDocstring("Greater", "Accept if value if greater than current value. "),
VXCMPFUNC.VXCMP_NOTEQUAL.value: EnumDocstring("Not Equal", "Accept if value if different than current value. "),
VXCMPFUNC.VXCMP_GREATEREQUAL.value: EnumDocstring("Greater Equal", "Accept if value if greater or equal current value. "),
VXCMPFUNC.VXCMP_ALWAYS.value: EnumDocstring("Always", "Always accept the test. "),
},
CK_TEXTURE_SAVEOPTIONS: {
CK_TEXTURE_SAVEOPTIONS.CKTEXTURE_RAWDATA.value: EnumAnnotation("Raw Data", "Save raw data inside file. The bitmap is saved in a raw 32 bit per pixel format. "),
CK_TEXTURE_SAVEOPTIONS.CKTEXTURE_EXTERNAL.value: EnumAnnotation("External", "Store only the file name for the texture. The bitmap file must be present in the bitmap paths when loading the composition. "),
CK_TEXTURE_SAVEOPTIONS.CKTEXTURE_IMAGEFORMAT.value: EnumAnnotation("Image Format", "Save using format specified. The bitmap data will be converted to the specified format by the correspondant bitmap plugin and saved inside file. "),
CK_TEXTURE_SAVEOPTIONS.CKTEXTURE_USEGLOBAL.value: EnumAnnotation("Use Global", "Use Global settings, that is the settings given with CKContext::SetGlobalImagesSaveOptions. (Not valid when using CKContext::SetImagesSaveOptions). "),
CK_TEXTURE_SAVEOPTIONS.CKTEXTURE_INCLUDEORIGINALFILE.value: EnumAnnotation("Include Original File", "Insert original image file inside CMO file. The bitmap file that was used originally for the texture or sprite will be append to the composition file and extracted when the file is loaded. "),
CK_TEXTURE_SAVEOPTIONS.CKTEXTURE_RAWDATA.value: EnumDocstring("Raw Data", "Save raw data inside file. The bitmap is saved in a raw 32 bit per pixel format. "),
CK_TEXTURE_SAVEOPTIONS.CKTEXTURE_EXTERNAL.value: EnumDocstring("External", "Store only the file name for the texture. The bitmap file must be present in the bitmap paths when loading the composition. "),
CK_TEXTURE_SAVEOPTIONS.CKTEXTURE_IMAGEFORMAT.value: EnumDocstring("Image Format", "Save using format specified. The bitmap data will be converted to the specified format by the correspondant bitmap plugin and saved inside file. "),
CK_TEXTURE_SAVEOPTIONS.CKTEXTURE_USEGLOBAL.value: EnumDocstring("Use Global", "Use Global settings, that is the settings given with CKContext::SetGlobalImagesSaveOptions. (Not valid when using CKContext::SetImagesSaveOptions). "),
CK_TEXTURE_SAVEOPTIONS.CKTEXTURE_INCLUDEORIGINALFILE.value: EnumDocstring("Include Original File", "Insert original image file inside CMO file. The bitmap file that was used originally for the texture or sprite will be append to the composition file and extracted when the file is loaded. "),
},
VX_PIXELFORMAT: {
VX_PIXELFORMAT._32_ARGB8888.value: EnumAnnotation("32 Bits ARGB8888", "32-bit ARGB pixel format with alpha "),
VX_PIXELFORMAT._32_RGB888.value: EnumAnnotation("32 Bits RGB888", "32-bit RGB pixel format without alpha "),
VX_PIXELFORMAT._24_RGB888.value: EnumAnnotation("24 Bits RGB888", "24-bit RGB pixel format "),
VX_PIXELFORMAT._16_RGB565.value: EnumAnnotation("16 Bits RGB565", "16-bit RGB pixel format "),
VX_PIXELFORMAT._16_RGB555.value: EnumAnnotation("16 Bits RGB555", "16-bit RGB pixel format (5 bits per color) "),
VX_PIXELFORMAT._16_ARGB1555.value: EnumAnnotation("16 Bits ARGB1555", "16-bit ARGB pixel format (5 bits per color + 1 bit for alpha) "),
VX_PIXELFORMAT._16_ARGB4444.value: EnumAnnotation("16 Bits ARGB4444", "16-bit ARGB pixel format (4 bits per color) "),
VX_PIXELFORMAT._8_RGB332.value: EnumAnnotation("8 Bits RGB332", "8-bit RGB pixel format "),
VX_PIXELFORMAT._8_ARGB2222.value: EnumAnnotation("8 Bits ARGB2222", "8-bit ARGB pixel format "),
VX_PIXELFORMAT._32_ABGR8888.value: EnumAnnotation("32 Bits ABGR8888", "32-bit ABGR pixel format "),
VX_PIXELFORMAT._32_RGBA8888.value: EnumAnnotation("32 Bits RGBA8888", "32-bit RGBA pixel format "),
VX_PIXELFORMAT._32_BGRA8888.value: EnumAnnotation("32 Bits BGRA8888", "32-bit BGRA pixel format "),
VX_PIXELFORMAT._32_BGR888.value: EnumAnnotation("32 Bits BGR888", "32-bit BGR pixel format "),
VX_PIXELFORMAT._24_BGR888.value: EnumAnnotation("24 Bits BGR888", "24-bit BGR pixel format "),
VX_PIXELFORMAT._16_BGR565.value: EnumAnnotation("16 Bits BGR565", "16-bit BGR pixel format "),
VX_PIXELFORMAT._16_BGR555.value: EnumAnnotation("16 Bits BGR555", "16-bit BGR pixel format (5 bits per color) "),
VX_PIXELFORMAT._16_ABGR1555.value: EnumAnnotation("16 Bits ABGR1555", "16-bit ABGR pixel format (5 bits per color + 1 bit for alpha) "),
VX_PIXELFORMAT._16_ABGR4444.value: EnumAnnotation("16 Bits ABGR4444", "16-bit ABGR pixel format (4 bits per color) "),
VX_PIXELFORMAT._DXT1.value: EnumAnnotation("DXT1", "S3/DirectX Texture Compression 1 "),
VX_PIXELFORMAT._DXT2.value: EnumAnnotation("DXT2", "S3/DirectX Texture Compression 2 "),
VX_PIXELFORMAT._DXT3.value: EnumAnnotation("DXT3", "S3/DirectX Texture Compression 3 "),
VX_PIXELFORMAT._DXT4.value: EnumAnnotation("DXT4", "S3/DirectX Texture Compression 4 "),
VX_PIXELFORMAT._DXT5.value: EnumAnnotation("DXT5", "S3/DirectX Texture Compression 5 "),
VX_PIXELFORMAT._16_V8U8.value: EnumAnnotation("16 Bits V8U8", "16-bit Bump Map format format (8 bits per color) "),
VX_PIXELFORMAT._32_V16U16.value: EnumAnnotation("32 Bits V16U16", "32-bit Bump Map format format (16 bits per color) "),
VX_PIXELFORMAT._16_L6V5U5.value: EnumAnnotation("16 Bits L6V5U5", "16-bit Bump Map format format with luminance "),
VX_PIXELFORMAT._32_X8L8V8U8.value: EnumAnnotation("32 Bits X8L8V8U8", "32-bit Bump Map format format with luminance "),
VX_PIXELFORMAT._8_ABGR8888_CLUT.value: EnumAnnotation("8 Bits ABGR8888 CLUT", "8 bits indexed CLUT (ABGR) "),
VX_PIXELFORMAT._8_ARGB8888_CLUT.value: EnumAnnotation("8 Bits ARGB8888 CLUT", "8 bits indexed CLUT (ARGB) "),
VX_PIXELFORMAT._4_ABGR8888_CLUT.value: EnumAnnotation("4 Bits ABGR8888 CLUT", "4 bits indexed CLUT (ABGR) "),
VX_PIXELFORMAT._4_ARGB8888_CLUT.value: EnumAnnotation("4 Bits ARGB8888 CLUT", "4 bits indexed CLUT (ARGB) "),
VX_PIXELFORMAT._32_ARGB8888.value: EnumDocstring("32 Bits ARGB8888", "32-bit ARGB pixel format with alpha "),
VX_PIXELFORMAT._32_RGB888.value: EnumDocstring("32 Bits RGB888", "32-bit RGB pixel format without alpha "),
VX_PIXELFORMAT._24_RGB888.value: EnumDocstring("24 Bits RGB888", "24-bit RGB pixel format "),
VX_PIXELFORMAT._16_RGB565.value: EnumDocstring("16 Bits RGB565", "16-bit RGB pixel format "),
VX_PIXELFORMAT._16_RGB555.value: EnumDocstring("16 Bits RGB555", "16-bit RGB pixel format (5 bits per color) "),
VX_PIXELFORMAT._16_ARGB1555.value: EnumDocstring("16 Bits ARGB1555", "16-bit ARGB pixel format (5 bits per color + 1 bit for alpha) "),
VX_PIXELFORMAT._16_ARGB4444.value: EnumDocstring("16 Bits ARGB4444", "16-bit ARGB pixel format (4 bits per color) "),
VX_PIXELFORMAT._8_RGB332.value: EnumDocstring("8 Bits RGB332", "8-bit RGB pixel format "),
VX_PIXELFORMAT._8_ARGB2222.value: EnumDocstring("8 Bits ARGB2222", "8-bit ARGB pixel format "),
VX_PIXELFORMAT._32_ABGR8888.value: EnumDocstring("32 Bits ABGR8888", "32-bit ABGR pixel format "),
VX_PIXELFORMAT._32_RGBA8888.value: EnumDocstring("32 Bits RGBA8888", "32-bit RGBA pixel format "),
VX_PIXELFORMAT._32_BGRA8888.value: EnumDocstring("32 Bits BGRA8888", "32-bit BGRA pixel format "),
VX_PIXELFORMAT._32_BGR888.value: EnumDocstring("32 Bits BGR888", "32-bit BGR pixel format "),
VX_PIXELFORMAT._24_BGR888.value: EnumDocstring("24 Bits BGR888", "24-bit BGR pixel format "),
VX_PIXELFORMAT._16_BGR565.value: EnumDocstring("16 Bits BGR565", "16-bit BGR pixel format "),
VX_PIXELFORMAT._16_BGR555.value: EnumDocstring("16 Bits BGR555", "16-bit BGR pixel format (5 bits per color) "),
VX_PIXELFORMAT._16_ABGR1555.value: EnumDocstring("16 Bits ABGR1555", "16-bit ABGR pixel format (5 bits per color + 1 bit for alpha) "),
VX_PIXELFORMAT._16_ABGR4444.value: EnumDocstring("16 Bits ABGR4444", "16-bit ABGR pixel format (4 bits per color) "),
VX_PIXELFORMAT._DXT1.value: EnumDocstring("DXT1", "S3/DirectX Texture Compression 1 "),
VX_PIXELFORMAT._DXT2.value: EnumDocstring("DXT2", "S3/DirectX Texture Compression 2 "),
VX_PIXELFORMAT._DXT3.value: EnumDocstring("DXT3", "S3/DirectX Texture Compression 3 "),
VX_PIXELFORMAT._DXT4.value: EnumDocstring("DXT4", "S3/DirectX Texture Compression 4 "),
VX_PIXELFORMAT._DXT5.value: EnumDocstring("DXT5", "S3/DirectX Texture Compression 5 "),
VX_PIXELFORMAT._16_V8U8.value: EnumDocstring("16 Bits V8U8", "16-bit Bump Map format format (8 bits per color) "),
VX_PIXELFORMAT._32_V16U16.value: EnumDocstring("32 Bits V16U16", "32-bit Bump Map format format (16 bits per color) "),
VX_PIXELFORMAT._16_L6V5U5.value: EnumDocstring("16 Bits L6V5U5", "16-bit Bump Map format format with luminance "),
VX_PIXELFORMAT._32_X8L8V8U8.value: EnumDocstring("32 Bits X8L8V8U8", "32-bit Bump Map format format with luminance "),
VX_PIXELFORMAT._8_ABGR8888_CLUT.value: EnumDocstring("8 Bits ABGR8888 CLUT", "8 bits indexed CLUT (ABGR) "),
VX_PIXELFORMAT._8_ARGB8888_CLUT.value: EnumDocstring("8 Bits ARGB8888 CLUT", "8 bits indexed CLUT (ARGB) "),
VX_PIXELFORMAT._4_ABGR8888_CLUT.value: EnumDocstring("4 Bits ABGR8888 CLUT", "4 bits indexed CLUT (ABGR) "),
VX_PIXELFORMAT._4_ARGB8888_CLUT.value: EnumDocstring("4 Bits ARGB8888 CLUT", "4 bits indexed CLUT (ARGB) "),
},
VXLIGHT_TYPE: {
VXLIGHT_TYPE.VX_LIGHTPOINT.value: EnumAnnotation("Point", "The Light is a point of light "),
VXLIGHT_TYPE.VX_LIGHTSPOT.value: EnumAnnotation("Spot", "The light is a spotlight "),
VXLIGHT_TYPE.VX_LIGHTDIREC.value: EnumAnnotation("Directional", "The light is directional light : Lights comes from an infinite point so only direction of light can be given "),
#VXLIGHT_TYPE.VX_LIGHTPARA.value: EnumAnnotation("Lightpara", "Obsolete, do not use "),
VXLIGHT_TYPE.VX_LIGHTPOINT.value: EnumDocstring("Point", "The Light is a point of light "),
VXLIGHT_TYPE.VX_LIGHTSPOT.value: EnumDocstring("Spot", "The light is a spotlight "),
VXLIGHT_TYPE.VX_LIGHTDIREC.value: EnumDocstring("Directional", "The light is directional light : Lights comes from an infinite point so only direction of light can be given "),
#VXLIGHT_TYPE.VX_LIGHTPARA.value: EnumDocstring("Lightpara", "Obsolete, do not use "),
},
VXMESH_LITMODE: {
VXMESH_LITMODE.VX_PRELITMESH.value: EnumAnnotation("Prelit", "Lighting use color information store with vertices "),
VXMESH_LITMODE.VX_LITMESH.value: EnumAnnotation("Lit", "Lighting is done by renderer using normals and face material information. "),
VXMESH_LITMODE.VX_PRELITMESH.value: EnumDocstring("Prelit", "Lighting use color information store with vertices "),
VXMESH_LITMODE.VX_LITMESH.value: EnumDocstring("Lit", "Lighting is done by renderer using normals and face material information. "),
},
CK_CAMERA_PROJECTION: {
CK_CAMERA_PROJECTION.CK_PERSPECTIVEPROJECTION.value: EnumDocstring("Perspective Projection", ""),
CK_CAMERA_PROJECTION.CK_ORTHOGRAPHICPROJECTION.value: EnumDocstring("Orthographic Projection", ""),
}
}
@@ -241,13 +271,13 @@ class EnumPropHelper(UTIL_functions.EnumPropHelper[_TRawEnum]):
"""
Virtools type specified Blender EnumProp helper.
"""
__mAnnotationDict: dict[int, EnumAnnotation]
__mDocstringDict: dict[int, EnumDocstring]
__mEnumTy: type[_TRawEnum]
def __init__(self, ty: type[_TRawEnum]):
# set enum type and annotation ref first
# set enum type and docstring ref first
self.__mEnumTy = ty
self.__mAnnotationDict = _g_Annotation[ty]
self.__mDocstringDict = _g_Docstring[ty]
# YYC MARK:
# It seems that Pylance has bad generic analyse ability in there.
@@ -259,8 +289,8 @@ class EnumPropHelper(UTIL_functions.EnumPropHelper[_TRawEnum]):
self.__mEnumTy, # enum.Enum its self is iterable
lambda x: str(x.value), # convert enum.Enum's value to string
lambda x: self.__mEnumTy(int(x)), # use stored enum type and int() to get enum member
lambda x: self.__mAnnotationDict[x.value].mDisplayName,
lambda x: self.__mAnnotationDict[x.value].mDescription,
lambda x: self.__mDocstringDict[x.value].display_name,
lambda x: self.__mDocstringDict[x.value].description,
lambda _: ''
)
@@ -273,11 +303,11 @@ def virtools_name_regulator(name: str | None) -> str:
else: return bpy.app.translations.pgettext_data('annoymous', 'BME/UTIL_virtools_types.virtools_name_regulator()')
# YYC MARK:
# There are default encodings for PyBMap. We support Western European and Simplified Chinese in default.
# There are default encodings for pybmap. We support Western European and Simplified Chinese in default.
# Since LibCmo 0.2, the encoding name of LibCmo become universal encoding which is platfoorm independent.
# So no need set it according to different platform.
# Use universal encoding name (like Python).
g_PyBMapDefaultEncodings: tuple[str, ...] = (
g_PybmapDefaultEncodings: tuple[str, ...] = (
'cp1252',
'gbk'
)

View File

@@ -17,7 +17,7 @@ UTIL_icons_manager.register()
# then load other modules
from . import UTIL_translation
from . import PROP_preferences, PROP_ptrprop_resolver, PROP_virtools_material, PROP_virtools_texture, PROP_virtools_mesh, PROP_virtools_light, PROP_virtools_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 OP_IMPORT_bmfile, OP_EXPORT_bmfile, OP_IMPORT_virtools, OP_EXPORT_virtools
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.separator()
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.separator()
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_mesh.register()
PROP_virtools_light.register()
PROP_virtools_camera.register()
PROP_virtools_group.register()
PROP_ballance_element.register()
PROP_bme_material.register()
@@ -397,6 +397,7 @@ def unregister() -> None:
PROP_bme_material.unregister()
PROP_ballance_element.unregister()
PROP_virtools_group.unregister()
PROP_virtools_camera.unregister()
PROP_virtools_light.unregister()
PROP_virtools_mesh.unregister()
PROP_virtools_texture.unregister()

View File

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

View File

@@ -1,18 +1,25 @@
# Add Floor
!!! info "Not latest version"
This translated page is not the latest version because the modification of source page. Please see source page of the latest version.
!!! info "BME is extensible"
BME's floor adder is extensible, each item in the menu is actually described by a set of JSON data. You can read the [Technical Information](./tech-infos.md) section to learn how we write this JSON, and you can even expand the types of floors that BME can create to suit your needs.
## Start Generating
### From Menu
In the 3D view, click `Add - Floors` to expand the Add Floors menu. The menu is shown below.
![](../imgs/bme-adder.png)
Click on the menu to see all supported floor types in the submenu that pops up. Their names and icons hint at the style and shape of the floor it is intended to create.
Click on the menu to see all supported floor types by categories in the submenu that pops up. Their names and icons hint at the style and shape of the floor it is intended to create.
!!! info "BME is extensible"
BME's floor adder is extensible, each item in the menu is actually described by a set of JSON data. You can read the [Technical Information](./tech-infos.md) section to learn how we write this JSON, and you can even expand the types of floors that BME can create to suit your needs.
### From Sidebar
Additionally, you can press `N` on keyboard to expand the sidebar of 3D view and find `Ballance` tab inside it. You also can find these adder by clicking it and expanding `Floor` panel as presented in following image:
![](../imgs/bme-adder-sidebar.png)
Comparing with menu, the advantage of this solution is that the sidebar is persistent in UI layout. It is more convenient and effective when adding multiple floors by reducing the time of repetitive opening add menu. Also, there is Rail and Component adder panels under this tab. This will not be introduced again in following chapters.
## Configure Floor
@@ -26,11 +33,14 @@ Then it also asks us to provide the height of the platform, which defaults to 5,
Finally, it tells us which sides of the floor we need to configure to display. Note that Top and Bottom are the top and bottom surfaces along the height direction (Z axis), while Front, Back, Left, and Right are the front, back, left, and right surfaces when looking down with your head on the -X axis and your eyes on the -Z axis. You may notice that there is a perspective cube in the center of these six face buttons, and in fact the positions of these six face options correspond to the positions of the six faces of this perspective cube.
## Extra Transform
At the bottom of the BME configuration dialog, you can always find an area called Extra Transform. In this area, there are two options for configuration: extra translation and rotation. These fields are primarily intended for visual edit.
Before explaining the actual functions of these fields, it is important to understand that the floor created by BME is always generated based on the current position of the 3D cursor. In other words, wherever the 3D cursor is located, the newly created surface will be positioned accordingly. This design primarily considers visual edit, allowing users to first move the 3D cursor to the desired location for adding a floor, and then add the corresponding BME structure. This enables users to preview results in real-time while adjusting parameters. Sometimes, the position of your 3D cursor may not be entirely accurate, or the final generated structure may require certain rotation to meet your expectations. In such cases, Extra Transform fields can be utilized to apply extra translation and rotation to the generated structure in relation to the 3D cursor, ensuring it is accurately positioned. In this way, the correct results can be previewed when adjusting the parameters.
## Tips
Each floor type has a different number of configuration entries, so for different floor types, you will need to follow the configuration hint text to understand what the corresponding configuration does. Some floor types may have a large number of configuration entries, while others may have no configuration entries at all.
The default values for the floor type configuration are set to the values that were most commonly used when the floor was created. The values are reset to the defaults each time the floor type is switched or recreated.

View File

@@ -1,25 +1,104 @@
# Compile and Distribute Plugin
!!! info "Not latest version"
This translated page is not the latest version because the modification of source page. Please see source page of the latest version.
This page will guide you in compiling the plugin as well as distributing it.
## Compiling LibCmo with BMap
BBP's Virtools file native import/export functionality relies on BMap and its Python binding PyBMap. In order to distribute the plugin, we need to first compile BMap and its predecessor LibCmo, and before doing so, you need to check the version of BMap you need. Because BBP doesn't always use the latest version of BMap, e.g. if you're compiling an older version of BBP, it's obviously not possible to rely on the latest version of BMap. BMap is constantly being upgraded, and the functionality it provides is constantly changing, and different versions of BMap are incompatible. BBP usually states the version of BMap it uses at the time of release, but if BBP doesn't point it out, you may need to look for the most recent version of BMap that compiles with the version of BBP at the time of its release.
BBP's Virtools file native import/export functionality relies on BMap and its Python binding pybmap. In order to distribute the plugin, we need to first compile BMap and its predecessor LibCmo, and before doing so, you need to check the version of BMap you need. Because BBP doesn't always use the latest version of BMap, e.g. if you're compiling an older version of BBP, it's obviously not possible to rely on the latest version of BMap. BMap is constantly being upgraded, and the functionality it provides is constantly changing, and different versions of BMap are incompatible. BBP usually states the version of BMap it uses at the time of release, but if BBP doesn't point it out, you may need to look for the most recent version of BMap that compiles with the version of BBP at the time of its release.
After specifying the version, you need to visit [LibCmo GitHub repository](https://github.com/yyc12345/libcmo21). Then clone the project and use the Git command to go to the corresponding version (or just download the source code of the corresponding version). Then follow LibCmo's compilation manual to compile to get BMap. on Windows, you'll usually get the files `BMap.dll` and `BMap.pdb`. On Linux, it will be `BMap.so`.
After specifying the version, you need to visit [LibCmo GitHub repository](https://github.com/yyc12345/libcmo21). Then clone the project and use the Git command to go to the corresponding version (or just download the source code of the corresponding version). Then follow LibCmo's compilation manual to compile to get BMap. on Windows, you'll usually get the file `BMap.dll`. On Linux, it will be `BMap.so`.
Then we need to configure PyBMap, which comes with LibCmo. Please follow the manual of PyBMap to combine the compiled binary BMap library with PyBMap. That is to complete the PyBMap configuration.
Then we need to configure pybmap, which comes with LibCmo. Please follow the manual of pybmap to combine the compiled binary BMap library with pybmap. That is to complete the pybmap configuration.
Then we need to copy the configured PyBMap to our project under `bbp_ng/PyBMap` to complete this step.
Then we need to copy the configured pybmap to our project under `bbp_ng/pybmap` to complete this step.
## Generate Thumbnails and Compress JSON
## Generate Resources
BBP comes with a built-in set of custom icons, as well as the JSON files needed by its component BME to describe the structure. By batch generating thumbnails and compressing JSON operations, the size of these parts can be reduced, making them suitable for loading in Blender and easier to distribute.
BBP needs some resoures to run, and these resources need to be processed before using them.
Go to the `bbp_ng/tools` folder and run `python3 build_icons.py` which will batch generate thumbnails (this requires the PIL library, please install it via pip in advance). It actually generates thumbnails from the original images in the `bbp_ng/raw_icons` directory and stores them in the `bbp_ng/icons` folder. Running `python3 build_jsons.py` will compress the JSON, which actually reads, compresses, and writes the raw JSON files from the `bbp_ng/raw_jsons` directory into the `bbp_ng/jsons` folder.
For generating these resrouces, we firstly need to navigate to `scripts` directory, and execute `uv sync` to restore the environment for scripts (Astral UV required).
### Generate Thumbnails
BBP comes with a built-in set of custom icons, however these icons are stored as their original size in repository for keeping convenient editing and high quality. We need to reduce the size of these icons to make them are easy to be loaded on Blender and easy for distribution by generating thumbnails for them.
Execute `uv run build_icons.py` to generate thumbnails. It actually generates thumbnails from the original images in the `assets/icons` directory and stores them in the `bbp_ng/icons` folder.
### Generate JSONs
The BME component in BBP relies on a series of JSON files to describe the prototype. These profiles are stored in the library in JSON5 format, making them easy for writers to read and write. We converte these JSON5 files to JSON files and compressing their size makes them easier to load in Blender, as well as to facilitate plugin distribution, by batchly generating them.
If you are the plugin developer or writer of these prototypes, you need to do an additional thing before generating these JSON files: verify these JSON files. The BBP plugin will assume that these JSON files are correct when loading them. If you put a JSON file with errors (e.g. missing some fields or has some typos, etc.), it will cause Blender to throw an error when creating prototype. Therefore, it is necessary to verify these JSON files. Execute `uv run validate_jsons.py` to verify all prototype files. If there are no errors, it means that everything is okey. It is important to note that the validator is not perfect, it can only verify the data as much as possible to ensure that some common erros (e.g. typo in field name) will not occur. It can not make 100% sure about that there is no error inside these files.
For compilers, all you need to do is that execute `uv run build_jsons.py` to generate JSON files. It actually reads, compresses, and writes the original JSON5 files in `assets/jsons` directory to the `bbp_ng/jsons` folder in JSON format.
### Generate Element Meshes
BBP has built-in mesh data for all Ballance element placeholders. Execute `uv run build_jsons.py` to deploy these meshes, which simply copies the mesh files under `assets/jsons` folder to `bbp_ng/jsons` folder.
## Translation
The BBP plugin supports multilingual functionality, so we need to extract and update the content to be translated before the official release, and then proceed to the next step after translating all the content.
Blender's multilingual support for plugins is not satisfactory, and BBP's design is relatively special, so BBP adopts a different way to manage translations than the official recommended plugin translation management method of Blender: that is, use PO files to manage translations instead of the officially recommended Python script format.
!!! info "Do NOT submit translations in Python format"
As mentioned above, BBP uses PO files to manage translations, rather than the Python source format recommended by Blender. However, this does not prevent Blender's multilingual plugins from writing translations in the Python source format into the plugin's source code. Submitting duplicate translations not only increases the repository size but also complicates management. Therefore, BBP requires you to delete the Python-format translations before submission.
The specific operational method is to open the `bbp_ng/UTIL_translation.py` file before submission and change the value of the translation tuple variable `translations_tuple` to an empty tuple (i.e., `translations_tuple = ()`).
### Extract Translation Template
Before translating, it is important to first recognize that the text requiring translation for BBP consists of two parts. One part is the BBP plugin itself, whose text to be translated can be extracted by Blender's built-in multilingual plugin. The other part is the JSON file in the BME component that describes the structure, where the names of various showcase fields need to be translated. However, this part cannot be handled by Blender's multilingual plugin, as it is dynamically loaded. Fortunately, we have written an extractor that can extract the relevant text to be translated from the BME's JSON file. To do this, execute `uv run extract_jsons.py` in the folder where the previous script was run, and the script will extract the text to be translated and write it into the `i18n/bme.pot` file. Therefore, the next task is simply to extract the translation for the plugin portion.
First, you need to enable Blender's built-in multilingual plugin, "Manage UI translations". To enable it, you may also need to download the source code and translation repository corresponding to your version of Blender. For specific instructions, please refer to the [official Blender Documentation](https://developer.blender.org/docs/handbook/translating/translator_guide/). Once you have enabled the plugin and configured the appropriate related paths in the preferences, you can find the "I18n Update Translation" panel under the "Render" panel. You can then proceed to extract the translations by following these steps:
1. First, ensure that all Blender processes are closed; otherwise, the plugin will remain in a loading state, and modifications to the translated tuple variables will not take effect.
1. Change the value of the translation tuple variable `translations_tuple` in the plugin to an empty tuple (refer to the earlier mentioned submission notes). Setting the translation tuple to empty can reset the translation status of the plugin, ensuring that subsequent text extraction operations are not affected by any existing translations.
1. Open Blender, go to the "I18n Update Translation" panel, click "Deselect All" to uncheck all languages, and then only check the boxes next to the following languages (as BBP currently supports a limited number of languages):
* Simplified Chinese (简体中文)
1. Click the "Refresh I18n Data" button at the bottom of the section, then in the pop-up window, select "Ballance Blender Plugin". After a short wait, the plugin will complete the extraction of the characters to be translated. At this point, the plugin merely extracts the translation fields into the source code of the plugin in a format recommended by Blender, using Python source code.
1. In order to obtain the desired editable POT file, you need to click the "Export PO" button. In the pop-up window, select the "Ballance Blender Plugin", and you can choose any folder for saving the location (for example, the desktop, as it will generate many files, including our desired POT file). Uncheck the "Update Existing" option on the right and ensure that "Export POT" is checked, then proceed to save. After the export is complete, you will find a translation template file named `blender.pot` and numerous `.po` files named after language identifiers in the folder you selected.
1. You need to copy `blender.pot` to the `i18n` folder and rename it to `bbp_ng.pot`. At this point, we have extracted all the content that needs to be translated.
### Merge Translation Template
There are currently two POT files in the `i18n` folder, which represent two sets of extracted text awaiting translation. We need to merge them. Execute `xgettext -o blender.pot bbp_ng.pot bme.pot` in the `i18n` folder to perform the merge. The merged `i18n/blender.pot` will serve as the translation template.
### Create New Language Translation
If BBP needs to support more languages in the future, you will need to create the corresponding PO translation files for the new languages from the POT files. You can create them through one of the following methods.
* By using software such as Poedit to open the POT file, select function like create new translation from it, and then save to create.
* Create a new language PO translation file using commands such as `msginit -i blender.pot -o zh_HANS.po -l zh_CN.utf8`.
There are various ways to create, but the only point to note is that you need to set the file name (if the file name is incorrect, Blender will refuse to accept it) and the area name (which will be used when using `msginit`, with the purpose of ensuring UTF8 format encoding) as shown in the table below.
|Language|File Name|Area Name|
|:---|:---|:---|
|Simplified Chinese (简体中文)|`zh_HANS.po`|`zh_CN.utf8`|
### Update Language Translation
Creating new language translations is not common; a more common practice is to update existing language translation files based on translation templates. You can update them through one of the following methods.
* Open the PO file using software such as Poedit, and then select to update from the POT file.
* Update using commands such as `msgmerge -U zh-HANS.po blender.pot --backup=none`.
### Start Translating
After updating the PO translation files for all languages, you may choose your preferred method for translation, such as using Poedit or editing directly.
The BBP requires the use of the KDE community's translation standards to standardize the translations of plugins. For example, you can find the KDE community's translation standards for Simplified Chinese on the [KDE China](https://kde-china.org/tutorial.html) website.
### Write Translation Back
The translation in PO format cannot be recognized by Blender. Therefore, after the translation is completed, you also need to utilize Blender's multilingual plugin to convert the PO file back into a translation in Python source code format that Blender can recognize. Due to issues with the design of Blender's multilingual plugin, we cannot directly use the "Import PO" function to convert the PO file back into Python source code format. You need to follow the steps below sequentially in order to import the PO translation into the plugin:
1. First, ensure that all Blender processes are closed; otherwise, the plugin will remain in a loading state, and modifications to the translated tuple variables will not take effect.
1. Change the value of the translation tuple variable `translations_tuple` in the plugin to an empty tuple (refer to the earlier notes regarding submissions). The purpose of this step is to ensure that the entire plugin lacks translation entries, so that when using the "Import PO" feature, Blender's multilingual plugin will consider all fields stored in the PO file as needing translation, thereby preventing situations where only a portion of the translations is imported (as the translations for the BME portion were merged later).
1. Open Blender, navigate to the "I18n Update Translation" panel, and following the procedure used when extracting the translation template, select only the languages that need to be translated from the language list.
1. Click the "Import PO" button in the bottom row, then select the "Ballance Blender Plugin" in the pop-up window, and choose the `i18n` folder for import. In this way, we have completed the process of importing the PO file into a Python source code format recognizable by Blender.
## Packaging
@@ -29,13 +108,11 @@ Assuming that the final output file is `redist/bbp_ng.zip`. If you are in the ro
Blender will package the plugin according to the instructions in `blender_manifest.toml` with the following files excluded:
* `bbp_ng/raw_icons`: raw thumbnail folder.
* `bbp_ng/raw_jsons`: raw JSON folder.
* `bbp_ng/tools`: tools for compiling.
* `bbp_ng/.style.yapf`: code style description file.
* `bbp_ng/.gitignore`: gitignore
* `bbp_ng/icons/.gitkeep`: folder placeholder
* `bbp_ng/jsons/.gitkeep`: folder placeholder
* `__pycache__/`Python cache.
* `.style.yapf`code style description file.
* `.gitignore`gitignore
* `.gitkeep`folder placeholder
* `.md`documentation
## Generating Help Documentation

View File

@@ -19,7 +19,9 @@ The BBP plugin currently has 2 settings to configure.
Please fill in the `Texture` directory of Ballance, from which the plugin will use the external texture files (i.e. the ones Ballance originally came with). Click on the folder button on the right to browse the folders and select it.
This is crucial for BBP to work properly, and only if it is filled out correctly will BBP not make errors during operation.
This option is almost mandatory. If you don't fill it, various core functions like Virtools file import export, BME creation, rail creation, etc. will not be available (button is in grey color).
This is crucial for BBP to work properly, and only if it is filled correctly will BBP not make errors during operation.
### No Component Collection

View File

@@ -1,18 +1,17 @@
# Import and Export Virtools Document
!!! info "Not latest version"
This translated page is not the latest version because the modification of source page. Please see source page of the latest version.
!!! warning "This is experimental content"
Native importing and exporting of Virtools documents is experimental content for the BBP plugin, it may have many problems, see the [Report Issue](./report-bugs.md) section to learn more. When problems are encountered, please report them. the authors of the BBP plugin are not responsible for any consequences resulting from problems with the BBP plugin.
## Import Virtools File
Virtools files can be imported by clicking `File - Import - Virtools File`. Importing supports CMO, VMO and NMO files. Clicking on it will bring up the file opening window and show the import settings in the sidebar. First of all, you need to select the Virtools file to be imported, and then configure the import settings in the sidebar. After configuring the import settings, you can click Import to start the import, and wait for the status bar at the bottom of Blender to indicate that the import is complete.
Virtools files can be imported by clicking `File - Import - Virtools File`. Importing supports CMO, VMO and NMO files. Clicking on it will bring up the file opening window and show the import settings in the sidebar as shown below. First of all, you need to select the Virtools file to be imported, and then configure the import settings in the sidebar. After configuring the import settings, you can click Import to start the import, and wait for the status bar at the bottom of Blender to indicate that the import is complete.
![](../imgs/import-virtools.png)
### Conflict Options
The Conflict Options section indicates what to do when the importer encounters duplicate object names. There are 4 levels, for Object, Mesh, Material and Texture. There are 2 ways to handle it: Rename and Use Current. When Rename is selected and a duplicate name is encountered, a suffix will be added to the name to make it unique. By choosing Use Current, the import of the item from the file will be ignored and the item with the same name will be used instead, which already exists in the Blender document.
The Conflict Options section indicates what to do when the importer encounters duplicate object names. There are 6 levels, for Object, Light, Camera, Mesh, Material and Texture. There are 2 ways to handle it: Rename and Use Current. When Rename is selected and a duplicate name is encountered, a suffix will be added to the name to make it unique. By choosing Use Current, the import of the item from the file will be ignored and the item with the same name will be used instead, which already exists in the Blender document.
!!! info "Differences from Virtools conflict resolution"
Compared to the conflict resolution dialog in Virtools, the conflict resolution options provided by the BBP plugin do not support replacement, and the granularity is not fine-tuned to individual instances, but only for an entire type. So you can't set a different conflict resolution for each instance of a conflict individually. However, this setting is sufficient for most scenarios.
@@ -21,7 +20,7 @@ The default values for the options in the Conflict Options section are the solut
### Virtools Params
It is well known that Virtools uses a system-based multi-byte character encoding to process documents, and is therefore prone to what is known as garbling; Blender itself does not suffer from garbling, however, if we do not read a Virtools document with the correct encoding, the characters stored in it may still appear garbled when the Virtools document is imported into Blender. The Encodings property in the Virtools Params section specifies the encodings for reading Virtools documents. Multiple encodings can be specified, separated by a `;` (semicolon). Some common encodings are listed below:
It is well known that Virtools uses a system-based multi-byte character encoding to process documents, and is therefore prone to what is known as garbling; Blender itself does not suffer from garbling, however, if we do not read a Virtools document with the correct encoding, the characters stored in it may still appear garbled when the Virtools document is imported into Blender. The Encodings property in the Virtools Params section specifies the encodings for reading Virtools documents. Multiple encodings may be specified, with the encodings higher up in the list being prioritized for use. The next level of encoding will only be utilized when decoding with the current level fails. Some common encodings are listed below:
* cp1252: Western European encoding used by Ballance.
* gbk: The default encoding for Chinese Windows system.
@@ -38,11 +37,23 @@ The encoding attribute is very important. If the wrong encoding is set, the name
## Export Virtools File
Virtools files can be exported by clicking `File - Export - Virtools File`. Clicking on it will bring up the file opening window and show you the export settings in the sidebar. First of all, you need to select the location of the exported Virtools file, then configure the export settings in the sidebar, after configuring the export settings, you can click Export to start the export, and wait for the status bar at the bottom of Blender to indicate that the export is complete.
Virtools files can be exported by clicking `File - Export - Virtools File`. Clicking on it will bring up the file opening window and show you the export settings in the sidebar as shown below. First of all, you need to select the location of the exported Virtools file, then configure the export settings in the sidebar, after configuring the export settings, you can click Export to start the export, and wait for the status bar at the bottom of Blender to indicate that the export is complete.
![](../imgs/export-virtools.png)
### Export Target
The Export Target section is used to determine which objects you need to export to a Virtools document. You can choose to export a collection or an object and select the corresponding collection or object below. Note that selecting a collection will export the objects in the internal collection as well, i.e. exporting nested collections is supported.
The Export Target section is used to determine which objects you need to export to a Virtools document. You can choose 1 of 4 options:
* Object: Export single object. Select an object in following input box.
* Collection: Export single collection. **This is the most commonly used option.** Select a collection in following input box. Note that selecting a collection will export the objects in the internal collection as well, i.e. exporting nested collections is supported.
* Selected Object: Export selected objects. Select the objects to be exported before enter this dialog.
* All Objects: Export all objects inside this document. This option should be used with caution. Because it brutely iterate the list of document objects to export, and it is likely to export many objects you don't need.
!!! warning "Exportable Targets and Modifiers"
Starting with BBP version 4.4, BBP not only supports exporting Mesh type objects, but also supports exporting all objects that can be converted to Mesh type, including: Curves, Surfaces, Fonts, and Meta Balls. Previously, BBP only supported exporting Mesh type objects. With this feature, users no longer need to convert various objects into meshes before exporting and testing maps. For example, when creating fancy rails using curve lofting, users can now export directly in curve form, test their playability in the game, and then solidify it into a mesh with appropriate materials after the complete test. This feature significantly speeds up map creation and adjustment.
Additionally, starting with BBP version 4.4, it's no longer necessary to apply modifiers to objects during export. BBP applies all modifiers to something like a "temporary object" before exporting (you can think of it this way, but the actual implementation is not exactly the same). This feature lays a solid foundation for enabling rapid creation of Balance maps using modifiers such as geometry nodes.
### Virtools Params
@@ -59,3 +70,11 @@ The Ballance Params section contains parameters that optimize the export process
Successive Sector is an option to work around a bug that occurs when exporting groups of sectors. For some reason, if there are no elements in a sector (actually, no objects are grouped in a sector group), the export plugin thinks that the sector group doesn't exist and misses the export. And since Ballance determines the final sector, i.e. the sector where the spaceships appears, by incrementing the number of sectors from 1 to the last sector group that exists, the combination of the two causes Ballance to incorrectly determine the number of sectors in the map, and thus display the spaceships in the wrong sector, which is an export bug. when this option is checked, the exported document will be pre-defined according to the number of sectors specified in the Ballance Map information in the current Blender file. When this option is checked, the exported document will pre-create all of the sectors according to the number of sectors specified in the Ballance map information in the current Blender file before exporting, so that you don't miss creating some sector groups, and the spaceships will be displayed in the correct sectors.
This option is usually checked when exporting playable maps, if you just want to export some models then you need to turn this option off, otherwise it will create a lot of useless sector groups in the final file.
## Can't Import or Export
In general, after you have properly installed and configured the plugin according to the previously introduced steps, you should theoretically be able to use the import and export functions for Virtools documents. However, unexpected issues may arise. The inability to import or export refers to the buttons for importing and exporting Virtools documents being greyed out and unclickable. You will need to follow the steps outlined below for checking. If you are referring to an error occurring during the import and export process, please refer to the warning information at the top of this page.
First, check whether you have correctly configured the `External Texture Folder` setting of the plugin in the [Configure Plugin](configure-plugin.md) section. If you have not configured it, you will naturally be unable to import or export Virtools files. This is because importing and exporting Virtools files relies on the original Ballance texture data. Please configure this setting carefully according to the tutorial.
If you are certain that you have set the correct texture path, you may try clicking the menu `Window - Toggle System Console` to open the console. There may be some relevant error messages outputted there, such as a failure to load the underlying BMap library, etc. The failure to load the underlying BMap library typically only occurs on machines with rare architectures, such as Windows systems using Snapdragon processors, as the plugin we packaged does not include the Virtools file underlying reading library BMap that supports these platforms. In this situation, you have two options: one is to report this to the developers and wait for support, and the other is to compile the library on yourself (this is only recommended for those with extensive computer knowledge).

View File

@@ -3,6 +3,16 @@
!!! info "May be Outdated"
This document is translated from other languages and may not always be up to date.
<!---
!!! info "Not latest version"
This translated page is not the latest version because the modification of source page. Please see source page of the latest version.
--->
<!---
!!! info "No Translation"
This page has not been translated. Please see source page of this page.
--->
Welcome to the Ballance Blender Plugin, the user manual for the free and open source Ballance map creation suite.
Ballance Blender Plugin (aka BBP) is a plugin that focuses on the creation of Ballance custom maps. It offers a wide range of features that can be used to create Ballance maps by anyone from novice to experienced mappers. BBP provides the ability to import and export formats used in Ballance maps, and a series of convenient features tailored to the creation of Ballance maps: for novice mappers, you can quickly assemble a map with prefabricated road blocks, and for the experienced mappers, the mapping tools needed to build complex structures are also provided.

View File

@@ -2,7 +2,7 @@
## Determining the Version
The principle of BBP's Blender support is to support the latest **LTS** version, and to spend some time migrating the plugin after the latest LTS version is released. The current plugin version **4.0** is based on Blender version **4.2.x**.
The principle of BBP's Blender support is to support the latest **LTS** version, and to spend some time migrating the plugin after the latest LTS version is released. The current plugin version **4.3** is based on Blender version **4.5.x**.
Theoretically, BBP will work fine on other versions of Blender if no major changes have been made. For example you can try to run BBP plugin based on Blender 3.6 LTS on Blender 4.0. However, the developers of BBP do not deal with bugs that only appear in non-LTS versions. before installing the plugin, please select the appropriate version.

View File

@@ -14,9 +14,11 @@ Legacy alignment supports aligning multiple objects to a single object by first
In the panel, `Align Axis` specifies the axis you want to align to, you can multi-select here to specify more than one axis, without specifying any axis you will not be able to do the alignment operation, and thus you will not be able to click the `Apply` button.
`Current Object` is the alignment reference object, which is the active object in the scene, usually the last object you selected. This option specifies what value you need to reference for alignment, with `Min` (minimum value on axis), `Center (Bounding Box)` (center of the bounding box), `Center (Axis)` (origin of the object), and `Max` (maximum value on axis) available. These options are consistent with the alignment options in 3ds Max.
`Current Object` indicates which instance was picked as the alignment reference. You may choose an active object in the scene, typically the last object you selected, or the 3D cursor. It is important to note that if you select active object mode, the active object will be excluded from the alignment operation and will not be moved, as the reference object is immovable. Conversely, if you select the 3D cursor, the active object will be included within the scope of the alignment operation.
The `Target Objects` are the objects that are being aligned, there may be many of them, in this option it is also specified what values you need to refer to them for alignment. The options have the same meaning as `Current Object`.
`Current Object Align Mode` is the alignment mode for aligning to a reference object, which only appears when you select the active object in `Current Object`. Since the 3D cursor is merely a point, while objects occupy a certain space, we need to select a point in this space according to a specific pattern (described later) to be used for subsequent alignment operations. In this option, you specify what value you need to align to, with available selections including `Min` (minimum value on axis), `Center (Bounding Box)` (center of the bounding box), `Center (Axis)` (origin of the object), and `Max` (maximum value on axis). These options are consistent with the alignment options in 3ds Max.
The `Target Objects Align Mode` are the objects that are being aligned, there may be many of them, in this option it is also specified what values you need to refer to them for alignment. The options have the same meaning as `Current Object`.
The `Apply` button, when clicked, will press the current page's configuration into the operation stack and reset the settings above, allowing you to start a new round of alignment operations without having to perform a legacy alignment again. The number of operations in the stack is shown below the `Apply` button.

View File

@@ -1,8 +1,5 @@
# Add Rail
!!! info "Not latest version"
This translated page is not the latest version because the modification of source page. Please see source page of the latest version.
In the 3D view, click `Add - Rails` to expand the Add Rails menu. The menu is shown on the left side of the image below.
![](../imgs/rail-adder.png)
@@ -63,6 +60,8 @@ The first thing you need to do with an arc rail is to specify the Angle and Radi
Steps of the arc rail, the number of steps indicates the number of segments of the arc rail, the larger the number, the smoother the arc rail looks, relatively, the vertices will be more, the storage space and rendering requirements are also higher, so you need to choose a reasonable value.
The Flip option for arc rail allows you to flip (in other word, mirror) the generated structure along a specified axis, and the flip options available on all three axes can accommodate the generation of all possible arc rail structures.
Arc rails also support double-rail mono-rail selection, you can create mono-rail arc rails and double-rail arc rails. The capping attribute is also supported.
### Spiral Rail
@@ -73,7 +72,7 @@ Spiral rails have an Iterations property, which indicates how many times the rai
The spiral rail also needs to set the Steps property, which has the same meaning as the arc rail. However, it should be noted that the number of steps refers to the number of steps in each iteration, not the overall number of steps. Therefore, when adjusting the iteration attribute, you do not need to change the Steps attribute again.
Side Spiral Rail also has a capping property.
Side Spiral Rail also has capping and flip property.
### Side Spiral Rail
@@ -82,3 +81,9 @@ Side Spiral Rail, similar to spiral rail, but the ball is rolled along the side,
Side Spiral Rail does not have a Screw property, because Side Spiral Rail is designed so that adjacent spins share a common edge, so the screw is fixed.
The Radius, Iterations and Steps attributes in the Side Spiral Rail settings have the same meaning as the spiral rail. Side Spiral Rail also have a capping attribute.
Spiral Rail also has capping and flip property.
## Extra Transform
When adding straight rails and curved rails, you can always set a property known as the Extra Transform. Its effect is consistent with the Extra Transform field in adding floors, both serving the purpose of visual edit.

View File

@@ -1,8 +1,5 @@
# Report Issue
!!! info "Not latest version"
This translated page is not the latest version because the modification of source page. Please see source page of the latest version.
## What Can Go Wrong
BBP is not perfect, and since BBP's Virtools file import/export module is written in C++, BBP is more prone to errors than other plugins, and the consequences of errors can be more serious (including but not limited to memory leaks, accidental deletion of user files, etc.).
@@ -15,7 +12,7 @@ In Blender, you will observe if the plugin execution goes wrong:
## What Part Went Wrong
For the BBP plugin, if you observe something like `BMap operation failed` in the Python exception output, or the `IronPad.log` file in the `<Plugin-Install-Location>/PyBMap` folder, it means that The BBP plugin's BMap section, written in C++, is in error, and **you need to immediately save your current Blender document and exit Blender**. Because the plugin is in an abnormal state at this point, you should not continue any operations.
For the BBP plugin, if you observe something like `BMap operation failed` in the Python exception output, or the `blender.exe.<xxx>.log` file (`<xxx>` is a string of numbers) in the `%LOCALAPPDATA%/CrashDumps` folder (`%LOCALAPPDATA%` is a Windows environment variable. If you are not familiar with its purpose, you can simply copy and paste this address directly into the Windows Explorer, which will automatically navigate you to the correct location), it means that The BBP plugin's BMap section, written in C++, is in error, and **you need to immediately save your current Blender document and exit Blender**. Because the plugin is in an abnormal state at this point, you should not continue any operations.
If there is no such thing as the above, then this is just a normal Python code execution error and you don't need to worry too much about it, but the error is still fatal and it is recommended to exit Blender and report the error after doing all the necessary operations.
@@ -29,4 +26,4 @@ If you can't do that and you have proper way to contact with plugin author, then
First of all you need to describe in detail how you raise this error and what are the results of this error. If you can upload the documentation that led to the error, please try to do so (if it's not convenient to post it publicly, you can send it to the author through a private way such as email).
You also need to provide the Python stack report output in the Blender console (use `Window - Toggle System Console` to open the console). If your error is an error in the BMap section, you also need to provide the `IronPad.log` and `IronPad.dmp` files in the `<Plugin-Install-Location>/PyBMap` folder to make it easier for developers to locate the error.
You also need to provide the Python stack report output in the Blender console (use `Window - Toggle System Console` to open the console). If your error is an error in the BMap section, you also need to provide the error log file `blender.exe.<xxx>.log` (where `<xxx>` represents a string of numbers) and the memory dump file `blender.exe.<xxx>.dmp` generated by the BMap module. These files can also be found in the `%LOCALAPPDATA%/CrashDumps` folder.

View File

@@ -1,12 +1,30 @@
# Technical Information
## Standards and Protocol Documentation
* BM File Specification: https://github.com/yyc12345/gist/blob/master/BMFileSpec/BMSpec_ZH.md
* Mapping toolchain standards and format of files in the `meshes' folder: https://github.com/yyc12345/gist/blob/master/BMFileSpec/YYCToolsChainSpec_ZH.md
* Format of the JSON file for BMERevenge: https://github.com/yyc12345/gist/blob/master/BMERevenge/DevDocument_v2.0_ZH.md
## Development Auxiliary Package
This plugin works with the `fake-bpy-module` module to implement type hinting to speed up development. Use the following command to install Blender's type hinting library.
* Blender 3.6: `pip install fake-bpy-module-latest==20230627`
* Blender 4.2: `pip install fake-bpy-module-latest==20240716`
* Blender 4.5: `pip install fake-bpy-module-latest==20250604`
The main reason for doing this is that `fake-bpy-module` doesn't release an official package for the given Blender version, so I had to install it by choosing the daily build closest to the release time of the corresponding Blender version.
The primary reason for this is that the `fake-bpy-module` has not timely released packages suitable for the specified Blender version. Therefore, I can only install it by selecting the daily build version that is closest to the date of the corresponding Blender version leaving from the `main` branch (as daily builds only compile from the `main` branch).
!!! question "Why not use Blender's official bpy module?"
Blender provides an official package named `bpy` on PyPI, but we will not adopt it as our development auxiliary package. This is because it basically repackages Blender into a module (which basically means you are downloading Blender again), allowing you to manipulate Blender through Python. This is contrary to our purpose of using a package that only provides type hints to assist in plugin development.
## Version Rule
The version number format of BBP follows [Semantic Versioning](https://semver.org), but with slight differences:
* The major version number is only increased when the entire plugin is reconstructed.
* The minor version number is used for regular updates.
* The patch version number is incremented when there is no modification for any functionalities. For example, version 4.2.1 only includes updates for macOS Blender without changing any features.
Before the formal release of BBP, there are typically three phased versions: Alpha version, Beta version, and RC version. The Alpha version focuses on functional updates, used to verify whether newly added or modified features are functioning correctly, and does not include documentation or translations. The Beta version is focus on plugin documentation, while the RC version concentrates on plugin translations. However, these three versions do not always exist; if the update content is minimal, some versions may be skipped, or a direct release may occur.

View File

@@ -1,8 +1,5 @@
# Virtools Properties
!!! info "Not latest version"
This translated page is not the latest version because the modification of source page. Please see source page of the latest version.
## Virtools Group
The BBP plugin adds a new property to every Blender object, called Virtools Group. has the same functionality as Group in Virtools. Select an object and the `Virtools Group` panel can be found in the `Object` properties panel.
@@ -13,6 +10,8 @@ In the `Virtools Group` panel, you can click Add to group objects. After clickin
BBP also provides access to Virtools groups in Blender's other menus, see [Group Operation](./group-operations.md).
It's important to note that Virtools groups only apply to mesh objects and objects that can be converted to meshes. For a list of supported objects, please refer to the "Export Virtools File" section on the [Import and Export Virtools Document](./import-export-virtools.md) page. When you open the Virtools group panel on an unsupported object, you will see a warning message indicating that the Virtools group is invalid on that object. Virtools group data set on unsupported objects will be recognized and stored by Blender, but will not be saved to the Virtools file during export.
## Virtools Material
The plugin adds a new property to every Blender material, called Virtools Material, which bridges the gap between Virtools materials and Blender materials. Go to the `Material` properties panel and select a material to find the `Virtools Material` panel.
@@ -59,4 +58,27 @@ The BBP plugin adds a new property to all Blender meshes called Virtools Mesh. g
![](../imgs/virtools-mesh.png)
The Virtools Mesh is currently only used as a compatibility feature. It has only one property, Lit Mode, that can be set. Most early maps had black floor issue because they didn't know how to set the material correctly, so the Lit Mode was often set to Prelit to get the floor to show up properly. This attribute exists for compatibility with this compromise and the user usually does not need to set this option.
The Virtools Mesh is currently only used as a compatibility feature. It has only one property, Lit Mode, that can be set. Most early maps had black floor issue because they didn't know how to set the material correctly, so the Lit Mode was often set to Prelit to get the floor to show up properly. This attribute exists for compatibility with this compromise and the user usually does not need to set this option.
## Virtools Light
The BBP plugin adds a new property called Virtools Light to all Blender lights. You can find the Virtools Light panel by navigating to the Data properties panel.
![](../imgs/virtools-light.png)
Similar to the materials in Virtools, the lighting system in Virtools differs significantly from that in Blender. The Virtools Light acts as a bridge, accurately reflecting the settings of Virtools, allowing for seamless storage within Blender files and providing necessary data during import and export. Additionally, this panel includes an Apply button to apply the Virtools lighting settings to Blender's lighting.
## Virtools Camera
The BBP plugin adds a new property called Virtools Camera to all Blender cameras. You can find the Virtools Camera panel by navigating to the Data properties panel.
![](../imgs/virtools-camera.png)
Similar to the lights in Virtools, the cameraing system in Virtools differs significantly from that in Blender. The Virtools Camera acts as a bridge, accurately reflecting the settings of Virtools, allowing for seamless storage within Blender files and providing necessary data during import and export.
However, unlike Virtools light, Virtools camera and the Blender camera system have a significant difference. Each Virtools camera can hold a different aspect ratio, such as 4:3, 16:9, etc.; while the Blender camera system does not hold this data, but instead stores it in the scene's rendering settings, where only a single value can be entered. To solve this problem, the "Apply" button in this panel only precisely maps properties other than Aspect Ratio to the Blender camera. For Aspect Ratio, a separate button called "Apply Resolution" is provided to reflect the resolution value in the scene's rendering settings. When you want to use the current camera as the viewpoint, click this button, apply the resolution, and then enter the camera's view to see the camera result that matches the Aspect Ratio.
!!! tip "Specific Values for Applying Resolution"
When applying resolution, we do not provide a function to specify the number of pixels on a particular side. Instead, we use a fully automatic algorithm to calculate a suitable resolution. Specifically: First, we obtain the LCM of the Aspect Ratio. Then, we find the first number greater than 1000 that is an integer multiple of that LCM, and use this as the number of pixels on the shorter side. Then, we calculate the number of pixels on the longer side proportionally. This algorithm guarantees that the calculated resolution will always be an integer.
1000 was chosen because it is a relatively moderate resolution size. However, this randomly selected fixed value also means that the final resolution may not be what the user wants. Users can adjust the resolution proportionally according to their needs.

View File

@@ -1,4 +1,39 @@
# ZZQ Features
!!! info "No Translation"
This page has not been translated. Please see source page of this page.
This plugin received many suggestions from ZZQ during its development process, resulting in a rather disordered set of features, which are therefore described together on a separate page.
## Snoop Group then to Mesh
Snoop Group then to Mesh, fully referred to as: snoop and copy the Virtools Group infomations into corresponding curve object and convert it into mesh object. You can select certain objects and then right-click to find this function in the context menu of the object.
This function, as its name, converts the selected object into a mesh. If the selected object is a curve and there is a beveled object set, the grouping information of the beveled object will be assigned to the current curve (overriding the curve's current grouping settings). If the selected object is not a curve, or if it is a curve but lacks a beveled object, then this function is basically the same as executing a conversion to mesh. This function is extremely useful in loft modeling, as it only requires the correct grouping of the section object, after which using this function to convert the curve to a mesh will ensure that the grouping of the lofted object is correct.
## Game Camera
Many map makers often lack a clear concept of the size of their maps when creating them, which can easily lead to the production of maps that are either too large or too small. The in-game camera function of the menu item `Ballance - Game Camera` offers a way to preview the map from the perspective of the in-game camera within Blender, allowing map makers to better grasp the dimensions of the map.
In order to use this feature, there must first be a camera in your scene, set as the active camera of the scene (when no camera is present, a newly added camera through the add menu will be automatically set). Additionally, you will need an active object, which cannot be the same as this camera.
!!! question "Why is it necessary to have active object?"
Due to the limitations of the Blender plugin, an active object is required since the in-game camera functionality supports targeting either the 3D cursor **or an active object**.
Typically, the situation where there are no active objects available only occurs in a blank Blender document, which consequently renders the functionality unavailable. In a custom map, there is no any lack of objects which can become active objects; simply clicking on any object is enough (when wishing to use the 3D cursor as a target).
After clicking this function, the view will automatically switch to the perspective of the active camera, facilitating your preview. You can adjust the relevant settings in the panel located at the bottom left.
![](../imgs/game-camera.png)
First, select the target, which essentially means choosing the position of the player's ball. If 3D cursor is selected, it is treated as the origin of player's ball. If active object is selected, the player's ball is placed at the origin of the active object.
!!! info "The position of the player's ball is not that straightforward."
Regardless of whether one chooses 3D cursor or active object, if no extremely fine adjustments are made, the preview results may have slight differences compared with in-game result.
When you use the 3D cursor mode and simply snap it to the ground, it does not represent the position of player ball. Since the radius of player ball is 2, you actually need to move 2 units in +Z direction to achieve the exact same perspective as in the game. However, this operation is too complex, and not performing this action does not cause a significant difference in the preview effect.
When using active object mode, it is important to note that the origin of the object corresponds to the position of the dot displayed in viewport when the object is selected. For most objects, this may not be what you desire, as they may be located inside or outside the object, and are not always positioned on the surface of the object or at the center of a particular face. To address this situation, I recommend using the 3D cursor as a target and, by entering edit mode, utilizing snapping and the `Shift + S` menu to place the cursor in the correct position.
Next is the selection of the camera's rotation angles. We offer 8 common used in-game preset angles, corresponding to 4 each for 90 degrees camera and 45 degrees camera. Furthermore, if these preset angles do not meet your requirements, you can also set custom angles.
Then, there is the selection of camera perspectives, which can be divided into 3 types: Ordinary (standard perspective), Lift (perspective activated by holding the spacebar), and Easter Egg (special Easter Egg perspective).
Finally, there's the resolution option, where you can choose between Normal (normal game view) and Wide Screen (view after adding the widescreen patch). Generally, choosing the normal game view is sufficient; no special configuration is needed for widescreen. Furthermore, if this resolution option is not checked, the resolution of the currently selected camera will not be modified. Due to the implementation differences between Blender and Virtools cameras, the size of the Blender rendering window will also not be changed. For more information on this behavior, please refer to the "Virtools Camera" section on the [Virtools Properties](./virtools-properties.md) page.

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

View File

@@ -37,7 +37,7 @@
在BME配置对话框的底部您总是可以找到一个被称为额外变换的区域。在这个区域中有额外移动和额外旋转两个选项可以配置。这些字段主要是为了可视化服务的。
在说明这些字段的实际作用前你需要知道BME创建的路面总是以当前3D游标的位置进行创建的即3D游标在哪里新创建的路面就在哪里。这主要是为了可视化来考虑到用户可以先将3D游标移动到想要添加路面的位置再添加对应的BME结构便可以在调整参数的时候即时预览到结果。有时候你的3D游标的位置不是那么的准确又或者最终生成的结构需要一定的旋转才是你期望的此时就可以利用额外变换字段为最终生成的结构增加相对于3D游标的额外的移动和旋转使之处于正确的位置这样就可以在调整参数的时候实时预览到结果了。
在说明这些字段的实际作用前你需要知道BME创建的路面总是以当前3D游标的位置进行创建的即3D游标在哪里新创建的路面就在哪里。这主要是为了可视化来考虑到用户可以先将3D游标移动到想要添加路面的位置再添加对应的BME结构便可以在调整参数的时候即时预览到结果。有时候你的3D游标的位置不是那么的准确又或者最终生成的结构需要一定的旋转才是你期望的此时就可以利用额外变换字段为最终生成的结构增加相对于3D游标的额外的移动和旋转使之处于正确的位置这样就可以在调整参数的时候预览到正确的结果了。
## 小贴士

View File

@@ -4,13 +4,13 @@
## 编译LibCmo与BMap
BBP的Virtools文件原生导入导出功能依赖BMap以及其Python绑定PyBMap来实现。为了分发插件我们需要首先编译BMap及其前置LibCmo。而在编译前你需要先确认你需要的BMap版本。因为BBP并不总是使用最新的BMap例如你正在编译一个旧版的BBP其显然不可能依赖最新的BMap。BMap也在不断升级中其提供的功能也在不断变化不同版本的BMap是不兼容的。BBP通常会在发布时写明其所用的BMap版本如果BBP没有指出你可能需要寻找与BBP发布时最近的BMap版本来编译。
BBP的Virtools文件原生导入导出功能依赖BMap以及其Python绑定pybmap来实现。为了分发插件我们需要首先编译BMap及其前置LibCmo。而在编译前你需要先确认你需要的BMap版本。因为BBP并不总是使用最新的BMap例如你正在编译一个旧版的BBP其显然不可能依赖最新的BMap。BMap也在不断升级中其提供的功能也在不断变化不同版本的BMap是不兼容的。BBP通常会在发布时写明其所用的BMap版本如果BBP没有指出你可能需要寻找与BBP发布时最近的BMap版本来编译。
在明确版本后,你需要访问[LibCmo位于GitHub的存储库](https://github.com/yyc12345/libcmo21)。然后克隆项目使用Git命令转到对应版本或者直接下载对应版本的源码。然后按照LibCmo的编译手册编译得到BMap。在Windows上你通常会得到`BMap.dll``BMap.pdb`这两个文件。而在Linux上则会是`BMap.so`
在明确版本后,你需要访问[LibCmo位于GitHub的存储库](https://github.com/yyc12345/libcmo21)。然后克隆项目使用Git命令转到对应版本或者直接下载对应版本的源码。然后按照LibCmo的编译手册编译得到BMap。在Windows上你通常会得到`BMap.dll`文件。而在Linux上则会是`BMap.so`
然后我们需要配置PyBMap。PyBMap是随LibCmo一起提供的。请按照PyBMap的手册将编译得到的二进制BMap库PyBMap结合在一起。即完成PyBMap配置。
然后我们需要配置pybmap。pybmap是随LibCmo一起提供的。请按照pybmap的手册将编译得到的二进制BMap库pybmap结合在一起。即完成pybmap配置。
然后我们需要将配置好的PyBMap拷贝到本项目的根目录下即可完成此步即存在`bbp_ng/PyBMap`文件夹,为配置好的PyBMap
然后我们需要将配置好的pybmap拷贝到本项目的根目录下即可完成此步即存在`bbp_ng/pybmap`文件夹,为配置好的pybmap
## 生成资源
@@ -69,8 +69,8 @@ Blender对于插件的多语言支持不尽如人意且BBP的设计比较特
如果未来BBP需要支持更多语言你需要从POT文件为新语言创建其对应的PO翻译文件。你可以通过以下方式之一来创建它们。
1. 通过使用Poedit等软件打开POT文件选择创建新的翻译再进行保存来创建。
1. 通过诸如`msginit -i blender.pot -o zh_HANS.po -l zh_CN.utf8`的命令来创建新语言的PO翻译文件。
* 通过使用Poedit等软件打开POT文件选择创建新的翻译再进行保存来创建。
* 通过诸如`msginit -i blender.pot -o zh_HANS.po -l zh_CN.utf8`的命令来创建新语言的PO翻译文件。
创建的方式多种多样唯一需要注意的是你需要按下表所示设定文件名文件名错误Blender会拒绝接受和区域名称使用`msginit`时会用到目的是确保是UTF8格式编码的
@@ -82,8 +82,8 @@ Blender对于插件的多语言支持不尽如人意且BBP的设计比较特
创建新的语言翻译并不常见,更为常见的操作是根据翻译模板,对现有语言翻译文件进行更新。你可以通过以下方式之一来更新它们
1. 通过Poedit等软件打开PO文件再选择从POT文件更新。
1. 通过诸如`msgmerge -U zh-HANS.po blender.pot --backup=none`的命令来更新。
* 通过Poedit等软件打开PO文件再选择从POT文件更新。
* 通过诸如`msgmerge -U zh-HANS.po blender.pot --backup=none`的命令来更新。
### 进行翻译
@@ -108,14 +108,11 @@ PO格式的翻译并不能被Blender识别因此在翻译完成后你还
Blender会根据`blender_manifest.toml`的指示,在排除下列文件的情况下将插件打包:
* `bbp_ng/i18n`:翻译文件夹。
* `bbp_ng/raw_icons`:原始图片文件夹。
* `bbp_ng/raw_jsons`原始JSON文件夹。
* `bbp_ng/tools`:编译用工具。
* `bbp_ng/.style.yapf`:代码风格描述文件
* `bbp_ng/.gitignore`gitignore
* `bbp_ng/icons/.gitkeep`:文件夹占位符
* `bbp_ng/jsons/.gitkeep`:文件夹占位符
* `__pycache__/`Python缓存
* `.style.yapf`:代码风格描述文件
* `.gitignore`gitignore
* `.gitkeep`:文件夹占位符
* `.md`:文档
## 生成帮助文档

View File

@@ -5,11 +5,13 @@
## 导入Virtools文档
点击`File - Import - Virtools File`可以导入Virtools文档。导入支持CMOVMONMO文件。点击后会弹出文件打开界面并在侧边栏展示导入设置。首先你需要选择导入的Virtools文档然后在侧边栏配置导入设置配置完导入设置后即可点击导入开始导入并等待Blender下方提示导入完成即可。
点击`File - Import - Virtools File`可以导入Virtools文档。导入支持CMOVMONMO文件。点击后会弹出文件打开界面并在侧边栏展示导入设置,如下图所示。首先你需要选择导入的Virtools文档然后在侧边栏配置导入设置配置完导入设置后即可点击导入开始导入并等待Blender下方提示导入完成即可。
![](../imgs/import-virtools.png)
### 冲突解决选项
Conflict Options冲突解决选项章节指示了当导入器遇到物体名称重复的情况时该如何处理。分为4个等级分别针对Object物体Light灯光Mesh网格Material材质Texture贴图。处理方式则有2种Rename重命名和Use Current使用当前。选择重命名后当遇到重复名称时将会自动为其添加名称后缀使其名称唯一化。而选择使用当前则会忽略从文件中导入此项转而使用Blender文档中已经存在的同名的项目。
Conflict Options冲突解决选项章节指示了当导入器遇到物体名称重复的情况时该如何处理。分为6个等级分别针对Object物体Light灯光Camera摄像机Mesh网格Material材质Texture贴图。处理方式则有2种Rename重命名和Use Current使用当前。选择重命名后当遇到重复名称时将会自动为其添加名称后缀使其名称唯一化。而选择使用当前则会忽略从文件中导入此项转而使用Blender文档中已经存在的同名的项目。
!!! info "与Virtools冲突解决的不同"
相比较于在Virtools的冲突解决对话框BBP插件提供的冲突解决选项不支持替换功能同时其粒度也不支持精细到单个实例只能针对一整个类型进行设定。因此你无法单独为每一个冲突的实例设置不同的冲突解决方案。但目前这种设置已经能满足绝大对数的使用场景了。
@@ -35,7 +37,9 @@ Conflict Options冲突解决选项章节指示了当导入器遇到物体
## 导出Virtools文档
点击`File - Export - Virtools File`可以导出Virtools文档。点击后会弹出文件打开界面并在侧边栏展示导出设置。首先你需要选择导出的Virtools文档的位置然后在侧边栏配置导出设置配置完导出设置后即可点击导出开始导出并等待Blender下方提示导出完成即可。
点击`File - Export - Virtools File`可以导出Virtools文档。点击后会弹出文件打开界面并在侧边栏展示导出设置,如下图所示。首先你需要选择导出的Virtools文档的位置然后在侧边栏配置导出设置配置完导出设置后即可点击导出开始导出并等待Blender下方提示导出完成即可。
![](../imgs/export-virtools.png)
### 导出目标
@@ -46,6 +50,11 @@ Export Target导出目标章节用于决定你需要将哪写物体导出
* Selected Objects导出选择的物体。你需要在导出前选择好需要导出的物体。
* All Objects导出该文档中的所有物体。该选项慎用因为它是粗暴地遍历文档中的物体列表来进行导出很可能会导出许多你不需要的物体。
!!! warning "可导出目标与修改器"
自BBP 4.4版本开始BBP不仅支持导出Mesh类型物体还开始支持导出所有可转换为Mesh类型的物体包括Curve曲线Surface表面Font字体和Meta Ball融球。此前BBP仅支持Mesh类型物体的导出。通过该功能用户不再需要将各类物体转换为网格之后再进行地图导出和测试例如使用曲线放样得到花式钢轨的工作现在可以直接在曲线状态进行导出然后在游戏中进行可通关性的测试测试完毕后再将其固化为网格并添加相应材质。凭借该功能可大幅加速地图制作和调整速度。
额外的自BBP 4.4版本开始导出时也不再需要给物体应用修改器了。BBP在导出时会在一个类似“临时物体”的东西上应用所有修改器后再导出你可以这么近似理解实际实现并非如此。该功能可为之后使用几何节点等修改器赋能Ballance地图快速创作打下坚实基础。
### Virtools参数
Virtools ParamsVirtools参数章节与导入Virtools文档中的类似。Encodings编码属性决定了导出Virtools文档时所用的编码。
@@ -66,6 +75,6 @@ Successive Sector小节连续是一个解决导出小节组时出现的Bug
通常而言在你正确按照之前介绍的步骤安装和配置插件后你理论上就可以使用Virtools文档的导入导出功能了。但难免有意外发生。这里说的无法导入导出指的是导入和导出Virtools文档的按钮是灰色的不可点击的。你需要按照下述步骤检查。如果你所指的是在导入导出过程中出错了请参考本页页首的警告信息。
首先检查你是否已经在[配置插件](configure-plugin.md)章节正确配置了插件的`External Texture Folder`设置项。如果你没有配置则自然无法导入导出Virtools文件。因为导入导出Virtools文件需要依赖Ballance原版关卡数据。请按教程认真配置这一设置项。
首先检查你是否已经在[配置插件](configure-plugin.md)章节正确配置了插件的`External Texture Folder`设置项。如果你没有配置则自然无法导入导出Virtools文件。因为导入导出Virtools文件需要依赖Ballance原版贴图数据。请按教程认真配置这一设置项。
如果你确定你已经设置了正确的贴图路径,你可以尝试点击菜单`Window - Toggle System Console`来打开控制台。在控制台中可能会有一些相关的错误信息输出在其中例如加载底层BMap库时失败等。加载底层BMap库失败通常只发生在一些罕见架构的机器上例如使用Snapdragon处理器的Windows系统等因为我们打包的插件中并未包含支持这些平台的Virtools文件底层读取库BMap。在面对这种情况时你有两个选择一是汇报给开发者等待开发者主动支持二是自行编译该库仅建议有丰富计算机知识的人这样做

View File

@@ -10,7 +10,7 @@ BBP插件为每一个Blender物体添加了新的属性被称为Virtools Grou
BBP还在Blender的其它菜单提供了对Virtools组的访问具体内容请参阅[按组操作](./group-operations.md)。
需要注意的是Virtools组仅对网格物体生效,当你在其它物体上打开Virtools组面板时你会在面板中看到一条警告消息提示你Virtools组在该物体上无效。在非网格物体上设置的Virtools组数据尽管会被Blender承认和存储但不会在导出时保存到Virtools文件中。
需要注意的是Virtools组仅对网格物体和可转换为网格的物体生效,具体支持的物体可参考[导入导出Virtools文档](./import-export-virtools.md)页面的导出Virtools文档章节。当你在不支持的物体上打开Virtools组面板时你会在面板中看到一条警告消息提示你Virtools组在该物体上无效。在不支持的物体上设置的Virtools组数据尽管会被Blender承认和存储但不会在导出时保存到Virtools文件中。
## Virtools材质
@@ -67,3 +67,18 @@ BBP插件为所有Blender灯光添加了新的属性称为Virtools Light。
![](../imgs/virtools-light.png)
与Virtools材质类似Virtools的灯光系统与Blender的灯光系统差距较大Virtools灯光相当于一个桥梁它可以准确地反映Virtools的设置使得其可以完美地存储于Blender文件中并在导入导出时提供必要的数据。同时该面板并提供一个应用按钮用以将Virtools灯光设置应用到Blender灯光中。
## Virtools摄像机
BBP插件为所有Blender摄像机添加了新的属性称为Virtools Camera。转到`Data`属性面板,即可以找到`Virtools Camera`面板。
![](../imgs/virtools-camera.png)
与Virtools灯光类似Virtools的摄像机系统与Blender的摄像机系统差距较大Virtools摄像机相当于一个桥梁它可以准确地反映Virtools的设置使得其可以完美地存储于Blender文件中并在导入导出时提供必要的数据。
然而与Virtools灯光不同的是Virtools摄像机与Blender摄像机系统还有一个巨大的差异。每一个Virtools摄像机均可持有一个不同的分辨率比例例如4:316:9等而对于Blender摄像机系统Blender摄像机不持有这些数据而转而将他们存储在场景的渲染设置中该位置只能填写唯一的数值。为了解决该问题该面板提供的应用按钮仅将除了Aspect Ratio以外的属性精准地映射到Blender摄像机之上而对于Aspect Ratio单独提供了一个名为Apply Resolution的按钮用于将其反映到场景的渲染设置中的分辨率数值。当你想要义当前摄像机作为查看时点击该按钮应用分辨率后再进入摄像机视角就可以看到符合Aspect Ratio的摄像机结果了。
!!! tip "应用分辨率时的具体数值"
在应用分辨率时我们没有提供具体指定某一边像素个数的功能我们转而使用全自动的算法来计算出一个合适的分辨率。具体而言是首先取得Aspect Ratio的最小公倍数然后找到第一个大于1000且是其整数倍的数作为短边的像素数然后再以比例计算长边。该算法可保证计算出来的分辨率一定是整数。
选取1000是因为1000是一个较为适中的分辨率大小。然而这种随机选定的固定值也决定了其最终分辨率可能并不是用户想要的用户可酌情根据自身需要按比率增减分辨率。

View File

@@ -34,4 +34,6 @@
然后是选择摄像机的旋转角度。我们提供了8种游戏内预设角度分别对应90度和45度的各4种。此外如果这些预设角度不能满足你的需求你还可以设置自定义角度。
最后是选择摄像机视角分为Ordinary常规视角Lift按住空格键的视角和Easter Egg彩蛋视角三种。
接着是选择摄像机视角分为Ordinary常规视角Lift按住空格键的视角和Easter Egg彩蛋视角三种。
最后是分辨率选项可以在Normal正常游戏视角和Wide Screen加入宽屏补丁后的视角之间进行选择。通常来说选择正常游戏视角即可无需针对宽屏进行特殊配置。此外如果不勾选分辨率这个选项则不会修改当前选择摄像机的分辨率由于Blender与Virtools摄像机的实现差异也因此不会修改Blender渲染窗口的大小。有关该行为可参考[Virtools属性](./virtools-properties.md)页面的的Virtools摄像机章节。

View File

@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-08-26 21:47+0800\n"
"POT-Creation-Date: 2026-03-24 19:55+0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -252,6 +252,24 @@ msgctxt "BBP_OT_add_virtools_group"
msgid "Add to Virtools Groups"
msgstr ""
#. :src: bpy.types.BBP_OT_apply_virtools_camera
msgid "Apply Virtools Camera to Blender Camera except Resolution."
msgstr ""
#. :src: bpy.types.BBP_OT_apply_virtools_camera
msgctxt "BBP_OT_apply_virtools_camera"
msgid "Apply to Blender Camera"
msgstr ""
#. :src: bpy.types.BBP_OT_apply_virtools_camera_resolution
msgid "Apply Virtools Camera Resolution to Blender Scene."
msgstr ""
#. :src: bpy.types.BBP_OT_apply_virtools_camera_resolution
msgctxt "BBP_OT_apply_virtools_camera_resolution"
msgid "Apply to Blender Scene Resolution"
msgstr ""
#. :src: bpy.types.BBP_OT_apply_virtools_light
msgid "Apply Virtools Light to Blender Light."
msgstr ""
@@ -389,15 +407,6 @@ msgctxt "BBP_OT_game_camera"
msgid "Game Camera"
msgstr ""
#. :src: bpy.types.BBP_OT_game_resolution
msgid "Set Blender render resolution to Ballance game"
msgstr ""
#. :src: bpy.types.BBP_OT_game_resolution
msgctxt "BBP_OT_game_resolution"
msgid "Game Resolution"
msgstr ""
#. :src: bpy.types.BBP_OT_import_bmfile
msgid "Load a Ballance Map File (BM File Spec 1.4)"
msgstr ""
@@ -425,6 +434,15 @@ msgctxt "BBP_OT_legacy_align"
msgid "3ds Max Align"
msgstr ""
#. :src: bpy.types.BBP_OT_preset_virtools_camera_aspect_ratio
msgid "Preset Virtools Camera Aspect Ratio with Virtools Presets."
msgstr ""
#. :src: bpy.types.BBP_OT_preset_virtools_camera_aspect_ratio
msgctxt "BBP_OT_preset_virtools_camera_aspect_ratio"
msgid "Preset Virtools Camera Aspect Ratio"
msgstr ""
#. :src: bpy.types.BBP_OT_preset_virtools_material
msgid "Preset Virtools Material with Original Ballance Data."
msgstr ""
@@ -1609,6 +1627,15 @@ msgstr ""
msgid "The rotation angle of camera relative to 3D Cursor or Active Object"
msgstr ""
#. :src: bpy.types.BBP_OT_game_camera.modify_resolution
msgctxt "BBP_OT_game_camera/property"
msgid "Modify Resolution"
msgstr ""
#. :src: bpy.types.BBP_OT_game_camera.modify_resolution
msgid "Whether modify the resolution of camera."
msgstr ""
#. :src: bpy.types.BBP_OT_game_camera.perspective_kind
#. :src: bpy.types.BBP_OT_game_camera.rotation_kind
msgctxt "BBP_OT_game_camera/property"
@@ -1648,6 +1675,7 @@ msgid "Preset Rotation Angle"
msgstr ""
#. :src: bpy.types.BBP_OT_game_camera.preset_rotation_angle:'1'
#. :src: bpy.types.BBP_OT_game_camera.preset_rotation_angle_deg0
msgctxt "BBP_OT_game_camera/property"
msgid "0 Degree"
msgstr ""
@@ -1657,6 +1685,7 @@ msgid "0 degree"
msgstr ""
#. :src: bpy.types.BBP_OT_game_camera.preset_rotation_angle:'2'
#. :src: bpy.types.BBP_OT_game_camera.preset_rotation_angle_deg45
msgctxt "BBP_OT_game_camera/property"
msgid "45 Degree"
msgstr ""
@@ -1666,6 +1695,7 @@ msgid "45 degree"
msgstr ""
#. :src: bpy.types.BBP_OT_game_camera.preset_rotation_angle:'3'
#. :src: bpy.types.BBP_OT_game_camera.preset_rotation_angle_deg90
msgctxt "BBP_OT_game_camera/property"
msgid "90 Degree"
msgstr ""
@@ -1675,6 +1705,7 @@ msgid "90 degree"
msgstr ""
#. :src: bpy.types.BBP_OT_game_camera.preset_rotation_angle:'4'
#. :src: bpy.types.BBP_OT_game_camera.preset_rotation_angle_deg135
msgctxt "BBP_OT_game_camera/property"
msgid "135 Degree"
msgstr ""
@@ -1684,6 +1715,7 @@ msgid "135 degree"
msgstr ""
#. :src: bpy.types.BBP_OT_game_camera.preset_rotation_angle:'5'
#. :src: bpy.types.BBP_OT_game_camera.preset_rotation_angle_deg180
msgctxt "BBP_OT_game_camera/property"
msgid "180 Degree"
msgstr ""
@@ -1693,6 +1725,7 @@ msgid "180 degree"
msgstr ""
#. :src: bpy.types.BBP_OT_game_camera.preset_rotation_angle:'6'
#. :src: bpy.types.BBP_OT_game_camera.preset_rotation_angle_deg225
msgctxt "BBP_OT_game_camera/property"
msgid "225 Degree"
msgstr ""
@@ -1702,6 +1735,7 @@ msgid "225 degree"
msgstr ""
#. :src: bpy.types.BBP_OT_game_camera.preset_rotation_angle:'7'
#. :src: bpy.types.BBP_OT_game_camera.preset_rotation_angle_deg270
msgctxt "BBP_OT_game_camera/property"
msgid "270 Degree"
msgstr ""
@@ -1711,6 +1745,7 @@ msgid "270 degree"
msgstr ""
#. :src: bpy.types.BBP_OT_game_camera.preset_rotation_angle:'8'
#. :src: bpy.types.BBP_OT_game_camera.preset_rotation_angle_deg315
msgctxt "BBP_OT_game_camera/property"
msgid "315 Degree"
msgstr ""
@@ -1719,6 +1754,33 @@ msgstr ""
msgid "315 degree"
msgstr ""
#. :src: bpy.types.BBP_OT_game_camera.resolution_kind
msgctxt "BBP_OT_game_camera/property"
msgid "Resolution Kind"
msgstr ""
#. :src: bpy.types.BBP_OT_game_camera.resolution_kind
msgid "The type of preset resolution."
msgstr ""
#. :src: bpy.types.BBP_OT_game_camera.resolution_kind:'1'
msgctxt "BBP_OT_game_camera/property"
msgid "Normal"
msgstr ""
#. :src: bpy.types.BBP_OT_game_camera.resolution_kind:'1'
msgid "Vanilla Ballance Resolution"
msgstr ""
#. :src: bpy.types.BBP_OT_game_camera.resolution_kind:'2'
msgctxt "BBP_OT_game_camera/property"
msgid "Wide Screen"
msgstr ""
#. :src: bpy.types.BBP_OT_game_camera.resolution_kind:'2'
msgid "Ballance Resolution with Wide Screen Fix"
msgstr ""
#. :src: bpy.types.BBP_OT_game_camera.rotation_kind:'1'
msgctxt "BBP_OT_game_camera/property"
msgid "Preset"
@@ -1760,49 +1822,77 @@ msgstr ""
msgid "The origin point of active object is player ball."
msgstr ""
#. :src: bpy.types.BBP_OT_game_resolution.resolution_kind
msgctxt "BBP_OT_game_resolution/property"
msgid "Resolution Kind"
#. :src: bpy.types.BBP_OT_import_bmfile.camera_conflict_strategy
#. :src: bpy.types.BBP_OT_import_virtools.camera_conflict_strategy
msgctxt "BBP/UTIL_ioport_shared.ImportParams/property"
msgid "Camera Name Conflict"
msgstr ""
#. :src: bpy.types.BBP_OT_game_resolution.resolution_kind
msgid "The type of preset resolution."
#. :src: bpy.types.BBP_OT_import_bmfile.camera_conflict_strategy
#. :src: bpy.types.BBP_OT_import_virtools.camera_conflict_strategy
msgid "Define how to process camera name conflict"
msgstr ""
#. :src: bpy.types.BBP_OT_game_resolution.resolution_kind:'1'
msgctxt "BBP_OT_game_resolution/property"
msgid "Normal"
#. :src: bpy.types.BBP_OT_import_bmfile.camera_conflict_strategy:'1'
#. :src: bpy.types.BBP_OT_import_bmfile.light_conflict_strategy:'1'
#. :src: bpy.types.BBP_OT_import_bmfile.material_conflict_strategy:'1'
#. :src: bpy.types.BBP_OT_import_bmfile.mesh_conflict_strategy:'1'
#. :src: bpy.types.BBP_OT_import_bmfile.object_conflict_strategy:'1'
#. :src: bpy.types.BBP_OT_import_bmfile.texture_conflict_strategy:'1'
#. :src: bpy.types.BBP_OT_import_virtools.camera_conflict_strategy:'1'
#. :src: bpy.types.BBP_OT_import_virtools.light_conflict_strategy:'1'
#. :src: bpy.types.BBP_OT_import_virtools.material_conflict_strategy:'1'
#. :src: bpy.types.BBP_OT_import_virtools.mesh_conflict_strategy:'1'
#. :src: bpy.types.BBP_OT_import_virtools.object_conflict_strategy:'1'
#. :src: bpy.types.BBP_OT_import_virtools.texture_conflict_strategy:'1'
msgctxt "BBP/UTIL_ioport_shared.ImportParams/property"
msgid "Rename"
msgstr ""
#. :src: bpy.types.BBP_OT_game_resolution.resolution_kind:'1'
msgid "Aspect ratio: 4:3."
#. :src: bpy.types.BBP_OT_import_bmfile.camera_conflict_strategy:'1'
#. :src: bpy.types.BBP_OT_import_bmfile.light_conflict_strategy:'1'
#. :src: bpy.types.BBP_OT_import_bmfile.material_conflict_strategy:'1'
#. :src: bpy.types.BBP_OT_import_bmfile.mesh_conflict_strategy:'1'
#. :src: bpy.types.BBP_OT_import_bmfile.object_conflict_strategy:'1'
#. :src: bpy.types.BBP_OT_import_bmfile.texture_conflict_strategy:'1'
#. :src: bpy.types.BBP_OT_import_virtools.camera_conflict_strategy:'1'
#. :src: bpy.types.BBP_OT_import_virtools.light_conflict_strategy:'1'
#. :src: bpy.types.BBP_OT_import_virtools.material_conflict_strategy:'1'
#. :src: bpy.types.BBP_OT_import_virtools.mesh_conflict_strategy:'1'
#. :src: bpy.types.BBP_OT_import_virtools.object_conflict_strategy:'1'
#. :src: bpy.types.BBP_OT_import_virtools.texture_conflict_strategy:'1'
msgid "Rename the new one"
msgstr ""
#. :src: bpy.types.BBP_OT_game_resolution.resolution_kind:'2'
msgctxt "BBP_OT_game_resolution/property"
msgid "Extended"
#. :src: bpy.types.BBP_OT_import_bmfile.camera_conflict_strategy:'2'
#. :src: bpy.types.BBP_OT_import_bmfile.light_conflict_strategy:'2'
#. :src: bpy.types.BBP_OT_import_bmfile.material_conflict_strategy:'2'
#. :src: bpy.types.BBP_OT_import_bmfile.mesh_conflict_strategy:'2'
#. :src: bpy.types.BBP_OT_import_bmfile.object_conflict_strategy:'2'
#. :src: bpy.types.BBP_OT_import_bmfile.texture_conflict_strategy:'2'
#. :src: bpy.types.BBP_OT_import_virtools.camera_conflict_strategy:'2'
#. :src: bpy.types.BBP_OT_import_virtools.light_conflict_strategy:'2'
#. :src: bpy.types.BBP_OT_import_virtools.material_conflict_strategy:'2'
#. :src: bpy.types.BBP_OT_import_virtools.mesh_conflict_strategy:'2'
#. :src: bpy.types.BBP_OT_import_virtools.object_conflict_strategy:'2'
#. :src: bpy.types.BBP_OT_import_virtools.texture_conflict_strategy:'2'
msgctxt "BBP/UTIL_ioport_shared.ImportParams/property"
msgid "Use Current"
msgstr ""
#. :src: bpy.types.BBP_OT_game_resolution.resolution_kind:'2'
msgid "Aspect ratio: 16:9."
msgstr ""
#. :src: bpy.types.BBP_OT_game_resolution.resolution_kind:'3'
msgctxt "BBP_OT_game_resolution/property"
msgid "Widescreen"
msgstr ""
#. :src: bpy.types.BBP_OT_game_resolution.resolution_kind:'3'
msgid "Aspect ratio: 7:3."
msgstr ""
#. :src: bpy.types.BBP_OT_game_resolution.resolution_kind:'4'
msgctxt "BBP_OT_game_resolution/property"
msgid "Panoramic"
msgstr ""
#. :src: bpy.types.BBP_OT_game_resolution.resolution_kind:'4'
msgid "Aspect ratio: 20:7."
#. :src: bpy.types.BBP_OT_import_bmfile.camera_conflict_strategy:'2'
#. :src: bpy.types.BBP_OT_import_bmfile.light_conflict_strategy:'2'
#. :src: bpy.types.BBP_OT_import_bmfile.material_conflict_strategy:'2'
#. :src: bpy.types.BBP_OT_import_bmfile.mesh_conflict_strategy:'2'
#. :src: bpy.types.BBP_OT_import_bmfile.object_conflict_strategy:'2'
#. :src: bpy.types.BBP_OT_import_bmfile.texture_conflict_strategy:'2'
#. :src: bpy.types.BBP_OT_import_virtools.camera_conflict_strategy:'2'
#. :src: bpy.types.BBP_OT_import_virtools.light_conflict_strategy:'2'
#. :src: bpy.types.BBP_OT_import_virtools.material_conflict_strategy:'2'
#. :src: bpy.types.BBP_OT_import_virtools.mesh_conflict_strategy:'2'
#. :src: bpy.types.BBP_OT_import_virtools.object_conflict_strategy:'2'
#. :src: bpy.types.BBP_OT_import_virtools.texture_conflict_strategy:'2'
msgid "Use current one"
msgstr ""
#. :src: bpy.types.BBP_OT_import_bmfile.light_conflict_strategy
@@ -1816,60 +1906,6 @@ msgstr ""
msgid "Define how to process light name conflict"
msgstr ""
#. :src: bpy.types.BBP_OT_import_bmfile.light_conflict_strategy:'1'
#. :src: bpy.types.BBP_OT_import_bmfile.material_conflict_strategy:'1'
#. :src: bpy.types.BBP_OT_import_bmfile.mesh_conflict_strategy:'1'
#. :src: bpy.types.BBP_OT_import_bmfile.object_conflict_strategy:'1'
#. :src: bpy.types.BBP_OT_import_bmfile.texture_conflict_strategy:'1'
#. :src: bpy.types.BBP_OT_import_virtools.light_conflict_strategy:'1'
#. :src: bpy.types.BBP_OT_import_virtools.material_conflict_strategy:'1'
#. :src: bpy.types.BBP_OT_import_virtools.mesh_conflict_strategy:'1'
#. :src: bpy.types.BBP_OT_import_virtools.object_conflict_strategy:'1'
#. :src: bpy.types.BBP_OT_import_virtools.texture_conflict_strategy:'1'
msgctxt "BBP/UTIL_ioport_shared.ImportParams/property"
msgid "Rename"
msgstr ""
#. :src: bpy.types.BBP_OT_import_bmfile.light_conflict_strategy:'1'
#. :src: bpy.types.BBP_OT_import_bmfile.material_conflict_strategy:'1'
#. :src: bpy.types.BBP_OT_import_bmfile.mesh_conflict_strategy:'1'
#. :src: bpy.types.BBP_OT_import_bmfile.object_conflict_strategy:'1'
#. :src: bpy.types.BBP_OT_import_bmfile.texture_conflict_strategy:'1'
#. :src: bpy.types.BBP_OT_import_virtools.light_conflict_strategy:'1'
#. :src: bpy.types.BBP_OT_import_virtools.material_conflict_strategy:'1'
#. :src: bpy.types.BBP_OT_import_virtools.mesh_conflict_strategy:'1'
#. :src: bpy.types.BBP_OT_import_virtools.object_conflict_strategy:'1'
#. :src: bpy.types.BBP_OT_import_virtools.texture_conflict_strategy:'1'
msgid "Rename the new one"
msgstr ""
#. :src: bpy.types.BBP_OT_import_bmfile.light_conflict_strategy:'2'
#. :src: bpy.types.BBP_OT_import_bmfile.material_conflict_strategy:'2'
#. :src: bpy.types.BBP_OT_import_bmfile.mesh_conflict_strategy:'2'
#. :src: bpy.types.BBP_OT_import_bmfile.object_conflict_strategy:'2'
#. :src: bpy.types.BBP_OT_import_bmfile.texture_conflict_strategy:'2'
#. :src: bpy.types.BBP_OT_import_virtools.light_conflict_strategy:'2'
#. :src: bpy.types.BBP_OT_import_virtools.material_conflict_strategy:'2'
#. :src: bpy.types.BBP_OT_import_virtools.mesh_conflict_strategy:'2'
#. :src: bpy.types.BBP_OT_import_virtools.object_conflict_strategy:'2'
#. :src: bpy.types.BBP_OT_import_virtools.texture_conflict_strategy:'2'
msgctxt "BBP/UTIL_ioport_shared.ImportParams/property"
msgid "Use Current"
msgstr ""
#. :src: bpy.types.BBP_OT_import_bmfile.light_conflict_strategy:'2'
#. :src: bpy.types.BBP_OT_import_bmfile.material_conflict_strategy:'2'
#. :src: bpy.types.BBP_OT_import_bmfile.mesh_conflict_strategy:'2'
#. :src: bpy.types.BBP_OT_import_bmfile.object_conflict_strategy:'2'
#. :src: bpy.types.BBP_OT_import_bmfile.texture_conflict_strategy:'2'
#. :src: bpy.types.BBP_OT_import_virtools.light_conflict_strategy:'2'
#. :src: bpy.types.BBP_OT_import_virtools.material_conflict_strategy:'2'
#. :src: bpy.types.BBP_OT_import_virtools.mesh_conflict_strategy:'2'
#. :src: bpy.types.BBP_OT_import_virtools.object_conflict_strategy:'2'
#. :src: bpy.types.BBP_OT_import_virtools.texture_conflict_strategy:'2'
msgid "Use current one"
msgstr ""
#. :src: bpy.types.BBP_OT_import_bmfile.material_conflict_strategy
#. :src: bpy.types.BBP_OT_import_virtools.material_conflict_strategy
msgctxt "BBP/UTIL_ioport_shared.ImportParams/property"
@@ -1914,15 +1950,57 @@ msgstr ""
msgid "Define how to process texture name conflict"
msgstr ""
#. :src: bpy.types.BBP_OT_preset_virtools_material.preset_type
msgctxt "BBP_OT_preset_virtools_material/property"
#. :src: bpy.types.BBP_OT_preset_virtools_camera_aspect_ratio.preset_type
msgctxt "BBP_OT_preset_virtools_camera_aspect_ratio/property"
msgid "Preset"
msgstr ""
#. :src: bpy.types.BBP_OT_preset_virtools_camera_aspect_ratio.preset_type
#. :src: bpy.types.BBP_OT_preset_virtools_material.preset_type
msgid "The preset which you want to apply."
msgstr ""
#. :src: bpy.types.BBP_OT_preset_virtools_camera_aspect_ratio.preset_type:'1'
msgctxt "BBP_OT_preset_virtools_camera_aspect_ratio/property"
msgid "Normal"
msgstr ""
#. :src: bpy.types.BBP_OT_preset_virtools_camera_aspect_ratio.preset_type:'1'
msgid "Aspect ratio: 4:3."
msgstr ""
#. :src: bpy.types.BBP_OT_preset_virtools_camera_aspect_ratio.preset_type:'2'
msgctxt "BBP_OT_preset_virtools_camera_aspect_ratio/property"
msgid "Extended"
msgstr ""
#. :src: bpy.types.BBP_OT_preset_virtools_camera_aspect_ratio.preset_type:'2'
msgid "Aspect ratio: 16:9."
msgstr ""
#. :src: bpy.types.BBP_OT_preset_virtools_camera_aspect_ratio.preset_type:'3'
msgctxt "BBP_OT_preset_virtools_camera_aspect_ratio/property"
msgid "Widescreen"
msgstr ""
#. :src: bpy.types.BBP_OT_preset_virtools_camera_aspect_ratio.preset_type:'3'
msgid "Aspect ratio: 7:3."
msgstr ""
#. :src: bpy.types.BBP_OT_preset_virtools_camera_aspect_ratio.preset_type:'4'
msgctxt "BBP_OT_preset_virtools_camera_aspect_ratio/property"
msgid "Panoramic"
msgstr ""
#. :src: bpy.types.BBP_OT_preset_virtools_camera_aspect_ratio.preset_type:'4'
msgid "Aspect ratio: 20:7."
msgstr ""
#. :src: bpy.types.BBP_OT_preset_virtools_material.preset_type
msgctxt "BBP_OT_preset_virtools_material/property"
msgid "Preset"
msgstr ""
#. :src: bpy.types.BBP_OT_preset_virtools_material.preset_type:'1'
msgctxt "BBP_OT_preset_virtools_material/property"
msgid "FloorSide"
@@ -2054,6 +2132,15 @@ msgctxt "BBP_PT_bme_materials"
msgid "BME Materials"
msgstr ""
#. :src: bpy.types.BBP_PT_virtools_camera
msgid "Show Virtools Camera Properties"
msgstr ""
#. :src: bpy.types.BBP_PT_virtools_camera
msgctxt "BBP_PT_virtools_camera"
msgid "Virtools Camera"
msgstr ""
#. :src: bpy.types.BBP_PT_virtools_groups
msgid "Show Virtools Groups Properties."
msgstr ""
@@ -2310,6 +2397,79 @@ msgstr ""
msgid "The material used for rail"
msgstr ""
#. :src: bpy.types.BBP_PG_virtools_camera.aspect_ratio_h
msgctxt "BBP_PG_virtools_camera/property"
msgid "Aspect Ratio Height"
msgstr ""
#. :src: bpy.types.BBP_PG_virtools_camera.aspect_ratio_h
msgid "Defines the height of aspect ratio."
msgstr ""
#. :src: bpy.types.BBP_PG_virtools_camera.aspect_ratio_w
msgctxt "BBP_PG_virtools_camera/property"
msgid "Aspect Ratio Width"
msgstr ""
#. :src: bpy.types.BBP_PG_virtools_camera.aspect_ratio_w
msgid "Defines the width of aspect ratio."
msgstr ""
#. :src: bpy.types.BBP_PG_virtools_camera.back_plane
msgctxt "BBP_PG_virtools_camera/property"
msgid "Back Plane"
msgstr ""
#. :src: bpy.types.BBP_PG_virtools_camera.back_plane
msgid "Defines the back plane."
msgstr ""
#. :src: bpy.types.BBP_PG_virtools_camera.fov
msgctxt "BBP_PG_virtools_camera/property"
msgid "Field of View"
msgstr ""
#. :src: bpy.types.BBP_PG_virtools_camera.fov
msgid "Defines the field of view."
msgstr ""
#. :src: bpy.types.BBP_PG_virtools_camera.front_plane
msgctxt "BBP_PG_virtools_camera/property"
msgid "Front Plane"
msgstr ""
#. :src: bpy.types.BBP_PG_virtools_camera.front_plane
msgid "Defines the front plane."
msgstr ""
#. :src: bpy.types.BBP_PG_virtools_camera.orthographic_zoom
msgctxt "BBP_PG_virtools_camera/property"
msgid "Orthographic Zoom"
msgstr ""
#. :src: bpy.types.BBP_PG_virtools_camera.orthographic_zoom
msgid "Defines the orthographic zoom."
msgstr ""
#. :src: bpy.types.BBP_PG_virtools_camera.projection_type
msgctxt "BBP_PG_virtools_camera/property"
msgid "Type"
msgstr ""
#. :src: bpy.types.BBP_PG_virtools_camera.projection_type
msgid "The type of this camera."
msgstr ""
#. :src: bpy.types.BBP_PG_virtools_camera.projection_type:'1'
msgctxt "BBP_PG_virtools_camera/property"
msgid "Perspective Projection"
msgstr ""
#. :src: bpy.types.BBP_PG_virtools_camera.projection_type:'2'
msgctxt "BBP_PG_virtools_camera/property"
msgid "Orthographic Projection"
msgstr ""
#. :src: bpy.types.BBP_PG_virtools_group.group_name
msgctxt "BBP_PG_virtools_group/property"
msgid "Group Name"
@@ -3582,104 +3742,114 @@ msgstr ""
msgid "This feature is not supported yet."
msgstr ""
#: extensions/user_default/bbp_ng/OP_EXPORT_virtools.py:148
#: extensions/user_default/bbp_ng/OP_EXPORT_virtools.py:167
msgctxt "BBP_OT_export_virtools/execute"
msgid "Creating 3dObjects and Lights"
msgstr ""
#: extensions/user_default/bbp_ng/OP_EXPORT_virtools.py:192
#: extensions/user_default/bbp_ng/OP_EXPORT_virtools.py:221
msgctxt "BBP_OT_export_virtools/execute"
msgid "Saving Groups"
msgstr ""
#: extensions/user_default/bbp_ng/OP_EXPORT_virtools.py:238
#: extensions/user_default/bbp_ng/OP_EXPORT_virtools.py:267
msgctxt "BBP_OT_export_virtools/execute"
msgid "Saving Lights"
msgstr ""
#: extensions/user_default/bbp_ng/OP_EXPORT_virtools.py:286
#: extensions/user_default/bbp_ng/OP_EXPORT_virtools.py:312
msgctxt "BBP_OT_export_virtools/execute"
msgid "Saving Cameras"
msgstr ""
#: extensions/user_default/bbp_ng/OP_EXPORT_virtools.py:358
msgctxt "BBP_OT_export_virtools/execute"
msgid "Saving 3dObjects"
msgstr ""
#: extensions/user_default/bbp_ng/OP_EXPORT_virtools.py:333
#: extensions/user_default/bbp_ng/OP_EXPORT_virtools.py:405
msgctxt "BBP_OT_export_virtools/execute"
msgid "Saving Meshes"
msgstr ""
#: extensions/user_default/bbp_ng/OP_EXPORT_virtools.py:448
#: extensions/user_default/bbp_ng/OP_EXPORT_virtools.py:520
msgctxt "BBP_OT_export_virtools/execute"
msgid "Saving Materials"
msgstr ""
#: extensions/user_default/bbp_ng/OP_EXPORT_virtools.py:514
#: extensions/user_default/bbp_ng/OP_EXPORT_virtools.py:586
msgctxt "BBP_OT_export_virtools/execute"
msgid "Saving Textures"
msgstr ""
#: extensions/user_default/bbp_ng/OP_EXPORT_virtools.py:554
#: extensions/user_default/bbp_ng/OP_EXPORT_virtools.py:626
msgctxt "BBP_OT_export_virtools/execute"
msgid "Saving Document"
msgstr ""
#: extensions/user_default/bbp_ng/OP_EXPORT_virtools.py:66
#: extensions/user_default/bbp_ng/OP_EXPORT_virtools.py:76
msgid "Virtools File Exporting Finished."
msgstr ""
#: extensions/user_default/bbp_ng/OP_EXPORT_virtools.py:95
#: extensions/user_default/bbp_ng/OP_EXPORT_virtools.py:112
msgctxt "BBP_OT_export_virtools/execute"
msgid "Virtools Engine Temporary Directory: {0}"
msgstr ""
#: extensions/user_default/bbp_ng/OP_EXPORT_virtools.py:32
#: extensions/user_default/bbp_ng/OP_EXPORT_virtools.py:33
msgid "No selected target!"
msgstr ""
#: extensions/user_default/bbp_ng/OP_EXPORT_virtools.py:38
#: extensions/user_default/bbp_ng/OP_EXPORT_virtools.py:39
msgid "You can not specify \"Use Global\" as global texture save option!"
msgstr ""
#: extensions/user_default/bbp_ng/OP_EXPORT_virtools.py:44
#: extensions/user_default/bbp_ng/OP_EXPORT_virtools.py:45
msgid ""
"You must specify at least one encoding for file saving (e.g. cp1252, gbk)!"
msgstr ""
#: extensions/user_default/bbp_ng/OP_EXPORT_virtools.py:50
#: extensions/user_default/bbp_ng/OP_EXPORT_virtools.py:60
#: extensions/user_default/bbp_ng/OP_IMPORT_virtools.py:38
msgid "No file was selected!"
msgstr ""
#: extensions/user_default/bbp_ng/OP_IMPORT_virtools.py:96
#: extensions/user_default/bbp_ng/OP_IMPORT_virtools.py:97
msgctxt "BBP_OT_import_virtools/execute"
msgid "Loading Textures"
msgstr ""
#: extensions/user_default/bbp_ng/OP_IMPORT_virtools.py:166
#: extensions/user_default/bbp_ng/OP_IMPORT_virtools.py:167
msgctxt "BBP_OT_import_virtools/execute"
msgid "Loading Materials"
msgstr ""
#: extensions/user_default/bbp_ng/OP_IMPORT_virtools.py:233
#: extensions/user_default/bbp_ng/OP_IMPORT_virtools.py:234
msgctxt "BBP_OT_import_virtools/execute"
msgid "Loading Meshes"
msgstr ""
#: extensions/user_default/bbp_ng/OP_IMPORT_virtools.py:331
#: extensions/user_default/bbp_ng/OP_IMPORT_virtools.py:332
msgctxt "BBP_OT_import_virtools/execute"
msgid "Loading 3dObjects"
msgstr ""
#: extensions/user_default/bbp_ng/OP_IMPORT_virtools.py:376
#: extensions/user_default/bbp_ng/OP_IMPORT_virtools.py:377
msgctxt "BBP_OT_import_virtools/execute"
msgid "Loading Lights"
msgstr ""
#: extensions/user_default/bbp_ng/OP_IMPORT_virtools.py:438
#: extensions/user_default/bbp_ng/OP_IMPORT_virtools.py:433
msgctxt "BBP_OT_import_virtools/execute"
msgid "Loading Cameras"
msgstr ""
#: extensions/user_default/bbp_ng/OP_IMPORT_virtools.py:486
msgctxt "BBP_OT_import_virtools/execute"
msgid "Loading Groups"
msgstr ""
#: extensions/user_default/bbp_ng/OP_IMPORT_virtools.py:474
#: extensions/user_default/bbp_ng/OP_IMPORT_virtools.py:522
msgctxt "BBP_OT_import_virtools/execute"
msgid "Applying Groups"
msgstr ""
@@ -3693,7 +3863,7 @@ msgctxt "BBP_OT_import_virtools/execute"
msgid "Virtools Engine Temporary Directory: {0}"
msgstr ""
#: extensions/user_default/bbp_ng/OP_IMPORT_virtools.py:102
#: extensions/user_default/bbp_ng/OP_IMPORT_virtools.py:103
msgctxt "BBP_OT_import_virtools/execute"
msgid "Texture Raw Data Temporary Directory: {0}"
msgstr ""
@@ -3708,21 +3878,26 @@ msgctxt "BBP_OT_fix_all_materials/draw"
msgid "Fix {0}/{1} materials."
msgstr ""
#: extensions/user_default/bbp_ng/OP_OBJECT_game_view.py:248
#: extensions/user_default/bbp_ng/OP_OBJECT_game_view.py:262
msgctxt "BBP_OT_game_camera/draw"
msgid "Target"
msgstr ""
#: extensions/user_default/bbp_ng/OP_OBJECT_game_view.py:253
#: extensions/user_default/bbp_ng/OP_OBJECT_game_view.py:267
msgctxt "BBP_OT_game_camera/draw"
msgid "Rotation"
msgstr ""
#: extensions/user_default/bbp_ng/OP_OBJECT_game_view.py:264
#: extensions/user_default/bbp_ng/OP_OBJECT_game_view.py:290
msgctxt "BBP_OT_game_camera/draw"
msgid "Perspective"
msgstr ""
#: extensions/user_default/bbp_ng/OP_OBJECT_game_view.py:295
msgctxt "BBP_OT_game_camera/draw"
msgid "Resolution"
msgstr ""
#: extensions/user_default/bbp_ng/OP_OBJECT_legacy_align.py:208
msgctxt "BBP_OT_legacy_align/draw"
msgid "Align Axis (Multi-selection)"
@@ -3845,175 +4020,210 @@ msgstr ""
msgid "Reset BME materials successfully."
msgstr ""
#: extensions/user_default/bbp_ng/PROP_preferences.py:42
#: extensions/user_default/bbp_ng/PROP_preferences.py:40
msgctxt "BBPPreferences/draw"
msgid "Ballance Texture Folder"
msgstr ""
#: extensions/user_default/bbp_ng/PROP_preferences.py:44
#: extensions/user_default/bbp_ng/PROP_preferences.py:42
msgctxt "BBPPreferences/draw"
msgid "No Component Collection"
msgstr ""
#: extensions/user_default/bbp_ng/PROP_virtools_group.py:392
msgctxt "BBP_PT_virtools_groups/draw"
msgid "Virtools Group is invalid on non-mesh object!"
#: extensions/user_default/bbp_ng/PROP_virtools_camera.py:339
msgctxt "BBP_PT_virtools_camera/draw"
msgid "Apply"
msgstr ""
#: extensions/user_default/bbp_ng/PROP_virtools_light.py:287
#: extensions/user_default/bbp_ng/PROP_virtools_camera.py:342
msgctxt "BBP_PT_virtools_camera/draw"
msgid "Apply Resolution"
msgstr ""
#: extensions/user_default/bbp_ng/PROP_virtools_camera.py:350
msgctxt "BBP_PT_virtools_camera/draw"
msgid "Clipping"
msgstr ""
#: extensions/user_default/bbp_ng/PROP_virtools_camera.py:375
msgctxt "BBP_PT_virtools_camera/draw"
msgid "Aspect Ratio"
msgstr ""
#: extensions/user_default/bbp_ng/PROP_virtools_camera.py:359
msgctxt "BBP_PT_virtools_camera/draw"
msgid "Perspective Parameters"
msgstr ""
#: extensions/user_default/bbp_ng/PROP_virtools_camera.py:367
msgctxt "BBP_PT_virtools_camera/draw"
msgid "Orthographic Parameters"
msgstr ""
#: extensions/user_default/bbp_ng/PROP_virtools_group.py:392
msgctxt "BBP_PT_virtools_groups/draw"
msgid "Virtools Group is invalid on non-mesh-like object!"
msgstr ""
#: extensions/user_default/bbp_ng/PROP_virtools_light.py:260
msgctxt "BBP_PT_virtools_light/draw"
msgid "Apply"
msgstr ""
#: extensions/user_default/bbp_ng/PROP_virtools_light.py:292
#: extensions/user_default/bbp_ng/PROP_virtools_light.py:265
msgctxt "BBP_PT_virtools_light/draw"
msgid "Basics"
msgstr ""
#: extensions/user_default/bbp_ng/PROP_virtools_light.py:305
#: extensions/user_default/bbp_ng/PROP_virtools_light.py:278
msgctxt "BBP_PT_virtools_light/draw"
msgid "Attenuation"
msgstr ""
#: extensions/user_default/bbp_ng/PROP_virtools_light.py:306
#: extensions/user_default/bbp_ng/PROP_virtools_light.py:279
msgctxt "BBP_PT_virtools_light/draw"
msgid "Constant"
msgstr ""
#: extensions/user_default/bbp_ng/PROP_virtools_light.py:307
#: extensions/user_default/bbp_ng/PROP_virtools_light.py:280
msgctxt "BBP_PT_virtools_light/draw"
msgid "Linear"
msgstr ""
#: extensions/user_default/bbp_ng/PROP_virtools_light.py:308
#: extensions/user_default/bbp_ng/PROP_virtools_light.py:281
msgctxt "BBP_PT_virtools_light/draw"
msgid "Quadratic"
msgstr ""
#: extensions/user_default/bbp_ng/PROP_virtools_light.py:313
#: extensions/user_default/bbp_ng/PROP_virtools_light.py:286
msgctxt "BBP_PT_virtools_light/draw"
msgid "Spot Cone"
msgstr ""
#: extensions/user_default/bbp_ng/PROP_virtools_material.py:1056
#: extensions/user_default/bbp_ng/PROP_virtools_material.py:1001
msgctxt "BBP_PT_virtools_material/draw"
msgid "Preset"
msgstr ""
#: extensions/user_default/bbp_ng/PROP_virtools_material.py:1059
#: extensions/user_default/bbp_ng/PROP_virtools_material.py:1004
msgctxt "BBP_PT_virtools_material/draw"
msgid "Apply"
msgstr ""
#: extensions/user_default/bbp_ng/PROP_virtools_material.py:1064
#: extensions/user_default/bbp_ng/PROP_virtools_material.py:1009
msgctxt "BBP_PT_virtools_material/draw"
msgid "Color Parameters"
msgstr ""
#: extensions/user_default/bbp_ng/PROP_virtools_material.py:1072
#: extensions/user_default/bbp_ng/PROP_virtools_material.py:1017
msgctxt "BBP_PT_virtools_material/draw"
msgid "Mode Parameters"
msgstr ""
#: extensions/user_default/bbp_ng/PROP_virtools_material.py:1078
#: extensions/user_default/bbp_ng/PROP_virtools_material.py:1023
msgctxt "BBP_PT_virtools_material/draw"
msgid "Texture Parameters"
msgstr ""
#: extensions/user_default/bbp_ng/PROP_virtools_material.py:1099
#: extensions/user_default/bbp_ng/PROP_virtools_material.py:1044
msgctxt "BBP_PT_virtools_material/draw"
msgid "Alpha Test Parameters"
msgstr ""
#: extensions/user_default/bbp_ng/PROP_virtools_material.py:1106
#: extensions/user_default/bbp_ng/PROP_virtools_material.py:1051
msgctxt "BBP_PT_virtools_material/draw"
msgid "Alpha Blend Parameters"
msgstr ""
#: extensions/user_default/bbp_ng/PROP_virtools_material.py:1113
#: extensions/user_default/bbp_ng/PROP_virtools_material.py:1058
msgctxt "BBP_PT_virtools_material/draw"
msgid "Z Write Parameters"
msgstr ""
#: extensions/user_default/bbp_ng/PROP_virtools_material.py:939
#: extensions/user_default/bbp_ng/PROP_virtools_material.py:884
msgid "Fix material successfully."
msgstr ""
#: extensions/user_default/bbp_ng/PROP_virtools_material.py:942
#: extensions/user_default/bbp_ng/PROP_virtools_material.py:887
msgid "This material is not suit for fixer."
msgstr ""
#: extensions/user_default/bbp_ng/PROP_virtools_material.py:1087
#: extensions/user_default/bbp_ng/PROP_virtools_material.py:1032
msgctxt "BBP_PT_virtools_material/draw"
msgid "Virtools Texture Settings"
msgstr ""
#: extensions/user_default/bbp_ng/UTIL_ioport_shared.py:168
#: extensions/user_default/bbp_ng/UTIL_ioport_shared.py:195
msgctxt "BBP/UTIL_ioport_shared.ImportParams/draw"
msgid "Import Parameters"
msgstr ""
#: extensions/user_default/bbp_ng/UTIL_ioport_shared.py:172
#: extensions/user_default/bbp_ng/UTIL_ioport_shared.py:199
msgctxt "BBP/UTIL_ioport_shared.ImportParams/draw"
msgid "Name Conflict Strategy"
msgstr ""
#: extensions/user_default/bbp_ng/UTIL_ioport_shared.py:174
#: extensions/user_default/bbp_ng/UTIL_ioport_shared.py:201
msgctxt "BBP/UTIL_ioport_shared.ImportParams/draw"
msgid "Object"
msgstr ""
#: extensions/user_default/bbp_ng/UTIL_ioport_shared.py:175
#: extensions/user_default/bbp_ng/UTIL_ioport_shared.py:202
msgctxt "BBP/UTIL_ioport_shared.ImportParams/draw"
msgid "Light"
msgstr ""
#: extensions/user_default/bbp_ng/UTIL_ioport_shared.py:176
#: extensions/user_default/bbp_ng/UTIL_ioport_shared.py:203
msgctxt "BBP/UTIL_ioport_shared.ImportParams/draw"
msgid "Camera"
msgstr ""
#: extensions/user_default/bbp_ng/UTIL_ioport_shared.py:204
msgctxt "BBP/UTIL_ioport_shared.ImportParams/draw"
msgid "Mesh"
msgstr ""
#: extensions/user_default/bbp_ng/UTIL_ioport_shared.py:177
#: extensions/user_default/bbp_ng/UTIL_ioport_shared.py:205
msgctxt "BBP/UTIL_ioport_shared.ImportParams/draw"
msgid "Material"
msgstr ""
#: extensions/user_default/bbp_ng/UTIL_ioport_shared.py:178
#: extensions/user_default/bbp_ng/UTIL_ioport_shared.py:206
msgctxt "BBP/UTIL_ioport_shared.ImportParams/draw"
msgid "Texture"
msgstr ""
#: extensions/user_default/bbp_ng/UTIL_ioport_shared.py:277
#: extensions/user_default/bbp_ng/UTIL_ioport_shared.py:310
msgctxt "BBP/UTIL_ioport_shared.ExportParams/draw"
msgid "Export Parameters"
msgstr ""
#: extensions/user_default/bbp_ng/UTIL_ioport_shared.py:359
#: extensions/user_default/bbp_ng/UTIL_ioport_shared.py:392
msgctxt "BBP/UTIL_ioport_shared.VirtoolsParams/draw"
msgid "Virtools Parameters"
msgstr ""
#: extensions/user_default/bbp_ng/UTIL_ioport_shared.py:363
#: extensions/user_default/bbp_ng/UTIL_ioport_shared.py:396
msgctxt "BBP/UTIL_ioport_shared.VirtoolsParams/draw"
msgid "Encodings"
msgstr ""
#: extensions/user_default/bbp_ng/UTIL_ioport_shared.py:414
#: extensions/user_default/bbp_ng/UTIL_ioport_shared.py:447
msgctxt "BBP/UTIL_ioport_shared.BallanceParams/draw"
msgid "Ballance Parameters"
msgstr ""
#: extensions/user_default/bbp_ng/UTIL_ioport_shared.py:420
#: extensions/user_default/bbp_ng/UTIL_ioport_shared.py:453
msgctxt "BBP/UTIL_ioport_shared.BallanceParams/draw"
msgid "Map Sectors: {0}"
msgstr ""
#: extensions/user_default/bbp_ng/UTIL_ioport_shared.py:370
#: extensions/user_default/bbp_ng/UTIL_ioport_shared.py:403
msgctxt "BBP/UTIL_ioport_shared.VirtoolsParams/draw"
msgid "Global Texture Save Options"
msgstr ""
#: extensions/user_default/bbp_ng/UTIL_ioport_shared.py:374
#: extensions/user_default/bbp_ng/UTIL_ioport_shared.py:407
msgctxt "BBP/UTIL_ioport_shared.VirtoolsParams/draw"
msgid "Compression"
msgstr ""
@@ -4115,7 +4325,7 @@ msgid ""
"Can't distinguish object between Floors and Rails. Suppose it is Floors."
msgstr ""
#: extensions/user_default/bbp_ng/UTIL_virtools_types.py:273
#: extensions/user_default/bbp_ng/UTIL_virtools_types.py:303
msgctxt "BME/UTIL_virtools_types.virtools_name_regulator()"
msgid "annoymous"
msgstr ""
@@ -4155,47 +4365,47 @@ msgctxt "BBP/__init__.reuse_draw_add_component()"
msgid "Components Pair"
msgstr ""
#: extensions/user_default/bbp_ng/__init__.py:206
#: extensions/user_default/bbp_ng/__init__.py:205
msgctxt "BBP/__init__.menu_drawer_import()"
msgid "Virtools File (.nmo/.cmo/.vmo) (experimental)"
msgstr ""
#: extensions/user_default/bbp_ng/__init__.py:219
#: extensions/user_default/bbp_ng/__init__.py:218
msgctxt "BBP/__init__.menu_drawer_export()"
msgid "Virtools File (.nmo/.cmo/.vmo) (experimental)"
msgstr ""
#: extensions/user_default/bbp_ng/__init__.py:230
#: extensions/user_default/bbp_ng/__init__.py:229
msgctxt "BBP/__init__.menu_drawer_add()"
msgid "Ballance"
msgstr ""
#: extensions/user_default/bbp_ng/__init__.py:247
#: extensions/user_default/bbp_ng/__init__.py:246
msgctxt "BBP/__init__.menu_drawer_grouping()"
msgid "Virtools Group"
msgstr ""
#: extensions/user_default/bbp_ng/__init__.py:249
#: extensions/user_default/bbp_ng/__init__.py:248
msgctxt "BBP/__init__.menu_drawer_grouping()"
msgid "Group into..."
msgstr ""
#: extensions/user_default/bbp_ng/__init__.py:252
#: extensions/user_default/bbp_ng/__init__.py:251
msgctxt "BBP/__init__.menu_drawer_grouping()"
msgid "Ungroup from..."
msgstr ""
#: extensions/user_default/bbp_ng/__init__.py:255
#: extensions/user_default/bbp_ng/__init__.py:254
msgctxt "BBP/__init__.menu_drawer_grouping()"
msgid "Clear All Groups"
msgstr ""
#: extensions/user_default/bbp_ng/__init__.py:261
#: extensions/user_default/bbp_ng/__init__.py:260
msgctxt "BBP/__init__.menu_drawer_snoop_then_conv()"
msgid "Ballance"
msgstr ""
#: extensions/user_default/bbp_ng/__init__.py:273
#: extensions/user_default/bbp_ng/__init__.py:272
msgctxt "BBP/__init__.menu_drawer_naming_convention()"
msgid "Ballance"
msgstr ""
@@ -4215,12 +4425,12 @@ msgctxt "BBP_MT_View3DMenu/draw"
msgid "Camera"
msgstr ""
#: extensions/user_default/bbp_ng/__init__.py:185
#: extensions/user_default/bbp_ng/__init__.py:184
msgctxt "BBP_MT_View3DMenu/draw"
msgid "Select"
msgstr ""
#: extensions/user_default/bbp_ng/__init__.py:188
#: extensions/user_default/bbp_ng/__init__.py:187
msgctxt "BBP_MT_View3DMenu/draw"
msgid "Material"
msgstr ""

File diff suppressed because it is too large Load Diff