Compare commits
14 Commits
v3.0-alpha
...
v3.1
Author | SHA1 | Date | |
---|---|---|---|
0be036fcea | |||
e153e51abd | |||
d292ce389a | |||
807e006245 | |||
8d7a982e50 | |||
ddf6b7befe | |||
a300ddbb49 | |||
c9e51c9b6a | |||
b58f837a94 | |||
5fe865c621 | |||
9b9fc9cde8 | |||
ef459a210d | |||
7680d11c0e | |||
e7376a3e9c |
2
.gitattributes
vendored
@ -1,3 +1,5 @@
|
|||||||
|
# all png are binary
|
||||||
|
*.png binary
|
||||||
# our generated mesh should be save as binary
|
# our generated mesh should be save as binary
|
||||||
*.bin binary
|
*.bin binary
|
||||||
# json is data and not good for human reading(althought I edit it on my own hand.)
|
# json is data and not good for human reading(althought I edit it on my own hand.)
|
||||||
|
26
README.md
@ -13,9 +13,9 @@ This plugin contain various aspect of Ballance mapping. However, if some feature
|
|||||||
|
|
||||||
## Technical Infomation
|
## Technical Infomation
|
||||||
|
|
||||||
Used BM file spec can be found in [there](https://github.com/yyc12345/gist/blob/master/BMFileSpec/BMSpec_ZH.md)(Chinese only).
|
Used BM file spec can be found in [there](https://github.com/yyc12345/gist/blob/master/BMFileSpec/BMSpec_ZH.md) (Chinese only).
|
||||||
Used tools chain principle and the file format located in `meshes` can be found in [there](https://github.com/yyc12345/gist/blob/master/BMFileSpec/YYCToolsChainSpec_ZH.md)(Chinese only).
|
Used tools chain principle and the file format located in `meshes` can be found in [there](https://github.com/yyc12345/gist/blob/master/BMFileSpec/YYCToolsChainSpec_ZH.md) (Chinese only).
|
||||||
The format of the files which are under the `jsons` folder and belong to the BMERevenge section, can be found in [here](https://github.com/yyc12345/gist/blob/master/BMERevenge/DevDocument_ZH.md)
|
The format of the files which are under the `jsons` folder and belong to the BMERevenge section, can be found in [here](https://github.com/yyc12345/gist/blob/master/BMERevenge/DevDocument_ZH.md) (Chinese only).
|
||||||
|
|
||||||
This plugin will continuously support Blender lastest **LTS** version. This plugin will migrate to new version when the new LTS version released. Currently, it based on Blender **3.3.x**.
|
This plugin will continuously support Blender lastest **LTS** version. This plugin will migrate to new version when the new LTS version released. Currently, it based on Blender **3.3.x**.
|
||||||
|
|
||||||
@ -109,26 +109,22 @@ Navigate to `Material Properties` panel, select a material, you can find `Virtoo
|
|||||||
In default, user created material will not enable Virtools Material feature. You need to click checkbox of `Virtools Material` panel to enable or disable it.
|
In default, user created material will not enable Virtools Material feature. You need to click checkbox of `Virtools Material` panel to enable or disable it.
|
||||||
|
|
||||||
After enable Virtools Material, `Basic Parameters` section and `Advanced Parameters` section can be set. Set your material peroperties just like operating in Virtools.
|
After enable Virtools Material, `Basic Parameters` section and `Advanced Parameters` section can be set. Set your material peroperties just like operating in Virtools.
|
||||||
Just like its name, `Basic Parameters` is basic material properties. `Advanced Parameters` is mainly related to transparent properties and usually used in the bottom of transparent column.
|
Just like its name, `Basic Parameters` is basic material properties. `Advanced Parameters` is mainly related to transparent properties and usually used in the bottom of transparent column.
|
||||||
|
Additionally, `Basic Parameters` section provide a preset function, allowing user to use some preset material settings, which only affect 4 basic colors, just for convenient using.
|
||||||
|
|
||||||
In `Operation` section, `Apply Virtools Material` will clean all existed Blender material and create a new material graph according to Virtools material properties.
|
In `Operation` section, `Apply Virtools Material` will clean all existed Blender material and create a new material graph according to Virtools material properties.
|
||||||
And, `Parse from Blender Principled BSDF` will try parsing a Principled BSDF to Virtools material.
|
And, `Parse from Blender Principled BSDF` will try parsing a Principled BSDF to Virtools material.
|
||||||
If your material highly rely on Blender material, please execute `Parse from Blender Principled BSDF` or disable Virtools Material feature before exporting BM file, otherwise material can not be saved correctly.
|
If your material highly rely on Blender material, please execute `Parse from Blender Principled BSDF` or disable Virtools Material feature before exporting BM file, otherwise material can not be saved correctly.
|
||||||
|
|
||||||
### Select by Group
|
### Select by Virtools Group
|
||||||
|
|
||||||
Plugin add 2 selection functions according to Virtools Group in Select menu.
|
Plugin add a selection function according to Virtools Group in Select menu.
|
||||||
|
|
||||||
#### Select by Virtools Group
|
This function firstly have 5 different selection strategies which is exactly matched with Blender selection method. Just use it like Blender selection (Set, Extend, Subtract, Invert, Intersect).
|
||||||
|
Then, select your group name to start a selection.
|
||||||
|
|
||||||
Select objects in active collection according to its Virtools Group properties.
|
If you can, using Subtract or Intersect modes would be better than other modes. Because these modes avoid analyzing too many objects.
|
||||||
The hidden object also can be selected if you check `Ignore Hide Property`.
|
For example, first, select a rough range, and then use the Intersect mode to filter objects, which is more efficient than directly using the Start mode to select.
|
||||||
Check `Merge Selection` will merge current selection and previous selection.
|
|
||||||
|
|
||||||
#### Filter by Virtools Group
|
|
||||||
|
|
||||||
Filter current selected object by its Virtools Group properties.
|
|
||||||
Check `Reverse` remove objects matching the requirements, not keep them.
|
|
||||||
|
|
||||||
### Quick Grouping
|
### Quick Grouping
|
||||||
|
|
||||||
|
21
README_ZH.md
@ -109,7 +109,8 @@ Ballance 3D是一套简单的用于制图3D相关的轻型工具集合,可以
|
|||||||
默认情况下,由用户创建的材质不启用Virtools Material,您可以通过点击`Virtools Material`面板的复选框来启用或关闭它。
|
默认情况下,由用户创建的材质不启用Virtools Material,您可以通过点击`Virtools Material`面板的复选框来启用或关闭它。
|
||||||
|
|
||||||
在启用Virtools Material后,可以在`Basic Parameters`和`Advanced Parameters`中设置材质属性,就像在Virtools中操作一般。
|
在启用Virtools Material后,可以在`Basic Parameters`和`Advanced Parameters`中设置材质属性,就像在Virtools中操作一般。
|
||||||
`Basic Parameters`是基础材质属性。`Advanced Parameters`则是与透明相关的材质属性,主要用于半透明柱子底部等。
|
`Basic Parameters`是基础材质属性。`Advanced Parameters`则是与透明相关的材质属性,主要用于半透明柱子底部等。
|
||||||
|
另外,`Basic Parameters`部分提供了预设功能,允许用户使用一些预设的材质设置,这些设置只影响4种基本颜色,方便使用。
|
||||||
|
|
||||||
`Operation`中的`Apply Virtools Material`将把Virtools Material应用到Blender材质上。
|
`Operation`中的`Apply Virtools Material`将把Virtools Material应用到Blender材质上。
|
||||||
而`Parse from Blender Principled BSDF`将尝试将一个原理化BSDF转换为Virtools材质数据。
|
而`Parse from Blender Principled BSDF`将尝试将一个原理化BSDF转换为Virtools材质数据。
|
||||||
@ -117,21 +118,13 @@ Ballance 3D是一套简单的用于制图3D相关的轻型工具集合,可以
|
|||||||
|
|
||||||
### 按组选择
|
### 按组选择
|
||||||
|
|
||||||
选择菜单中新增了两项按照Virtools归组数据进行筛选的功能。
|
选择菜单中新增了一项按照Virtools归组数据进行筛选的功能。
|
||||||
|
|
||||||
#### Select by Virtools Group
|
该功能首先有5种不同的选择策略,与Blender的选择方法完全匹配(开始、扩选、相减、反转、相交)。只需像Blender选择那样使用它。
|
||||||
|
然后,选择你需要的组的名称,然后开始一次选择或筛选。
|
||||||
|
|
||||||
将对当前活动集合内的物体按照其Virtools Group属性进行选择。
|
如果可以,请尽可能使用相减或相交模式。因为这样可以避免分析过多的物体。
|
||||||
勾选`Ignore Hide Property`后,即使是隐藏的物体,也会被筛选。
|
例如先选定一个大致的范围,然后使用相交模式过滤,比直接使用开始模式效率更高。
|
||||||
勾选`Merge Selection`,将会把选中的物体合并到当前选定的内容中。
|
|
||||||
|
|
||||||
#### Filter by Virtools Group
|
|
||||||
|
|
||||||
将会按照Virtools Group属性,过滤当前选中物体。
|
|
||||||
勾选`Reverse`将会反向操作,即去除掉符合条件的物体。
|
|
||||||
|
|
||||||
如果可以,请尽可能使用`Filter by Virtools Group`而不是`Select by Virtools Group`。因为这样可以避免分析过多的物体。
|
|
||||||
例如先选定一个大致的范围,然后使用`Filter by Virtools Group`过滤,比直接使用`Select by Virtools Group`效率更高。
|
|
||||||
|
|
||||||
### 快速归组
|
### 快速归组
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ import bpy,bmesh,bpy_extras,mathutils
|
|||||||
import pathlib,zipfile,time,os,tempfile,math
|
import pathlib,zipfile,time,os,tempfile,math
|
||||||
import struct, shutil
|
import struct, shutil
|
||||||
from bpy_extras import io_utils, node_shader_utils
|
from bpy_extras import io_utils, node_shader_utils
|
||||||
from . import UTILS_constants, UTILS_functions, UTILS_file_io, UTILS_zip_helper, UTILS_virtools_prop
|
from . import UTILS_constants, UTILS_functions, UTILS_file_io, UTILS_zip_helper, UTILS_virtools_prop, UTILS_icons_manager
|
||||||
|
|
||||||
class BALLANCE_OT_export_bm(bpy.types.Operator, bpy_extras.io_utils.ExportHelper):
|
class BALLANCE_OT_export_bm(bpy.types.Operator, bpy_extras.io_utils.ExportHelper):
|
||||||
"""Save a Ballance Map File (BM file spec 1.4)"""
|
"""Save a Ballance Map File (BM file spec 1.4)"""
|
||||||
@ -26,9 +26,15 @@ class BALLANCE_OT_export_bm(bpy.types.Operator, bpy_extras.io_utils.ExportHelper
|
|||||||
)
|
)
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
|
# detect edit mode
|
||||||
|
in_edit_mode = False
|
||||||
|
if bpy.context.object and bpy.context.object.mode == "EDIT":
|
||||||
|
in_edit_mode = True
|
||||||
|
bpy.ops.object.editmode_toggle()
|
||||||
|
|
||||||
if ((self.export_mode == 'COLLECTION' and context.scene.BallanceBlenderPluginProperty.collection_picker is None) or
|
if ((self.export_mode == 'COLLECTION' and context.scene.BallanceBlenderPluginProperty.collection_picker is None) or
|
||||||
(self.export_mode == 'OBJECT' and context.scene.BallanceBlenderPluginProperty.object_picker is None)):
|
(self.export_mode == 'OBJECT' and context.scene.BallanceBlenderPluginProperty.object_picker is None)):
|
||||||
UTILS_functions.show_message_box(("No specific target", ), "Lost parameter", 'ERROR')
|
UTILS_functions.show_message_box(("No specific target", ), "Lost parameter", UTILS_icons_manager.blender_error_icon)
|
||||||
else:
|
else:
|
||||||
prefs = bpy.context.preferences.addons[__package__].preferences
|
prefs = bpy.context.preferences.addons[__package__].preferences
|
||||||
|
|
||||||
@ -40,6 +46,12 @@ class BALLANCE_OT_export_bm(bpy.types.Operator, bpy_extras.io_utils.ExportHelper
|
|||||||
export_bm(context, self.filepath,
|
export_bm(context, self.filepath,
|
||||||
prefs.no_component_collection,
|
prefs.no_component_collection,
|
||||||
self.export_mode, context.scene.BallanceBlenderPluginProperty.object_picker)
|
self.export_mode, context.scene.BallanceBlenderPluginProperty.object_picker)
|
||||||
|
|
||||||
|
# restore edit mode
|
||||||
|
if in_edit_mode:
|
||||||
|
bpy.ops.object.editmode_toggle()
|
||||||
|
|
||||||
|
self.report({'INFO'}, "BM File Export Finished.")
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
|
@ -4,7 +4,7 @@ import struct, shutil
|
|||||||
from bpy_extras import io_utils,node_shader_utils
|
from bpy_extras import io_utils,node_shader_utils
|
||||||
from bpy_extras.io_utils import unpack_list
|
from bpy_extras.io_utils import unpack_list
|
||||||
from bpy_extras.image_utils import load_image
|
from bpy_extras.image_utils import load_image
|
||||||
from . import UTILS_constants, UTILS_functions, UTILS_file_io, UTILS_zip_helper, UTILS_virtools_prop
|
from . import UTILS_constants, UTILS_functions, UTILS_file_io, UTILS_zip_helper, UTILS_virtools_prop, UTILS_icons_manager
|
||||||
|
|
||||||
class BALLANCE_OT_import_bm(bpy.types.Operator, bpy_extras.io_utils.ImportHelper):
|
class BALLANCE_OT_import_bm(bpy.types.Operator, bpy_extras.io_utils.ImportHelper):
|
||||||
"""Load a Ballance Map File (BM file spec 1.4)"""
|
"""Load a Ballance Map File (BM file spec 1.4)"""
|
||||||
@ -63,6 +63,8 @@ class BALLANCE_OT_import_bm(bpy.types.Operator, bpy_extras.io_utils.ImportHelper
|
|||||||
prefs.no_component_collection, prefs.external_folder, prefs.temp_texture_folder,
|
prefs.no_component_collection, prefs.external_folder, prefs.temp_texture_folder,
|
||||||
self.texture_conflict_strategy, self.material_conflict_strategy,
|
self.texture_conflict_strategy, self.material_conflict_strategy,
|
||||||
self.mesh_conflict_strategy, self.object_conflict_strategy)
|
self.mesh_conflict_strategy, self.object_conflict_strategy)
|
||||||
|
|
||||||
|
self.report({'INFO'}, "BM File Import Finished.")
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
@ -89,7 +91,7 @@ def import_bm(context, bmx_filepath, prefs_fncg, prefs_externalTexture, prefs_te
|
|||||||
# clean temp folder, output error
|
# clean temp folder, output error
|
||||||
UTILS_functions.show_message_box(
|
UTILS_functions.show_message_box(
|
||||||
("Unsupported BM spec. Expect: {} Gotten: {}".format(UTILS_constants.bmfile_currentVersion, index_gottenVersion), ),
|
("Unsupported BM spec. Expect: {} Gotten: {}".format(UTILS_constants.bmfile_currentVersion, index_gottenVersion), ),
|
||||||
"Unsupported BM spec", 'ERROR')
|
"Unsupported BM spec", UTILS_icons_manager.blender_error_icon)
|
||||||
findex.close()
|
findex.close()
|
||||||
utils_tempFolderObj.cleanup()
|
utils_tempFolderObj.cleanup()
|
||||||
return
|
return
|
||||||
|
@ -5,7 +5,7 @@ class BALLANCE_OT_super_align(bpy.types.Operator):
|
|||||||
"""Align object with 3ds Max style"""
|
"""Align object with 3ds Max style"""
|
||||||
bl_idname = "ballance.super_align"
|
bl_idname = "ballance.super_align"
|
||||||
bl_label = "3ds Max Align"
|
bl_label = "3ds Max Align"
|
||||||
bl_options = {'UNDO'}
|
bl_options = {'REGISTER', 'UNDO'}
|
||||||
|
|
||||||
align_x: bpy.props.BoolProperty(name="X position")
|
align_x: bpy.props.BoolProperty(name="X position")
|
||||||
align_y: bpy.props.BoolProperty(name="Y position")
|
align_y: bpy.props.BoolProperty(name="Y position")
|
||||||
@ -18,7 +18,8 @@ class BALLANCE_OT_super_align(bpy.types.Operator):
|
|||||||
('POINT', "Center (axis)", ""),
|
('POINT', "Center (axis)", ""),
|
||||||
('MAX', "Max", "")
|
('MAX', "Max", "")
|
||||||
),
|
),
|
||||||
)
|
default='POINT',
|
||||||
|
)
|
||||||
|
|
||||||
target_references: bpy.props.EnumProperty(
|
target_references: bpy.props.EnumProperty(
|
||||||
name="Target (Other Objects)",
|
name="Target (Other Objects)",
|
||||||
@ -27,7 +28,8 @@ class BALLANCE_OT_super_align(bpy.types.Operator):
|
|||||||
('POINT', "Center (axis)", ""),
|
('POINT', "Center (axis)", ""),
|
||||||
('MAX', "Max", "")
|
('MAX', "Max", "")
|
||||||
),
|
),
|
||||||
)
|
default='POINT',
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(self, context):
|
def poll(self, context):
|
||||||
@ -37,9 +39,11 @@ class BALLANCE_OT_super_align(bpy.types.Operator):
|
|||||||
_align_object(self.align_x, self.align_y, self.align_z, self.current_references, self.target_references)
|
_align_object(self.align_x, self.align_y, self.align_z, self.current_references, self.target_references)
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
"""
|
||||||
def invoke(self, context, event):
|
def invoke(self, context, event):
|
||||||
wm = context.window_manager
|
wm = context.window_manager
|
||||||
return wm.invoke_props_dialog(self)
|
return wm.invoke_props_dialog(self)
|
||||||
|
"""
|
||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
layout = self.layout
|
layout = self.layout
|
||||||
|
@ -1,12 +1,26 @@
|
|||||||
import bpy,mathutils
|
import bpy,mathutils
|
||||||
import bmesh
|
import bmesh
|
||||||
|
import math
|
||||||
from . import UTILS_functions
|
from . import UTILS_functions
|
||||||
|
|
||||||
class BALLANCE_OT_flatten_uv(bpy.types.Operator):
|
class BALLANCE_OT_flatten_uv(bpy.types.Operator):
|
||||||
"""Flatten selected face UV. Only works for convex face"""
|
"""Flatten selected face UV. Only works for convex face"""
|
||||||
bl_idname = "ballance.flatten_uv"
|
bl_idname = "ballance.flatten_uv"
|
||||||
bl_label = "Flatten UV"
|
bl_label = "Flatten UV"
|
||||||
bl_options = {'UNDO'}
|
bl_options = {'REGISTER', 'UNDO'}
|
||||||
|
|
||||||
|
normal_scale_correction = 5.0
|
||||||
|
sink_scale_correction = 5.0 * (math.sqrt(2.5 ** 2 + 0.7 ** 2) / 2.5)
|
||||||
|
|
||||||
|
scale_correction: bpy.props.EnumProperty(
|
||||||
|
name="Scale Correction",
|
||||||
|
description="Choose your UV scale.",
|
||||||
|
items=(
|
||||||
|
("NORMAL", "Normal Floor", "Normal floor scale, 5.0"),
|
||||||
|
("SINK", "Sink Floor", "Sink floor scale, around 5.19")
|
||||||
|
),
|
||||||
|
default='NORMAL',
|
||||||
|
)
|
||||||
|
|
||||||
reference_edge : bpy.props.IntProperty(
|
reference_edge : bpy.props.IntProperty(
|
||||||
name="Reference edge",
|
name="Reference edge",
|
||||||
@ -20,7 +34,7 @@ class BALLANCE_OT_flatten_uv(bpy.types.Operator):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def poll(self, context):
|
def poll(self, context):
|
||||||
obj = bpy.context.active_object
|
obj = bpy.context.active_object
|
||||||
if obj == None:
|
if obj is None:
|
||||||
return False
|
return False
|
||||||
if obj.type != 'MESH':
|
if obj.type != 'MESH':
|
||||||
return False
|
return False
|
||||||
@ -28,24 +42,26 @@ class BALLANCE_OT_flatten_uv(bpy.types.Operator):
|
|||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def invoke(self, context, event):
|
def get_scale_correction(self):
|
||||||
wm = context.window_manager
|
if self.scale_correction == 'NORMAL':
|
||||||
return wm.invoke_props_dialog(self)
|
return BALLANCE_OT_flatten_uv.normal_scale_correction
|
||||||
|
elif self.scale_correction == 'SINK':
|
||||||
|
return BALLANCE_OT_flatten_uv.sink_scale_correction
|
||||||
|
else:
|
||||||
|
raise Exception("Unknow scale correction.")
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
no_processed_count = _real_flatten_uv(bpy.context.active_object.data, self.reference_edge)
|
no_processed_count = _real_flatten_uv(bpy.context.active_object.data, self.reference_edge, self.get_scale_correction())
|
||||||
if no_processed_count != 0:
|
if no_processed_count != 0:
|
||||||
UTILS_functions.show_message_box(
|
print("[Flatten UV] {} faces may not be processed correctly because they have problem.".format(no_processed_count))
|
||||||
("{} faces may not be processed correctly because they have problem.".format(no_processed_count), ),
|
|
||||||
"Warning", 'ERROR'
|
|
||||||
)
|
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
layout = self.layout
|
layout = self.layout
|
||||||
|
layout.prop(self, "scale_correction")
|
||||||
layout.prop(self, "reference_edge")
|
layout.prop(self, "reference_edge")
|
||||||
|
|
||||||
def _real_flatten_uv(mesh, reference_edge):
|
def _real_flatten_uv(mesh, reference_edge, scale_correction):
|
||||||
no_processed_count = 0
|
no_processed_count = 0
|
||||||
|
|
||||||
if mesh.uv_layers.active is None:
|
if mesh.uv_layers.active is None:
|
||||||
@ -58,13 +74,26 @@ def _real_flatten_uv(mesh, reference_edge):
|
|||||||
if not face.select:
|
if not face.select:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# check whether ref edge is legal
|
||||||
allPoint = len(face.loops)
|
allPoint = len(face.loops)
|
||||||
|
|
||||||
if allPoint <= reference_edge:
|
if allPoint <= reference_edge:
|
||||||
no_processed_count+=1
|
no_processed_count+=1
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# get correct new corrdinate system
|
# get correct new corrdinate system
|
||||||
|
# yyc mark:
|
||||||
|
# we use 3 points located in this face to calc
|
||||||
|
# the base of this local uv corredinate system.
|
||||||
|
# however if this 3 points are set in a line,
|
||||||
|
# this method will cause a error, zero vector error.
|
||||||
|
#
|
||||||
|
# if z axis is zero vector, we will try using face normal instead
|
||||||
|
# to try getting correct data.
|
||||||
|
#
|
||||||
|
# zero base is not important. because it will not raise any math exceptio
|
||||||
|
# just a weird uv. user will notice this problem.
|
||||||
|
|
||||||
|
# get point
|
||||||
p1Relative = reference_edge
|
p1Relative = reference_edge
|
||||||
p2Relative = reference_edge + 1
|
p2Relative = reference_edge + 1
|
||||||
p3Relative = reference_edge + 2
|
p3Relative = reference_edge + 2
|
||||||
@ -77,13 +106,19 @@ def _real_flatten_uv(mesh, reference_edge):
|
|||||||
p2=mathutils.Vector(tuple(face.loops[p2Relative].vert.co[x] for x in range(3)))
|
p2=mathutils.Vector(tuple(face.loops[p2Relative].vert.co[x] for x in range(3)))
|
||||||
p3=mathutils.Vector(tuple(face.loops[p3Relative].vert.co[x] for x in range(3)))
|
p3=mathutils.Vector(tuple(face.loops[p3Relative].vert.co[x] for x in range(3)))
|
||||||
|
|
||||||
|
# get y axis
|
||||||
new_y_axis = p2 - p1
|
new_y_axis = p2 - p1
|
||||||
new_y_axis.normalize()
|
new_y_axis.normalize()
|
||||||
vec1 = p3 - p2
|
vec1 = p3 - p2
|
||||||
vec1.normalize()
|
vec1.normalize()
|
||||||
|
|
||||||
|
# get z axis
|
||||||
new_z_axis = new_y_axis.cross(vec1)
|
new_z_axis = new_y_axis.cross(vec1)
|
||||||
new_z_axis.normalize()
|
new_z_axis.normalize()
|
||||||
|
if not any(round(v, 7) for v in new_z_axis):
|
||||||
|
new_z_axis = face.normal.normalized()
|
||||||
|
|
||||||
|
# get x axis
|
||||||
new_x_axis = new_y_axis.cross(new_z_axis)
|
new_x_axis = new_y_axis.cross(new_z_axis)
|
||||||
new_x_axis.normalize()
|
new_x_axis.normalize()
|
||||||
|
|
||||||
@ -93,14 +128,14 @@ def _real_flatten_uv(mesh, reference_edge):
|
|||||||
(0, 1.0, 0),
|
(0, 1.0, 0),
|
||||||
(0, 0, 1.0)
|
(0, 0, 1.0)
|
||||||
))
|
))
|
||||||
origin_base.invert()
|
origin_base.invert_safe()
|
||||||
new_base = mathutils.Matrix((
|
new_base = mathutils.Matrix((
|
||||||
(new_x_axis.x, new_y_axis.x, new_z_axis.x),
|
(new_x_axis.x, new_y_axis.x, new_z_axis.x),
|
||||||
(new_x_axis.y, new_y_axis.y, new_z_axis.y),
|
(new_x_axis.y, new_y_axis.y, new_z_axis.y),
|
||||||
(new_x_axis.z, new_y_axis.z, new_z_axis.z)
|
(new_x_axis.z, new_y_axis.z, new_z_axis.z)
|
||||||
))
|
))
|
||||||
transition_matrix = origin_base @ new_base
|
transition_matrix = origin_base @ new_base
|
||||||
transition_matrix.invert()
|
transition_matrix.invert_safe()
|
||||||
|
|
||||||
# process each face
|
# process each face
|
||||||
for loop_index in range(allPoint):
|
for loop_index in range(allPoint):
|
||||||
@ -108,9 +143,11 @@ def _real_flatten_uv(mesh, reference_edge):
|
|||||||
vec = pp-p1
|
vec = pp-p1
|
||||||
new_vec = transition_matrix @ vec
|
new_vec = transition_matrix @ vec
|
||||||
|
|
||||||
|
# y axis always use 5.0 to scale
|
||||||
|
# however, x need use custom scale correction.
|
||||||
face.loops[loop_index][uv_lay].uv = (
|
face.loops[loop_index][uv_lay].uv = (
|
||||||
(new_vec.x if new_vec.x >=0 else -new_vec.x) / 5,
|
(new_vec.x if new_vec.x >=0 else -new_vec.x) / scale_correction,
|
||||||
(new_vec.y) / 5
|
(new_vec.y) / 5.0
|
||||||
)
|
)
|
||||||
|
|
||||||
# Show the updates in the viewport
|
# Show the updates in the viewport
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import bpy,bmesh
|
import bpy,bmesh
|
||||||
import mathutils
|
import mathutils
|
||||||
import bpy.types
|
import bpy.types
|
||||||
from . import UTILS_functions
|
from . import UTILS_functions, UTILS_icons_manager
|
||||||
|
|
||||||
class BALLANCE_OT_rail_uv(bpy.types.Operator):
|
class BALLANCE_OT_rail_uv(bpy.types.Operator):
|
||||||
"""Create a UV for rail"""
|
"""Create a UV for rail"""
|
||||||
@ -47,7 +47,7 @@ class BALLANCE_OT_rail_uv(bpy.types.Operator):
|
|||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
if context.scene.BallanceBlenderPluginProperty.material_picker == None:
|
if context.scene.BallanceBlenderPluginProperty.material_picker == None:
|
||||||
UTILS_functions.show_message_box(("No specific material", ), "Lost parameter", 'ERROR')
|
UTILS_functions.show_message_box(("No specific material", ), "Lost parameter", UTILS_icons_manager.blender_error_icon)
|
||||||
else:
|
else:
|
||||||
_create_rail_uv(self.uv_type, context.scene.BallanceBlenderPluginProperty.material_picker, self.uv_scale, self.projection_axis)
|
_create_rail_uv(self.uv_type, context.scene.BallanceBlenderPluginProperty.material_picker, self.uv_scale, self.projection_axis)
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
@ -175,7 +175,7 @@ def _create_rail_uv(rail_type, material_pointer, scale_size, projection_axis):
|
|||||||
if len(ignoredObj) != 0:
|
if len(ignoredObj) != 0:
|
||||||
UTILS_functions.show_message_box(
|
UTILS_functions.show_message_box(
|
||||||
("Following objects are not processed due to they are not suit for this function now: ", ) + tuple(ignoredObj),
|
("Following objects are not processed due to they are not suit for this function now: ", ) + tuple(ignoredObj),
|
||||||
"Execution result", 'INFO'
|
"Execution result", UTILS_icons_manager.blender_info_icon
|
||||||
)
|
)
|
||||||
|
|
||||||
def _tt_reflection_mapping_compute(_point, _n, _refobj):
|
def _tt_reflection_mapping_compute(_point, _n, _refobj):
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import bpy
|
import bpy
|
||||||
from . import UTILS_constants, UTILS_functions, UTILS_virtools_prop
|
from . import UTILS_constants, UTILS_functions, UTILS_virtools_prop, UTILS_icons_manager
|
||||||
|
|
||||||
class rename_system_props(bpy.types.Operator):
|
class rename_system_props(bpy.types.Operator):
|
||||||
name_standard: bpy.props.EnumProperty(
|
name_standard: bpy.props.EnumProperty(
|
||||||
@ -69,6 +69,52 @@ class BALLANCE_OT_auto_grouping(rename_system_props):
|
|||||||
# ==========================================
|
# ==========================================
|
||||||
# rename misc funcs
|
# rename misc funcs
|
||||||
|
|
||||||
|
class _RenameErrorType():
|
||||||
|
ERROR = 0
|
||||||
|
WARNING = 1
|
||||||
|
INFO = 2
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def cvt_err_from_int_to_str(err_t):
|
||||||
|
if err_t == _RenameErrorType.ERROR:
|
||||||
|
return "ERROR"
|
||||||
|
elif err_t == _RenameErrorType.WARNING:
|
||||||
|
return "WARNING"
|
||||||
|
elif err_t == _RenameErrorType.INFO:
|
||||||
|
return "INFO"
|
||||||
|
else:
|
||||||
|
raise Exception("Unknow error type.")
|
||||||
|
|
||||||
|
class _RenameErrorItem():
|
||||||
|
def __init__(self, err_t, description):
|
||||||
|
self.err_type = err_t
|
||||||
|
self.description = description
|
||||||
|
|
||||||
|
def get_presentation(self):
|
||||||
|
return "[{}]\t{}".format(_RenameErrorType.cvt_err_from_int_to_str(self.err_type), self.description)
|
||||||
|
|
||||||
|
class _RenameErrorReporter():
|
||||||
|
def __init__(self):
|
||||||
|
self.err_container: list[_RenameErrorItem] = []
|
||||||
|
|
||||||
|
def add_error(self, description):
|
||||||
|
self.err_container.append(_RenameErrorItem(_RenameErrorType.ERROR, description))
|
||||||
|
def add_warning(self, description):
|
||||||
|
self.err_container.append(_RenameErrorItem(_RenameErrorType.WARNING, description))
|
||||||
|
def add_info(self, description):
|
||||||
|
self.err_container.append(_RenameErrorItem(_RenameErrorType.INFO, description))
|
||||||
|
|
||||||
|
def can_report(self):
|
||||||
|
return len(self.err_container) != 0
|
||||||
|
|
||||||
|
def report(self, header):
|
||||||
|
print(header)
|
||||||
|
for i in self.err_container:
|
||||||
|
print('\t' + i.get_presentation())
|
||||||
|
|
||||||
|
def clear(self):
|
||||||
|
self.err_container.clear()
|
||||||
|
|
||||||
class _ObjectBasicType():
|
class _ObjectBasicType():
|
||||||
COMPONENT = 0
|
COMPONENT = 0
|
||||||
|
|
||||||
@ -140,12 +186,12 @@ def _get_sector_from_ckgroup(group_set):
|
|||||||
# YYC Tools Chains name standard is Ballance-compatible name standard.
|
# YYC Tools Chains name standard is Ballance-compatible name standard.
|
||||||
# So this functions also serving for `_get_name_info_from_group` function
|
# So this functions also serving for `_get_name_info_from_group` function
|
||||||
# to help get sector field from PC/PR elements. In ordinary call(external call)
|
# to help get sector field from PC/PR elements. In ordinary call(external call)
|
||||||
# The final error output should be outputed nromally. But in the call from
|
# The final error output should be outputed normally. But in the call from
|
||||||
# `_get_name_info_from_group`, this function should not output any error.
|
# `_get_name_info_from_group`, this function should not output any error.
|
||||||
# So parameter `call_internal` is served for this work. In common it is False
|
# So parameter `call_internal` is served for this work. In common it is False
|
||||||
# to let function output error str normally. But only set it to True in
|
# to let function output error str normally. But only set it to True in
|
||||||
# the call from `_get_name_info_from_group` to disable error output.
|
# the call from `_get_name_info_from_group` to disable error output.
|
||||||
def _get_name_info_from_yyc_name(obj_name, call_internal = False):
|
def _get_name_info_from_yyc_name(obj_name, err_reporter: _RenameErrorReporter, call_internal = False):
|
||||||
|
|
||||||
# check component first
|
# check component first
|
||||||
regex_result = UTILS_constants.rename_regexYYCComponent.match(obj_name)
|
regex_result = UTILS_constants.rename_regexYYCComponent.match(obj_name)
|
||||||
@ -191,11 +237,11 @@ def _get_name_info_from_yyc_name(obj_name, call_internal = False):
|
|||||||
|
|
||||||
# only output in external calling
|
# only output in external calling
|
||||||
if not call_internal:
|
if not call_internal:
|
||||||
print("[ERROR]\t{}:\tName match lost.".format(obj_name))
|
err_reporter.add_error("Name match lost.")
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _get_name_info_from_imengyu_name(obj_name):
|
def _get_name_info_from_imengyu_name(obj_name, err_reporter: _RenameErrorReporter):
|
||||||
|
|
||||||
# check component first
|
# check component first
|
||||||
regex_result = UTILS_constants.rename_regexImengyuComponent.match(obj_name)
|
regex_result = UTILS_constants.rename_regexImengyuComponent.match(obj_name)
|
||||||
@ -238,10 +284,10 @@ def _get_name_info_from_imengyu_name(obj_name):
|
|||||||
if obj_name.startswith("O_"):
|
if obj_name.startswith("O_"):
|
||||||
return _NameInfoHelper(_ObjectBasicType.DECORATION)
|
return _NameInfoHelper(_ObjectBasicType.DECORATION)
|
||||||
|
|
||||||
print("[ERROR]\t{}:\tName match lost.".format(obj_name))
|
err_reporter.add_error("Name match lost.")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _get_name_info_from_group(obj):
|
def _get_name_info_from_group(obj, err_reporter: _RenameErrorReporter):
|
||||||
group_list = UTILS_virtools_prop.get_virtools_group_data(obj)
|
group_list = UTILS_virtools_prop.get_virtools_group_data(obj)
|
||||||
if len(group_list) == 0:
|
if len(group_list) == 0:
|
||||||
# name it as a decoration
|
# name it as a decoration
|
||||||
@ -262,24 +308,24 @@ def _get_name_info_from_group(obj):
|
|||||||
# these type's data should be gotten from its name
|
# these type's data should be gotten from its name
|
||||||
# use _get_name_info_from_yyc_name to get it
|
# use _get_name_info_from_yyc_name to get it
|
||||||
# _get_name_info_from_yyc_name is Ballance-compatible name standard
|
# _get_name_info_from_yyc_name is Ballance-compatible name standard
|
||||||
data = _get_name_info_from_yyc_name(obj.name, call_internal=True)
|
data = _get_name_info_from_yyc_name(obj.name, err_reporter, call_internal=True)
|
||||||
if data is None:
|
if data is None:
|
||||||
print("[ERROR]\t{}:\tPC_Checkpoints or PR_Resetpoints detected. But couldn't get sector from name.".format(obj.name))
|
err_reporter.add_error("PC_Checkpoints or PR_Resetpoints detected. But couldn't get sector from name.")
|
||||||
return None
|
return None
|
||||||
if data.basic_type != _ObjectBasicType.CHECKPOINT and data.basic_type != _ObjectBasicType.RESETPOINT:
|
if data.basic_type != _ObjectBasicType.CHECKPOINT and data.basic_type != _ObjectBasicType.RESETPOINT:
|
||||||
# check whether it is checkpoint or resetpoint
|
# check whether it is checkpoint or resetpoint
|
||||||
# if not, it mean that we got error data from name
|
# if not, it mean that we got error data from name
|
||||||
# return None instead
|
# return None instead
|
||||||
print("[ERROR]\t{}:\tPC_Checkpoints or PR_Resetpoints detected. But name is illegal.".format(obj.name))
|
err_reporter.add_error("PC_Checkpoints or PR_Resetpoints detected. But name is illegal.")
|
||||||
return None
|
return None
|
||||||
# otherwise return data
|
# otherwise return data
|
||||||
return data
|
return data
|
||||||
else:
|
else:
|
||||||
print("[ERROR]\t{}:\tThe match of Unique Component lost.".format(obj.name))
|
err_reporter.add_error("The match of Unique Component lost.")
|
||||||
return None
|
return None
|
||||||
elif len(set_result) != 0:
|
elif len(set_result) != 0:
|
||||||
# must be a weird grouping, report it
|
# must be a weird grouping, report it
|
||||||
print("[ERROR]\t{}:\tA Multi-grouping Unique Component.".format(obj.name))
|
err_reporter.add_error("A Multi-grouping Unique Component.")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# distinguish normal elements
|
# distinguish normal elements
|
||||||
@ -291,7 +337,7 @@ def _get_name_info_from_group(obj):
|
|||||||
gotten_sector = _get_sector_from_ckgroup(group_set)
|
gotten_sector = _get_sector_from_ckgroup(group_set)
|
||||||
if gotten_sector is None:
|
if gotten_sector is None:
|
||||||
# fail to get sector
|
# fail to get sector
|
||||||
print("[ERROR]\t{}:\tComponent detected. But couldn't get sector from CKGroup data.".format(obj.name))
|
err_reporter.add_error("Component detected. But couldn't get sector from CKGroup data.")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
data = _NameInfoHelper(_ObjectBasicType.COMPONENT)
|
data = _NameInfoHelper(_ObjectBasicType.COMPONENT)
|
||||||
@ -300,7 +346,7 @@ def _get_name_info_from_group(obj):
|
|||||||
return data
|
return data
|
||||||
elif len(set_result) != 0:
|
elif len(set_result) != 0:
|
||||||
# must be a weird grouping, report it
|
# must be a weird grouping, report it
|
||||||
print("[ERROR]\t{}:\tA Multi-grouping Component.".format(obj.name))
|
err_reporter.add_error("A Multi-grouping Component.")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# distinguish road
|
# distinguish road
|
||||||
@ -316,7 +362,7 @@ def _get_name_info_from_group(obj):
|
|||||||
elif len(floor_result) == 0 and len(rail_result) > 0:
|
elif len(floor_result) == 0 and len(rail_result) > 0:
|
||||||
return _NameInfoHelper(_ObjectBasicType.WOOD)
|
return _NameInfoHelper(_ObjectBasicType.WOOD)
|
||||||
else:
|
else:
|
||||||
print("[WARNING]\t{}:\tCan't distinguish between Floors and Rails. Suppose it is Floors".format(obj.name))
|
err_reporter.add_warning("Can't distinguish object between Floors and Rails. Suppose it is Floors.")
|
||||||
return _NameInfoHelper(_ObjectBasicType.FLOOR)
|
return _NameInfoHelper(_ObjectBasicType.FLOOR)
|
||||||
elif 'Phys_FloorStopper' in group_set:
|
elif 'Phys_FloorStopper' in group_set:
|
||||||
return _NameInfoHelper(_ObjectBasicType.STOPPER)
|
return _NameInfoHelper(_ObjectBasicType.STOPPER)
|
||||||
@ -324,10 +370,10 @@ def _get_name_info_from_group(obj):
|
|||||||
return _NameInfoHelper(_ObjectBasicType.DEPTH_CUBE)
|
return _NameInfoHelper(_ObjectBasicType.DEPTH_CUBE)
|
||||||
|
|
||||||
# no matched
|
# no matched
|
||||||
print("[ERROR]\t{}:\tGroup match lost.".format(obj.name))
|
err_reporter.add_error("Group match lost.")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _set_for_yyc_name(obj, name_info):
|
def _set_for_yyc_name(obj, name_info, err_reporter: _RenameErrorReporter):
|
||||||
basic_type = name_info.basic_type
|
basic_type = name_info.basic_type
|
||||||
if basic_type == _ObjectBasicType.DECORATION:
|
if basic_type == _ObjectBasicType.DECORATION:
|
||||||
obj.name = "D_"
|
obj.name = "D_"
|
||||||
@ -357,7 +403,7 @@ def _set_for_yyc_name(obj, name_info):
|
|||||||
obj.name = "{}_{:0>2d}_".format(name_info.component_type, name_info.sector)
|
obj.name = "{}_{:0>2d}_".format(name_info.component_type, name_info.sector)
|
||||||
|
|
||||||
|
|
||||||
def _set_for_imengyu_name(obj, name_info):
|
def _set_for_imengyu_name(obj, name_info, err_reporter: _RenameErrorReporter):
|
||||||
basic_type = name_info.basic_type
|
basic_type = name_info.basic_type
|
||||||
if basic_type == _ObjectBasicType.DECORATION:
|
if basic_type == _ObjectBasicType.DECORATION:
|
||||||
obj.name = "O_"
|
obj.name = "O_"
|
||||||
@ -388,7 +434,7 @@ def _set_for_imengyu_name(obj, name_info):
|
|||||||
|
|
||||||
# NOTE: the implement of this function are copied from
|
# NOTE: the implement of this function are copied from
|
||||||
# BallanceVirtoolsHelper/bvh/features/mapping/grouping.cpp
|
# BallanceVirtoolsHelper/bvh/features/mapping/grouping.cpp
|
||||||
def _set_for_group(obj, name_info):
|
def _set_for_group(obj, name_info, err_reporter: _RenameErrorReporter):
|
||||||
gps = []
|
gps = []
|
||||||
basic_type = name_info.basic_type
|
basic_type = name_info.basic_type
|
||||||
|
|
||||||
@ -441,23 +487,23 @@ def _set_for_group(obj, name_info):
|
|||||||
# ==========================================
|
# ==========================================
|
||||||
# assemble funcs
|
# assemble funcs
|
||||||
|
|
||||||
def _get_data(obj, standard):
|
def _get_data(obj, standard, err_reporter: _RenameErrorReporter):
|
||||||
if standard == _NameStandard.YYC:
|
if standard == _NameStandard.YYC:
|
||||||
return _get_name_info_from_yyc_name(obj.name)
|
return _get_name_info_from_yyc_name(obj.name, err_reporter)
|
||||||
elif standard == _NameStandard.IMENGYU:
|
elif standard == _NameStandard.IMENGYU:
|
||||||
return _get_name_info_from_imengyu_name(obj.name)
|
return _get_name_info_from_imengyu_name(obj.name, err_reporter)
|
||||||
elif standard == _NameStandard.CKGROUP:
|
elif standard == _NameStandard.CKGROUP:
|
||||||
return _get_name_info_from_group(obj)
|
return _get_name_info_from_group(obj, err_reporter)
|
||||||
else:
|
else:
|
||||||
raise Exception("Unknow standard")
|
raise Exception("Unknow standard")
|
||||||
|
|
||||||
def _set_data(obj, name_info, standard):
|
def _set_data(obj, name_info, standard, err_reporter: _RenameErrorReporter):
|
||||||
if standard == _NameStandard.YYC:
|
if standard == _NameStandard.YYC:
|
||||||
return _set_for_yyc_name(obj, name_info)
|
return _set_for_yyc_name(obj, name_info, err_reporter)
|
||||||
elif standard == _NameStandard.IMENGYU:
|
elif standard == _NameStandard.IMENGYU:
|
||||||
return _set_for_imengyu_name(obj, name_info)
|
return _set_for_imengyu_name(obj, name_info, err_reporter)
|
||||||
elif standard == _NameStandard.CKGROUP:
|
elif standard == _NameStandard.CKGROUP:
|
||||||
return _set_for_group(obj, name_info)
|
return _set_for_group(obj, name_info, err_reporter)
|
||||||
else:
|
else:
|
||||||
raise Exception("Unknow standard")
|
raise Exception("Unknow standard")
|
||||||
|
|
||||||
@ -467,23 +513,43 @@ def _rename_core(source_std, dest_std):
|
|||||||
# we do not to do anything
|
# we do not to do anything
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# create fail counter and error reporter
|
||||||
failed_obj_counter = 0
|
failed_obj_counter = 0
|
||||||
all_obj_counter = 0
|
all_obj_counter = 0
|
||||||
|
err_reporter = _RenameErrorReporter()
|
||||||
|
|
||||||
print('============')
|
print('============')
|
||||||
print('Rename system report')
|
print('Rename System Report')
|
||||||
print('------------')
|
print('------------')
|
||||||
for obj in _get_selected_objects():
|
for obj in _get_selected_objects():
|
||||||
|
# set counter and name
|
||||||
all_obj_counter += 1
|
all_obj_counter += 1
|
||||||
info = _get_data(obj, source_std)
|
old_name = new_name = obj.name
|
||||||
|
# get data
|
||||||
|
info = _get_data(obj, source_std, err_reporter)
|
||||||
|
|
||||||
|
# do operation according to whether getting data successfully
|
||||||
if info is None:
|
if info is None:
|
||||||
failed_obj_counter += 1
|
failed_obj_counter += 1
|
||||||
continue
|
else:
|
||||||
|
_set_data(obj, info, dest_std, err_reporter)
|
||||||
|
# refresh obj name
|
||||||
|
new_name = obj.name
|
||||||
|
|
||||||
_set_data(obj, info, dest_std)
|
# report result
|
||||||
|
if err_reporter.can_report():
|
||||||
|
if new_name == old_name:
|
||||||
|
report_header = 'For object "{}"'.format(new_name)
|
||||||
|
else:
|
||||||
|
report_header = 'For object "{}" (Old name: "{}")'.format(new_name, old_name)
|
||||||
|
|
||||||
|
err_reporter.report(report_header)
|
||||||
|
# clear report
|
||||||
|
err_reporter.clear()
|
||||||
|
|
||||||
|
|
||||||
print('------------')
|
print('------------')
|
||||||
print('All/failed - {}/{}'.format(all_obj_counter, failed_obj_counter))
|
print('All / Failed - {} / {}'.format(all_obj_counter, failed_obj_counter))
|
||||||
print('============')
|
print('============')
|
||||||
|
|
||||||
UTILS_functions.show_message_box(
|
UTILS_functions.show_message_box(
|
||||||
@ -491,6 +557,5 @@ def _rename_core(source_std, dest_std):
|
|||||||
'View console to get more detail',
|
'View console to get more detail',
|
||||||
'All: {}'.format(all_obj_counter),
|
'All: {}'.format(all_obj_counter),
|
||||||
'Failed: {}'.format(failed_obj_counter)),
|
'Failed: {}'.format(failed_obj_counter)),
|
||||||
"Info",
|
"Info", UTILS_icons_manager.blender_error_icon
|
||||||
"INFO"
|
|
||||||
)
|
)
|
||||||
|
@ -1,43 +1,55 @@
|
|||||||
import bpy, mathutils
|
import bpy, mathutils
|
||||||
from . import UTILS_constants, UTILS_functions
|
from . import UTILS_constants, UTILS_functions, UTILS_icons_manager
|
||||||
|
|
||||||
# ================================================= actual add
|
# =============== Common Class ================
|
||||||
|
class common_add_component_props(bpy.types.Operator):
|
||||||
|
attentionElements = ("PC_TwoFlames", "PR_Resetpoint")
|
||||||
|
uniqueElements = ("PS_FourFlames", "PE_Balloon")
|
||||||
|
|
||||||
class BALLANCE_OT_add_components(bpy.types.Operator):
|
elements_sector: bpy.props.IntProperty(
|
||||||
"""Add sector related elements"""
|
name="Sector",
|
||||||
|
description="Define which sector the object will be grouped in",
|
||||||
|
min=1, max=8,
|
||||||
|
default=1,
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_component_name(self, raw_comp_name):
|
||||||
|
if raw_comp_name in self.uniqueElements:
|
||||||
|
return raw_comp_name + "_01"
|
||||||
|
elif raw_comp_name in self.attentionElements:
|
||||||
|
return raw_comp_name + "_0" + str(self.elements_sector)
|
||||||
|
else:
|
||||||
|
return raw_comp_name + "_0" + str(self.elements_sector) + "_"
|
||||||
|
|
||||||
|
def parent_draw(self, parent_layout, raw_comp_name):
|
||||||
|
if raw_comp_name not in self.uniqueElements:
|
||||||
|
parent_layout.prop(self, 'elements_sector')
|
||||||
|
|
||||||
|
class BALLANCE_OT_add_components(common_add_component_props):
|
||||||
|
"""Add Elements"""
|
||||||
bl_idname = "ballance.add_components"
|
bl_idname = "ballance.add_components"
|
||||||
bl_label = "Add elements"
|
bl_label = "Add Elements"
|
||||||
bl_options = {'UNDO'}
|
bl_options = {'UNDO'}
|
||||||
|
|
||||||
elements_type: bpy.props.EnumProperty(
|
elements_type: bpy.props.EnumProperty(
|
||||||
name="Type",
|
name="Type",
|
||||||
description="This element type",
|
description="This element type",
|
||||||
items=tuple(map(lambda x: (x, x, ""), UTILS_constants.bmfile_componentList)),
|
#items=tuple(map(lambda x: (x, x, ""), UTILS_constants.bmfile_componentList)),
|
||||||
)
|
items=tuple(
|
||||||
|
# token, display name, descriptions, icon, index
|
||||||
attentionElements = ["PC_TwoFlames", "PR_Resetpoint"]
|
(blk, blk, "", UTILS_icons_manager.get_element_icon(blk), idx)
|
||||||
uniqueElements = ["PS_FourFlames", "PE_Balloon"]
|
for idx, blk in enumerate(UTILS_constants.bmfile_componentList)
|
||||||
|
),
|
||||||
elements_sector: bpy.props.IntProperty(
|
|
||||||
name="Sector",
|
|
||||||
description="Define which sector the object will be grouped in",
|
|
||||||
min=1,
|
|
||||||
max=8,
|
|
||||||
default=1,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
# get name
|
# get name
|
||||||
if self.elements_type in self.uniqueElements:
|
finalObjectName = self.get_component_name(self.elements_type)
|
||||||
finalObjectName = self.elements_type + "_01"
|
|
||||||
elif self.elements_type in self.attentionElements:
|
|
||||||
finalObjectName = self.elements_type + "_0" + str(self.elements_sector)
|
|
||||||
else:
|
|
||||||
finalObjectName = self.elements_type + "_0" + str(self.elements_sector) + "_"
|
|
||||||
|
|
||||||
# create object
|
# create object
|
||||||
loadedMesh = UTILS_functions.load_component(
|
loadedMesh = UTILS_functions.load_component(
|
||||||
UTILS_constants.bmfile_componentList.index(self.elements_type))
|
UTILS_constants.bmfile_componentList.index(self.elements_type)
|
||||||
|
)
|
||||||
obj = bpy.data.objects.new(finalObjectName, loadedMesh)
|
obj = bpy.data.objects.new(finalObjectName, loadedMesh)
|
||||||
UTILS_functions.add_into_scene_and_move_to_cursor(obj)
|
UTILS_functions.add_into_scene_and_move_to_cursor(obj)
|
||||||
|
|
||||||
@ -49,8 +61,174 @@ class BALLANCE_OT_add_components(bpy.types.Operator):
|
|||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
layout = self.layout
|
layout = self.layout
|
||||||
layout.prop(self, "elements_type")
|
# attension notice
|
||||||
if self.elements_type not in self.uniqueElements:
|
|
||||||
layout.prop(self, "elements_sector")
|
|
||||||
if self.elements_type in self.attentionElements:
|
if self.elements_type in self.attentionElements:
|
||||||
layout.label(text="Please note that sector is suffix.")
|
layout.label(text="NOTE: Check Sector ID carefully.")
|
||||||
|
if self.elements_type in self.uniqueElements:
|
||||||
|
layout.label(text="NOTE: This element have unique name.")
|
||||||
|
|
||||||
|
# cfg
|
||||||
|
layout.prop(self, "elements_type")
|
||||||
|
self.parent_draw(layout, self.elements_type)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def draw_blc_menu(self, layout):
|
||||||
|
for item in UTILS_constants.bmfile_componentList:
|
||||||
|
cop = layout.operator(
|
||||||
|
self.bl_idname, text=item,
|
||||||
|
icon_value = UTILS_icons_manager.get_element_icon(item))
|
||||||
|
cop.elements_type = item
|
||||||
|
|
||||||
|
|
||||||
|
class BALLANCE_OT_add_components_dup(common_add_component_props):
|
||||||
|
"""Add Duplicated Elements"""
|
||||||
|
bl_idname = "ballance.add_components_dup"
|
||||||
|
bl_label = "Add Duplicated Elements"
|
||||||
|
bl_options = {'UNDO'}
|
||||||
|
|
||||||
|
can_duplicated_elements = (
|
||||||
|
'P_Extra_Point', 'P_Modul_18', 'P_Modul_26'
|
||||||
|
)
|
||||||
|
|
||||||
|
elements_type: bpy.props.EnumProperty(
|
||||||
|
name="Type",
|
||||||
|
description="This element type",
|
||||||
|
#items=tuple(map(lambda x: (x, x, ""), UTILS_constants.bmfile_componentList)),
|
||||||
|
items=tuple(
|
||||||
|
# token, display name, descriptions, icon, index
|
||||||
|
(blk, blk, "", UTILS_icons_manager.get_element_icon(blk), idx)
|
||||||
|
for idx, blk in enumerate(can_duplicated_elements)
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
elements_dup_times: bpy.props.IntProperty(
|
||||||
|
name="Duplication Count",
|
||||||
|
description="How many this element should be duplicated.",
|
||||||
|
min=2, max=64,
|
||||||
|
soft_min=2, soft_max=32,
|
||||||
|
default=2,
|
||||||
|
)
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
# get name
|
||||||
|
finalObjectName = self.get_component_name(self.elements_type)
|
||||||
|
|
||||||
|
# load mesh
|
||||||
|
loadedMesh = UTILS_functions.load_component(
|
||||||
|
UTILS_constants.bmfile_componentList.index(self.elements_type)
|
||||||
|
)
|
||||||
|
# create object
|
||||||
|
for i in range(self.elements_dup_times):
|
||||||
|
obj = bpy.data.objects.new(finalObjectName, loadedMesh)
|
||||||
|
UTILS_functions.add_into_scene_and_move_to_cursor(obj)
|
||||||
|
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
def invoke(self, context, event):
|
||||||
|
wm = context.window_manager
|
||||||
|
return wm.invoke_props_dialog(self)
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
layout = self.layout
|
||||||
|
layout.prop(self, "elements_type")
|
||||||
|
self.parent_draw(layout, self.elements_type)
|
||||||
|
layout.prop(self, "elements_dup_times")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def draw_blc_menu(self, layout):
|
||||||
|
for item in self.can_duplicated_elements:
|
||||||
|
cop = layout.operator(
|
||||||
|
self.bl_idname, text=item,
|
||||||
|
icon_value = UTILS_icons_manager.get_element_icon(item))
|
||||||
|
cop.elements_type = item
|
||||||
|
|
||||||
|
|
||||||
|
class BALLANCE_OT_add_components_series(common_add_component_props):
|
||||||
|
"""Add Elements with a Series."""
|
||||||
|
bl_idname = "ballance.add_components_series"
|
||||||
|
bl_label = "Add Series Elements"
|
||||||
|
bl_options = {'REGISTER', 'UNDO'}
|
||||||
|
|
||||||
|
supported_series = {
|
||||||
|
# format: key: (description: str, real_component: str, unit_transition: mathutils.Vector, default_span: float)
|
||||||
|
# key will become enum property's identifier
|
||||||
|
"MODUL_41": ('Tilting Block Series', 'P_Modul_41', mathutils.Vector((1.0, 0.0, 0.0)), 6.0022),
|
||||||
|
"MODUL_18_V": ('Fan Vertical Series', 'P_Modul_18', mathutils.Vector((0.0, 0.0, 1.0)), 15),
|
||||||
|
"MODUL_18_H": ('Fan Horizonal Series', 'P_Modul_18', mathutils.Vector((1.0, 0.0, 0.0)), 30),
|
||||||
|
}
|
||||||
|
|
||||||
|
# the updator for default span
|
||||||
|
def element_type_updated(self, context):
|
||||||
|
# set span
|
||||||
|
self.elements_span = BALLANCE_OT_add_components_series.supported_series[self.elements_type][3]
|
||||||
|
|
||||||
|
# blender required
|
||||||
|
return None
|
||||||
|
|
||||||
|
elements_type: bpy.props.EnumProperty(
|
||||||
|
name="Type",
|
||||||
|
description="This element type",
|
||||||
|
#items=tuple(map(lambda x: (x, x, ""), UTILS_constants.bmfile_componentList)),
|
||||||
|
items=tuple(
|
||||||
|
# token, display name, descriptions, icon, index
|
||||||
|
(skey, sitem[0], "", UTILS_icons_manager.get_element_icon(sitem[1]), idx)
|
||||||
|
for (idx, (skey, sitem)) in enumerate(supported_series.items())
|
||||||
|
),
|
||||||
|
default=0,
|
||||||
|
update=element_type_updated
|
||||||
|
)
|
||||||
|
|
||||||
|
elements_dup_times: bpy.props.IntProperty(
|
||||||
|
name="Duplication Count",
|
||||||
|
description="How many this element should be duplicated.",
|
||||||
|
min=2, max=64,
|
||||||
|
soft_min=2, soft_max=32,
|
||||||
|
default=2,
|
||||||
|
)
|
||||||
|
|
||||||
|
elements_span: bpy.props.FloatProperty(
|
||||||
|
name="Elements Span",
|
||||||
|
description="The span between each elements.",
|
||||||
|
min=0.0,
|
||||||
|
default=0.0,
|
||||||
|
)
|
||||||
|
|
||||||
|
def invoke(self, context, event):
|
||||||
|
# force trigger span update once to treat span normally
|
||||||
|
self.element_type_updated(context)
|
||||||
|
|
||||||
|
return self.execute(context)
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
# get unit span and real element name for loading mesh and creating name
|
||||||
|
(_, real_element_name, unit_span, _) = self.supported_series[self.elements_type]
|
||||||
|
|
||||||
|
# get name
|
||||||
|
finalObjectName = self.get_component_name(real_element_name)
|
||||||
|
# load mesh
|
||||||
|
loadedMesh = UTILS_functions.load_component(
|
||||||
|
UTILS_constants.bmfile_componentList.index(real_element_name)
|
||||||
|
)
|
||||||
|
|
||||||
|
# create object
|
||||||
|
for i in range(self.elements_dup_times):
|
||||||
|
obj = bpy.data.objects.new(finalObjectName, loadedMesh)
|
||||||
|
UTILS_functions.add_into_scene_and_move_to_cursor(obj)
|
||||||
|
obj.matrix_world.translation += unit_span * (self.elements_span * i)
|
||||||
|
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
layout = self.layout
|
||||||
|
layout.prop(self, "elements_type")
|
||||||
|
self.parent_draw(layout, self.elements_type)
|
||||||
|
layout.prop(self, "elements_dup_times")
|
||||||
|
layout.prop(self, "elements_span")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def draw_blc_menu(self, layout):
|
||||||
|
for key, item in self.supported_series.items():
|
||||||
|
cop = layout.operator(
|
||||||
|
self.bl_idname, text=item[0],
|
||||||
|
icon_value = UTILS_icons_manager.get_element_icon(item[1]))
|
||||||
|
cop.elements_type = key
|
||||||
|
@ -4,18 +4,45 @@ import ast
|
|||||||
from bpy_extras import io_utils,node_shader_utils
|
from bpy_extras import io_utils,node_shader_utils
|
||||||
# from bpy_extras.io_utils import unpack_list
|
# from bpy_extras.io_utils import unpack_list
|
||||||
from bpy_extras.image_utils import load_image
|
from bpy_extras.image_utils import load_image
|
||||||
from . import UTILS_constants, UTILS_functions, UTILS_safe_eval
|
from . import UTILS_constants, UTILS_functions, UTILS_safe_eval, UTILS_icons_manager
|
||||||
|
|
||||||
class BALLANCE_OT_add_floors(bpy.types.Operator):
|
class BALLANCE_OT_add_floors(bpy.types.Operator):
|
||||||
"""Add Ballance floor"""
|
"""Add Ballance floor"""
|
||||||
bl_idname = "ballance.add_floors"
|
bl_idname = "ballance.add_floors"
|
||||||
bl_label = "Add floor"
|
bl_label = "Add floor"
|
||||||
bl_options = {'UNDO'}
|
bl_options = {'REGISTER', 'UNDO'}
|
||||||
|
|
||||||
|
# the updator for default side value
|
||||||
|
def floor_type_updated(self, context):
|
||||||
|
# get floor prototype
|
||||||
|
floor_prototype = UTILS_constants.floor_blockDict[self.floor_type]
|
||||||
|
|
||||||
|
# try sync default value
|
||||||
|
default_sides = floor_prototype['DefaultSideConfig']
|
||||||
|
self.use_2d_top = default_sides['UseTwoDTop']
|
||||||
|
self.use_2d_right = default_sides['UseTwoDRight']
|
||||||
|
self.use_2d_bottom = default_sides['UseTwoDBottom']
|
||||||
|
self.use_2d_left = default_sides['UseTwoDLeft']
|
||||||
|
self.use_3d_top = default_sides['UseThreeDTop']
|
||||||
|
self.use_3d_bottom = default_sides['UseThreeDBottom']
|
||||||
|
|
||||||
|
# blender required
|
||||||
|
return None
|
||||||
|
|
||||||
floor_type: bpy.props.EnumProperty(
|
floor_type: bpy.props.EnumProperty(
|
||||||
name="Type",
|
name="Type",
|
||||||
description="Floor type",
|
description="Floor type",
|
||||||
items=tuple((x, x, "") for x in UTILS_constants.floor_blockDict.keys()),
|
#items=tuple(
|
||||||
|
# # token, display name, descriptions
|
||||||
|
# (blk, blk, "")
|
||||||
|
# for blk in UTILS_constants.floor_blockDict.keys()
|
||||||
|
#),
|
||||||
|
items=tuple(
|
||||||
|
# token, display name, descriptions, icon, index
|
||||||
|
(blk, blk, "", UTILS_icons_manager.get_floor_icon(blk), idx)
|
||||||
|
for idx, blk in enumerate(UTILS_constants.floor_blockDict.keys())
|
||||||
|
),
|
||||||
|
update=floor_type_updated
|
||||||
)
|
)
|
||||||
|
|
||||||
expand_length_1 : bpy.props.IntProperty(
|
expand_length_1 : bpy.props.IntProperty(
|
||||||
@ -40,26 +67,30 @@ class BALLANCE_OT_add_floors(bpy.types.Operator):
|
|||||||
)
|
)
|
||||||
|
|
||||||
use_2d_top : bpy.props.BoolProperty(
|
use_2d_top : bpy.props.BoolProperty(
|
||||||
name="Top edge"
|
name="Top edge",
|
||||||
|
default=True
|
||||||
)
|
)
|
||||||
use_2d_right : bpy.props.BoolProperty(
|
use_2d_right : bpy.props.BoolProperty(
|
||||||
name="Right edge"
|
name="Right edge",
|
||||||
|
default=False
|
||||||
)
|
)
|
||||||
use_2d_bottom : bpy.props.BoolProperty(
|
use_2d_bottom : bpy.props.BoolProperty(
|
||||||
name="Bottom edge"
|
name="Bottom edge",
|
||||||
|
default=True
|
||||||
)
|
)
|
||||||
use_2d_left : bpy.props.BoolProperty(
|
use_2d_left : bpy.props.BoolProperty(
|
||||||
name="Left edge"
|
name="Left edge",
|
||||||
|
default=True
|
||||||
)
|
)
|
||||||
use_3d_top : bpy.props.BoolProperty(
|
use_3d_top : bpy.props.BoolProperty(
|
||||||
name="Top face"
|
name="Top face",
|
||||||
|
default=True
|
||||||
)
|
)
|
||||||
use_3d_bottom : bpy.props.BoolProperty(
|
use_3d_bottom : bpy.props.BoolProperty(
|
||||||
name="Bottom face"
|
name="Bottom face",
|
||||||
|
default=True
|
||||||
)
|
)
|
||||||
|
|
||||||
previous_floor_type = ''
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(self, context):
|
def poll(self, context):
|
||||||
prefs = bpy.context.preferences.addons[__package__].preferences
|
prefs = bpy.context.preferences.addons[__package__].preferences
|
||||||
@ -115,25 +146,25 @@ class BALLANCE_OT_add_floors(bpy.types.Operator):
|
|||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
def invoke(self, context, event):
|
def invoke(self, context, event):
|
||||||
wm = context.window_manager
|
# yyc marked. Blumia reported.
|
||||||
return wm.invoke_props_dialog(self)
|
# prepare settings before registing
|
||||||
|
# otherwise the mesh will not be created when first run.
|
||||||
|
# (do not change any properties)
|
||||||
|
|
||||||
|
# yyc marked again.
|
||||||
|
# now I migrate default side value setter to updator of enum property.
|
||||||
|
# nothing need to process in here now.
|
||||||
|
|
||||||
|
# trigger default side props updator
|
||||||
|
self.floor_type_updated(context)
|
||||||
|
|
||||||
|
return self.execute(context)
|
||||||
|
|
||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
# get floor prototype
|
# get floor prototype
|
||||||
floor_prototype = UTILS_constants.floor_blockDict[self.floor_type]
|
floor_prototype = UTILS_constants.floor_blockDict[self.floor_type]
|
||||||
|
|
||||||
# try sync default value
|
|
||||||
if self.previous_floor_type != self.floor_type:
|
|
||||||
self.previous_floor_type = self.floor_type
|
|
||||||
|
|
||||||
default_sides = floor_prototype['DefaultSideConfig']
|
|
||||||
self.use_2d_top = default_sides['UseTwoDTop']
|
|
||||||
self.use_2d_right = default_sides['UseTwoDRight']
|
|
||||||
self.use_2d_bottom = default_sides['UseTwoDBottom']
|
|
||||||
self.use_2d_left = default_sides['UseTwoDLeft']
|
|
||||||
self.use_3d_top = default_sides['UseThreeDTop']
|
|
||||||
self.use_3d_bottom = default_sides['UseThreeDBottom']
|
|
||||||
|
|
||||||
# show property
|
# show property
|
||||||
layout = self.layout
|
layout = self.layout
|
||||||
col = layout.column()
|
col = layout.column()
|
||||||
@ -149,12 +180,13 @@ class BALLANCE_OT_add_floors(bpy.types.Operator):
|
|||||||
col.prop(self, "expand_length_2")
|
col.prop(self, "expand_length_2")
|
||||||
col.label(text="Unit size: " + floor_prototype['UnitSize'])
|
col.label(text="Unit size: " + floor_prototype['UnitSize'])
|
||||||
col.label(text="Expand mode: " + floor_prototype['ExpandType'])
|
col.label(text="Expand mode: " + floor_prototype['ExpandType'])
|
||||||
grids = col.grid_flow(row_major=True, columns=3)
|
grids = col.grid_flow(row_major=True, columns=3, even_columns=True, even_rows=True, align=True)
|
||||||
|
grids.alignment = 'CENTER'
|
||||||
grids.separator()
|
grids.separator()
|
||||||
grids.label(text=UTILS_constants.floor_expandDirectionMap[floor_prototype['InitColumnDirection']][floor_prototype['ExpandType']][0])
|
grids.label(text=UTILS_constants.floor_expandDirectionMap[floor_prototype['InitColumnDirection']][floor_prototype['ExpandType']][0])
|
||||||
grids.separator()
|
grids.separator()
|
||||||
grids.label(text=UTILS_constants.floor_expandDirectionMap[floor_prototype['InitColumnDirection']][floor_prototype['ExpandType']][3])
|
grids.label(text=UTILS_constants.floor_expandDirectionMap[floor_prototype['InitColumnDirection']][floor_prototype['ExpandType']][3])
|
||||||
grids.template_icon(icon_value = UTILS_constants.icons_floorDict[self.floor_type])
|
grids.template_icon(icon_value = UTILS_icons_manager.get_floor_icon(self.floor_type))
|
||||||
grids.label(text=UTILS_constants.floor_expandDirectionMap[floor_prototype['InitColumnDirection']][floor_prototype['ExpandType']][1])
|
grids.label(text=UTILS_constants.floor_expandDirectionMap[floor_prototype['InitColumnDirection']][floor_prototype['ExpandType']][1])
|
||||||
grids.separator()
|
grids.separator()
|
||||||
grids.label(text=UTILS_constants.floor_expandDirectionMap[floor_prototype['InitColumnDirection']][floor_prototype['ExpandType']][2])
|
grids.label(text=UTILS_constants.floor_expandDirectionMap[floor_prototype['InitColumnDirection']][floor_prototype['ExpandType']][2])
|
||||||
@ -168,12 +200,13 @@ class BALLANCE_OT_add_floors(bpy.types.Operator):
|
|||||||
|
|
||||||
col.separator()
|
col.separator()
|
||||||
col.label(text="Sides")
|
col.label(text="Sides")
|
||||||
grids = col.grid_flow(row_major=True, columns=3)
|
grids = col.grid_flow(row_major=True, columns=3, even_columns=True, even_rows=True, align=True)
|
||||||
|
grids.alignment = 'CENTER'
|
||||||
grids.separator()
|
grids.separator()
|
||||||
grids.prop(self, "use_2d_top")
|
grids.prop(self, "use_2d_top")
|
||||||
grids.separator()
|
grids.separator()
|
||||||
grids.prop(self, "use_2d_left")
|
grids.prop(self, "use_2d_left")
|
||||||
grids.template_icon(icon_value = UTILS_constants.icons_floorDict[self.floor_type])
|
grids.template_icon(icon_value = UTILS_icons_manager.get_floor_icon(self.floor_type))
|
||||||
grids.prop(self, "use_2d_right")
|
grids.prop(self, "use_2d_right")
|
||||||
grids.separator()
|
grids.separator()
|
||||||
grids.prop(self, "use_2d_bottom")
|
grids.prop(self, "use_2d_bottom")
|
||||||
@ -221,7 +254,7 @@ def _create_or_get_material(material_name, prefs_externalTexture):
|
|||||||
try_item['data']['ambient'], try_item['data']['diffuse'],
|
try_item['data']['ambient'], try_item['data']['diffuse'],
|
||||||
try_item['data']['specular'], try_item['data']['emissive'],
|
try_item['data']['specular'], try_item['data']['emissive'],
|
||||||
try_item['data']['power'],
|
try_item['data']['power'],
|
||||||
False, False, False, False,
|
False, False, True, False,
|
||||||
texture)
|
texture)
|
||||||
)
|
)
|
||||||
break
|
break
|
||||||
|
@ -10,55 +10,41 @@ class BALLANCE_OT_add_rails(bpy.types.Operator):
|
|||||||
rail_type: bpy.props.EnumProperty(
|
rail_type: bpy.props.EnumProperty(
|
||||||
name="Type",
|
name="Type",
|
||||||
description="Rail type",
|
description="Rail type",
|
||||||
items=(('MONO', "Monorail", ""),
|
items=(
|
||||||
('DOUBLE', "Rail", ""),
|
('MONO', "Monorail", ""),
|
||||||
),
|
('DOUBLE', "Rail", ""),
|
||||||
|
),
|
||||||
|
default='DOUBLE',
|
||||||
)
|
)
|
||||||
|
|
||||||
rail_radius: bpy.props.FloatProperty(
|
rail_radius: bpy.props.FloatProperty(
|
||||||
name="Rail radius",
|
name="Rail Radius",
|
||||||
description="Define rail section radius",
|
description="Define rail section radius",
|
||||||
default=0.375,
|
default=0.375,
|
||||||
)
|
)
|
||||||
|
|
||||||
rail_span: bpy.props.FloatProperty(
|
rail_span: bpy.props.FloatProperty(
|
||||||
name="Rail span",
|
name="Rail Span",
|
||||||
description="Define rail span",
|
description="The length between 2 single rails.",
|
||||||
default=3.75,
|
default=3.75,
|
||||||
)
|
)
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
bpy.ops.object.select_all(action='DESELECT')
|
|
||||||
# create one first
|
# create one first
|
||||||
bpy.ops.mesh.primitive_circle_add(vertices=8,
|
firstObj = _create_ballance_circle(self.rail_radius, (0.0, 0.0, 0.0))
|
||||||
radius=self.rail_radius,
|
|
||||||
fill_type='NOTHING',
|
|
||||||
calc_uvs=False,
|
|
||||||
enter_editmode=False,
|
|
||||||
align='WORLD',
|
|
||||||
location=(0.0, 0.0, 0.0))
|
|
||||||
|
|
||||||
firstObj = bpy.context.selected_objects[0]
|
|
||||||
|
|
||||||
# for double rail
|
# for double rail
|
||||||
if self.rail_type == 'DOUBLE':
|
if self.rail_type == 'DOUBLE':
|
||||||
bpy.ops.object.select_all(action='DESELECT')
|
# create another one
|
||||||
bpy.ops.mesh.primitive_circle_add(vertices=8,
|
secondObj = _create_ballance_circle(self.rail_radius, (self.rail_span, 0.0, 0.0))
|
||||||
radius=self.rail_radius,
|
|
||||||
fill_type='NOTHING',
|
|
||||||
calc_uvs=False,
|
|
||||||
enter_editmode=False,
|
|
||||||
align='WORLD',
|
|
||||||
location=(self.rail_span, 0.0, 0.0))
|
|
||||||
secondObj = bpy.context.selected_objects[0]
|
|
||||||
|
|
||||||
# merge
|
# merge
|
||||||
bpy.ops.object.select_all(action='DESELECT')
|
firstObj = _merge_two_circle(firstObj, secondObj)
|
||||||
bpy.context.view_layer.objects.active = firstObj
|
|
||||||
firstObj.select_set(True)
|
|
||||||
secondObj.select_set(True)
|
|
||||||
bpy.ops.object.join()
|
|
||||||
|
|
||||||
|
# rename
|
||||||
|
if self.rail_type == 'DOUBLE':
|
||||||
|
firstObj.name = "A_Rail_"
|
||||||
|
else:
|
||||||
|
firstObj.name = "A_Rail_Mono_"
|
||||||
# apply 3d cursor
|
# apply 3d cursor
|
||||||
UTILS_functions.move_to_cursor(firstObj)
|
UTILS_functions.move_to_cursor(firstObj)
|
||||||
|
|
||||||
@ -75,3 +61,80 @@ class BALLANCE_OT_add_rails(bpy.types.Operator):
|
|||||||
if self.rail_type == 'DOUBLE':
|
if self.rail_type == 'DOUBLE':
|
||||||
layout.prop(self, "rail_span")
|
layout.prop(self, "rail_span")
|
||||||
|
|
||||||
|
class BALLANCE_OT_add_tunnels(bpy.types.Operator):
|
||||||
|
"""Add rail"""
|
||||||
|
bl_idname = "ballance.add_tunnels"
|
||||||
|
bl_label = "Add tunnel section"
|
||||||
|
bl_options = {'UNDO'}
|
||||||
|
|
||||||
|
use_outside: bpy.props.BoolProperty(
|
||||||
|
name="Double Sides",
|
||||||
|
description="Create tunnel section with double sides, not a single face.",
|
||||||
|
default=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
inside_radius: bpy.props.FloatProperty(
|
||||||
|
name="Inside Radius",
|
||||||
|
description="Tunnel inside radius",
|
||||||
|
default=2.5,
|
||||||
|
)
|
||||||
|
|
||||||
|
outside_radius: bpy.props.FloatProperty(
|
||||||
|
name="Outside Radius",
|
||||||
|
description="Tunnel outside radius",
|
||||||
|
default=2.6,
|
||||||
|
)
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
# create one first
|
||||||
|
firstObj = _create_ballance_circle(self.inside_radius, (0.0, 0.0, 0.0))
|
||||||
|
|
||||||
|
# for double rail
|
||||||
|
if self.use_outside:
|
||||||
|
# create another one
|
||||||
|
secondObj = _create_ballance_circle(self.outside_radius, (0.0, 0.0, 0.0))
|
||||||
|
# merge
|
||||||
|
firstObj = _merge_two_circle(firstObj, secondObj)
|
||||||
|
|
||||||
|
# rename
|
||||||
|
firstObj.name = "A_Rail_Tunnel_"
|
||||||
|
# apply 3d cursor
|
||||||
|
UTILS_functions.move_to_cursor(firstObj)
|
||||||
|
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
def invoke(self, context, event):
|
||||||
|
wm = context.window_manager
|
||||||
|
return wm.invoke_props_dialog(self)
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
layout = self.layout
|
||||||
|
layout.prop(self, "use_outside")
|
||||||
|
layout.prop(self, "inside_radius")
|
||||||
|
if self.use_outside:
|
||||||
|
layout.prop(self, "outside_radius")
|
||||||
|
|
||||||
|
def _create_ballance_circle(radius, loc):
|
||||||
|
bpy.ops.object.select_all(action='DESELECT')
|
||||||
|
bpy.ops.mesh.primitive_circle_add(
|
||||||
|
vertices=8,
|
||||||
|
radius=radius,
|
||||||
|
fill_type='NOTHING',
|
||||||
|
calc_uvs=False,
|
||||||
|
enter_editmode=False,
|
||||||
|
align='WORLD',
|
||||||
|
location=loc
|
||||||
|
)
|
||||||
|
|
||||||
|
created_obj = bpy.context.selected_objects[0]
|
||||||
|
bpy.ops.object.select_all(action='DESELECT')
|
||||||
|
return created_obj
|
||||||
|
|
||||||
|
def _merge_two_circle(obj1, obj2):
|
||||||
|
bpy.ops.object.select_all(action='DESELECT')
|
||||||
|
bpy.context.view_layer.objects.active = obj1
|
||||||
|
obj1.select_set(True)
|
||||||
|
obj2.select_set(True)
|
||||||
|
bpy.ops.object.join()
|
||||||
|
|
||||||
|
return obj1
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import bpy
|
import bpy
|
||||||
from . import UTILS_constants, UTILS_functions, UTILS_virtools_prop
|
from . import UTILS_constants, UTILS_functions, UTILS_virtools_prop, UTILS_icons_manager
|
||||||
|
|
||||||
class BALLANCE_OT_select_virtools_group(UTILS_virtools_prop.common_group_name_props):
|
class BALLANCE_OT_select_virtools_group(UTILS_virtools_prop.common_group_name_props):
|
||||||
"""Select objects by Virtools Group."""
|
"""Select objects by Virtools Group."""
|
||||||
@ -7,32 +7,55 @@ class BALLANCE_OT_select_virtools_group(UTILS_virtools_prop.common_group_name_pr
|
|||||||
bl_label = "Select by Virtools Group"
|
bl_label = "Select by Virtools Group"
|
||||||
bl_options = {'UNDO'}
|
bl_options = {'UNDO'}
|
||||||
|
|
||||||
merge_selection: bpy.props.BoolProperty(
|
selection_type: bpy.props.EnumProperty(
|
||||||
name="Merge Selection",
|
name="Mode",
|
||||||
description="Merge selection, rather than re-select them.",
|
description="Selection mode",
|
||||||
default=False,
|
items=(
|
||||||
)
|
('SET', "Set", "Sets a new selection.", "SELECT_SET", 0),
|
||||||
|
('EXTEND', "Extend", "Adds newly selected items to the existing selection.", "SELECT_EXTEND", 1),
|
||||||
ignore_hide: bpy.props.BoolProperty(
|
('SUBTRACT', "Subtract", "Removes newly selected items from the existing selection.", "SELECT_SUBTRACT", 2),
|
||||||
name="Ignore Hide Property",
|
('DIFFERENCE', "Invert", "Inverts the selection.", "SELECT_DIFFERENCE", 3),
|
||||||
description="Select objects without considering visibility.",
|
('INTERSECT', "Intersect", "Selects items that intersect with the existing selection.", "SELECT_INTERSECT", 4),
|
||||||
default=False,
|
),
|
||||||
|
default='SET'
|
||||||
)
|
)
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
# iterate object
|
if self.selection_type == 'SET':
|
||||||
for obj in bpy.context.scene.objects:
|
# iterate object
|
||||||
# ignore hidden objects
|
for obj in bpy.context.scene.objects:
|
||||||
if (not self.ignore_hide) and obj.hide_get() == True:
|
# check group and decide whether select this obj
|
||||||
continue
|
obj.select_set(UTILS_virtools_prop.check_virtools_group_data(obj, self.get_group_name_string()))
|
||||||
|
|
||||||
|
elif self.selection_type == 'EXTEND':
|
||||||
|
# also iterate all objects
|
||||||
|
for obj in bpy.context.scene.objects:
|
||||||
|
# directly add if group matched. do not deselect anything
|
||||||
|
if UTILS_virtools_prop.check_virtools_group_data(obj, self.get_group_name_string()):
|
||||||
|
obj.select_set(True)
|
||||||
|
elif self.selection_type == 'SUBTRACT':
|
||||||
|
# subtract only involving selected item. so we get selected objest first
|
||||||
|
# and iterate it to reduce useless operations
|
||||||
|
selected = bpy.context.selected_objects[:]
|
||||||
|
for obj in selected:
|
||||||
|
# remove matched only
|
||||||
|
if UTILS_virtools_prop.check_virtools_group_data(obj, self.get_group_name_string()):
|
||||||
|
obj.select_set(False)
|
||||||
|
|
||||||
# check group
|
elif self.selection_type == 'DIFFERENCE':
|
||||||
if UTILS_virtools_prop.check_virtools_group_data(obj, self.get_group_name_string()):
|
# construct a selected obj set for convenient operations
|
||||||
# select object
|
selected_set = set(bpy.context.selected_objects)
|
||||||
obj.select_set(True)
|
# iterate all objects
|
||||||
else:
|
for obj in bpy.context.scene.objects:
|
||||||
# if not in merge mode, deselect them
|
# use xor to select
|
||||||
if not self.merge_selection:
|
# in_selected XOR in_group
|
||||||
|
obj.select_set((obj in selected_set) ^ UTILS_virtools_prop.check_virtools_group_data(obj, self.get_group_name_string()))
|
||||||
|
elif self.selection_type == 'INTERSECT':
|
||||||
|
# like subtract, only iterate selected obj
|
||||||
|
selected = bpy.context.selected_objects[:]
|
||||||
|
for obj in selected:
|
||||||
|
# remove not matched
|
||||||
|
if not UTILS_virtools_prop.check_virtools_group_data(obj, self.get_group_name_string()):
|
||||||
obj.select_set(False)
|
obj.select_set(False)
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
@ -40,63 +63,13 @@ class BALLANCE_OT_select_virtools_group(UTILS_virtools_prop.common_group_name_pr
|
|||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
layout = self.layout
|
layout = self.layout
|
||||||
|
|
||||||
row = layout.row()
|
layout.label(text='Selection Parameters')
|
||||||
row.prop(self, 'ignore_hide')
|
layout.prop(self, 'selection_type', expand=True, icon_only=True)
|
||||||
row.prop(self, 'merge_selection')
|
|
||||||
|
|
||||||
layout.separator()
|
layout.separator()
|
||||||
|
layout.label(text='Group Parameters')
|
||||||
self.parent_draw(layout)
|
self.parent_draw(layout)
|
||||||
|
|
||||||
class BALLANCE_OT_filter_virtools_group(UTILS_virtools_prop.common_group_name_props):
|
|
||||||
"""Filter objects by Virtools Group."""
|
|
||||||
bl_idname = "ballance.filter_virtools_group"
|
|
||||||
bl_label = "Filter by Virtools Group"
|
|
||||||
bl_options = {'UNDO'}
|
|
||||||
|
|
||||||
reverse_selection: bpy.props.BoolProperty(
|
|
||||||
name="Reverse",
|
|
||||||
description="Reverse operation. Remove matched objects.",
|
|
||||||
default=False,
|
|
||||||
)
|
|
||||||
|
|
||||||
ignore_hide: bpy.props.BoolProperty(
|
|
||||||
name="Ignore Hide Property",
|
|
||||||
description="Select objects without considering visibility.",
|
|
||||||
default=False,
|
|
||||||
)
|
|
||||||
|
|
||||||
def execute(self, context):
|
|
||||||
# make a copy for all objects, to ensure it is not viotile
|
|
||||||
# becuase we need deselect some objects in for statement
|
|
||||||
selected = bpy.context.selected_objects[:]
|
|
||||||
# iterate object
|
|
||||||
for obj in selected:
|
|
||||||
# ignore hidden objects
|
|
||||||
if (not self.ignore_hide) and obj.hide_get() == True:
|
|
||||||
continue
|
|
||||||
|
|
||||||
# check group and decide select
|
|
||||||
is_selected = UTILS_virtools_prop.check_virtools_group_data(obj, self.get_group_name_string())
|
|
||||||
if self.reverse_selection:
|
|
||||||
is_selected = not is_selected
|
|
||||||
|
|
||||||
# select object
|
|
||||||
obj.select_set(is_selected)
|
|
||||||
|
|
||||||
return {'FINISHED'}
|
|
||||||
|
|
||||||
def draw(self, context):
|
|
||||||
layout = self.layout
|
|
||||||
|
|
||||||
row = layout.row()
|
|
||||||
row.prop(self, 'ignore_hide')
|
|
||||||
row.prop(self, 'reverse_selection')
|
|
||||||
|
|
||||||
layout.separator()
|
|
||||||
self.parent_draw(layout)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class BALLANCE_OT_ctx_set_group(UTILS_virtools_prop.common_group_name_props):
|
class BALLANCE_OT_ctx_set_group(UTILS_virtools_prop.common_group_name_props):
|
||||||
"""Grouping selected objects"""
|
"""Grouping selected objects"""
|
||||||
bl_idname = "ballance.ctx_set_group"
|
bl_idname = "ballance.ctx_set_group"
|
||||||
@ -118,7 +91,10 @@ class BALLANCE_OT_ctx_set_group(UTILS_virtools_prop.common_group_name_props):
|
|||||||
|
|
||||||
# throw a warning if some objects have duplicated group
|
# throw a warning if some objects have duplicated group
|
||||||
if has_duplicated:
|
if has_duplicated:
|
||||||
UTILS_functions.show_message_box(("Some objects have duplicated group name.", "These objects have been omitted.", ), "Duplicated Group", 'ERROR')
|
UTILS_functions.show_message_box(
|
||||||
|
("Some objects have duplicated group name.", "These objects have been omitted.", ),
|
||||||
|
"Duplicated Group", UTILS_icons_manager.blender_error_icon
|
||||||
|
)
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
@ -151,7 +127,10 @@ class BALLANCE_OT_ctx_unset_group(UTILS_virtools_prop.common_group_name_props):
|
|||||||
|
|
||||||
# throw a warning if some objects have duplicated group
|
# throw a warning if some objects have duplicated group
|
||||||
if lack_group:
|
if lack_group:
|
||||||
UTILS_functions.show_message_box(("Some objects lack specified group name.", "These objects have been omitted.", ), "Lack Group", 'ERROR')
|
UTILS_functions.show_message_box(
|
||||||
|
("Some objects lack specified group name.", "These objects have been omitted.", ),
|
||||||
|
"Lack Group", UTILS_icons_manager.blender_error_icon
|
||||||
|
)
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
@ -169,11 +148,14 @@ class BALLANCE_OT_ctx_clear_group(bpy.types.Operator):
|
|||||||
def poll(self, context):
|
def poll(self, context):
|
||||||
return len(bpy.context.selected_objects) != 0
|
return len(bpy.context.selected_objects) != 0
|
||||||
|
|
||||||
|
def invoke(self, context, event):
|
||||||
|
wm = context.window_manager
|
||||||
|
return wm.invoke_confirm(self, event)
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
# iterate object
|
# iterate object
|
||||||
for obj in bpy.context.selected_objects:
|
for obj in bpy.context.selected_objects:
|
||||||
UTILS_virtools_prop.clear_virtools_group_data(obj)
|
UTILS_virtools_prop.clear_virtools_group_data(obj)
|
||||||
|
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import bpy
|
import bpy
|
||||||
from . import UTILS_constants, UTILS_functions, UTILS_virtools_prop
|
from . import UTILS_constants, UTILS_functions, UTILS_virtools_prop, UTILS_icons_manager
|
||||||
|
|
||||||
class BALLANCE_OT_add_virtools_group(UTILS_virtools_prop.common_group_name_props):
|
class BALLANCE_OT_add_virtools_group(UTILS_virtools_prop.common_group_name_props):
|
||||||
"""Add a Virtools Group for Active Object."""
|
"""Add a Virtools Group for Active Object."""
|
||||||
@ -15,7 +15,7 @@ class BALLANCE_OT_add_virtools_group(UTILS_virtools_prop.common_group_name_props
|
|||||||
# try adding
|
# try adding
|
||||||
obj = context.object
|
obj = context.object
|
||||||
if not UTILS_virtools_prop.add_virtools_group_data(obj, self.get_group_name_string()):
|
if not UTILS_virtools_prop.add_virtools_group_data(obj, self.get_group_name_string()):
|
||||||
UTILS_functions.show_message_box(("Group name is duplicated!", ), "Duplicated Name", 'ERROR')
|
UTILS_functions.show_message_box(("Group name is duplicated!", ), "Duplicated Name", UTILS_icons_manager.blender_error_icon)
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
@ -44,9 +44,29 @@ class BALLANCE_OT_rm_virtools_group(bpy.types.Operator):
|
|||||||
UTILS_virtools_prop.remove_virtools_group_data_by_index(obj, int(UTILS_virtools_prop.get_active_virtools_group(obj)))
|
UTILS_virtools_prop.remove_virtools_group_data_by_index(obj, int(UTILS_virtools_prop.get_active_virtools_group(obj)))
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
class BALLANCE_OT_clear_virtools_group(bpy.types.Operator):
|
||||||
|
"""Clear All Virtools Group for Active Object."""
|
||||||
|
bl_idname = "ballance.clear_virtools_group"
|
||||||
|
bl_label = "Clear Virtools Group"
|
||||||
|
bl_options = {'UNDO'}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(self, context):
|
||||||
|
return context.object is not None
|
||||||
|
|
||||||
|
def invoke(self, context, event):
|
||||||
|
wm = context.window_manager
|
||||||
|
return wm.invoke_confirm(self, event)
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
obj = context.object
|
||||||
|
UTILS_virtools_prop.clear_virtools_group_data(obj)
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
class BALLANCE_UL_virtools_group(bpy.types.UIList):
|
class BALLANCE_UL_virtools_group(bpy.types.UIList):
|
||||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||||
layout.prop(item, 'group_name', icon='GROUP', emboss=False, text="")
|
layout.label(text=item.group_name, translate=False, icon='GROUP')
|
||||||
|
#layout.prop(item, 'group_name', icon='GROUP', emboss=False, text="")
|
||||||
|
|
||||||
class BALLANCE_PT_virtools_group(bpy.types.Panel):
|
class BALLANCE_PT_virtools_group(bpy.types.Panel):
|
||||||
"""Show Virtools Group Properties."""
|
"""Show Virtools Group Properties."""
|
||||||
@ -71,3 +91,5 @@ class BALLANCE_PT_virtools_group(bpy.types.Panel):
|
|||||||
col = row.column(align=True)
|
col = row.column(align=True)
|
||||||
col.operator(BALLANCE_OT_add_virtools_group.bl_idname, icon='ADD', text="")
|
col.operator(BALLANCE_OT_add_virtools_group.bl_idname, icon='ADD', text="")
|
||||||
col.operator(BALLANCE_OT_rm_virtools_group.bl_idname, icon='REMOVE', text="")
|
col.operator(BALLANCE_OT_rm_virtools_group.bl_idname, icon='REMOVE', text="")
|
||||||
|
col.separator()
|
||||||
|
col.operator(BALLANCE_OT_clear_virtools_group.bl_idname, icon='TRASH', text="")
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import bpy
|
import bpy
|
||||||
from . import UTILS_constants, UTILS_functions, UTILS_virtools_prop
|
from . import UTILS_constants, UTILS_functions, UTILS_virtools_prop, UTILS_icons_manager
|
||||||
|
|
||||||
class BALLANCE_OT_apply_virtools_material(bpy.types.Operator):
|
class BALLANCE_OT_apply_virtools_material(bpy.types.Operator):
|
||||||
"""Apply Virtools Material to Blender Material."""
|
"""Apply Virtools Material to Blender Material."""
|
||||||
@ -19,7 +19,7 @@ class BALLANCE_OT_apply_virtools_material(bpy.types.Operator):
|
|||||||
if mtl_data[0]:
|
if mtl_data[0]:
|
||||||
UTILS_functions.create_material_nodes(mtl, mtl_data)
|
UTILS_functions.create_material_nodes(mtl, mtl_data)
|
||||||
else:
|
else:
|
||||||
UTILS_functions.show_message_box(("Virtools Material is not enabled.", ), "Apply Failed", 'ERROR')
|
UTILS_functions.show_message_box(("Virtools Material is not enabled.", ), "Apply Failed", UTILS_icons_manager.blender_error_icon)
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
@ -37,12 +37,54 @@ class BALLANCE_OT_parse_virtools_material(bpy.types.Operator):
|
|||||||
mtl = context.material
|
mtl = context.material
|
||||||
mtl_data = UTILS_functions.parse_material_nodes(mtl)
|
mtl_data = UTILS_functions.parse_material_nodes(mtl)
|
||||||
if mtl_data is None:
|
if mtl_data is None:
|
||||||
UTILS_functions.show_message_box(("Fail to parse Principled BSDF.", ), "Parsing Failed", 'ERROR')
|
UTILS_functions.show_message_box(("Fail to parse Principled BSDF.", ), "Parsing Failed", UTILS_icons_manager.blender_error_icon)
|
||||||
else:
|
else:
|
||||||
UTILS_virtools_prop.set_virtools_material_data(mtl, mtl_data)
|
UTILS_virtools_prop.set_virtools_material_data(mtl, mtl_data)
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
class BALLANCE_OT_preset_virtools_material(bpy.types.Operator):
|
||||||
|
"""Preset Virtools Material with Original Ballance Data."""
|
||||||
|
bl_idname = "ballance.preset_virtools_material"
|
||||||
|
bl_label = "Preset Virtools Material"
|
||||||
|
bl_options = {'UNDO'}
|
||||||
|
|
||||||
|
preset_type: bpy.props.EnumProperty(
|
||||||
|
name="Preset",
|
||||||
|
description="The preset which you want to apply.",
|
||||||
|
items=tuple(
|
||||||
|
(str(idx), item["human-readable"], "Suit for: " + ", ".join(item["member"]))
|
||||||
|
for idx, item in enumerate(UTILS_constants.floor_materialStatistic)
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
return context.material 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):
|
||||||
|
preset_idx = int(self.preset_type)
|
||||||
|
preset_data = UTILS_constants.floor_materialStatistic[preset_idx]
|
||||||
|
|
||||||
|
# get data self and only change core colors
|
||||||
|
mtl = context.material
|
||||||
|
vtmtl = UTILS_virtools_prop.get_virtools_material(mtl)
|
||||||
|
|
||||||
|
vtmtl.ambient = preset_data['data']['ambient']
|
||||||
|
vtmtl.diffuse = preset_data['data']['diffuse']
|
||||||
|
vtmtl.specular = preset_data['data']['specular']
|
||||||
|
vtmtl.emissive = preset_data['data']['emissive']
|
||||||
|
vtmtl.specular_power = preset_data['data']['power']
|
||||||
|
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
class BALLANCE_PT_virtools_material(bpy.types.Panel):
|
class BALLANCE_PT_virtools_material(bpy.types.Panel):
|
||||||
"""Show Virtools Material Properties."""
|
"""Show Virtools Material Properties."""
|
||||||
bl_label = "Virtools Material"
|
bl_label = "Virtools Material"
|
||||||
@ -69,7 +111,9 @@ class BALLANCE_PT_virtools_material(bpy.types.Panel):
|
|||||||
layout.enabled = target.enable_virtools_material
|
layout.enabled = target.enable_virtools_material
|
||||||
|
|
||||||
# draw layout
|
# draw layout
|
||||||
layout.label(text="Basic Parameters")
|
row = layout.row()
|
||||||
|
row.label(text="Basic Parameters")
|
||||||
|
row.operator(BALLANCE_OT_preset_virtools_material.bl_idname, text="", icon="PRESET")
|
||||||
layout.prop(target, 'ambient')
|
layout.prop(target, 'ambient')
|
||||||
layout.prop(target, 'diffuse')
|
layout.prop(target, 'diffuse')
|
||||||
layout.prop(target, 'specular')
|
layout.prop(target, 'specular')
|
||||||
@ -86,6 +130,6 @@ class BALLANCE_PT_virtools_material(bpy.types.Panel):
|
|||||||
|
|
||||||
layout.separator()
|
layout.separator()
|
||||||
layout.label(text="Operations")
|
layout.label(text="Operations")
|
||||||
layout.operator("ballance.apply_virtools_material", icon="NODETREE")
|
layout.operator(BALLANCE_OT_apply_virtools_material.bl_idname, icon="NODETREE")
|
||||||
layout.operator("ballance.parse_virtools_material", icon="HIDE_OFF")
|
layout.operator(BALLANCE_OT_parse_virtools_material.bl_idname, icon="HIDE_OFF")
|
||||||
|
|
||||||
|
@ -169,13 +169,14 @@ floor_textureReflactionMap = {
|
|||||||
"BallStone": "Ball_Stone.bmp"
|
"BallStone": "Ball_Stone.bmp"
|
||||||
}
|
}
|
||||||
|
|
||||||
# WARNING: this data is shared with `BallanceVirtoolsPlugin/bvh/features/mapping/fix_texture.cpp`
|
# WARNING: this data is shared with `BallanceVirtoolsPlugin/bvh/features/mapping/bmfile_fix_texture.cpp`
|
||||||
floor_materialStatistic = [
|
floor_materialStatistic = [
|
||||||
{
|
{
|
||||||
|
"human-readable": "Floor Side",
|
||||||
"member": [
|
"member": [
|
||||||
"FloorSide",
|
"FloorSide",
|
||||||
"FloorTopBorder_ForSide",
|
"FloorTopBorder_ForSide",
|
||||||
"FloorTopBorderless_ForSide"
|
"FloorTopBorderless_ForSide"
|
||||||
],
|
],
|
||||||
"data": {
|
"data": {
|
||||||
"ambient": (0, 0, 0),
|
"ambient": (0, 0, 0),
|
||||||
@ -186,12 +187,13 @@ floor_materialStatistic = [
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"human-readable": "Floor Top",
|
||||||
"member": [
|
"member": [
|
||||||
"FloorTopBorder",
|
"FloorTopBorder",
|
||||||
"FloorTopBorderless",
|
"FloorTopBorderless",
|
||||||
"FloorTopFlat",
|
"FloorTopFlat",
|
||||||
"FloorTopProfil",
|
"FloorTopProfil",
|
||||||
"FloorTopProfilFlat"
|
"FloorTopProfilFlat"
|
||||||
],
|
],
|
||||||
"data": {
|
"data": {
|
||||||
"ambient": (0, 0, 0),
|
"ambient": (0, 0, 0),
|
||||||
@ -202,8 +204,9 @@ floor_materialStatistic = [
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"human-readable": "Transform Paper",
|
||||||
"member": [
|
"member": [
|
||||||
"BallPaper"
|
"BallPaper"
|
||||||
],
|
],
|
||||||
"data": {
|
"data": {
|
||||||
"ambient": (25 / 255.0, 25 / 255.0, 25 / 255.0),
|
"ambient": (25 / 255.0, 25 / 255.0, 25 / 255.0),
|
||||||
@ -213,10 +216,11 @@ floor_materialStatistic = [
|
|||||||
"power": 0
|
"power": 0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"human-readable": "Transform Stone & Wood",
|
||||||
"member": [
|
"member": [
|
||||||
"BallStone",
|
"BallStone",
|
||||||
"BallWood"
|
"BallWood"
|
||||||
],
|
],
|
||||||
"data": {
|
"data": {
|
||||||
"ambient": (25 / 255.0, 25 / 255.0, 25 / 255.0),
|
"ambient": (25 / 255.0, 25 / 255.0, 25 / 255.0),
|
||||||
@ -225,6 +229,45 @@ floor_materialStatistic = [
|
|||||||
"emissive": (60 / 255.0, 60 / 255.0, 60 / 255.0),
|
"emissive": (60 / 255.0, 60 / 255.0, 60 / 255.0),
|
||||||
"power": 0
|
"power": 0
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"human-readable": "Rail",
|
||||||
|
"member": [
|
||||||
|
"Rail"
|
||||||
|
],
|
||||||
|
"data": {
|
||||||
|
"ambient": (0.0, 0.0, 0.0),
|
||||||
|
"diffuse": (100 / 255.0, 118 / 255.0, 133 / 255.0),
|
||||||
|
"specular": (210 / 255.0, 210 / 255.0, 210 / 255.0),
|
||||||
|
"emissive": (124 / 255.0, 134 / 255.0, 150 / 255.0),
|
||||||
|
"power": 10
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"human-readable": "Wood Path",
|
||||||
|
"member": [
|
||||||
|
"WoodPanel"
|
||||||
|
],
|
||||||
|
"data": {
|
||||||
|
"ambient": (2 / 255.0, 2 / 255.0, 2 / 255.0),
|
||||||
|
"diffuse": (1.0, 1.0, 1.0),
|
||||||
|
"specular": (59 / 255.0, 59 / 255.0, 59 / 255.0),
|
||||||
|
"emissive": (30 / 255.0, 30 / 255.0, 30 / 255.0),
|
||||||
|
"power": 25
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"human-readable": "Wood Chip",
|
||||||
|
"member": [
|
||||||
|
"WoodPlain2"
|
||||||
|
],
|
||||||
|
"data": {
|
||||||
|
"ambient": (25 / 255.0, 25 / 255.0, 25 / 255.0),
|
||||||
|
"diffuse": (1.0, 1.0, 1.0),
|
||||||
|
"specular": (100 / 255.0, 100 / 255.0, 100 / 255.0),
|
||||||
|
"emissive": (50 / 255.0, 50 / 255.0, 50 / 255.0),
|
||||||
|
"power": 50
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -247,11 +290,6 @@ for walk_root, walk_dirs, walk_files in os.walk(os.path.join(os.path.dirname(__f
|
|||||||
floor_derivedBlockList.append(item["Type"])
|
floor_derivedBlockList.append(item["Type"])
|
||||||
floor_blockDict[item["Type"]] = item
|
floor_blockDict[item["Type"]] = item
|
||||||
|
|
||||||
icons_floor = None
|
|
||||||
icons_floorDict = {}
|
|
||||||
# blenderIcon_elements = None
|
|
||||||
# blenderIcon_elements_dict = {}
|
|
||||||
|
|
||||||
rename_normalComponentsGroupName = set([
|
rename_normalComponentsGroupName = set([
|
||||||
"P_Extra_Life",
|
"P_Extra_Life",
|
||||||
"P_Extra_Point",
|
"P_Extra_Point",
|
||||||
|
@ -105,7 +105,7 @@ def parse_material_nodes(mtl):
|
|||||||
# return value
|
# return value
|
||||||
return (True,
|
return (True,
|
||||||
mtl_ambient, mtl_diffuse, mtl_specular, mtl_emissive, mtl_specularPower,
|
mtl_ambient, mtl_diffuse, mtl_specular, mtl_emissive, mtl_specularPower,
|
||||||
False, False, False, False,
|
False, False, True, False,
|
||||||
mtl_texture
|
mtl_texture
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -116,8 +116,21 @@ def parse_material_nodes(mtl):
|
|||||||
# load component
|
# load component
|
||||||
|
|
||||||
def load_component(component_id):
|
def load_component(component_id):
|
||||||
# get file first
|
# get component name from id
|
||||||
component_name = UTILS_constants.bmfile_componentList[component_id]
|
component_name = UTILS_constants.bmfile_componentList[component_id]
|
||||||
|
|
||||||
|
# create real mesh.
|
||||||
|
# if component mesh is existed, use existed one.
|
||||||
|
(mesh, skip_init) = create_instance_with_option(
|
||||||
|
UTILS_constants.BmfileInfoType.MESH,
|
||||||
|
"BlcBldPlg_EleMesh_" + component_name,
|
||||||
|
'CURRENT'
|
||||||
|
)
|
||||||
|
if skip_init:
|
||||||
|
return mesh
|
||||||
|
|
||||||
|
# mesh is not existing. start to load mesh
|
||||||
|
# get file first
|
||||||
selected_file = os.path.join(
|
selected_file = os.path.join(
|
||||||
os.path.dirname(__file__),
|
os.path.dirname(__file__),
|
||||||
'meshes',
|
'meshes',
|
||||||
@ -126,9 +139,6 @@ def load_component(component_id):
|
|||||||
|
|
||||||
# read file. please note this sector is sync with import_bm's mesh's code. when something change, please change each other.
|
# read file. please note this sector is sync with import_bm's mesh's code. when something change, please change each other.
|
||||||
fmesh = open(selected_file, 'rb')
|
fmesh = open(selected_file, 'rb')
|
||||||
|
|
||||||
# create real mesh, we don't need to consider name. blender will solve duplicated name
|
|
||||||
mesh = bpy.data.meshes.new('mesh_' + component_name)
|
|
||||||
|
|
||||||
vList = []
|
vList = []
|
||||||
vnList = []
|
vnList = []
|
||||||
@ -204,6 +214,10 @@ def create_instance_with_option(instance_type, instance_name, instance_opt,
|
|||||||
For object, you should provide `extra_mesh`.
|
For object, you should provide `extra_mesh`.
|
||||||
For texture, you should provide `extra_texture_path` and `extra_texture_filename`.
|
For texture, you should provide `extra_texture_path` and `extra_texture_filename`.
|
||||||
|
|
||||||
|
Value type:
|
||||||
|
`instance_type`: one integer in UTILS_constants.BmfileInfoType
|
||||||
|
`instance_name`: a string of new data block name
|
||||||
|
`instance_opt`: 'RENAME' or 'CURRENT'
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def get_instance():
|
def get_instance():
|
||||||
|
98
ballance_blender_plugin/UTILS_icons_manager.py
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
import bpy
|
||||||
|
import bpy.utils.previews
|
||||||
|
import os
|
||||||
|
from . import UTILS_constants
|
||||||
|
|
||||||
|
blender_info_icon = 'INFO'
|
||||||
|
blender_warning_icon = 'ERROR'
|
||||||
|
blender_error_icon = 'CANCEL'
|
||||||
|
|
||||||
|
# universal icon loader, all icon are stored in this preview collection
|
||||||
|
universal_icons = None
|
||||||
|
|
||||||
|
# empty icon for placeholder
|
||||||
|
empty_icon_id = 0
|
||||||
|
|
||||||
|
# a map. key is block name, value is loaded icon id
|
||||||
|
floor_icons_map: dict = {}
|
||||||
|
element_icons_map: dict = {}
|
||||||
|
groupext_icons_map: dict = {}
|
||||||
|
|
||||||
|
group_name_conv_map: dict = {
|
||||||
|
"PS_Levelstart": "PS_FourFlames",
|
||||||
|
"PE_Levelende": "PE_Balloon",
|
||||||
|
"PC_Checkpoints": "PC_TwoFlames",
|
||||||
|
"PR_Resetpoints": "PR_Resetpoint",
|
||||||
|
|
||||||
|
"Sound_HitID_01": "SoundID_01",
|
||||||
|
"Sound_RollID_01": "SoundID_01",
|
||||||
|
"Sound_HitID_02": "SoundID_02",
|
||||||
|
"Sound_RollID_02": "SoundID_02",
|
||||||
|
"Sound_HitID_03": "SoundID_03",
|
||||||
|
"Sound_RollID_03": "SoundID_03"
|
||||||
|
}
|
||||||
|
|
||||||
|
def register_icons():
|
||||||
|
global universal_icons
|
||||||
|
global empty_icon_id
|
||||||
|
global floor_icons_map, element_icons_map, groupext_icons_map
|
||||||
|
|
||||||
|
# create preview collection and get icon folder
|
||||||
|
icon_path = os.path.join(os.path.dirname(__file__), "icons")
|
||||||
|
universal_icons = bpy.utils.previews.new()
|
||||||
|
|
||||||
|
# load empty
|
||||||
|
universal_icons.load("BlcBldPlg_EmptyIcon", os.path.join(icon_path, "Empty.png"), 'IMAGE')
|
||||||
|
empty_icon_id = universal_icons["BlcBldPlg_EmptyIcon"].icon_id
|
||||||
|
|
||||||
|
# add floor icon
|
||||||
|
for key, value in UTILS_constants.floor_blockDict.items():
|
||||||
|
blockIconName = "BlcBldPlg_FloorIcon_" + key
|
||||||
|
universal_icons.load(blockIconName, os.path.join(icon_path, "floor", value["BindingDisplayTexture"]), 'IMAGE')
|
||||||
|
floor_icons_map[key] = universal_icons[blockIconName].icon_id
|
||||||
|
|
||||||
|
# add elements icon
|
||||||
|
for elename in UTILS_constants.bmfile_componentList:
|
||||||
|
blockIconName = "BlcBldPlg_ElementIcon_" + elename
|
||||||
|
universal_icons.load(blockIconName, os.path.join(icon_path, "element", elename + '.png'), 'IMAGE')
|
||||||
|
element_icons_map[elename] = universal_icons[blockIconName].icon_id
|
||||||
|
|
||||||
|
# add extra group icon
|
||||||
|
for grp in ("SoundID_01", "SoundID_02", "SoundID_03"):
|
||||||
|
blockIconName = "BlcBldPlg_GroupIcon_" + grp
|
||||||
|
universal_icons.load(blockIconName, os.path.join(icon_path, "group", grp + '.png'), 'IMAGE')
|
||||||
|
groupext_icons_map[grp] = universal_icons[blockIconName].icon_id
|
||||||
|
|
||||||
|
def unregister_icons():
|
||||||
|
global universal_icons
|
||||||
|
global floor_icons_map, element_icons_map, groupext_icons_map
|
||||||
|
|
||||||
|
bpy.utils.previews.remove(universal_icons)
|
||||||
|
floor_icons_map.clear()
|
||||||
|
element_icons_map.clear()
|
||||||
|
groupext_icons_map.clear()
|
||||||
|
|
||||||
|
def get_floor_icon(floor_blk_name: str):
|
||||||
|
# default return empty icon
|
||||||
|
return floor_icons_map.get(floor_blk_name, empty_icon_id)
|
||||||
|
|
||||||
|
def get_element_icon(element_name: str):
|
||||||
|
# default return empty icon
|
||||||
|
return element_icons_map.get(element_name, empty_icon_id)
|
||||||
|
|
||||||
|
def get_group_icon(group_name: str):
|
||||||
|
# try parse string
|
||||||
|
# if not found, return self
|
||||||
|
conv_name = group_name_conv_map.get(group_name, group_name)
|
||||||
|
|
||||||
|
# get from extra group icon first
|
||||||
|
idx = groupext_icons_map.get(conv_name, empty_icon_id)
|
||||||
|
if idx != empty_icon_id:
|
||||||
|
return idx
|
||||||
|
|
||||||
|
# if failed, get from element. if still failed, return empty icon
|
||||||
|
return get_element_icon(conv_name)
|
||||||
|
|
||||||
|
# no matter how, register icon always
|
||||||
|
# and no unregister call
|
||||||
|
register_icons()
|
@ -1,5 +1,5 @@
|
|||||||
import bpy
|
import bpy
|
||||||
from . import UTILS_constants, UTILS_functions
|
from . import UTILS_constants, UTILS_functions, UTILS_icons_manager
|
||||||
|
|
||||||
class BALLANCE_PG_virtools_material(bpy.types.PropertyGroup):
|
class BALLANCE_PG_virtools_material(bpy.types.PropertyGroup):
|
||||||
enable_virtools_material: bpy.props.BoolProperty(
|
enable_virtools_material: bpy.props.BoolProperty(
|
||||||
@ -7,29 +7,37 @@ class BALLANCE_PG_virtools_material(bpy.types.PropertyGroup):
|
|||||||
default=False,
|
default=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
ambient: bpy.props.FloatVectorProperty(name="Ambient",
|
ambient: bpy.props.FloatVectorProperty(
|
||||||
subtype='COLOR',
|
name="Ambient",
|
||||||
min=0.0,
|
subtype='COLOR',
|
||||||
max=1.0,
|
min=0.0,
|
||||||
default=[0.0,0.0,0.0])
|
max=1.0,
|
||||||
|
default=[76 / 255, 76 / 255, 76 / 255]
|
||||||
|
)
|
||||||
|
|
||||||
diffuse: bpy.props.FloatVectorProperty(name="Diffuse",
|
diffuse: bpy.props.FloatVectorProperty(
|
||||||
subtype='COLOR',
|
name="Diffuse",
|
||||||
min=0.0,
|
subtype='COLOR',
|
||||||
max=1.0,
|
min=0.0,
|
||||||
default=[0.0,0.0,0.0])
|
max=1.0,
|
||||||
|
default=[178 / 255, 178 / 255, 178 / 255]
|
||||||
|
)
|
||||||
|
|
||||||
specular: bpy.props.FloatVectorProperty(name="Specular",
|
specular: bpy.props.FloatVectorProperty(
|
||||||
subtype='COLOR',
|
name="Specular",
|
||||||
min=0.0,
|
subtype='COLOR',
|
||||||
max=1.0,
|
min=0.0,
|
||||||
default=[0.0,0.0,0.0])
|
max=1.0,
|
||||||
|
default=[127 / 255, 127 / 255, 127 / 255]
|
||||||
|
)
|
||||||
|
|
||||||
emissive: bpy.props.FloatVectorProperty(name="Emissive",
|
emissive: bpy.props.FloatVectorProperty(
|
||||||
subtype='COLOR',
|
name="Emissive",
|
||||||
min=0.0,
|
subtype='COLOR',
|
||||||
max=1.0,
|
min=0.0,
|
||||||
default=[0.0,0.0,0.0])
|
max=1.0,
|
||||||
|
default=[0.0, 0.0, 0.0]
|
||||||
|
)
|
||||||
|
|
||||||
specular_power: bpy.props.FloatProperty(
|
specular_power: bpy.props.FloatProperty(
|
||||||
name="Specular Power",
|
name="Specular Power",
|
||||||
@ -53,7 +61,7 @@ class BALLANCE_PG_virtools_material(bpy.types.PropertyGroup):
|
|||||||
z_buffer: bpy.props.BoolProperty(
|
z_buffer: bpy.props.BoolProperty(
|
||||||
name="Z Buffer",
|
name="Z Buffer",
|
||||||
description="ZFunc: VXCMP_LESSEQUAL.",
|
description="ZFunc: VXCMP_LESSEQUAL.",
|
||||||
default=False,
|
default=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
two_sided: bpy.props.BoolProperty(
|
two_sided: bpy.props.BoolProperty(
|
||||||
@ -84,7 +92,12 @@ class common_group_name_props(bpy.types.Operator):
|
|||||||
group_name: bpy.props.EnumProperty(
|
group_name: bpy.props.EnumProperty(
|
||||||
name="Group Name",
|
name="Group Name",
|
||||||
description="Pick vanilla Ballance group name.",
|
description="Pick vanilla Ballance group name.",
|
||||||
items=tuple((x, x, "") for x in UTILS_constants.propsVtGroups_availableGroups),
|
#items=tuple((x, x, "") for x in UTILS_constants.propsVtGroups_availableGroups),
|
||||||
|
items=tuple(
|
||||||
|
# token, display name, descriptions, icon, index
|
||||||
|
(grp, grp, "", UTILS_icons_manager.get_group_icon(grp), idx)
|
||||||
|
for idx, grp in enumerate(UTILS_constants.propsVtGroups_availableGroups)
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
custom_group_name: bpy.props.StringProperty(
|
custom_group_name: bpy.props.StringProperty(
|
||||||
@ -98,7 +111,7 @@ class common_group_name_props(bpy.types.Operator):
|
|||||||
if (self.group_name_source == 'CUSTOM'):
|
if (self.group_name_source == 'CUSTOM'):
|
||||||
parent_layout.prop(self, 'custom_group_name')
|
parent_layout.prop(self, 'custom_group_name')
|
||||||
else:
|
else:
|
||||||
parent_layout.prop(self, 'group_name')
|
parent_layout.prop(self, 'group_name') # do not translate group name. it's weird
|
||||||
|
|
||||||
def get_group_name_string(self):
|
def get_group_name_string(self):
|
||||||
return str(self.custom_group_name if self.group_name_source == 'CUSTOM' else self.group_name)
|
return str(self.custom_group_name if self.group_name_source == 'CUSTOM' else self.group_name)
|
||||||
|
@ -2,7 +2,7 @@ bl_info={
|
|||||||
"name":"Ballance Blender Plugin",
|
"name":"Ballance Blender Plugin",
|
||||||
"description":"Ballance mapping tools for Blender",
|
"description":"Ballance mapping tools for Blender",
|
||||||
"author":"yyc12345",
|
"author":"yyc12345",
|
||||||
"version":(3,0),
|
"version":(3,1),
|
||||||
"blender":(3,3,0),
|
"blender":(3,3,0),
|
||||||
"category":"Object",
|
"category":"Object",
|
||||||
"support":"TESTING",
|
"support":"TESTING",
|
||||||
@ -13,9 +13,8 @@ bl_info={
|
|||||||
|
|
||||||
# =============================================
|
# =============================================
|
||||||
# import system
|
# import system
|
||||||
import bpy, bpy_extras
|
import bpy
|
||||||
import bpy.utils.previews
|
|
||||||
import os
|
|
||||||
# import my code (with reload)
|
# import my code (with reload)
|
||||||
if "bpy" in locals():
|
if "bpy" in locals():
|
||||||
import importlib
|
import importlib
|
||||||
@ -33,6 +32,8 @@ if "bpy" in locals():
|
|||||||
importlib.reload(UTILS_virtools_prop)
|
importlib.reload(UTILS_virtools_prop)
|
||||||
if "UTILS_safe_eval" in locals():
|
if "UTILS_safe_eval" in locals():
|
||||||
importlib.reload(UTILS_safe_eval)
|
importlib.reload(UTILS_safe_eval)
|
||||||
|
if "UTILS_icons_manager" in locals():
|
||||||
|
importlib.reload(UTILS_icons_manager)
|
||||||
|
|
||||||
if "BMFILE_export" in locals():
|
if "BMFILE_export" in locals():
|
||||||
importlib.reload(BMFILE_export)
|
importlib.reload(BMFILE_export)
|
||||||
@ -63,7 +64,7 @@ if "bpy" in locals():
|
|||||||
if "PROPS_virtools_material" in locals():
|
if "PROPS_virtools_material" in locals():
|
||||||
importlib.reload(PROPS_virtools_material)
|
importlib.reload(PROPS_virtools_material)
|
||||||
|
|
||||||
from . import UTILS_constants, UTILS_functions, UTILS_preferences, UTILS_virtools_prop, UTILS_safe_eval
|
from . import UTILS_constants, UTILS_functions, UTILS_preferences, UTILS_virtools_prop, UTILS_safe_eval, UTILS_icons_manager
|
||||||
from . import BMFILE_export, BMFILE_import
|
from . import BMFILE_export, BMFILE_import
|
||||||
from . import MODS_3dsmax_align, MODS_flatten_uv, MODS_rail_uv
|
from . import MODS_3dsmax_align, MODS_flatten_uv, MODS_rail_uv
|
||||||
from . import OBJS_add_components, OBJS_add_floors, OBJS_add_rails, OBJS_group_opers
|
from . import OBJS_add_components, OBJS_add_floors, OBJS_add_rails, OBJS_group_opers
|
||||||
@ -86,7 +87,7 @@ class BALLANCE_MT_ThreeDViewerMenu(bpy.types.Menu):
|
|||||||
layout.operator(MODS_flatten_uv.BALLANCE_OT_flatten_uv.bl_idname)
|
layout.operator(MODS_flatten_uv.BALLANCE_OT_flatten_uv.bl_idname)
|
||||||
|
|
||||||
class BALLANCE_MT_AddFloorMenu(bpy.types.Menu):
|
class BALLANCE_MT_AddFloorMenu(bpy.types.Menu):
|
||||||
"""Add Ballance floor"""
|
"""Add Ballance Floor"""
|
||||||
bl_idname = "BALLANCE_MT_AddFloorMenu"
|
bl_idname = "BALLANCE_MT_AddFloorMenu"
|
||||||
bl_label = "Floors"
|
bl_label = "Floors"
|
||||||
|
|
||||||
@ -97,7 +98,7 @@ class BALLANCE_MT_AddFloorMenu(bpy.types.Menu):
|
|||||||
for item in UTILS_constants.floor_basicBlockList:
|
for item in UTILS_constants.floor_basicBlockList:
|
||||||
cop = layout.operator(
|
cop = layout.operator(
|
||||||
OBJS_add_floors.BALLANCE_OT_add_floors.bl_idname,
|
OBJS_add_floors.BALLANCE_OT_add_floors.bl_idname,
|
||||||
text=item, icon_value = UTILS_constants.icons_floorDict[item])
|
text=item, icon_value = UTILS_icons_manager.get_floor_icon(item))
|
||||||
cop.floor_type = item
|
cop.floor_type = item
|
||||||
|
|
||||||
layout.separator()
|
layout.separator()
|
||||||
@ -105,9 +106,50 @@ class BALLANCE_MT_AddFloorMenu(bpy.types.Menu):
|
|||||||
for item in UTILS_constants.floor_derivedBlockList:
|
for item in UTILS_constants.floor_derivedBlockList:
|
||||||
cop = layout.operator(
|
cop = layout.operator(
|
||||||
OBJS_add_floors.BALLANCE_OT_add_floors.bl_idname,
|
OBJS_add_floors.BALLANCE_OT_add_floors.bl_idname,
|
||||||
text=item, icon_value = UTILS_constants.icons_floorDict[item])
|
text=item, icon_value = UTILS_icons_manager.get_floor_icon(item))
|
||||||
cop.floor_type = item
|
cop.floor_type = item
|
||||||
|
|
||||||
|
class BALLANCE_MT_AddRailMenu(bpy.types.Menu):
|
||||||
|
"""Add Ballance Rail"""
|
||||||
|
bl_idname = "BALLANCE_MT_AddRailMenu"
|
||||||
|
bl_label = "Rails"
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
layout = self.layout
|
||||||
|
layout.operator(OBJS_add_rails.BALLANCE_OT_add_rails.bl_idname, text="Rail Section")
|
||||||
|
layout.operator(OBJS_add_rails.BALLANCE_OT_add_tunnels.bl_idname, text="Tunnel Section")
|
||||||
|
|
||||||
|
class BALLANCE_MT_AddNormalElementsMenu(bpy.types.Menu):
|
||||||
|
"""Add Ballance Elements"""
|
||||||
|
bl_idname = "BALLANCE_MT_AddNormalElementsMenu"
|
||||||
|
bl_label = "Elements"
|
||||||
|
def draw(self, context):
|
||||||
|
layout = self.layout
|
||||||
|
OBJS_add_components.BALLANCE_OT_add_components.draw_blc_menu(layout)
|
||||||
|
class BALLANCE_MT_AddDupElementsMenu(bpy.types.Menu):
|
||||||
|
"""Add Ballance Elements"""
|
||||||
|
bl_idname = "BALLANCE_MT_AddDupElementsMenu"
|
||||||
|
bl_label = "Elements"
|
||||||
|
def draw(self, context):
|
||||||
|
layout = self.layout
|
||||||
|
OBJS_add_components.BALLANCE_OT_add_components_dup.draw_blc_menu(layout)
|
||||||
|
class BALLANCE_MT_AddElementsMenu(bpy.types.Menu):
|
||||||
|
"""Add Ballance Elements"""
|
||||||
|
bl_idname = "BALLANCE_MT_AddElementsMenu"
|
||||||
|
bl_label = "Elements"
|
||||||
|
def draw(self, context):
|
||||||
|
layout = self.layout
|
||||||
|
|
||||||
|
layout.label(text="Basic Elements")
|
||||||
|
OBJS_add_components.BALLANCE_OT_add_components.draw_blc_menu(layout)
|
||||||
|
|
||||||
|
layout.separator()
|
||||||
|
layout.label(text="Duplicated Elements")
|
||||||
|
OBJS_add_components.BALLANCE_OT_add_components_dup.draw_blc_menu(layout)
|
||||||
|
|
||||||
|
layout.separator()
|
||||||
|
layout.label(text="Elements Series")
|
||||||
|
OBJS_add_components.BALLANCE_OT_add_components_series.draw_blc_menu(layout)
|
||||||
|
|
||||||
# =============================================
|
# =============================================
|
||||||
# blender call system
|
# blender call system
|
||||||
@ -125,9 +167,14 @@ classes = (
|
|||||||
BALLANCE_MT_ThreeDViewerMenu,
|
BALLANCE_MT_ThreeDViewerMenu,
|
||||||
|
|
||||||
OBJS_add_components.BALLANCE_OT_add_components,
|
OBJS_add_components.BALLANCE_OT_add_components,
|
||||||
|
OBJS_add_components.BALLANCE_OT_add_components_dup,
|
||||||
|
OBJS_add_components.BALLANCE_OT_add_components_series,
|
||||||
OBJS_add_rails.BALLANCE_OT_add_rails,
|
OBJS_add_rails.BALLANCE_OT_add_rails,
|
||||||
|
OBJS_add_rails.BALLANCE_OT_add_tunnels,
|
||||||
OBJS_add_floors.BALLANCE_OT_add_floors,
|
OBJS_add_floors.BALLANCE_OT_add_floors,
|
||||||
BALLANCE_MT_AddFloorMenu,
|
BALLANCE_MT_AddFloorMenu,
|
||||||
|
BALLANCE_MT_AddRailMenu,
|
||||||
|
BALLANCE_MT_AddElementsMenu,
|
||||||
|
|
||||||
NAMES_rename_system.BALLANCE_OT_rename_by_group,
|
NAMES_rename_system.BALLANCE_OT_rename_by_group,
|
||||||
NAMES_rename_system.BALLANCE_OT_convert_name,
|
NAMES_rename_system.BALLANCE_OT_convert_name,
|
||||||
@ -137,14 +184,15 @@ classes = (
|
|||||||
UTILS_virtools_prop.BALLANCE_PG_virtools_group,
|
UTILS_virtools_prop.BALLANCE_PG_virtools_group,
|
||||||
PROPS_virtools_group.BALLANCE_OT_add_virtools_group,
|
PROPS_virtools_group.BALLANCE_OT_add_virtools_group,
|
||||||
PROPS_virtools_group.BALLANCE_OT_rm_virtools_group,
|
PROPS_virtools_group.BALLANCE_OT_rm_virtools_group,
|
||||||
|
PROPS_virtools_group.BALLANCE_OT_clear_virtools_group,
|
||||||
PROPS_virtools_group.BALLANCE_UL_virtools_group,
|
PROPS_virtools_group.BALLANCE_UL_virtools_group,
|
||||||
PROPS_virtools_group.BALLANCE_PT_virtools_group,
|
PROPS_virtools_group.BALLANCE_PT_virtools_group,
|
||||||
PROPS_virtools_material.BALLANCE_OT_apply_virtools_material,
|
PROPS_virtools_material.BALLANCE_OT_apply_virtools_material,
|
||||||
PROPS_virtools_material.BALLANCE_OT_parse_virtools_material,
|
PROPS_virtools_material.BALLANCE_OT_parse_virtools_material,
|
||||||
|
PROPS_virtools_material.BALLANCE_OT_preset_virtools_material,
|
||||||
PROPS_virtools_material.BALLANCE_PT_virtools_material,
|
PROPS_virtools_material.BALLANCE_PT_virtools_material,
|
||||||
|
|
||||||
OBJS_group_opers.BALLANCE_OT_select_virtools_group,
|
OBJS_group_opers.BALLANCE_OT_select_virtools_group,
|
||||||
OBJS_group_opers.BALLANCE_OT_filter_virtools_group,
|
|
||||||
OBJS_group_opers.BALLANCE_OT_ctx_set_group,
|
OBJS_group_opers.BALLANCE_OT_ctx_set_group,
|
||||||
OBJS_group_opers.BALLANCE_OT_ctx_unset_group,
|
OBJS_group_opers.BALLANCE_OT_ctx_unset_group,
|
||||||
OBJS_group_opers.BALLANCE_OT_ctx_clear_group,
|
OBJS_group_opers.BALLANCE_OT_ctx_clear_group,
|
||||||
@ -162,11 +210,12 @@ def menu_func_ballance_add(self, context):
|
|||||||
layout = self.layout
|
layout = self.layout
|
||||||
layout.separator()
|
layout.separator()
|
||||||
layout.label(text="Ballance")
|
layout.label(text="Ballance")
|
||||||
layout.operator_menu_enum(
|
|
||||||
OBJS_add_components.BALLANCE_OT_add_components.bl_idname,
|
|
||||||
"elements_type", icon='MESH_ICOSPHERE', text="Elements")
|
|
||||||
layout.operator(OBJS_add_rails.BALLANCE_OT_add_rails.bl_idname, icon='MESH_CIRCLE', text="Rail section")
|
|
||||||
layout.menu(BALLANCE_MT_AddFloorMenu.bl_idname, icon='MESH_CUBE')
|
layout.menu(BALLANCE_MT_AddFloorMenu.bl_idname, icon='MESH_CUBE')
|
||||||
|
layout.menu(BALLANCE_MT_AddRailMenu.bl_idname, icon='MESH_CIRCLE')
|
||||||
|
layout.menu(BALLANCE_MT_AddElementsMenu.bl_idname, icon='MESH_ICOSPHERE')
|
||||||
|
#layout.operator_menu_enum(
|
||||||
|
# OBJS_add_components.BALLANCE_OT_add_components.bl_idname,
|
||||||
|
# "elements_type", icon='MESH_ICOSPHERE', text="Elements")
|
||||||
def menu_func_ballance_rename(self, context):
|
def menu_func_ballance_rename(self, context):
|
||||||
layout = self.layout
|
layout = self.layout
|
||||||
layout.separator()
|
layout.separator()
|
||||||
@ -180,8 +229,7 @@ def menu_func_ballance_select(self, context):
|
|||||||
layout = self.layout
|
layout = self.layout
|
||||||
layout.separator()
|
layout.separator()
|
||||||
layout.label(text="Ballance")
|
layout.label(text="Ballance")
|
||||||
layout.operator(OBJS_group_opers.BALLANCE_OT_select_virtools_group.bl_idname, icon='SELECT_SET')
|
layout.operator(OBJS_group_opers.BALLANCE_OT_select_virtools_group.bl_idname, icon='RESTRICT_SELECT_OFF')
|
||||||
layout.operator(OBJS_group_opers.BALLANCE_OT_filter_virtools_group.bl_idname, icon='FILTER')
|
|
||||||
def menu_func_ballance_grouping(self, context):
|
def menu_func_ballance_grouping(self, context):
|
||||||
layout = self.layout
|
layout = self.layout
|
||||||
layout.separator()
|
layout.separator()
|
||||||
@ -195,12 +243,7 @@ def menu_func_ballance_grouping(self, context):
|
|||||||
|
|
||||||
def register():
|
def register():
|
||||||
# we need init all icon first
|
# we need init all icon first
|
||||||
icon_path = os.path.join(os.path.dirname(__file__), "icons")
|
#UTILS_icons_manager.register_icons()
|
||||||
UTILS_constants.icons_floor = bpy.utils.previews.new()
|
|
||||||
for key, value in UTILS_constants.floor_blockDict.items():
|
|
||||||
blockIconName = "Ballance_FloorIcon_" + key
|
|
||||||
UTILS_constants.icons_floor.load(blockIconName, os.path.join(icon_path, "floor", value["BindingDisplayTexture"]), 'IMAGE')
|
|
||||||
UTILS_constants.icons_floorDict[key] = UTILS_constants.icons_floor[blockIconName].icon_id
|
|
||||||
|
|
||||||
for cls in classes:
|
for cls in classes:
|
||||||
bpy.utils.register_class(cls)
|
bpy.utils.register_class(cls)
|
||||||
@ -241,7 +284,7 @@ def unregister():
|
|||||||
bpy.utils.unregister_class(cls)
|
bpy.utils.unregister_class(cls)
|
||||||
|
|
||||||
# we need uninstall all icon after all classes unregister
|
# we need uninstall all icon after all classes unregister
|
||||||
bpy.utils.previews.remove(UTILS_constants.icons_floor)
|
#UTILS_icons_manager.unregister_icons()
|
||||||
|
|
||||||
if __name__=="__main__":
|
if __name__=="__main__":
|
||||||
register()
|
register()
|
BIN
ballance_blender_plugin/icons/Empty.png
Normal file
After Width: | Height: | Size: 785 B |
BIN
ballance_blender_plugin/icons/element/PC_TwoFlames.png
Normal file
After Width: | Height: | Size: 663 B |
BIN
ballance_blender_plugin/icons/element/PE_Balloon.png
Normal file
After Width: | Height: | Size: 745 B |
BIN
ballance_blender_plugin/icons/element/PR_Resetpoint.png
Normal file
After Width: | Height: | Size: 945 B |
BIN
ballance_blender_plugin/icons/element/PS_FourFlames.png
Normal file
After Width: | Height: | Size: 818 B |
BIN
ballance_blender_plugin/icons/element/P_Ball_Paper.png
Normal file
After Width: | Height: | Size: 2.3 KiB |
BIN
ballance_blender_plugin/icons/element/P_Ball_Stone.png
Normal file
After Width: | Height: | Size: 2.6 KiB |
BIN
ballance_blender_plugin/icons/element/P_Ball_Wood.png
Normal file
After Width: | Height: | Size: 2.6 KiB |
BIN
ballance_blender_plugin/icons/element/P_Box.png
Normal file
After Width: | Height: | Size: 984 B |
BIN
ballance_blender_plugin/icons/element/P_Dome.png
Normal file
After Width: | Height: | Size: 611 B |
BIN
ballance_blender_plugin/icons/element/P_Extra_Life.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
ballance_blender_plugin/icons/element/P_Extra_Point.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
ballance_blender_plugin/icons/element/P_Modul_01.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
ballance_blender_plugin/icons/element/P_Modul_03.png
Normal file
After Width: | Height: | Size: 471 B |
BIN
ballance_blender_plugin/icons/element/P_Modul_08.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
ballance_blender_plugin/icons/element/P_Modul_17.png
Normal file
After Width: | Height: | Size: 494 B |
BIN
ballance_blender_plugin/icons/element/P_Modul_18.png
Normal file
After Width: | Height: | Size: 360 B |
BIN
ballance_blender_plugin/icons/element/P_Modul_19.png
Normal file
After Width: | Height: | Size: 992 B |
BIN
ballance_blender_plugin/icons/element/P_Modul_25.png
Normal file
After Width: | Height: | Size: 444 B |
BIN
ballance_blender_plugin/icons/element/P_Modul_26.png
Normal file
After Width: | Height: | Size: 691 B |
BIN
ballance_blender_plugin/icons/element/P_Modul_29.png
Normal file
After Width: | Height: | Size: 775 B |
BIN
ballance_blender_plugin/icons/element/P_Modul_30.png
Normal file
After Width: | Height: | Size: 807 B |
BIN
ballance_blender_plugin/icons/element/P_Modul_34.png
Normal file
After Width: | Height: | Size: 652 B |
BIN
ballance_blender_plugin/icons/element/P_Modul_37.png
Normal file
After Width: | Height: | Size: 695 B |
BIN
ballance_blender_plugin/icons/element/P_Modul_41.png
Normal file
After Width: | Height: | Size: 550 B |
BIN
ballance_blender_plugin/icons/element/P_Trafo_Paper.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
ballance_blender_plugin/icons/element/P_Trafo_Stone.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
ballance_blender_plugin/icons/element/P_Trafo_Wood.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
ballance_blender_plugin/icons/group/SoundID_01.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
ballance_blender_plugin/icons/group/SoundID_02.png
Normal file
After Width: | Height: | Size: 2.5 KiB |
BIN
ballance_blender_plugin/icons/group/SoundID_03.png
Normal file
After Width: | Height: | Size: 251 B |