Compare commits
37 Commits
v1.0
...
v2.0-beta1
Author | SHA1 | Date | |
---|---|---|---|
9c24569a06 | |||
031458bfc8 | |||
3d1cf14334 | |||
df44426195 | |||
670cc830fd | |||
582e96615a | |||
ac9d196e3e | |||
f332e15791 | |||
0fb53f6827 | |||
c74141a346 | |||
b3ee636848 | |||
83ba270184 | |||
027e267cc7 | |||
f5275b2abd | |||
93d077f18d | |||
078dc952e7 | |||
112b63319f | |||
e2949708e6 | |||
512cbce7fe | |||
f0e3c3a597 | |||
75bfcbea02 | |||
4259d057fd | |||
6ae4cbeddc | |||
35655a671d | |||
a8203dcb9c | |||
612c641391 | |||
da722fe4bb | |||
c5fc214384 | |||
ff24707186 | |||
1fe77d843e | |||
d236925612 | |||
0af7f204dc | |||
79a04f1496 | |||
08eb6dcff9 | |||
751ff15b95 | |||
97659f74cc | |||
41d3c5f3b7 |
40
README.md
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
This is a Blender plugin which is served for Ballance mapping in Blender.
|
This is a Blender plugin which is served for Ballance mapping in Blender.
|
||||||
|
|
||||||
Currently, it only contain fundamental functions. More useful features will be added in future.
|
The latest commit may not be stable to use, please visit the Release page to get a stable version.
|
||||||
|
|
||||||
## Technical infomation
|
## Technical infomation
|
||||||
|
|
||||||
@ -14,6 +14,8 @@ Used BM file spec can be found in [there](https://github.com/yyc12345/gist/blob/
|
|||||||
|
|
||||||
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)
|
||||||
|
|
||||||
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 2.83.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 2.83.x.
|
||||||
|
|
||||||
## Function introduction
|
## Function introduction
|
||||||
@ -36,19 +38,43 @@ It should be noted that once the BM is exported, all the faces in the file will
|
|||||||
|
|
||||||
Ballance 3D is a set of light tools related to 3D operations, which can be found in the upper right corner of the 3D view.
|
Ballance 3D is a set of light tools related to 3D operations, which can be found in the upper right corner of the 3D view.
|
||||||
|
|
||||||
#### Super Align
|
#### 3ds Max Align
|
||||||
|
|
||||||
Provide 3ds Max like align tools. Current active will be seen as reference object. All selected objects(except active object) will be seen as operating object (So you can select multiple objects to align to the reference object).
|
Provide 3ds Max like align tools. Current active will be seen as reference object. All selected objects(except active object) will be seen as operating object (So you can select multiple objects to align to the reference object).
|
||||||
|
|
||||||
#### Create Rail UV
|
#### Create Rail UV
|
||||||
|
|
||||||
Create UV for rails. You should select the object which you want add rail like UV to. Then, click this menu. Before doing this, you need make sure all selected object have at least 1 UV map (If it have more than 1 UV map, only the first UV map will be changed).
|
To create UVs for the rails in the map, you need to select the objects that need to add UVs similar to the rails, and then click this button to create.
|
||||||
|
|
||||||
|
In the dialog, you can select the material to be used. You can also choose the unfolding mode. For shorter rails, you can choose Point mode. For longer rails, you can use Uniform mode. If you need to manually adjust the zoom ratio, please select Scale mode and specify the ratio (not recommended).
|
||||||
|
|
||||||
|
You can also select the projection axis for better UV distribution.
|
||||||
|
|
||||||
|
### Flatten UV
|
||||||
|
|
||||||
|
In the object editing mode, it is a operator which is used to attach the currently selected surface to the UV. And you can specific the edge which will be attached into the V axis. Note that only convex faces are supported.
|
||||||
|
|
||||||
|
In the edit mode, select the surface, click Flatten UV, and then scroll the slider to select an edge as a reference. If the generated UV is not attached correctly, such as the FloorSide's band is pasted to the bottom, you can reselect the reference edge and redo the operation until it is correct.
|
||||||
|
|
||||||
|
### Add Menu
|
||||||
|
|
||||||
|
In the add menu, we have added a set of commonly used objects. After adding, the object will move to the 3D cursor.
|
||||||
|
|
||||||
|
#### Elements
|
||||||
|
|
||||||
|
Add elements, you can also specify attributes such as section when adding (it will not be displayed for unique objects such as start point)
|
||||||
|
|
||||||
|
#### Rail section
|
||||||
|
|
||||||
|
Add rail section, you can choose monorail or rail (just decide the number of rail section loops added, and will not help you rotate the angle), as well as rail radius and rail span.
|
||||||
|
|
||||||
|
#### Floors
|
||||||
|
|
||||||
|
Adding floor is part of the BMERevenge project. Basic floor is a basic floor component, and Derived floor is a common component composed of basic components. The extension(length) and the side configuration can be set according to its properties. It also has the advantage of reducing vertices.
|
||||||
|
|
||||||
|
It is recommended to merge the vertices by distance, unless there is a need to delete the surface after adding it
|
||||||
|
|
||||||
## Install
|
## Install
|
||||||
|
|
||||||
Put `ballance_blender_plugin` into Blender's plugin folder, `scripts/addons_contrib`. Then enable this plugin in Blender's preferences (Don't forget to configure this plugin's settings).
|
Put `ballance_blender_plugin` into Blender's plugin folder, `scripts/addons_contrib`. Then enable this plugin in Blender's preferences (Don't forget to configure this plugin's settings).
|
||||||
|
|
||||||
## Dev plan
|
|
||||||
|
|
||||||
* Add elements in Add menu.
|
|
||||||
* The assisted tools for creating custom floor in Blender (for example: add UV for floor).
|
|
||||||
|
40
README_ZH.md
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
这是一个用于Blender的插件,其主要是服务于Ballance制图。
|
这是一个用于Blender的插件,其主要是服务于Ballance制图。
|
||||||
|
|
||||||
目前仅仅包含比较基本的功能,其余的更多有用的功能将在未来版本中进行开发
|
请使用Release中打tag的最新版本,最新的commit不能保证其是稳定可用的
|
||||||
|
|
||||||
## 技术信息
|
## 技术信息
|
||||||
|
|
||||||
@ -14,6 +14,8 @@
|
|||||||
|
|
||||||
使用的制图链标准以及`meshes`文件夹下的文件的格式可以在[这里](https://github.com/yyc12345/gist/blob/master/BMFileSpec/YYCToolsChainSpec_ZH.md)查找
|
使用的制图链标准以及`meshes`文件夹下的文件的格式可以在[这里](https://github.com/yyc12345/gist/blob/master/BMFileSpec/YYCToolsChainSpec_ZH.md)查找
|
||||||
|
|
||||||
|
`jsons`文件夹下的,隶属于BMERevenge部分的文件的格式可以在[这里](https://github.com/yyc12345/gist/blob/master/BMERevenge/DevDocument_ZH.md)查找
|
||||||
|
|
||||||
支持Blender的原则是支持当前最新的 **LTS** 版本,在最新的LTS版本释出之后会花一些时间迁移插件。当前插件基于2.83.x版本
|
支持Blender的原则是支持当前最新的 **LTS** 版本,在最新的LTS版本释出之后会花一些时间迁移插件。当前插件基于2.83.x版本
|
||||||
|
|
||||||
## 功能介绍
|
## 功能介绍
|
||||||
@ -36,19 +38,43 @@
|
|||||||
|
|
||||||
Ballance 3D是一套简单的用于制图3D相关的轻型工具集合,可以在3D视图右上角找到。
|
Ballance 3D是一套简单的用于制图3D相关的轻型工具集合,可以在3D视图右上角找到。
|
||||||
|
|
||||||
#### Super Align
|
#### 3ds Max Align
|
||||||
|
|
||||||
提供一种类似于3ds Max的对齐方式。当前活动物体将被设为参照对象,当前选中的所有物体(如果参照也被选中则去掉参照对象)将被视为操作对象(因此可以选择多个物体一起对齐到参照对象)。
|
提供一种类似于3ds Max的对齐方式。当前活动物体将被设为参照对象,当前选中的所有物体(如果参照也被选中则去掉参照对象)将被视为操作对象(因此可以选择多个物体一起对齐到参照对象)。
|
||||||
|
|
||||||
#### Create Rail UV
|
#### Create Rail UV
|
||||||
|
|
||||||
为地图中的钢轨创建UV,你需要先选中需要添加类似钢轨UV的物体,然后点击这个按钮以创建。在创建之前需要保证选中物体在右侧属性列表中至少有一个UV(若有多个UV则会只操作第一个)。
|
为地图中的钢轨创建UV,你需要先选中需要添加类似钢轨UV的物体,然后点击这个按钮以创建。
|
||||||
|
|
||||||
|
在弹出设置窗口中,可以选择使用的材质。还可以选择展开模式,对于较短的钢轨,可以选择Point模式,对于较长的钢轨,可以使用Uniform模式,如果需要手动调整缩放比,请选择Scale模式并指定比率(不推荐)。
|
||||||
|
|
||||||
|
还可以选择投影轴以获取更好的UV分布。
|
||||||
|
|
||||||
|
### Flatten UV
|
||||||
|
|
||||||
|
在物体编辑模式下,用于将当前选中面按某一边贴附到V轴上的模式,展开到UV上。注意,只支持凸边面。
|
||||||
|
|
||||||
|
编辑模式下,选中面,点击Flatten UV,然后滚动滑条选中一个边作为参考,如果最后生成的边贴附不对,比如把路面花纹贴到了下部,可以重新选择参考边再进行操作,直到正确为止。
|
||||||
|
|
||||||
|
### 添加菜单
|
||||||
|
|
||||||
|
在添加菜单中我们添加了一套较为常用的物体。添加后物体会移动到3D游标处。
|
||||||
|
|
||||||
|
#### Elements
|
||||||
|
|
||||||
|
添加机关,添加时还可以指定添加的小节等属性(对于飞船等唯一物体不会显示)
|
||||||
|
|
||||||
|
#### Rail section
|
||||||
|
|
||||||
|
添加钢轨截面,可以选择单轨还是双轨(只是决定添加的界面数量,并不会帮你旋转角度),以及轨道半径和轨道间距
|
||||||
|
|
||||||
|
#### Floors
|
||||||
|
|
||||||
|
添加路面,隶属于BMERevenge工程的拓展。Basic floor是基本的路面组件,而Derived floor则是由基本组件组成的常用组件。可以根据其属性设置其延展,以及各边是否显示。其还具有减少顶点的优点。
|
||||||
|
|
||||||
|
建议添加后除非有消除面的需求外,应该立即按距离合并顶点一次以避免各类问题
|
||||||
|
|
||||||
## 安装
|
## 安装
|
||||||
|
|
||||||
将`ballance_blender_plugin`直接复制到Blender插件目录`scripts/addons_contrib`内即可。然后在Blender偏好设置中启用即可(记得配置插件设置)。
|
将`ballance_blender_plugin`直接复制到Blender插件目录`scripts/addons_contrib`内即可。然后在Blender偏好设置中启用即可(记得配置插件设置)。
|
||||||
|
|
||||||
## 后续开发计划
|
|
||||||
|
|
||||||
* 直接从添加菜单中添加机关
|
|
||||||
* 在Blender中创建自定义路面的辅助工具(例如辅助添加路面UV等)
|
|
||||||
|
@ -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":(1,0),
|
"version":(2,0),
|
||||||
"blender":(2,83,0),
|
"blender":(2,83,0),
|
||||||
"category":"Object",
|
"category":"Object",
|
||||||
"support":"TESTING",
|
"support":"TESTING",
|
||||||
@ -13,72 +13,135 @@ bl_info={
|
|||||||
|
|
||||||
# ============================================= import system
|
# ============================================= import system
|
||||||
import bpy,bpy_extras
|
import bpy,bpy_extras
|
||||||
|
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
|
||||||
if "bm_import_export" in locals():
|
if "bm_import_export" in locals():
|
||||||
importlib.reload(bm_import_export)
|
importlib.reload(bm_import_export)
|
||||||
if "floor_rail_uv" in locals():
|
if "rail_uv" in locals():
|
||||||
importlib.reload(floor_rail_uv)
|
importlib.reload(rail_uv)
|
||||||
if "utils" in locals():
|
if "utils" in locals():
|
||||||
importlib.reload(utils)
|
importlib.reload(utils)
|
||||||
if "config" in locals():
|
if "config" in locals():
|
||||||
importlib.reload(config)
|
importlib.reload(config)
|
||||||
if "preferences" in locals():
|
if "preferences" in locals():
|
||||||
importlib.reload(preferences)
|
importlib.reload(preferences)
|
||||||
if "super_align" in locals():
|
if "threedsmax_align" in locals():
|
||||||
importlib.reload(super_align)
|
importlib.reload(threedsmax_align)
|
||||||
from . import config, utils, bm_import_export, floor_rail_uv, preferences, super_align
|
if "no_uv_checker" in locals():
|
||||||
|
importlib.reload(no_uv_checker)
|
||||||
|
if "add_elements" in locals():
|
||||||
|
importlib.reload(add_elements)
|
||||||
|
if "add_floor" in locals():
|
||||||
|
importlib.reload(add_floor)
|
||||||
|
if "flatten_uv" in locals():
|
||||||
|
importlib.reload(flatten_uv)
|
||||||
|
from . import config, utils, bm_import_export, rail_uv, preferences, threedsmax_align, no_uv_checker, add_elements, add_floor, flatten_uv
|
||||||
|
|
||||||
# ============================================= menu system
|
# ============================================= menu system
|
||||||
|
|
||||||
class ThreeDViewerMenu(bpy.types.Menu):
|
class BALLANCE_MT_ThreeDViewerMenu(bpy.types.Menu):
|
||||||
"""Ballance related 3D operator"""
|
"""Ballance related 3D operator"""
|
||||||
|
bl_idname = "BALLANCE_MT_ThreeDViewerMenu"
|
||||||
bl_label = "Ballance 3D"
|
bl_label = "Ballance 3D"
|
||||||
bl_idname = "OBJECT_MT_ballance3d_menu"
|
|
||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
layout = self.layout
|
layout = self.layout
|
||||||
|
|
||||||
layout.operator("ballance.super_align")
|
layout.operator("ballance.super_align")
|
||||||
layout.operator("ballance.rail_uv")
|
layout.operator("ballance.rail_uv")
|
||||||
|
layout.operator("ballance.no_uv_checker")
|
||||||
|
layout.operator("ballance.flatten_uv")
|
||||||
|
|
||||||
|
class BALLANCE_MT_AddFloorMenu(bpy.types.Menu):
|
||||||
|
"""Add Ballance floor"""
|
||||||
|
bl_idname = "BALLANCE_MT_AddFloorMenu"
|
||||||
|
bl_label = "Floors"
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
layout = self.layout
|
||||||
|
|
||||||
|
layout.label(text="Basic floor")
|
||||||
|
for item in config.floor_basic_block_list:
|
||||||
|
cop = layout.operator("ballance.add_floor", text=item, icon_value = config.blenderIcon_floor_dict[item])
|
||||||
|
cop.floor_type = item
|
||||||
|
|
||||||
|
layout.separator()
|
||||||
|
layout.label(text="Derived floor")
|
||||||
|
for item in config.floor_derived_block_list:
|
||||||
|
cop = layout.operator("ballance.add_floor", text=item, icon_value = config.blenderIcon_floor_dict[item])
|
||||||
|
cop.floor_type = item
|
||||||
|
|
||||||
|
|
||||||
# ============================================= blender call system
|
# ============================================= blender call system
|
||||||
|
|
||||||
classes = (
|
classes = (
|
||||||
preferences.BallanceBlenderPluginPreferences,
|
preferences.BallanceBlenderPluginPreferences,
|
||||||
bm_import_export.ImportBM,
|
preferences.MyPropertyGroup,
|
||||||
bm_import_export.ExportBM,
|
|
||||||
floor_rail_uv.RailUVOperator,
|
bm_import_export.BALLANCE_OT_import_bm,
|
||||||
super_align.SuperAlignOperator,
|
bm_import_export.BALLANCE_OT_export_bm,
|
||||||
ThreeDViewerMenu
|
rail_uv.BALLANCE_OT_rail_uv,
|
||||||
|
threedsmax_align.BALLANCE_OT_super_align,
|
||||||
|
no_uv_checker.BALLANCE_OT_no_uv_checker,
|
||||||
|
flatten_uv.BALLANCE_OT_flatten_uv,
|
||||||
|
BALLANCE_MT_ThreeDViewerMenu,
|
||||||
|
|
||||||
|
add_elements.BALLANCE_OT_add_elements,
|
||||||
|
add_elements.BALLANCE_OT_add_rail,
|
||||||
|
add_floor.BALLANCE_OT_add_floor,
|
||||||
|
BALLANCE_MT_AddFloorMenu
|
||||||
)
|
)
|
||||||
|
|
||||||
def menu_func_bm_import(self, context):
|
def menu_func_bm_import(self, context):
|
||||||
self.layout.operator(bm_import_export.ImportBM.bl_idname, text="Ballance Map (.bm)")
|
self.layout.operator(bm_import_export.BALLANCE_OT_import_bm.bl_idname, text="Ballance Map (.bmx)")
|
||||||
def menu_func_bm_export(self, context):
|
def menu_func_bm_export(self, context):
|
||||||
self.layout.operator(bm_import_export.ExportBM.bl_idname, text="Ballance Map (.bm)")
|
self.layout.operator(bm_import_export.BALLANCE_OT_export_bm.bl_idname, text="Ballance Map (.bmx)")
|
||||||
def menu_func_ballance_3d(self, context):
|
def menu_func_ballance_3d(self, context):
|
||||||
layout = self.layout
|
layout = self.layout
|
||||||
layout.menu(ThreeDViewerMenu.bl_idname)
|
layout.menu(BALLANCE_MT_ThreeDViewerMenu.bl_idname)
|
||||||
|
def menu_func_ballance_add(self, context):
|
||||||
|
layout = self.layout
|
||||||
|
layout.separator()
|
||||||
|
layout.label(text="Ballance")
|
||||||
|
layout.operator_menu_enum("ballance.add_elements", "elements_type", icon='MESH_ICOSPHERE', text="Elements")
|
||||||
|
layout.operator("ballance.add_rail", icon='MESH_CIRCLE', text="Rail section")
|
||||||
|
layout.menu(BALLANCE_MT_AddFloorMenu.bl_idname, icon='MESH_CUBE')
|
||||||
|
|
||||||
def register():
|
def register():
|
||||||
|
# we need init all icon first
|
||||||
|
icon_path = os.path.join(os.path.dirname(__file__), "icons")
|
||||||
|
config.blenderIcon_floor = bpy.utils.previews.new()
|
||||||
|
for key, value in config.floor_block_dict.items():
|
||||||
|
blockIconName = "Ballance_FloorIcon_" + key
|
||||||
|
config.blenderIcon_floor.load(blockIconName, os.path.join(icon_path, "floor", value["BindingDisplayTexture"]), 'IMAGE')
|
||||||
|
config.blenderIcon_floor_dict[key] = config.blenderIcon_floor[blockIconName].icon_id
|
||||||
|
|
||||||
for cls in classes:
|
for cls in classes:
|
||||||
bpy.utils.register_class(cls)
|
bpy.utils.register_class(cls)
|
||||||
|
|
||||||
|
bpy.types.Scene.BallanceBlenderPluginProperty = bpy.props.PointerProperty(type=preferences.MyPropertyGroup)
|
||||||
|
|
||||||
bpy.types.TOPBAR_MT_file_import.append(menu_func_bm_import)
|
bpy.types.TOPBAR_MT_file_import.append(menu_func_bm_import)
|
||||||
bpy.types.TOPBAR_MT_file_export.append(menu_func_bm_export)
|
bpy.types.TOPBAR_MT_file_export.append(menu_func_bm_export)
|
||||||
|
|
||||||
bpy.types.VIEW3D_HT_header.append(menu_func_ballance_3d)
|
bpy.types.VIEW3D_HT_header.append(menu_func_ballance_3d)
|
||||||
|
bpy.types.VIEW3D_MT_add.append(menu_func_ballance_add)
|
||||||
|
|
||||||
def unregister():
|
def unregister():
|
||||||
bpy.types.TOPBAR_MT_file_import.remove(menu_func_bm_import)
|
bpy.types.TOPBAR_MT_file_import.remove(menu_func_bm_import)
|
||||||
bpy.types.TOPBAR_MT_file_export.remove(menu_func_bm_export)
|
bpy.types.TOPBAR_MT_file_export.remove(menu_func_bm_export)
|
||||||
|
|
||||||
bpy.types.VIEW3D_HT_header.remove(menu_func_ballance_3d)
|
bpy.types.VIEW3D_HT_header.remove(menu_func_ballance_3d)
|
||||||
|
bpy.types.VIEW3D_MT_add.remove(menu_func_ballance_add)
|
||||||
|
|
||||||
for cls in classes:
|
for cls in classes:
|
||||||
bpy.utils.unregister_class(cls)
|
bpy.utils.unregister_class(cls)
|
||||||
|
|
||||||
|
# we need uninstall all icon after all classes unregister
|
||||||
|
bpy.utils.previews.remove(config.blenderIcon_floor)
|
||||||
|
|
||||||
if __name__=="__main__":
|
if __name__=="__main__":
|
||||||
register()
|
register()
|
129
ballance_blender_plugin/add_elements.py
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
import bpy,mathutils
|
||||||
|
from . import utils, config, bm_import_export
|
||||||
|
|
||||||
|
# ================================================= actual add
|
||||||
|
|
||||||
|
class BALLANCE_OT_add_elements(bpy.types.Operator):
|
||||||
|
"""Add sector related elements"""
|
||||||
|
bl_idname = "ballance.add_elements"
|
||||||
|
bl_label = "Add elements"
|
||||||
|
bl_options = {'UNDO'}
|
||||||
|
|
||||||
|
elements_type: bpy.props.EnumProperty(
|
||||||
|
name="Type",
|
||||||
|
description="This element type",
|
||||||
|
items=tuple(map(lambda x: (x, x, ""), config.component_list)),
|
||||||
|
)
|
||||||
|
|
||||||
|
attentionElements = ["PC_TwoFlames", "PR_Resetpoint"]
|
||||||
|
uniqueElements = ["PS_FourFlames", "PE_Balloon"]
|
||||||
|
|
||||||
|
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):
|
||||||
|
# get name
|
||||||
|
if self.elements_type in self.uniqueElements:
|
||||||
|
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
|
||||||
|
loadedMesh = bm_import_export.load_component(config.component_list.index(self.elements_type))
|
||||||
|
obj = bpy.data.objects.new(finalObjectName, loadedMesh)
|
||||||
|
utils.AddSceneAndMove2Cursor(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")
|
||||||
|
if self.elements_type not in self.uniqueElements:
|
||||||
|
layout.prop(self, "elements_sector")
|
||||||
|
if self.elements_type in self.attentionElements:
|
||||||
|
layout.label(text="Please note that sector is suffix.")
|
||||||
|
|
||||||
|
class BALLANCE_OT_add_rail(bpy.types.Operator):
|
||||||
|
"""Add rail"""
|
||||||
|
bl_idname = "ballance.add_rail"
|
||||||
|
bl_label = "Add rail section"
|
||||||
|
bl_options = {'UNDO'}
|
||||||
|
|
||||||
|
rail_type: bpy.props.EnumProperty(
|
||||||
|
name="Type",
|
||||||
|
description="Rail type",
|
||||||
|
items=(('MONO', "Monorail", ""),
|
||||||
|
('DOUBLE', "Rail", ""),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
rail_radius: bpy.props.FloatProperty(
|
||||||
|
name="Rail radius",
|
||||||
|
description="Define rail section radius",
|
||||||
|
default=0.375,
|
||||||
|
)
|
||||||
|
|
||||||
|
rail_span: bpy.props.FloatProperty(
|
||||||
|
name="Rail span",
|
||||||
|
description="Define rail span",
|
||||||
|
default=3.75,
|
||||||
|
)
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
bpy.ops.object.select_all(action='DESELECT')
|
||||||
|
# create one first
|
||||||
|
bpy.ops.mesh.primitive_circle_add(vertices=8,
|
||||||
|
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
|
||||||
|
if self.rail_type == 'DOUBLE':
|
||||||
|
bpy.ops.object.select_all(action='DESELECT')
|
||||||
|
bpy.ops.mesh.primitive_circle_add(vertices=8,
|
||||||
|
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
|
||||||
|
bpy.ops.object.select_all(action='DESELECT')
|
||||||
|
bpy.context.view_layer.objects.active = firstObj
|
||||||
|
firstObj.select_set(True)
|
||||||
|
secondObj.select_set(True)
|
||||||
|
bpy.ops.object.join()
|
||||||
|
|
||||||
|
# apply 3d cursor
|
||||||
|
utils.Move2Cursor(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, "rail_type")
|
||||||
|
layout.prop(self, "rail_radius")
|
||||||
|
if self.rail_type == 'DOUBLE':
|
||||||
|
layout.prop(self, "rail_span")
|
512
ballance_blender_plugin/add_floor.py
Normal file
@ -0,0 +1,512 @@
|
|||||||
|
import bpy,mathutils
|
||||||
|
import os, math
|
||||||
|
from bpy_extras import io_utils,node_shader_utils
|
||||||
|
# from bpy_extras.io_utils import unpack_list
|
||||||
|
from bpy_extras.image_utils import load_image
|
||||||
|
from . import utils, config
|
||||||
|
|
||||||
|
class BALLANCE_OT_add_floor(bpy.types.Operator):
|
||||||
|
"""Add Ballance floor"""
|
||||||
|
bl_idname = "ballance.add_floor"
|
||||||
|
bl_label = "Add floor"
|
||||||
|
bl_options = {'UNDO'}
|
||||||
|
|
||||||
|
floor_type: bpy.props.EnumProperty(
|
||||||
|
name="Type",
|
||||||
|
description="Floor type",
|
||||||
|
items=tuple((x, x, "") for x in config.floor_block_dict.keys()),
|
||||||
|
)
|
||||||
|
|
||||||
|
expand_length_1 : bpy.props.IntProperty(
|
||||||
|
name="D1 length",
|
||||||
|
description="The length of expand direction 1",
|
||||||
|
min=0,
|
||||||
|
default=0,
|
||||||
|
)
|
||||||
|
|
||||||
|
expand_length_2 : bpy.props.IntProperty(
|
||||||
|
name="D2 length",
|
||||||
|
description="The length of expand direction 2",
|
||||||
|
min=0,
|
||||||
|
default=0,
|
||||||
|
)
|
||||||
|
|
||||||
|
height_multiplier : bpy.props.FloatProperty(
|
||||||
|
name="Height",
|
||||||
|
description="The multiplier for height. Default height is 5",
|
||||||
|
min=0.0,
|
||||||
|
default=1.0,
|
||||||
|
)
|
||||||
|
|
||||||
|
use_2d_top : bpy.props.BoolProperty(
|
||||||
|
name="Top side"
|
||||||
|
)
|
||||||
|
use_2d_right : bpy.props.BoolProperty(
|
||||||
|
name="Right side"
|
||||||
|
)
|
||||||
|
use_2d_bottom : bpy.props.BoolProperty(
|
||||||
|
name="Bottom side"
|
||||||
|
)
|
||||||
|
use_2d_left : bpy.props.BoolProperty(
|
||||||
|
name="Left side"
|
||||||
|
)
|
||||||
|
use_3d_top : bpy.props.BoolProperty(
|
||||||
|
name="Top face"
|
||||||
|
)
|
||||||
|
use_3d_bottom : bpy.props.BoolProperty(
|
||||||
|
name="Bottom face"
|
||||||
|
)
|
||||||
|
|
||||||
|
previous_floor_type = ''
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(self, context):
|
||||||
|
prefs = bpy.context.preferences.addons[__package__].preferences
|
||||||
|
return os.path.isdir(prefs.external_folder)
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
# load mesh
|
||||||
|
objmesh = bpy.data.meshes.new('done_')
|
||||||
|
if self.floor_type in config.floor_basic_block_list:
|
||||||
|
load_basic_floor(
|
||||||
|
objmesh,
|
||||||
|
self.floor_type,
|
||||||
|
'R0',
|
||||||
|
self.height_multiplier,
|
||||||
|
self.expand_length_1,
|
||||||
|
self.expand_length_2,
|
||||||
|
(self.use_2d_top,
|
||||||
|
self.use_2d_right,
|
||||||
|
self.use_2d_bottom,
|
||||||
|
self.use_2d_left,
|
||||||
|
self.use_3d_top,
|
||||||
|
self.use_3d_bottom),
|
||||||
|
(0.0, 0.0))
|
||||||
|
elif self.floor_type in config.floor_derived_block_list:
|
||||||
|
load_derived_floor(
|
||||||
|
objmesh,
|
||||||
|
self.floor_type,
|
||||||
|
self.height_multiplier,
|
||||||
|
self.expand_length_1,
|
||||||
|
self.expand_length_2,
|
||||||
|
(self.use_2d_top,
|
||||||
|
self.use_2d_right,
|
||||||
|
self.use_2d_bottom,
|
||||||
|
self.use_2d_left,
|
||||||
|
self.use_3d_top,
|
||||||
|
self.use_3d_bottom))
|
||||||
|
|
||||||
|
# normalization mesh
|
||||||
|
objmesh.validate(clean_customdata=False)
|
||||||
|
objmesh.update(calc_edges=False, calc_edges_loose=False)
|
||||||
|
|
||||||
|
# create object and link it
|
||||||
|
obj=bpy.data.objects.new('A_Floor_BMERevenge_', objmesh)
|
||||||
|
utils.AddSceneAndMove2Cursor(obj)
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
def invoke(self, context, event):
|
||||||
|
wm = context.window_manager
|
||||||
|
return wm.invoke_props_dialog(self)
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
# get floor prototype
|
||||||
|
floor_prototype = config.floor_block_dict[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
|
||||||
|
layout = self.layout
|
||||||
|
col = layout.column()
|
||||||
|
col.label(text="Basic param")
|
||||||
|
col.prop(self, "floor_type")
|
||||||
|
col.prop(self, "height_multiplier")
|
||||||
|
|
||||||
|
col.separator()
|
||||||
|
col.label(text="Expand")
|
||||||
|
if floor_prototype['ExpandType'] == 'Column' or floor_prototype['ExpandType'] == 'Freedom':
|
||||||
|
col.prop(self, "expand_length_1")
|
||||||
|
if floor_prototype['ExpandType'] == 'Freedom':
|
||||||
|
col.prop(self, "expand_length_2")
|
||||||
|
col.label(text="Unit size: " + floor_prototype['UnitSize'])
|
||||||
|
col.label(text="Expand mode: " + floor_prototype['ExpandType'])
|
||||||
|
grids = col.grid_flow(row_major=True, columns=3)
|
||||||
|
grids.separator()
|
||||||
|
grids.label(text=config.floor_expand_direction_map[floor_prototype['InitColumnDirection']][floor_prototype['ExpandType']][0])
|
||||||
|
grids.separator()
|
||||||
|
grids.label(text=config.floor_expand_direction_map[floor_prototype['InitColumnDirection']][floor_prototype['ExpandType']][3])
|
||||||
|
grids.template_icon(icon_value = config.blenderIcon_floor_dict[self.floor_type])
|
||||||
|
grids.label(text=config.floor_expand_direction_map[floor_prototype['InitColumnDirection']][floor_prototype['ExpandType']][1])
|
||||||
|
grids.separator()
|
||||||
|
grids.label(text=config.floor_expand_direction_map[floor_prototype['InitColumnDirection']][floor_prototype['ExpandType']][2])
|
||||||
|
grids.separator()
|
||||||
|
|
||||||
|
col.separator()
|
||||||
|
col.label(text="Faces")
|
||||||
|
row = col.row()
|
||||||
|
row.prop(self, "use_3d_top")
|
||||||
|
row.prop(self, "use_3d_bottom")
|
||||||
|
|
||||||
|
col.separator()
|
||||||
|
col.label(text="Sides")
|
||||||
|
grids = col.grid_flow(row_major=True, columns=3)
|
||||||
|
grids.separator()
|
||||||
|
grids.prop(self, "use_2d_top")
|
||||||
|
grids.separator()
|
||||||
|
grids.prop(self, "use_2d_left")
|
||||||
|
grids.template_icon(icon_value = config.blenderIcon_floor_dict[self.floor_type])
|
||||||
|
grids.prop(self, "use_2d_right")
|
||||||
|
grids.separator()
|
||||||
|
grids.prop(self, "use_2d_bottom")
|
||||||
|
grids.separator()
|
||||||
|
|
||||||
|
def face_fallback(normal_face, expand_face, height):
|
||||||
|
if expand_face == None:
|
||||||
|
return normal_face
|
||||||
|
|
||||||
|
if height <= 1.0:
|
||||||
|
return normal_face
|
||||||
|
else:
|
||||||
|
return expand_face
|
||||||
|
|
||||||
|
def create_or_get_material(material_name):
|
||||||
|
# WARNING: this code is shared with bm_import_export
|
||||||
|
deconflict_name = "BMERevenge_" + material_name
|
||||||
|
try:
|
||||||
|
m = bpy.data.materials[deconflict_name]
|
||||||
|
except:
|
||||||
|
# it is not existed, we need create a new one
|
||||||
|
m = bpy.data.materials.new(deconflict_name)
|
||||||
|
# we need init it.
|
||||||
|
# load texture first
|
||||||
|
externalTextureFolder = bpy.context.preferences.addons[__package__].preferences.external_folder
|
||||||
|
txur = load_image(config.floor_texture_corresponding_map[material_name], externalTextureFolder, check_existing=False) # force reload, we don't want it is shared with normal material
|
||||||
|
# create material and link texture
|
||||||
|
m.use_nodes=True
|
||||||
|
for node in m.node_tree.nodes:
|
||||||
|
m.node_tree.nodes.remove(node)
|
||||||
|
bnode=m.node_tree.nodes.new(type="ShaderNodeBsdfPrincipled")
|
||||||
|
mnode=m.node_tree.nodes.new(type="ShaderNodeOutputMaterial")
|
||||||
|
m.node_tree.links.new(bnode.outputs[0],mnode.inputs[0])
|
||||||
|
|
||||||
|
inode=m.node_tree.nodes.new(type="ShaderNodeTexImage")
|
||||||
|
inode.image=txur
|
||||||
|
m.node_tree.links.new(inode.outputs[0],bnode.inputs[0])
|
||||||
|
|
||||||
|
# write custom property
|
||||||
|
for try_item in config.floor_material_statistic:
|
||||||
|
if material_name in try_item['member']:
|
||||||
|
m['virtools-ambient'] = try_item['data']['ambient']
|
||||||
|
m['virtools-diffuse'] = try_item['data']['diffuse']
|
||||||
|
m['virtools-specular'] = try_item['data']['specular']
|
||||||
|
m['virtools-emissive'] = try_item['data']['emissive']
|
||||||
|
m['virtools-power'] = try_item['data']['power']
|
||||||
|
break
|
||||||
|
|
||||||
|
return m
|
||||||
|
|
||||||
|
def solve_vec_data(str_data, d1, d2, d3, unit, unit_height):
|
||||||
|
sp = str_data.split(';')
|
||||||
|
sp_point = sp[0].split(',')
|
||||||
|
vec = [float(sp_point[0]), float(sp_point[1]), float(sp_point[2])]
|
||||||
|
|
||||||
|
for i in range(3):
|
||||||
|
symbol = sp[i+1]
|
||||||
|
if symbol == '':
|
||||||
|
continue
|
||||||
|
|
||||||
|
factor = 1.0 if symbol[0] == '+' else -1.0
|
||||||
|
p = symbol[1:]
|
||||||
|
if p == 'd1':
|
||||||
|
vec[i] += d1 * unit * factor
|
||||||
|
elif p == 'd2':
|
||||||
|
vec[i] += d2 * unit * factor
|
||||||
|
elif p == 'd3':
|
||||||
|
vec[i] += (d3 - 1) * unit_height * factor
|
||||||
|
|
||||||
|
return vec
|
||||||
|
|
||||||
|
def rotate_translate_vec(vec, rotation, unit, extra_translate):
|
||||||
|
vec[0] -= unit / 2
|
||||||
|
vec[1] -= unit / 2
|
||||||
|
|
||||||
|
if rotation == 'R0':
|
||||||
|
coso=1
|
||||||
|
sino=0
|
||||||
|
elif rotation == 'R90':
|
||||||
|
coso=0
|
||||||
|
sino=1
|
||||||
|
elif rotation == 'R180':
|
||||||
|
coso=-1
|
||||||
|
sino=0
|
||||||
|
elif rotation == 'R270':
|
||||||
|
coso=0
|
||||||
|
sino=-1
|
||||||
|
|
||||||
|
return (
|
||||||
|
coso * vec[0] - sino * vec[1] + unit / 2 + extra_translate[0],
|
||||||
|
sino * vec[0] + coso * vec[1] + unit / 2 + extra_translate[1],
|
||||||
|
vec[2]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def solve_uv_data(str_data, d1, d2, d3, unit):
|
||||||
|
sp = str_data.split(';')
|
||||||
|
sp_point = sp[0].split(',')
|
||||||
|
vec = [float(sp_point[0]), float(sp_point[1])]
|
||||||
|
|
||||||
|
for i in range(2):
|
||||||
|
symbol = sp[i+1]
|
||||||
|
if symbol == '':
|
||||||
|
continue
|
||||||
|
|
||||||
|
factor = 1.0 if symbol[0] == '+' else -1.0
|
||||||
|
p = symbol[1:]
|
||||||
|
if p == 'd1':
|
||||||
|
vec[i] += d1 * unit * factor
|
||||||
|
elif p == 'd2':
|
||||||
|
vec[i] += d2 * unit * factor
|
||||||
|
elif p == 'd3':
|
||||||
|
vec[i] += (d3 - 1) * unit * factor
|
||||||
|
|
||||||
|
return tuple(vec)
|
||||||
|
|
||||||
|
def solve_normal_data(point1, point2, point3):
|
||||||
|
vector1 = (
|
||||||
|
point2[0] - point1[0],
|
||||||
|
point2[1] - point1[1],
|
||||||
|
point2[2] - point1[2]
|
||||||
|
)
|
||||||
|
vector2 = (
|
||||||
|
point3[0] - point2[0],
|
||||||
|
point3[1] - point2[1],
|
||||||
|
point3[2] - point2[2]
|
||||||
|
)
|
||||||
|
|
||||||
|
# do vector x mutiply
|
||||||
|
# vector1 x vector2
|
||||||
|
nor = [
|
||||||
|
vector1[1] * vector2[2] - vector1[2] * vector2[1],
|
||||||
|
vector1[2] * vector2[0] - vector1[0] * vector2[2],
|
||||||
|
vector1[0] * vector2[1] - vector1[1] * vector2[0]
|
||||||
|
]
|
||||||
|
|
||||||
|
# do a normalization
|
||||||
|
length = math.sqrt(nor[0] ** 2 + nor[1] ** 2 + nor[2] ** 2)
|
||||||
|
nor[0] /= length
|
||||||
|
nor[1] /= length
|
||||||
|
nor[2] /= length
|
||||||
|
|
||||||
|
return tuple(nor)
|
||||||
|
|
||||||
|
def solve_smashed_position(str_data, d1, d2):
|
||||||
|
sp=str_data.split(';')
|
||||||
|
sp_pos = sp[0].split(',')
|
||||||
|
sp_sync = sp[1].split(',')
|
||||||
|
|
||||||
|
vec = [int(sp_pos[0]), int(sp_pos[1])]
|
||||||
|
|
||||||
|
for i in range(2):
|
||||||
|
offset = 0 if sp_sync[i * 2] == '' else int(sp_sync[i * 2])
|
||||||
|
if sp_sync[i*2+1] == 'd1':
|
||||||
|
vec[i] += d1 + offset
|
||||||
|
elif sp_sync[i*2+1] == 'd2':
|
||||||
|
vec[i] += d2 + offset
|
||||||
|
|
||||||
|
return tuple(vec)
|
||||||
|
|
||||||
|
def virtual_foreach_set(collection, field, base_num, data):
|
||||||
|
counter = 0
|
||||||
|
for i in data:
|
||||||
|
exec("a[j]." + field + "=q", {}, {
|
||||||
|
'a': collection,
|
||||||
|
'j': counter + base_num,
|
||||||
|
'q': i
|
||||||
|
})
|
||||||
|
counter+=1
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
'''
|
||||||
|
sides_struct should be a tuple and it always have 6 bool items
|
||||||
|
|
||||||
|
(use_2d_top, use_2d_right, use_2d_bottom, use_2d_left, use_3d_top, use_3d_bottom)
|
||||||
|
|
||||||
|
WARNING: this code is shared with bm import export
|
||||||
|
|
||||||
|
'''
|
||||||
|
def load_basic_floor(mesh, floor_type, rotation, height_multiplier, d1, d2, sides_struct, extra_translate):
|
||||||
|
floor_prototype = config.floor_block_dict[floor_type]
|
||||||
|
|
||||||
|
# set some unit
|
||||||
|
height_unit = 5.0
|
||||||
|
if floor_prototype['UnitSize'] == 'Small':
|
||||||
|
block_3dworld_unit = 2.5
|
||||||
|
block_uvworld_unit = 0.5
|
||||||
|
elif floor_prototype['UnitSize'] == 'Large':
|
||||||
|
block_3dworld_unit = 5.0
|
||||||
|
block_uvworld_unit = 1.0
|
||||||
|
|
||||||
|
# got all needed faces
|
||||||
|
needCreatedFaces = []
|
||||||
|
if sides_struct[0]:
|
||||||
|
needCreatedFaces.append(face_fallback(floor_prototype['TwoDTopSide'], floor_prototype['TwoDTopSideExpand'], height_multiplier))
|
||||||
|
if sides_struct[1]:
|
||||||
|
needCreatedFaces.append(face_fallback(floor_prototype['TwoDRightSide'], floor_prototype['TwoDRightSideExpand'], height_multiplier))
|
||||||
|
if sides_struct[2]:
|
||||||
|
needCreatedFaces.append(face_fallback(floor_prototype['TwoDBottomSide'], floor_prototype['TwoDBottomSideExpand'], height_multiplier))
|
||||||
|
if sides_struct[3]:
|
||||||
|
needCreatedFaces.append(face_fallback(floor_prototype['TwoDLeftSide'], floor_prototype['TwoDLeftSideExpand'], height_multiplier))
|
||||||
|
if sides_struct[4]:
|
||||||
|
needCreatedFaces.append(floor_prototype['ThreeDTopFace'])
|
||||||
|
if sides_struct[5]:
|
||||||
|
needCreatedFaces.append(floor_prototype['ThreeDBottomFace'])
|
||||||
|
|
||||||
|
# resolve face
|
||||||
|
# solve material first
|
||||||
|
materialDict = {}
|
||||||
|
allmat = mesh.materials[:]
|
||||||
|
counter = len(allmat)
|
||||||
|
for face_define in needCreatedFaces:
|
||||||
|
for face in face_define['Faces']:
|
||||||
|
new_texture = face['Textures']
|
||||||
|
if new_texture not in materialDict.keys():
|
||||||
|
# try get from existed solt
|
||||||
|
pending_material = create_or_get_material(new_texture)
|
||||||
|
if pending_material not in allmat:
|
||||||
|
# no matched. add it
|
||||||
|
mesh.materials.append(pending_material)
|
||||||
|
materialDict[new_texture] = counter
|
||||||
|
counter += 1
|
||||||
|
else:
|
||||||
|
# use existed index
|
||||||
|
materialDict[new_texture] = allmat.index(pending_material)
|
||||||
|
|
||||||
|
# now, we can process real mesh
|
||||||
|
# load existed base count
|
||||||
|
global_offset_vec = len(mesh.vertices)
|
||||||
|
global_offset_polygons = len(mesh.polygons)
|
||||||
|
global_offset_loops = len(mesh.loops)
|
||||||
|
vecList = []
|
||||||
|
uvList = []
|
||||||
|
normalList = []
|
||||||
|
faceList = []
|
||||||
|
faceIndList = []
|
||||||
|
faceMatList = []
|
||||||
|
for face_define in needCreatedFaces:
|
||||||
|
base_indices = len(vecList)
|
||||||
|
for vec in face_define['Vertices']:
|
||||||
|
vecList.append(rotate_translate_vec(
|
||||||
|
solve_vec_data(vec, d1, d2, height_multiplier, block_3dworld_unit, height_unit),
|
||||||
|
rotation, block_3dworld_unit, extra_translate))
|
||||||
|
|
||||||
|
for uv in face_define['UVs']:
|
||||||
|
uvList.append(solve_uv_data(uv, d1, d2, height_multiplier, block_uvworld_unit))
|
||||||
|
|
||||||
|
for face in face_define['Faces']:
|
||||||
|
if face['Type'] == 'RECTANGLE':
|
||||||
|
# rectangle
|
||||||
|
vec_indices = (
|
||||||
|
face['P1'] + base_indices,
|
||||||
|
face['P2'] + base_indices,
|
||||||
|
face['P3'] + base_indices,
|
||||||
|
face['P4'] + base_indices)
|
||||||
|
indCount = 4
|
||||||
|
elif face['Type'] == 'TRIANGLE':
|
||||||
|
# triangle
|
||||||
|
vec_indices = (
|
||||||
|
face['P1'] + base_indices,
|
||||||
|
face['P2'] + base_indices,
|
||||||
|
face['P3'] + base_indices)
|
||||||
|
indCount = 3
|
||||||
|
|
||||||
|
# we need calc normal and push it into list
|
||||||
|
point_normal = solve_normal_data(vecList[vec_indices[0]], vecList[vec_indices[1]], vecList[vec_indices[2]])
|
||||||
|
for i in range(indCount):
|
||||||
|
normalList.append(point_normal)
|
||||||
|
|
||||||
|
# push indices into list
|
||||||
|
for i in range(indCount):
|
||||||
|
faceList.append(vec_indices[i] + global_offset_vec)
|
||||||
|
|
||||||
|
# push material into list
|
||||||
|
faceMatList.append(materialDict[face['Textures']])
|
||||||
|
|
||||||
|
# push face vec count into list
|
||||||
|
faceIndList.append(indCount)
|
||||||
|
|
||||||
|
# push data into blender struct
|
||||||
|
mesh.vertices.add(len(vecList))
|
||||||
|
mesh.loops.add(len(faceList))
|
||||||
|
mesh.polygons.add(len(faceMatList))
|
||||||
|
mesh.create_normals_split()
|
||||||
|
if mesh.uv_layers.active is None:
|
||||||
|
# if no uv, create it
|
||||||
|
mesh.uv_layers.new(do_init=False)
|
||||||
|
|
||||||
|
virtual_foreach_set(mesh.vertices, "co", global_offset_vec, vecList)
|
||||||
|
virtual_foreach_set(mesh.loops, "vertex_index", global_offset_loops, faceList)
|
||||||
|
virtual_foreach_set(mesh.loops, "normal", global_offset_loops, normalList)
|
||||||
|
virtual_foreach_set(mesh.uv_layers[0].data, "uv", global_offset_loops, uvList)
|
||||||
|
|
||||||
|
cache_counter = 0
|
||||||
|
for i in range(len(faceMatList)):
|
||||||
|
indCount = faceIndList[i]
|
||||||
|
mesh.polygons[i + global_offset_polygons].loop_start = global_offset_loops + cache_counter
|
||||||
|
mesh.polygons[i + global_offset_polygons].loop_total = indCount
|
||||||
|
mesh.polygons[i + global_offset_polygons].material_index = faceMatList[i]
|
||||||
|
mesh.polygons[i + global_offset_polygons].use_smooth = True
|
||||||
|
cache_counter += indCount
|
||||||
|
|
||||||
|
|
||||||
|
def load_derived_floor(mesh, floor_type, height_multiplier, d1, d2, sides_struct):
|
||||||
|
floor_prototype = config.floor_block_dict[floor_type]
|
||||||
|
|
||||||
|
# set some unit
|
||||||
|
if floor_prototype['UnitSize'] == 'Small':
|
||||||
|
block_3dworld_unit = 2.5
|
||||||
|
elif floor_prototype['UnitSize'] == 'Large':
|
||||||
|
block_3dworld_unit = 5.0
|
||||||
|
|
||||||
|
# construct face dict
|
||||||
|
sides_dict = {
|
||||||
|
'True': True,
|
||||||
|
'False': False,
|
||||||
|
'2dTop': sides_struct[0],
|
||||||
|
'2dRight': sides_struct[1],
|
||||||
|
'2dBottom': sides_struct[2],
|
||||||
|
'2dLeft': sides_struct[3],
|
||||||
|
'3dTop': sides_struct[4],
|
||||||
|
'3dBottom': sides_struct[5]
|
||||||
|
}
|
||||||
|
|
||||||
|
# iterate smahsed blocks
|
||||||
|
for blk in floor_prototype['SmashedBlocks']:
|
||||||
|
start_pos = solve_smashed_position(blk['StartPosition'], d1, d2)
|
||||||
|
expand_pos = solve_smashed_position(blk['ExpandPosition'], d1, d2)
|
||||||
|
|
||||||
|
sides_data = tuple(sides_dict[x] for x in blk['SideSync'].split(';'))
|
||||||
|
|
||||||
|
# call basic floor creator
|
||||||
|
load_basic_floor(
|
||||||
|
mesh,
|
||||||
|
blk['Type'],
|
||||||
|
blk['Rotation'],
|
||||||
|
height_multiplier,
|
||||||
|
expand_pos[0],
|
||||||
|
expand_pos[1],
|
||||||
|
sides_data,
|
||||||
|
(start_pos[0] * block_3dworld_unit, start_pos[1] * block_3dworld_unit)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
@ -6,12 +6,44 @@ 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, config
|
from . import utils, config
|
||||||
|
|
||||||
class ImportBM(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.0)"""
|
"""Load a Ballance Map File (BM file spec 1.3)"""
|
||||||
bl_idname = "import_scene.bm"
|
bl_idname = "ballance.import_bm"
|
||||||
bl_label = "Import BM "
|
bl_label = "Import BM "
|
||||||
bl_options = {'PRESET', 'UNDO'}
|
bl_options = {'PRESET', 'UNDO'}
|
||||||
filename_ext = ".bm"
|
filename_ext = ".bmx"
|
||||||
|
|
||||||
|
texture_conflict_strategy: bpy.props.EnumProperty(
|
||||||
|
name="Texture name conflict",
|
||||||
|
items=(('NEW', "New instance", "Create a new instance"),
|
||||||
|
('CURRENT', "Use current", "Use current"),),
|
||||||
|
description="Define how to process texture name conflict",
|
||||||
|
default='CURRENT',
|
||||||
|
)
|
||||||
|
|
||||||
|
material_conflict_strategy: bpy.props.EnumProperty(
|
||||||
|
name="Material name conflict",
|
||||||
|
items=(('RENAME', "Rename", "Rename the new one"),
|
||||||
|
('CURRENT', "Use current", "Use current"),),
|
||||||
|
description="Define how to process material name conflict",
|
||||||
|
default='RENAME',
|
||||||
|
)
|
||||||
|
|
||||||
|
mesh_conflict_strategy: bpy.props.EnumProperty(
|
||||||
|
name="Mesh name conflict",
|
||||||
|
items=(('RENAME', "Rename", "Rename the new one"),
|
||||||
|
('CURRENT', "Use current", "Use current"),),
|
||||||
|
description="Define how to process mesh name conflict",
|
||||||
|
default='RENAME',
|
||||||
|
)
|
||||||
|
|
||||||
|
object_conflict_strategy: bpy.props.EnumProperty(
|
||||||
|
name="Object name conflict",
|
||||||
|
items=(('RENAME', "Rename", "Rename the new one"),
|
||||||
|
('CURRENT', "Use current", "Use current"),),
|
||||||
|
description="Define how to process object name conflict",
|
||||||
|
default='RENAME',
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(self, context):
|
def poll(self, context):
|
||||||
@ -20,36 +52,47 @@ class ImportBM(bpy.types.Operator, bpy_extras.io_utils.ImportHelper):
|
|||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
prefs = bpy.context.preferences.addons[__package__].preferences
|
prefs = bpy.context.preferences.addons[__package__].preferences
|
||||||
import_bm(context, self.filepath, prefs.external_folder, prefs.temp_texture_folder)
|
import_bm(context, self.filepath, prefs.external_folder, prefs.temp_texture_folder,
|
||||||
|
self.texture_conflict_strategy, self.material_conflict_strategy, self.mesh_conflict_strategy, self.object_conflict_strategy)
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
class ExportBM(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.0)"""
|
"""Save a Ballance Map File (BM file spec 1.3)"""
|
||||||
bl_idname = "export_scene.bm"
|
bl_idname = "ballance.export_bm"
|
||||||
bl_label = 'Export BM'
|
bl_label = 'Export BM'
|
||||||
bl_options = {'PRESET'}
|
bl_options = {'PRESET'}
|
||||||
filename_ext = ".bm"
|
filename_ext = ".bmx"
|
||||||
|
|
||||||
export_mode: bpy.props.EnumProperty(
|
export_mode: bpy.props.EnumProperty(
|
||||||
name="Export mode",
|
name="Export mode",
|
||||||
items=(('COLLECTION', "Selected collection", "Export the selected collection"),
|
items=(('COLLECTION', "Collection", "Export a collection"),
|
||||||
('OBJECT', "Selected objects", "Export the selected objects"),
|
('OBJECT', "Objects", "Export an objects"),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
export_target: bpy.props.StringProperty(
|
|
||||||
name="Export target",
|
|
||||||
description="Which one will be exported",
|
|
||||||
)
|
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
export_bm(context, self.filepath, self.export_mode, self.export_target)
|
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):
|
||||||
|
utils.ShowMessageBox(("No specific target", ), "Lost parameter", 'ERROR')
|
||||||
|
else:
|
||||||
|
if self.export_mode == 'COLLECTION':
|
||||||
|
export_bm(context, self.filepath, self.export_mode, context.scene.BallanceBlenderPluginProperty.collection_picker)
|
||||||
|
elif self.export_mode == 'OBJECT':
|
||||||
|
export_bm(context, self.filepath, self.export_mode, context.scene.BallanceBlenderPluginProperty.object_picker)
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
layout = self.layout
|
||||||
|
layout.prop(self, "export_mode")
|
||||||
|
if self.export_mode == 'COLLECTION':
|
||||||
|
layout.prop(context.scene.BallanceBlenderPluginProperty, "collection_picker")
|
||||||
|
elif self.export_mode == 'OBJECT':
|
||||||
|
layout.prop(context.scene.BallanceBlenderPluginProperty, "object_picker")
|
||||||
|
|
||||||
# ========================================== method
|
# ========================================== method
|
||||||
|
|
||||||
bm_current_version = 11
|
bm_current_version = 13
|
||||||
|
|
||||||
def import_bm(context,filepath,externalTexture,blenderTempFolder):
|
def import_bm(context,filepath,externalTexture,blenderTempFolder, textureOpt, materialOpt, meshOpt, objectOpt):
|
||||||
# ============================================ alloc a temp folder
|
# ============================================ alloc a temp folder
|
||||||
tempFolderObj = tempfile.TemporaryDirectory()
|
tempFolderObj = tempfile.TemporaryDirectory()
|
||||||
tempFolder = tempFolderObj.name
|
tempFolder = tempFolderObj.name
|
||||||
@ -64,18 +107,19 @@ def import_bm(context,filepath,externalTexture,blenderTempFolder):
|
|||||||
zipObj.extractall(tempFolder)
|
zipObj.extractall(tempFolder)
|
||||||
|
|
||||||
# index.bm
|
# index.bm
|
||||||
findex = open(os.path.join(tempFolder, "index.bm"), "rb")
|
|
||||||
# judge version first
|
|
||||||
gotten_version = read_uint32(findex)
|
|
||||||
if (gotten_version != bm_current_version):
|
|
||||||
utils.ShowMessageBox("Unsupported BM spec. Expect: {} Gotten: {}".format(bm_current_version, gotten_version), "Unsupported BM spec", 'WARNING')
|
|
||||||
findex.close()
|
|
||||||
tempFolderObj.cleanup()
|
|
||||||
return
|
|
||||||
objectList = []
|
objectList = []
|
||||||
meshList = []
|
meshList = []
|
||||||
materialList = []
|
materialList = []
|
||||||
textureList = []
|
textureList = []
|
||||||
|
with open(os.path.join(tempFolder, "index.bm"), "rb") as findex:
|
||||||
|
# judge version first
|
||||||
|
gotten_version = read_uint32(findex)
|
||||||
|
if (gotten_version != bm_current_version):
|
||||||
|
utils.ShowMessageBox(("Unsupported BM spec. Expect: {} Gotten: {}".format(bm_current_version, gotten_version), ), "Unsupported BM spec", 'ERROR')
|
||||||
|
findex.close()
|
||||||
|
tempFolderObj.cleanup()
|
||||||
|
return
|
||||||
|
|
||||||
while len(peek_stream(findex)) != 0:
|
while len(peek_stream(findex)) != 0:
|
||||||
index_name = read_string(findex)
|
index_name = read_string(findex)
|
||||||
index_type = read_uint8(findex)
|
index_type = read_uint8(findex)
|
||||||
@ -91,16 +135,16 @@ def import_bm(context,filepath,externalTexture,blenderTempFolder):
|
|||||||
textureList.append(blockCache)
|
textureList.append(blockCache)
|
||||||
else:
|
else:
|
||||||
pass
|
pass
|
||||||
findex.close()
|
|
||||||
|
|
||||||
# texture.bm
|
# texture.bm
|
||||||
ftexture = open(os.path.join(tempFolder, "texture.bm"), "rb")
|
with open(os.path.join(tempFolder, "texture.bm"), "rb") as ftexture:
|
||||||
for item in textureList:
|
for item in textureList:
|
||||||
ftexture.seek(item.offset, os.SEEK_SET)
|
ftexture.seek(item.offset, os.SEEK_SET)
|
||||||
texture_filename = read_string(ftexture)
|
texture_filename = read_string(ftexture)
|
||||||
texture_isExternal = read_bool(ftexture)
|
texture_isExternal = read_bool(ftexture)
|
||||||
if texture_isExternal:
|
if texture_isExternal:
|
||||||
txur = load_image(texture_filename, externalTextureFolder)
|
txur = load_image(texture_filename, externalTextureFolder, check_existing=(textureOpt == 'CURRENT'))
|
||||||
item.blenderData = txur
|
item.blenderData = txur
|
||||||
else:
|
else:
|
||||||
# not external. copy temp file into blender temp. then use it.
|
# not external. copy temp file into blender temp. then use it.
|
||||||
@ -109,14 +153,13 @@ def import_bm(context,filepath,externalTexture,blenderTempFolder):
|
|||||||
shutil.copy(os.path.join(tempTextureFolder, texture_filename), os.path.join(blenderTempTextureFolder, texture_filename))
|
shutil.copy(os.path.join(tempTextureFolder, texture_filename), os.path.join(blenderTempTextureFolder, texture_filename))
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
txur = load_image(texture_filename, blenderTempTextureFolder)
|
txur = load_image(texture_filename, blenderTempTextureFolder, check_existing=(textureOpt == 'CURRENT'))
|
||||||
item.blenderData = txur
|
item.blenderData = txur
|
||||||
txur.name = item.name
|
txur.name = item.name
|
||||||
|
|
||||||
ftexture.close()
|
|
||||||
|
|
||||||
# material.bm
|
# material.bm
|
||||||
fmaterial = open(os.path.join(tempFolder, "material.bm"), "rb")
|
# WARNING: this code is shared with add_floor - create_or_get_material()
|
||||||
|
with open(os.path.join(tempFolder, "material.bm"), "rb") as fmaterial:
|
||||||
for item in materialList:
|
for item in materialList:
|
||||||
fmaterial.seek(item.offset, os.SEEK_SET)
|
fmaterial.seek(item.offset, os.SEEK_SET)
|
||||||
|
|
||||||
@ -130,7 +173,11 @@ def import_bm(context,filepath,externalTexture,blenderTempFolder):
|
|||||||
material_texture = read_uint32(fmaterial)
|
material_texture = read_uint32(fmaterial)
|
||||||
|
|
||||||
# create basic material
|
# create basic material
|
||||||
m = bpy.data.materials.new(item.name)
|
(m, needSkip) = createInstanceWithOption(info_bm_type.MATERIAL, item.name, materialOpt)
|
||||||
|
item.blenderData = m
|
||||||
|
if needSkip:
|
||||||
|
continue
|
||||||
|
|
||||||
m.use_nodes=True
|
m.use_nodes=True
|
||||||
for node in m.node_tree.nodes:
|
for node in m.node_tree.nodes:
|
||||||
m.node_tree.nodes.remove(node)
|
m.node_tree.nodes.remove(node)
|
||||||
@ -156,12 +203,10 @@ def import_bm(context,filepath,externalTexture,blenderTempFolder):
|
|||||||
m['virtools-emissive'] = material_colEmissive
|
m['virtools-emissive'] = material_colEmissive
|
||||||
m['virtools-power'] = material_specularPower
|
m['virtools-power'] = material_specularPower
|
||||||
|
|
||||||
item.blenderData = m
|
|
||||||
|
|
||||||
fmaterial.close()
|
|
||||||
|
|
||||||
# mesh.bm
|
# mesh.bm
|
||||||
fmesh = open(os.path.join(tempFolder, "mesh.bm"), "rb")
|
# WARNING: this code is shared with add_floor
|
||||||
|
with open(os.path.join(tempFolder, "mesh.bm"), "rb") as fmesh:
|
||||||
vList=[]
|
vList=[]
|
||||||
vtList=[]
|
vtList=[]
|
||||||
vnList=[]
|
vnList=[]
|
||||||
@ -171,7 +216,10 @@ def import_bm(context,filepath,externalTexture,blenderTempFolder):
|
|||||||
fmesh.seek(item.offset, os.SEEK_SET)
|
fmesh.seek(item.offset, os.SEEK_SET)
|
||||||
|
|
||||||
# create real mesh
|
# create real mesh
|
||||||
mesh = bpy.data.meshes.new(item.name)
|
(mesh, needSkip) = createInstanceWithOption(info_bm_type.MESH, item.name, meshOpt)
|
||||||
|
item.blenderData = mesh
|
||||||
|
if needSkip:
|
||||||
|
continue
|
||||||
|
|
||||||
vList.clear()
|
vList.clear()
|
||||||
vtList.clear()
|
vtList.clear()
|
||||||
@ -246,13 +294,9 @@ def import_bm(context,filepath,externalTexture,blenderTempFolder):
|
|||||||
mesh.validate(clean_customdata=False)
|
mesh.validate(clean_customdata=False)
|
||||||
mesh.update(calc_edges=False, calc_edges_loose=False)
|
mesh.update(calc_edges=False, calc_edges_loose=False)
|
||||||
|
|
||||||
# add into item using
|
|
||||||
item.blenderData = mesh
|
|
||||||
|
|
||||||
fmesh.close()
|
|
||||||
|
|
||||||
# object
|
# object
|
||||||
fobject = open(os.path.join(tempFolder, "object.bm"), "rb")
|
with open(os.path.join(tempFolder, "object.bm"), "rb") as fobject:
|
||||||
|
|
||||||
# we need get needed collection first
|
# we need get needed collection first
|
||||||
view_layer = context.view_layer
|
view_layer = context.view_layer
|
||||||
@ -267,6 +311,7 @@ def import_bm(context,filepath,externalTexture,blenderTempFolder):
|
|||||||
view_layer.active_layer_collection.collection.children.link(forcedCollection)
|
view_layer.active_layer_collection.collection.children.link(forcedCollection)
|
||||||
|
|
||||||
# start process it
|
# start process it
|
||||||
|
object_groupList = []
|
||||||
for item in objectList:
|
for item in objectList:
|
||||||
fobject.seek(item.offset, os.SEEK_SET)
|
fobject.seek(item.offset, os.SEEK_SET)
|
||||||
|
|
||||||
@ -275,6 +320,10 @@ def import_bm(context,filepath,externalTexture,blenderTempFolder):
|
|||||||
object_isForcedNoComponent = read_bool(fobject)
|
object_isForcedNoComponent = read_bool(fobject)
|
||||||
object_isHidden = read_bool(fobject)
|
object_isHidden = read_bool(fobject)
|
||||||
object_worldMatrix = read_worldMaterix(fobject)
|
object_worldMatrix = read_worldMaterix(fobject)
|
||||||
|
object_groupListCount = read_uint32(fobject)
|
||||||
|
object_groupList.clear()
|
||||||
|
for i in range(object_groupListCount):
|
||||||
|
object_groupList.append(read_string(fobject))
|
||||||
object_meshIndex = read_uint32(fobject)
|
object_meshIndex = read_uint32(fobject)
|
||||||
|
|
||||||
# got mesh first
|
# got mesh first
|
||||||
@ -284,7 +333,9 @@ def import_bm(context,filepath,externalTexture,blenderTempFolder):
|
|||||||
neededMesh = meshList[object_meshIndex].blenderData
|
neededMesh = meshList[object_meshIndex].blenderData
|
||||||
|
|
||||||
# create real object
|
# create real object
|
||||||
obj = bpy.data.objects.new(item.name, neededMesh)
|
(obj, needSkip) = createInstanceWithOption(info_bm_type.OBJECT, item.name, objectOpt, extraMesh=neededMesh)
|
||||||
|
if needSkip:
|
||||||
|
continue
|
||||||
if (not object_isComponent) and object_isForcedNoComponent and (forcedCollection is not None):
|
if (not object_isComponent) and object_isForcedNoComponent and (forcedCollection is not None):
|
||||||
forcedCollection.objects.link(obj)
|
forcedCollection.objects.link(obj)
|
||||||
else:
|
else:
|
||||||
@ -292,12 +343,15 @@ def import_bm(context,filepath,externalTexture,blenderTempFolder):
|
|||||||
obj.matrix_world = object_worldMatrix
|
obj.matrix_world = object_worldMatrix
|
||||||
obj.hide_set(object_isHidden)
|
obj.hide_set(object_isHidden)
|
||||||
|
|
||||||
fobject.close()
|
# write custom property
|
||||||
|
if len(object_groupList) != 0:
|
||||||
|
obj['virtools-group'] = tuple(object_groupList)
|
||||||
|
|
||||||
view_layer.update()
|
view_layer.update()
|
||||||
|
|
||||||
tempFolderObj.cleanup()
|
tempFolderObj.cleanup()
|
||||||
|
|
||||||
def export_bm(context,filepath,export_mode, export_target):
|
def export_bm(context, filepath, export_mode, export_target):
|
||||||
# ============================================ alloc a temp folder
|
# ============================================ alloc a temp folder
|
||||||
tempFolderObj = tempfile.TemporaryDirectory()
|
tempFolderObj = tempfile.TemporaryDirectory()
|
||||||
tempFolder = tempFolderObj.name
|
tempFolder = tempFolderObj.name
|
||||||
@ -309,9 +363,9 @@ def export_bm(context,filepath,export_mode, export_target):
|
|||||||
|
|
||||||
# ============================================ find export target. don't need judge them in there. just collect them
|
# ============================================ find export target. don't need judge them in there. just collect them
|
||||||
if export_mode== "COLLECTION":
|
if export_mode== "COLLECTION":
|
||||||
objectList = bpy.data.collections[export_target].objects
|
objectList = export_target.objects
|
||||||
else:
|
else:
|
||||||
objectList = [bpy.data.objects[export_target]]
|
objectList = [export_target]
|
||||||
|
|
||||||
# try get forcedCollection
|
# try get forcedCollection
|
||||||
try:
|
try:
|
||||||
@ -320,14 +374,14 @@ def export_bm(context,filepath,export_mode, export_target):
|
|||||||
forcedCollection = None
|
forcedCollection = None
|
||||||
|
|
||||||
# ============================================ export
|
# ============================================ export
|
||||||
finfo = open(os.path.join(tempFolder, "index.bm"), "wb")
|
with open(os.path.join(tempFolder, "index.bm"), "wb") as finfo:
|
||||||
write_uint32(finfo, bm_current_version)
|
write_uint32(finfo, bm_current_version)
|
||||||
|
|
||||||
# ====================== export object
|
# ====================== export object
|
||||||
fobject = open(os.path.join(tempFolder, "object.bm"), "wb")
|
|
||||||
meshSet = set()
|
meshSet = set()
|
||||||
meshList = []
|
meshList = []
|
||||||
meshCount = 0
|
meshCount = 0
|
||||||
|
with open(os.path.join(tempFolder, "object.bm"), "wb") as fobject:
|
||||||
for obj in objectList:
|
for obj in objectList:
|
||||||
# only export mesh object
|
# only export mesh object
|
||||||
if obj.type != 'MESH':
|
if obj.type != 'MESH':
|
||||||
@ -362,6 +416,10 @@ def export_bm(context,filepath,export_mode, export_target):
|
|||||||
# get visibility
|
# get visibility
|
||||||
object_isHidden = not obj.visible_get()
|
object_isHidden = not obj.visible_get()
|
||||||
|
|
||||||
|
# try get grouping data
|
||||||
|
object_groupList = try_get_custom_property(obj, 'virtools-group')
|
||||||
|
object_groupList = set_value_when_none(object_groupList, [])
|
||||||
|
|
||||||
# write finfo first
|
# write finfo first
|
||||||
write_string(finfo, obj.name)
|
write_string(finfo, obj.name)
|
||||||
write_uint8(finfo, info_bm_type.OBJECT)
|
write_uint8(finfo, info_bm_type.OBJECT)
|
||||||
@ -370,17 +428,17 @@ def export_bm(context,filepath,export_mode, export_target):
|
|||||||
# write fobject
|
# write fobject
|
||||||
write_bool(fobject, object_isComponent)
|
write_bool(fobject, object_isComponent)
|
||||||
write_bool(fobject, object_isForcedNoComponent)
|
write_bool(fobject, object_isForcedNoComponent)
|
||||||
print(object_isHidden)
|
|
||||||
write_bool(fobject, object_isHidden)
|
write_bool(fobject, object_isHidden)
|
||||||
write_worldMatrix(fobject, obj.matrix_world)
|
write_worldMatrix(fobject, obj.matrix_world)
|
||||||
|
write_uint32(fobject, len(object_groupList))
|
||||||
|
for item in object_groupList:
|
||||||
|
write_string(fobject, item)
|
||||||
write_uint32(fobject, meshId)
|
write_uint32(fobject, meshId)
|
||||||
|
|
||||||
fobject.close()
|
|
||||||
|
|
||||||
# ====================== export mesh
|
# ====================== export mesh
|
||||||
fmesh = open(os.path.join(tempFolder, "mesh.bm"), "wb")
|
|
||||||
materialSet = set()
|
materialSet = set()
|
||||||
materialList = []
|
materialList = []
|
||||||
|
with open(os.path.join(tempFolder, "mesh.bm"), "wb") as fmesh:
|
||||||
for mesh in meshList:
|
for mesh in meshList:
|
||||||
mesh.calc_normals_split()
|
mesh.calc_normals_split()
|
||||||
|
|
||||||
@ -399,8 +457,9 @@ def export_bm(context,filepath,export_mode, export_target):
|
|||||||
|
|
||||||
# uv
|
# uv
|
||||||
face_index_pairs = [(face, index) for index, face in enumerate(mesh.polygons)]
|
face_index_pairs = [(face, index) for index, face in enumerate(mesh.polygons)]
|
||||||
uv_layer = mesh.uv_layers.active.data[:]
|
|
||||||
write_uint32(fmesh, len(face_index_pairs) * 3)
|
write_uint32(fmesh, len(face_index_pairs) * 3)
|
||||||
|
if mesh.uv_layers.active is not None:
|
||||||
|
uv_layer = mesh.uv_layers.active.data[:]
|
||||||
for f, f_index in face_index_pairs:
|
for f, f_index in face_index_pairs:
|
||||||
# it should be triangle face, otherwise throw a error
|
# it should be triangle face, otherwise throw a error
|
||||||
if (f.loop_total != 3):
|
if (f.loop_total != 3):
|
||||||
@ -410,6 +469,10 @@ def export_bm(context,filepath,export_mode, export_target):
|
|||||||
uv = uv_layer[loop_index].uv
|
uv = uv_layer[loop_index].uv
|
||||||
# reverse v
|
# reverse v
|
||||||
write_2vector(fmesh, uv[0], -uv[1])
|
write_2vector(fmesh, uv[0], -uv[1])
|
||||||
|
else:
|
||||||
|
# no uv data. write garbage
|
||||||
|
for i in range(len(face_index_pairs) * 3):
|
||||||
|
write_2vector(fmesh, 0.0, 0.0)
|
||||||
|
|
||||||
# normals
|
# normals
|
||||||
write_uint32(fmesh, len(face_index_pairs) * 3)
|
write_uint32(fmesh, len(face_index_pairs) * 3)
|
||||||
@ -463,13 +526,11 @@ def export_bm(context,filepath,export_mode, export_target):
|
|||||||
|
|
||||||
mesh.free_normals_split()
|
mesh.free_normals_split()
|
||||||
|
|
||||||
fmesh.close()
|
|
||||||
|
|
||||||
# ====================== export material
|
# ====================== export material
|
||||||
fmaterial = open(os.path.join(tempFolder, "material.bm"), "wb")
|
|
||||||
textureSet = set()
|
textureSet = set()
|
||||||
textureList = []
|
textureList = []
|
||||||
textureCount = 0
|
textureCount = 0
|
||||||
|
with open(os.path.join(tempFolder, "material.bm"), "wb") as fmaterial:
|
||||||
for material in materialList:
|
for material in materialList:
|
||||||
# write finfo first
|
# write finfo first
|
||||||
write_string(finfo, material.name)
|
write_string(finfo, material.name)
|
||||||
@ -540,13 +601,11 @@ def export_bm(context,filepath,export_mode, export_target):
|
|||||||
write_bool(fmaterial, material_useTexture)
|
write_bool(fmaterial, material_useTexture)
|
||||||
write_uint32(fmaterial, material_texture)
|
write_uint32(fmaterial, material_texture)
|
||||||
|
|
||||||
fmaterial.close()
|
|
||||||
|
|
||||||
# ====================== export texture
|
# ====================== export texture
|
||||||
ftexture = open(os.path.join(tempFolder, "texture.bm"), "wb")
|
|
||||||
source_dir = os.path.dirname(bpy.data.filepath)
|
source_dir = os.path.dirname(bpy.data.filepath)
|
||||||
existed_texture = set()
|
existed_texture = set()
|
||||||
|
with open(os.path.join(tempFolder, "texture.bm"), "wb") as ftexture:
|
||||||
for texture in textureList:
|
for texture in textureList:
|
||||||
# write finfo first
|
# write finfo first
|
||||||
write_string(finfo, texture.name)
|
write_string(finfo, texture.name)
|
||||||
@ -567,10 +626,6 @@ def export_bm(context,filepath,export_mode, export_target):
|
|||||||
shutil.copy(texture_filepath, os.path.join(tempTextureFolder, filename))
|
shutil.copy(texture_filepath, os.path.join(tempTextureFolder, filename))
|
||||||
existed_texture.add(filename)
|
existed_texture.add(filename)
|
||||||
|
|
||||||
ftexture.close()
|
|
||||||
|
|
||||||
# close info fs
|
|
||||||
finfo.close()
|
|
||||||
|
|
||||||
# ============================================ save zip and clean up folder
|
# ============================================ save zip and clean up folder
|
||||||
if os.path.isfile(filepath):
|
if os.path.isfile(filepath):
|
||||||
@ -601,6 +656,31 @@ class info_block_helper():
|
|||||||
self.offset = offset
|
self.offset = offset
|
||||||
self.blenderData = None
|
self.blenderData = None
|
||||||
|
|
||||||
|
def createInstanceWithOption(instType, instName, instOpt, extraMesh = None):
|
||||||
|
if instType == info_bm_type.OBJECT:
|
||||||
|
target = bpy.data.objects
|
||||||
|
args = (instName, extraMesh)
|
||||||
|
elif instType == info_bm_type.MESH:
|
||||||
|
target = bpy.data.meshes
|
||||||
|
args = (instName, )
|
||||||
|
elif instType == info_bm_type.MATERIAL:
|
||||||
|
target = bpy.data.materials
|
||||||
|
args = (instName, )
|
||||||
|
|
||||||
|
if instOpt == 'RENAME':
|
||||||
|
tempInst = target.new(*args)
|
||||||
|
tempSkip = False
|
||||||
|
elif instOpt == 'CURRENT':
|
||||||
|
try:
|
||||||
|
tempInst = target[instName]
|
||||||
|
tempSkip = True
|
||||||
|
except:
|
||||||
|
tempInst = target.new(*args)
|
||||||
|
tempSkip = False
|
||||||
|
|
||||||
|
return (tempInst, tempSkip)
|
||||||
|
|
||||||
|
# NOTE: this function also used by add_elements.py
|
||||||
def load_component(component_id):
|
def load_component(component_id):
|
||||||
# get file first
|
# get file first
|
||||||
compName = config.component_list[component_id]
|
compName = config.component_list[component_id]
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
import json
|
||||||
|
import os
|
||||||
|
|
||||||
external_texture_list = set([
|
external_texture_list = set([
|
||||||
"atari.avi",
|
"atari.avi",
|
||||||
"atari.bmp",
|
"atari.bmp",
|
||||||
@ -111,3 +114,120 @@ component_list = [
|
|||||||
"PR_Resetpoint",
|
"PR_Resetpoint",
|
||||||
"PS_FourFlames"
|
"PS_FourFlames"
|
||||||
]
|
]
|
||||||
|
|
||||||
|
'''
|
||||||
|
format: key is diection, value is a dict
|
||||||
|
dict's key is expand mode, value is a tuple
|
||||||
|
tuple always have 4 items, it means (TOP_STR, RIGHT_STR, BOTTOM_STR, LEFT_STR)
|
||||||
|
'''
|
||||||
|
floor_expand_direction_map = {
|
||||||
|
"PositiveX": {
|
||||||
|
"Static": ("X", "X", "X", "X"),
|
||||||
|
"Column": ("X", "X", "D1", "X"),
|
||||||
|
"Freedom": ("X", "X", "D1", "D2"),
|
||||||
|
},
|
||||||
|
"NegativeX": {
|
||||||
|
"Static": ("X", "X", "X", "X"),
|
||||||
|
"Column": ("D1", "X", "X", "X"),
|
||||||
|
"Freedom": ("D1", "D2", "X", "X"),
|
||||||
|
},
|
||||||
|
"PositiveY": {
|
||||||
|
"Static": ("X", "X", "X", "X"),
|
||||||
|
"Column": ("X", "D1", "X", "X"),
|
||||||
|
"Freedom": ("X", "D1", "D2", "X"),
|
||||||
|
},
|
||||||
|
"NegativeY": {
|
||||||
|
"Static": ("X", "X", "X", "X"),
|
||||||
|
"Column": ("X", "X", "X", "D1"),
|
||||||
|
"Freedom": ("D2", "X", "X", "D1"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
floor_texture_corresponding_map = {
|
||||||
|
"FloorSide": "Floor_Side.bmp",
|
||||||
|
"FloorTopBorder": "Floor_Top_Border.bmp",
|
||||||
|
"FloorTopBorder_ForSide": "Floor_Top_Border.bmp",
|
||||||
|
"FloorTopBorderless": "Floor_Top_Borderless.bmp",
|
||||||
|
"FloorTopBorderless_ForSide": "Floor_Top_Borderless.bmp",
|
||||||
|
"FloorTopFlat": "Floor_Top_Flat.bmp",
|
||||||
|
"FloorTopProfil": "Floor_Top_Profil.bmp",
|
||||||
|
"FloorTopProfilFlat": "Floor_Top_ProfilFlat.bmp",
|
||||||
|
"BallWood": "Ball_Wood.bmp",
|
||||||
|
"BallPaper": "Ball_Paper.bmp",
|
||||||
|
"BallStone": "Ball_Stone.bmp"
|
||||||
|
}
|
||||||
|
|
||||||
|
# WARNING: this data is shared with BallanceVirtoolsPlugin - mapping_BM.cpp - fix_blender_texture
|
||||||
|
floor_material_statistic = [
|
||||||
|
{
|
||||||
|
"member": [
|
||||||
|
"FloorSide",
|
||||||
|
"FloorTopBorder_ForSide",
|
||||||
|
"FloorTopBorderless_ForSide"
|
||||||
|
],
|
||||||
|
"data": {
|
||||||
|
"ambient": (0, 0, 0),
|
||||||
|
"diffuse": (122 / 255.0, 122 / 255.0, 122 / 255.0),
|
||||||
|
"specular": (0.0, 0.0, 0.0),
|
||||||
|
"emissive": (104 / 255.0, 104 / 255.0, 104 / 255.0),
|
||||||
|
"power": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"member": [
|
||||||
|
"FloorTopBorder",
|
||||||
|
"FloorTopBorderless",
|
||||||
|
"FloorTopFlat",
|
||||||
|
"FloorTopProfilFlat"
|
||||||
|
],
|
||||||
|
"data": {
|
||||||
|
"ambient": (0, 0, 0),
|
||||||
|
"diffuse": (1.0, 1.0, 1.0),
|
||||||
|
"specular": (80 / 255.0, 80 / 255.0, 80 / 255.0),
|
||||||
|
"emissive": (0.0, 0.0, 0.0),
|
||||||
|
"power": 100
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"member": [
|
||||||
|
"BallPaper"
|
||||||
|
],
|
||||||
|
"data": {
|
||||||
|
"ambient": (25 / 255.0, 25 / 255.0, 25 / 255.0),
|
||||||
|
"diffuse": (1.0, 1.0, 1.0),
|
||||||
|
"specular": (0.0, 0.0, 0.0),
|
||||||
|
"emissive": (100 / 255.0, 100 / 255.0, 100 / 255.0),
|
||||||
|
"power": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"member": [
|
||||||
|
"BallStone",
|
||||||
|
"BallWood"
|
||||||
|
],
|
||||||
|
"data": {
|
||||||
|
"ambient": (25 / 255.0, 25 / 255.0, 25 / 255.0),
|
||||||
|
"diffuse": (1.0, 1.0, 1.0),
|
||||||
|
"specular": (229 / 255.0, 229 / 255.0, 229 / 255.0),
|
||||||
|
"emissive": (60 / 255.0, 60 / 255.0, 60 / 255.0),
|
||||||
|
"power": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
floor_block_dict = {}
|
||||||
|
floor_basic_block_list = []
|
||||||
|
floor_derived_block_list = []
|
||||||
|
with open(os.path.join(os.path.dirname(__file__), "json", "BasicBlock.json")) as fp:
|
||||||
|
for item in json.load(fp):
|
||||||
|
floor_basic_block_list.append(item["Type"])
|
||||||
|
floor_block_dict[item["Type"]] = item
|
||||||
|
with open(os.path.join(os.path.dirname(__file__), "json", "DerivedBlock.json")) as fp:
|
||||||
|
for item in json.load(fp):
|
||||||
|
floor_derived_block_list.append(item["Type"])
|
||||||
|
floor_block_dict[item["Type"]] = item
|
||||||
|
|
||||||
|
blenderIcon_floor = None
|
||||||
|
blenderIcon_floor_dict = {}
|
||||||
|
# blenderIcon_elements = None
|
||||||
|
# blenderIcon_elements_dict = {}
|
117
ballance_blender_plugin/flatten_uv.py
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
import bpy,mathutils
|
||||||
|
import bmesh
|
||||||
|
from . import utils
|
||||||
|
|
||||||
|
class BALLANCE_OT_flatten_uv(bpy.types.Operator):
|
||||||
|
"""Flatten selected face UV. Only works for convex face"""
|
||||||
|
bl_idname = "ballance.flatten_uv"
|
||||||
|
bl_label = "Flatten UV"
|
||||||
|
bl_options = {'UNDO'}
|
||||||
|
|
||||||
|
reference_edge : bpy.props.IntProperty(
|
||||||
|
name="Reference_edge",
|
||||||
|
description="The references edge of UV. It will be placed in V axis.",
|
||||||
|
min=0,
|
||||||
|
soft_min=0,
|
||||||
|
soft_max=3,
|
||||||
|
default=0,
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(self, context):
|
||||||
|
obj = bpy.context.active_object
|
||||||
|
if obj == None:
|
||||||
|
return False
|
||||||
|
if obj.type != 'MESH':
|
||||||
|
return False
|
||||||
|
if obj.mode != 'EDIT':
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def invoke(self, context, event):
|
||||||
|
wm = context.window_manager
|
||||||
|
return wm.invoke_props_dialog(self)
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
no_processed_count = real_flatten_uv(bpy.context.active_object.data, self.reference_edge)
|
||||||
|
if no_processed_count != 0:
|
||||||
|
utils.ShowMessageBox(("{} faces may not be processed correctly because they have problem.".format(no_processed_count), ), "Warning", 'ERROR')
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
layout = self.layout
|
||||||
|
layout.prop(self, "reference_edge")
|
||||||
|
|
||||||
|
def real_flatten_uv(mesh, reference_edge):
|
||||||
|
no_processed_count = 0
|
||||||
|
|
||||||
|
if mesh.uv_layers.active is None:
|
||||||
|
# if no uv, create it
|
||||||
|
mesh.uv_layers.new(do_init=False)
|
||||||
|
|
||||||
|
bm = bmesh.from_edit_mesh(mesh)
|
||||||
|
uv_lay = bm.loops.layers.uv.active
|
||||||
|
for face in bm.faces:
|
||||||
|
if not face.select:
|
||||||
|
continue
|
||||||
|
|
||||||
|
allPoint = len(face.loops)
|
||||||
|
|
||||||
|
if allPoint <= reference_edge:
|
||||||
|
no_processed_count+=1
|
||||||
|
continue
|
||||||
|
|
||||||
|
# get correct new corrdinate system
|
||||||
|
p1Relative = reference_edge
|
||||||
|
p2Relative = reference_edge + 1
|
||||||
|
p3Relative = reference_edge + 2
|
||||||
|
if p2Relative >= allPoint:
|
||||||
|
p2Relative -= allPoint
|
||||||
|
if p3Relative >= allPoint:
|
||||||
|
p3Relative -= allPoint
|
||||||
|
|
||||||
|
p1=mathutils.Vector(tuple(face.loops[p1Relative].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)))
|
||||||
|
|
||||||
|
new_y_axis = p2 - p1
|
||||||
|
new_y_axis.normalize()
|
||||||
|
vec1 = p3 - p2
|
||||||
|
vec1.normalize()
|
||||||
|
|
||||||
|
new_z_axis = new_y_axis.cross(vec1)
|
||||||
|
new_z_axis.normalize()
|
||||||
|
new_x_axis = new_y_axis.cross(new_z_axis)
|
||||||
|
new_x_axis.normalize()
|
||||||
|
|
||||||
|
# construct rebase matrix
|
||||||
|
origin_base = mathutils.Matrix((
|
||||||
|
(1.0, 0, 0),
|
||||||
|
(0, 1.0, 0),
|
||||||
|
(0, 0, 1.0)
|
||||||
|
))
|
||||||
|
origin_base.invert()
|
||||||
|
new_base = mathutils.Matrix((
|
||||||
|
(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.z, new_y_axis.z, new_z_axis.z)
|
||||||
|
))
|
||||||
|
transition_matrix = origin_base @ new_base
|
||||||
|
transition_matrix.invert()
|
||||||
|
|
||||||
|
# process each face
|
||||||
|
for loop_index in range(allPoint):
|
||||||
|
pp = mathutils.Vector(tuple(face.loops[loop_index].vert.co[x] for x in range(3)))
|
||||||
|
vec = pp-p1
|
||||||
|
new_vec = transition_matrix @ vec
|
||||||
|
|
||||||
|
face.loops[loop_index][uv_lay].uv = (
|
||||||
|
(new_vec.x if new_vec.x >=0 else -new_vec.x) / 5,
|
||||||
|
(new_vec.y) / 5
|
||||||
|
)
|
||||||
|
|
||||||
|
# Show the updates in the viewport
|
||||||
|
bmesh.update_edit_mesh(mesh)
|
||||||
|
|
||||||
|
return no_processed_count
|
||||||
|
|
@ -1,68 +0,0 @@
|
|||||||
import bpy,bmesh
|
|
||||||
from . import utils
|
|
||||||
|
|
||||||
class RailUVOperator(bpy.types.Operator):
|
|
||||||
"""Create a UV for rail"""
|
|
||||||
bl_idname = "ballance.rail_uv"
|
|
||||||
bl_label = "Create Rail UV"
|
|
||||||
bl_options = {'UNDO'}
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def poll(self, context):
|
|
||||||
return check_rail_target()
|
|
||||||
|
|
||||||
def execute(self, context):
|
|
||||||
create_rail_uv()
|
|
||||||
return {'FINISHED'}
|
|
||||||
|
|
||||||
# ====================== method
|
|
||||||
|
|
||||||
def check_rail_target():
|
|
||||||
for obj in bpy.context.selected_objects:
|
|
||||||
if obj.type != 'MESH':
|
|
||||||
continue
|
|
||||||
if obj.mode != 'OBJECT':
|
|
||||||
continue
|
|
||||||
if obj.data.uv_layers.active.data == None:
|
|
||||||
continue
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
def create_rail_uv():
|
|
||||||
meshList = []
|
|
||||||
ignoredObj = []
|
|
||||||
for obj in bpy.context.selected_objects:
|
|
||||||
if obj.type != 'MESH':
|
|
||||||
ignoredObj.append(obj.name)
|
|
||||||
continue
|
|
||||||
if obj.mode != 'OBJECT':
|
|
||||||
ignoredObj.append(obj.name)
|
|
||||||
continue
|
|
||||||
if obj.data.uv_layers.active.data == None:
|
|
||||||
ignoredObj.append(obj.name)
|
|
||||||
continue
|
|
||||||
|
|
||||||
meshList.append(obj.data)
|
|
||||||
|
|
||||||
for mesh in meshList:
|
|
||||||
# vecList = mesh.vertices[:]
|
|
||||||
uv_layer = mesh.uv_layers.active.data
|
|
||||||
for poly in mesh.polygons:
|
|
||||||
for loop_index in range(poly.loop_start, poly.loop_start + poly.loop_total):
|
|
||||||
# index = mesh.loops[loop_index].vertex_index
|
|
||||||
uv_layer[loop_index].uv[0] = 0 # vecList[index].co[0]
|
|
||||||
uv_layer[loop_index].uv[1] = 1 # vecList[index].co[1]
|
|
||||||
|
|
||||||
if len(ignoredObj) != 0:
|
|
||||||
utils.ShowMessageBox("Following objects are not processed due to they are not suit for this function now: " + ', '.join(ignoredObj), "No processed object", 'WARNING')
|
|
||||||
|
|
||||||
|
|
||||||
def virtoolize_floor_uv():
|
|
||||||
pass
|
|
||||||
|
|
||||||
def mesh_triangulate(me):
|
|
||||||
bm = bmesh.new()
|
|
||||||
bm.from_mesh(me)
|
|
||||||
bmesh.ops.triangulate(bm, faces=bm.faces)
|
|
||||||
bm.to_mesh(me)
|
|
||||||
bm.free()
|
|
BIN
ballance_blender_plugin/icons/floor/Flat.png
Normal file
After Width: | Height: | Size: 6.3 KiB |
BIN
ballance_blender_plugin/icons/floor/Normal1x1.png
Normal file
After Width: | Height: | Size: 31 KiB |
BIN
ballance_blender_plugin/icons/floor/NormalBorder.png
Normal file
After Width: | Height: | Size: 6.7 KiB |
BIN
ballance_blender_plugin/icons/floor/NormalCrossroad.png
Normal file
After Width: | Height: | Size: 27 KiB |
BIN
ballance_blender_plugin/icons/floor/NormalFloor.png
Normal file
After Width: | Height: | Size: 24 KiB |
BIN
ballance_blender_plugin/icons/floor/NormalFloorHead.png
Normal file
After Width: | Height: | Size: 60 KiB |
BIN
ballance_blender_plugin/icons/floor/NormalInnerCorner.png
Normal file
After Width: | Height: | Size: 6.2 KiB |
BIN
ballance_blender_plugin/icons/floor/NormalLConnector.png
Normal file
After Width: | Height: | Size: 30 KiB |
BIN
ballance_blender_plugin/icons/floor/NormalOutterCorner.png
Normal file
After Width: | Height: | Size: 7.0 KiB |
BIN
ballance_blender_plugin/icons/floor/NormalPlatform.png
Normal file
After Width: | Height: | Size: 58 KiB |
BIN
ballance_blender_plugin/icons/floor/NormalSinkTransition.png
Normal file
After Width: | Height: | Size: 25 KiB |
BIN
ballance_blender_plugin/icons/floor/NormalTConnector.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
ballance_blender_plugin/icons/floor/PaperTrafo.png
Normal file
After Width: | Height: | Size: 39 KiB |
BIN
ballance_blender_plugin/icons/floor/Sink1x1.png
Normal file
After Width: | Height: | Size: 31 KiB |
BIN
ballance_blender_plugin/icons/floor/SinkBorder.png
Normal file
After Width: | Height: | Size: 6.8 KiB |
BIN
ballance_blender_plugin/icons/floor/SinkCrossroad.png
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
ballance_blender_plugin/icons/floor/SinkFloor.png
Normal file
After Width: | Height: | Size: 25 KiB |
BIN
ballance_blender_plugin/icons/floor/SinkFloorHead.png
Normal file
After Width: | Height: | Size: 62 KiB |
BIN
ballance_blender_plugin/icons/floor/SinkInnerCorner.png
Normal file
After Width: | Height: | Size: 6.5 KiB |
BIN
ballance_blender_plugin/icons/floor/SinkLConnector.png
Normal file
After Width: | Height: | Size: 30 KiB |
BIN
ballance_blender_plugin/icons/floor/SinkOutterCorner.png
Normal file
After Width: | Height: | Size: 7.2 KiB |
BIN
ballance_blender_plugin/icons/floor/SinkTConnector.png
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
ballance_blender_plugin/icons/floor/StoneTrafo.png
Normal file
After Width: | Height: | Size: 39 KiB |
BIN
ballance_blender_plugin/icons/floor/WoodTrafo.png
Normal file
After Width: | Height: | Size: 27 KiB |
2432
ballance_blender_plugin/json/BasicBlock.json
Normal file
608
ballance_blender_plugin/json/DerivedBlock.json
Normal file
@ -0,0 +1,608 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"Type": "SinkFloor",
|
||||||
|
"BindingDisplayTexture": "SinkFloor.png",
|
||||||
|
"UnitSize": "Small",
|
||||||
|
"ExpandType": "Column",
|
||||||
|
"InitColumnDirection": "PositiveX",
|
||||||
|
"DefaultSideConfig": {
|
||||||
|
"UseTwoDTop": false,
|
||||||
|
"UseTwoDRight": true,
|
||||||
|
"UseTwoDBottom": false,
|
||||||
|
"UseTwoDLeft": true,
|
||||||
|
"UseThreeDTop": true,
|
||||||
|
"UseThreeDBottom": false
|
||||||
|
},
|
||||||
|
"SmashedBlocks": [
|
||||||
|
{
|
||||||
|
"Type": "SinkBorder",
|
||||||
|
"StartPosition": "0,0;,,,",
|
||||||
|
"ExpandPosition": "0,0;,d1,,",
|
||||||
|
"Rotation": "R0",
|
||||||
|
"SideSync": "2dTop;False;2dBottom;2dLeft;3dTop;3dBottom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Type": "SinkBorder",
|
||||||
|
"StartPosition": "0,1;,d1,,",
|
||||||
|
"ExpandPosition": "0,0;,d1,,",
|
||||||
|
"Rotation": "R180",
|
||||||
|
"SideSync": "2dBottom;False;2dTop;2dRight;3dTop;3dBottom"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Type": "NormalFloor",
|
||||||
|
"BindingDisplayTexture": "NormalFloor.png",
|
||||||
|
"UnitSize": "Small",
|
||||||
|
"ExpandType": "Column",
|
||||||
|
"InitColumnDirection": "PositiveX",
|
||||||
|
"DefaultSideConfig": {
|
||||||
|
"UseTwoDTop": false,
|
||||||
|
"UseTwoDRight": true,
|
||||||
|
"UseTwoDBottom": false,
|
||||||
|
"UseTwoDLeft": true,
|
||||||
|
"UseThreeDTop": true,
|
||||||
|
"UseThreeDBottom": false
|
||||||
|
},
|
||||||
|
"SmashedBlocks": [
|
||||||
|
{
|
||||||
|
"Type": "NormalBorder",
|
||||||
|
"StartPosition": "0,0;,,,",
|
||||||
|
"ExpandPosition": "0,0;,d1,,",
|
||||||
|
"Rotation": "R0",
|
||||||
|
"SideSync": "2dTop;False;2dBottom;2dLeft;3dTop;3dBottom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Type": "NormalBorder",
|
||||||
|
"StartPosition": "0,1;,d1,,",
|
||||||
|
"ExpandPosition": "0,0;,d1,,",
|
||||||
|
"Rotation": "R180",
|
||||||
|
"SideSync": "2dBottom;False;2dTop;2dRight;3dTop;3dBottom"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Type": "NormalFloorHead",
|
||||||
|
"BindingDisplayTexture": "NormalFloorHead.png",
|
||||||
|
"UnitSize": "Small",
|
||||||
|
"ExpandType": "Column",
|
||||||
|
"InitColumnDirection": "PositiveX",
|
||||||
|
"DefaultSideConfig": {
|
||||||
|
"UseTwoDTop": true,
|
||||||
|
"UseTwoDRight": true,
|
||||||
|
"UseTwoDBottom": true,
|
||||||
|
"UseTwoDLeft": true,
|
||||||
|
"UseThreeDTop": true,
|
||||||
|
"UseThreeDBottom": false
|
||||||
|
},
|
||||||
|
"SmashedBlocks": [
|
||||||
|
{
|
||||||
|
"Type": "NormalBorder",
|
||||||
|
"StartPosition": "1,0;,,,",
|
||||||
|
"ExpandPosition": "0,0;,d1,,",
|
||||||
|
"Rotation": "R0",
|
||||||
|
"SideSync": "False;False;False;2dLeft;3dTop;3dBottom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Type": "NormalBorder",
|
||||||
|
"StartPosition": "1,1;,d1,,",
|
||||||
|
"ExpandPosition": "0,0;,d1,,",
|
||||||
|
"Rotation": "R180",
|
||||||
|
"SideSync": "False;False;False;2dRight;3dTop;3dBottom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Type": "NormalOutterCorner",
|
||||||
|
"StartPosition": "0,0;,,,",
|
||||||
|
"ExpandPosition": "0,0;,,,",
|
||||||
|
"Rotation": "R0",
|
||||||
|
"SideSync": "2dTop;False;False;2dLeft;3dTop;3dBottom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Type": "NormalOutterCorner",
|
||||||
|
"StartPosition": "0,0;2,d1,,",
|
||||||
|
"ExpandPosition": "0,0;,,,",
|
||||||
|
"Rotation": "R90",
|
||||||
|
"SideSync": "2dLeft;False;False;2dBottom;3dTop;3dBottom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Type": "NormalOutterCorner",
|
||||||
|
"StartPosition": "0,1;2,d1,,",
|
||||||
|
"ExpandPosition": "0,0;,,,",
|
||||||
|
"Rotation": "R180",
|
||||||
|
"SideSync": "2dBottom;False;False;2dRight;3dTop;3dBottom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Type": "NormalOutterCorner",
|
||||||
|
"StartPosition": "0,1;,,,",
|
||||||
|
"ExpandPosition": "0,0;,,,",
|
||||||
|
"Rotation": "R270",
|
||||||
|
"SideSync": "2dRight;False;False;2dTop;3dTop;3dBottom"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Type": "SinkFloorHead",
|
||||||
|
"BindingDisplayTexture": "SinkFloorHead.png",
|
||||||
|
"UnitSize": "Small",
|
||||||
|
"ExpandType": "Column",
|
||||||
|
"InitColumnDirection": "PositiveX",
|
||||||
|
"DefaultSideConfig": {
|
||||||
|
"UseTwoDTop": true,
|
||||||
|
"UseTwoDRight": true,
|
||||||
|
"UseTwoDBottom": true,
|
||||||
|
"UseTwoDLeft": true,
|
||||||
|
"UseThreeDTop": true,
|
||||||
|
"UseThreeDBottom": false
|
||||||
|
},
|
||||||
|
"SmashedBlocks": [
|
||||||
|
{
|
||||||
|
"Type": "SinkBorder",
|
||||||
|
"StartPosition": "1,0;,,,",
|
||||||
|
"ExpandPosition": "0,0;,d1,,",
|
||||||
|
"Rotation": "R0",
|
||||||
|
"SideSync": "False;False;False;2dLeft;3dTop;3dBottom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Type": "SinkBorder",
|
||||||
|
"StartPosition": "1,1;,d1,,",
|
||||||
|
"ExpandPosition": "0,0;,d1,,",
|
||||||
|
"Rotation": "R180",
|
||||||
|
"SideSync": "False;False;False;2dRight;3dTop;3dBottom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Type": "SinkOutterCorner",
|
||||||
|
"StartPosition": "0,0;,,,",
|
||||||
|
"ExpandPosition": "0,0;,,,",
|
||||||
|
"Rotation": "R0",
|
||||||
|
"SideSync": "2dTop;False;False;2dLeft;3dTop;3dBottom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Type": "SinkOutterCorner",
|
||||||
|
"StartPosition": "0,0;2,d1,,",
|
||||||
|
"ExpandPosition": "0,0;,,,",
|
||||||
|
"Rotation": "R90",
|
||||||
|
"SideSync": "2dLeft;False;False;2dBottom;3dTop;3dBottom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Type": "SinkOutterCorner",
|
||||||
|
"StartPosition": "0,1;2,d1,,",
|
||||||
|
"ExpandPosition": "0,0;,,,",
|
||||||
|
"Rotation": "R180",
|
||||||
|
"SideSync": "2dBottom;False;False;2dRight;3dTop;3dBottom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Type": "SinkOutterCorner",
|
||||||
|
"StartPosition": "0,1;,,,",
|
||||||
|
"ExpandPosition": "0,0;,,,",
|
||||||
|
"Rotation": "R270",
|
||||||
|
"SideSync": "2dRight;False;False;2dTop;3dTop;3dBottom"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Type": "Sink1x1",
|
||||||
|
"BindingDisplayTexture": "Sink1x1.png",
|
||||||
|
"UnitSize": "Small",
|
||||||
|
"ExpandType": "Static",
|
||||||
|
"InitColumnDirection": "PositiveX",
|
||||||
|
"DefaultSideConfig": {
|
||||||
|
"UseTwoDTop": true,
|
||||||
|
"UseTwoDRight": true,
|
||||||
|
"UseTwoDBottom": true,
|
||||||
|
"UseTwoDLeft": true,
|
||||||
|
"UseThreeDTop": true,
|
||||||
|
"UseThreeDBottom": false
|
||||||
|
},
|
||||||
|
"SmashedBlocks": [
|
||||||
|
{
|
||||||
|
"Type": "SinkOutterCorner",
|
||||||
|
"StartPosition": "0,0;,,,",
|
||||||
|
"ExpandPosition": "0,0;,,,",
|
||||||
|
"Rotation": "R0",
|
||||||
|
"SideSync": "2dTop;False;False;2dLeft;3dTop;3dBottom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Type": "SinkOutterCorner",
|
||||||
|
"StartPosition": "1,0;,,,",
|
||||||
|
"ExpandPosition": "0,0;,,,",
|
||||||
|
"Rotation": "R90",
|
||||||
|
"SideSync": "2dLeft;False;False;2dBottom;3dTop;3dBottom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Type": "SinkOutterCorner",
|
||||||
|
"StartPosition": "1,1;,,,",
|
||||||
|
"ExpandPosition": "0,0;,,,",
|
||||||
|
"Rotation": "R180",
|
||||||
|
"SideSync": "2dBottom;False;False;2dRight;3dTop;3dBottom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Type": "SinkOutterCorner",
|
||||||
|
"StartPosition": "0,1;,,,",
|
||||||
|
"ExpandPosition": "0,0;,,,",
|
||||||
|
"Rotation": "R270",
|
||||||
|
"SideSync": "2dRight;False;False;2dTop;3dTop;3dBottom"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Type": "Normal1x1",
|
||||||
|
"BindingDisplayTexture": "Normal1x1.png",
|
||||||
|
"UnitSize": "Small",
|
||||||
|
"ExpandType": "Static",
|
||||||
|
"InitColumnDirection": "PositiveX",
|
||||||
|
"DefaultSideConfig": {
|
||||||
|
"UseTwoDTop": true,
|
||||||
|
"UseTwoDRight": true,
|
||||||
|
"UseTwoDBottom": true,
|
||||||
|
"UseTwoDLeft": true,
|
||||||
|
"UseThreeDTop": true,
|
||||||
|
"UseThreeDBottom": false
|
||||||
|
},
|
||||||
|
"SmashedBlocks": [
|
||||||
|
{
|
||||||
|
"Type": "NormalOutterCorner",
|
||||||
|
"StartPosition": "0,0;,,,",
|
||||||
|
"ExpandPosition": "0,0;,,,",
|
||||||
|
"Rotation": "R0",
|
||||||
|
"SideSync": "2dTop;False;False;2dLeft;3dTop;3dBottom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Type": "NormalOutterCorner",
|
||||||
|
"StartPosition": "1,0;,,,",
|
||||||
|
"ExpandPosition": "0,0;,,,",
|
||||||
|
"Rotation": "R90",
|
||||||
|
"SideSync": "2dLeft;False;False;2dBottom;3dTop;3dBottom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Type": "NormalOutterCorner",
|
||||||
|
"StartPosition": "1,1;,,,",
|
||||||
|
"ExpandPosition": "0,0;,,,",
|
||||||
|
"Rotation": "R180",
|
||||||
|
"SideSync": "2dBottom;False;False;2dRight;3dTop;3dBottom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Type": "NormalOutterCorner",
|
||||||
|
"StartPosition": "0,1;,,,",
|
||||||
|
"ExpandPosition": "0,0;,,,",
|
||||||
|
"Rotation": "R270",
|
||||||
|
"SideSync": "2dRight;False;False;2dTop;3dTop;3dBottom"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Type": "NormalPlatform",
|
||||||
|
"BindingDisplayTexture": "NormalPlatform.png",
|
||||||
|
"UnitSize": "Small",
|
||||||
|
"ExpandType": "Freedom",
|
||||||
|
"InitColumnDirection": "PositiveY",
|
||||||
|
"DefaultSideConfig": {
|
||||||
|
"UseTwoDTop": true,
|
||||||
|
"UseTwoDRight": true,
|
||||||
|
"UseTwoDBottom": true,
|
||||||
|
"UseTwoDLeft": true,
|
||||||
|
"UseThreeDTop": true,
|
||||||
|
"UseThreeDBottom": false
|
||||||
|
},
|
||||||
|
"SmashedBlocks": [
|
||||||
|
{
|
||||||
|
"Type": "Flat",
|
||||||
|
"StartPosition": "1,1;,,,",
|
||||||
|
"ExpandPosition": "0,0;,d1,,d2",
|
||||||
|
"Rotation": "R0",
|
||||||
|
"SideSync": "False;False;False;False;3dTop;3dBottom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Type": "NormalBorder",
|
||||||
|
"StartPosition": "1,0;,,,",
|
||||||
|
"ExpandPosition": "0,0;,d2,,",
|
||||||
|
"Rotation": "R0",
|
||||||
|
"SideSync": "False;False;False;2dLeft;3dTop;3dBottom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Type": "NormalBorder",
|
||||||
|
"StartPosition": "0,1;2,d2,,",
|
||||||
|
"ExpandPosition": "0,0;,d1,,",
|
||||||
|
"Rotation": "R90",
|
||||||
|
"SideSync": "False;False;False;2dBottom;3dTop;3dBottom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Type": "NormalBorder",
|
||||||
|
"StartPosition": "0,0;1,d2,2,d1",
|
||||||
|
"ExpandPosition": "0,0;,d2,,",
|
||||||
|
"Rotation": "R180",
|
||||||
|
"SideSync": "False;False;False;2dRight;3dTop;3dBottom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Type": "NormalBorder",
|
||||||
|
"StartPosition": "0,0;,,1,d1",
|
||||||
|
"ExpandPosition": "0,0;,d1,,",
|
||||||
|
"Rotation": "R270",
|
||||||
|
"SideSync": "False;False;False;2dTop;3dTop;3dBottom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Type": "NormalOutterCorner",
|
||||||
|
"StartPosition": "0,0;,,,",
|
||||||
|
"ExpandPosition": "0,0;,,,",
|
||||||
|
"Rotation": "R0",
|
||||||
|
"SideSync": "2dTop;False;False;2dLeft;3dTop;3dBottom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Type": "NormalOutterCorner",
|
||||||
|
"StartPosition": "0,0;2,d2,,",
|
||||||
|
"ExpandPosition": "0,0;,,,",
|
||||||
|
"Rotation": "R90",
|
||||||
|
"SideSync": "2dLeft;False;False;2dBottom;3dTop;3dBottom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Type": "NormalOutterCorner",
|
||||||
|
"StartPosition": "0,0;2,d2,2,d1",
|
||||||
|
"ExpandPosition": "0,0;,,,",
|
||||||
|
"Rotation": "R180",
|
||||||
|
"SideSync": "2dBottom;False;False;2dRight;3dTop;3dBottom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Type": "NormalOutterCorner",
|
||||||
|
"StartPosition": "0,0;,,2,d1",
|
||||||
|
"ExpandPosition": "0,0;,,,",
|
||||||
|
"Rotation": "R270",
|
||||||
|
"SideSync": "2dRight;False;False;2dTop;3dTop;3dBottom"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Type": "NormalLConnector",
|
||||||
|
"BindingDisplayTexture": "NormalLConnector.png",
|
||||||
|
"UnitSize": "Small",
|
||||||
|
"ExpandType": "Static",
|
||||||
|
"InitColumnDirection": "PositiveX",
|
||||||
|
"DefaultSideConfig": {
|
||||||
|
"UseTwoDTop": true,
|
||||||
|
"UseTwoDRight": false,
|
||||||
|
"UseTwoDBottom": false,
|
||||||
|
"UseTwoDLeft": true,
|
||||||
|
"UseThreeDTop": true,
|
||||||
|
"UseThreeDBottom": false
|
||||||
|
},
|
||||||
|
"SmashedBlocks": [
|
||||||
|
{
|
||||||
|
"Type": "NormalOutterCorner",
|
||||||
|
"StartPosition": "0,0;,,,",
|
||||||
|
"ExpandPosition": "0,0;,,,",
|
||||||
|
"Rotation": "R0",
|
||||||
|
"SideSync": "2dTop;False;False;2dLeft;3dTop;3dBottom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Type": "NormalBorder",
|
||||||
|
"StartPosition": "1,0;,,,",
|
||||||
|
"ExpandPosition": "0,0;,,,",
|
||||||
|
"Rotation": "R0",
|
||||||
|
"SideSync": "False;False;2dBottom;2dLeft;3dTop;3dBottom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Type": "NormalInnerCorner",
|
||||||
|
"StartPosition": "1,1;,,,",
|
||||||
|
"ExpandPosition": "0,0;,,,",
|
||||||
|
"Rotation": "R0",
|
||||||
|
"SideSync": "False;2dRight;2dBottom;False;3dTop;3dBottom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Type": "NormalBorder",
|
||||||
|
"StartPosition": "0,1;,,,",
|
||||||
|
"ExpandPosition": "0,0;,,,",
|
||||||
|
"Rotation": "R270",
|
||||||
|
"SideSync": "2dRight;False;False;2dTop;3dTop;3dBottom"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Type": "NormalTConnector",
|
||||||
|
"BindingDisplayTexture": "NormalTConnector.png",
|
||||||
|
"UnitSize": "Small",
|
||||||
|
"ExpandType": "Static",
|
||||||
|
"InitColumnDirection": "PositiveX",
|
||||||
|
"DefaultSideConfig": {
|
||||||
|
"UseTwoDTop": true,
|
||||||
|
"UseTwoDRight": false,
|
||||||
|
"UseTwoDBottom": false,
|
||||||
|
"UseTwoDLeft": false,
|
||||||
|
"UseThreeDTop": true,
|
||||||
|
"UseThreeDBottom": false
|
||||||
|
},
|
||||||
|
"SmashedBlocks": [
|
||||||
|
{
|
||||||
|
"Type": "NormalInnerCorner",
|
||||||
|
"StartPosition": "1,0;,,,",
|
||||||
|
"ExpandPosition": "0,0;,,,",
|
||||||
|
"Rotation": "R270",
|
||||||
|
"SideSync": "False;2dBottom;2dLeft;False;3dTop;3dBottom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Type": "NormalInnerCorner",
|
||||||
|
"StartPosition": "1,1;,,,",
|
||||||
|
"ExpandPosition": "0,0;,,,",
|
||||||
|
"Rotation": "R0",
|
||||||
|
"SideSync": "False;2dRight;2dBottom;False;3dTop;3dBottom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Type": "NormalBorder",
|
||||||
|
"StartPosition": "0,1;,,,",
|
||||||
|
"ExpandPosition": "1,0;,,,",
|
||||||
|
"Rotation": "R270",
|
||||||
|
"SideSync": "2dRight;False;2dLeft;2dTop;3dTop;3dBottom"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Type": "NormalCrossroad",
|
||||||
|
"BindingDisplayTexture": "NormalCrossroad.png",
|
||||||
|
"UnitSize": "Small",
|
||||||
|
"ExpandType": "Static",
|
||||||
|
"InitColumnDirection": "PositiveX",
|
||||||
|
"DefaultSideConfig": {
|
||||||
|
"UseTwoDTop": false,
|
||||||
|
"UseTwoDRight": false,
|
||||||
|
"UseTwoDBottom": false,
|
||||||
|
"UseTwoDLeft": false,
|
||||||
|
"UseThreeDTop": true,
|
||||||
|
"UseThreeDBottom": false
|
||||||
|
},
|
||||||
|
"SmashedBlocks": [
|
||||||
|
{
|
||||||
|
"Type": "NormalInnerCorner",
|
||||||
|
"StartPosition": "1,1;,,,",
|
||||||
|
"ExpandPosition": "0,0;,,,",
|
||||||
|
"Rotation": "R0",
|
||||||
|
"SideSync": "False;2dRight;2dBottom;False;3dTop;3dBottom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Type": "NormalInnerCorner",
|
||||||
|
"StartPosition": "0,1;,,,",
|
||||||
|
"ExpandPosition": "0,0;,,,",
|
||||||
|
"Rotation": "R90",
|
||||||
|
"SideSync": "False;2dTop;2dRight;False;3dTop;3dBottom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Type": "NormalInnerCorner",
|
||||||
|
"StartPosition": "0,0;,,,",
|
||||||
|
"ExpandPosition": "0,0;,,,",
|
||||||
|
"Rotation": "R180",
|
||||||
|
"SideSync": "False;2dLeft;2dTop;False;3dTop;3dBottom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Type": "NormalInnerCorner",
|
||||||
|
"StartPosition": "1,0;,,,",
|
||||||
|
"ExpandPosition": "0,0;,,,",
|
||||||
|
"Rotation": "R270",
|
||||||
|
"SideSync": "False;2dBottom;2dLeft;False;3dTop;3dBottom"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Type": "SinkLConnector",
|
||||||
|
"BindingDisplayTexture": "SinkLConnector.png",
|
||||||
|
"UnitSize": "Small",
|
||||||
|
"ExpandType": "Static",
|
||||||
|
"InitColumnDirection": "PositiveX",
|
||||||
|
"DefaultSideConfig": {
|
||||||
|
"UseTwoDTop": true,
|
||||||
|
"UseTwoDRight": false,
|
||||||
|
"UseTwoDBottom": false,
|
||||||
|
"UseTwoDLeft": true,
|
||||||
|
"UseThreeDTop": true,
|
||||||
|
"UseThreeDBottom": false
|
||||||
|
},
|
||||||
|
"SmashedBlocks": [
|
||||||
|
{
|
||||||
|
"Type": "SinkOutterCorner",
|
||||||
|
"StartPosition": "0,0;,,,",
|
||||||
|
"ExpandPosition": "0,0;,,,",
|
||||||
|
"Rotation": "R0",
|
||||||
|
"SideSync": "2dTop;False;False;2dLeft;3dTop;3dBottom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Type": "SinkBorder",
|
||||||
|
"StartPosition": "1,0;,,,",
|
||||||
|
"ExpandPosition": "0,0;,,,",
|
||||||
|
"Rotation": "R0",
|
||||||
|
"SideSync": "False;False;2dBottom;2dLeft;3dTop;3dBottom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Type": "SinkInnerCorner",
|
||||||
|
"StartPosition": "1,1;,,,",
|
||||||
|
"ExpandPosition": "0,0;,,,",
|
||||||
|
"Rotation": "R0",
|
||||||
|
"SideSync": "False;2dRight;2dBottom;False;3dTop;3dBottom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Type": "SinkBorder",
|
||||||
|
"StartPosition": "0,1;,,,",
|
||||||
|
"ExpandPosition": "0,0;,,,",
|
||||||
|
"Rotation": "R270",
|
||||||
|
"SideSync": "2dRight;False;False;2dTop;3dTop;3dBottom"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Type": "SinkTConnector",
|
||||||
|
"BindingDisplayTexture": "SinkTConnector.png",
|
||||||
|
"UnitSize": "Small",
|
||||||
|
"ExpandType": "Static",
|
||||||
|
"InitColumnDirection": "PositiveX",
|
||||||
|
"DefaultSideConfig": {
|
||||||
|
"UseTwoDTop": true,
|
||||||
|
"UseTwoDRight": false,
|
||||||
|
"UseTwoDBottom": false,
|
||||||
|
"UseTwoDLeft": false,
|
||||||
|
"UseThreeDTop": true,
|
||||||
|
"UseThreeDBottom": false
|
||||||
|
},
|
||||||
|
"SmashedBlocks": [
|
||||||
|
{
|
||||||
|
"Type": "SinkInnerCorner",
|
||||||
|
"StartPosition": "1,0;,,,",
|
||||||
|
"ExpandPosition": "0,0;,,,",
|
||||||
|
"Rotation": "R270",
|
||||||
|
"SideSync": "False;2dBottom;2dLeft;False;3dTop;3dBottom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Type": "SinkInnerCorner",
|
||||||
|
"StartPosition": "1,1;,,,",
|
||||||
|
"ExpandPosition": "0,0;,,,",
|
||||||
|
"Rotation": "R0",
|
||||||
|
"SideSync": "False;2dRight;2dBottom;False;3dTop;3dBottom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Type": "SinkBorder",
|
||||||
|
"StartPosition": "0,1;,,,",
|
||||||
|
"ExpandPosition": "1,0;,,,",
|
||||||
|
"Rotation": "R270",
|
||||||
|
"SideSync": "2dRight;False;2dLeft;2dTop;3dTop;3dBottom"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Type": "SinkCrossroad",
|
||||||
|
"BindingDisplayTexture": "SinkCrossroad.png",
|
||||||
|
"UnitSize": "Small",
|
||||||
|
"ExpandType": "Static",
|
||||||
|
"InitColumnDirection": "PositiveX",
|
||||||
|
"DefaultSideConfig": {
|
||||||
|
"UseTwoDTop": false,
|
||||||
|
"UseTwoDRight": false,
|
||||||
|
"UseTwoDBottom": false,
|
||||||
|
"UseTwoDLeft": false,
|
||||||
|
"UseThreeDTop": true,
|
||||||
|
"UseThreeDBottom": false
|
||||||
|
},
|
||||||
|
"SmashedBlocks": [
|
||||||
|
{
|
||||||
|
"Type": "SinkInnerCorner",
|
||||||
|
"StartPosition": "1,1;,,,",
|
||||||
|
"ExpandPosition": "0,0;,,,",
|
||||||
|
"Rotation": "R0",
|
||||||
|
"SideSync": "False;2dRight;2dBottom;False;3dTop;3dBottom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Type": "SinkInnerCorner",
|
||||||
|
"StartPosition": "0,1;,,,",
|
||||||
|
"ExpandPosition": "0,0;,,,",
|
||||||
|
"Rotation": "R90",
|
||||||
|
"SideSync": "False;2dTop;2dRight;False;3dTop;3dBottom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Type": "SinkInnerCorner",
|
||||||
|
"StartPosition": "0,0;,,,",
|
||||||
|
"ExpandPosition": "0,0;,,,",
|
||||||
|
"Rotation": "R180",
|
||||||
|
"SideSync": "False;2dLeft;2dTop;False;3dTop;3dBottom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Type": "SinkInnerCorner",
|
||||||
|
"StartPosition": "1,0;,,,",
|
||||||
|
"ExpandPosition": "0,0;,,,",
|
||||||
|
"Rotation": "R270",
|
||||||
|
"SideSync": "False;2dBottom;2dLeft;False;3dTop;3dBottom"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
48
ballance_blender_plugin/no_uv_checker.py
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import bpy,bmesh
|
||||||
|
from . import utils
|
||||||
|
|
||||||
|
class BALLANCE_OT_no_uv_checker(bpy.types.Operator):
|
||||||
|
"""Check whether the currently selected object has UV"""
|
||||||
|
bl_idname = "ballance.no_uv_checker"
|
||||||
|
bl_label = "Check UV"
|
||||||
|
bl_options = {'UNDO'}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(self, context):
|
||||||
|
return check_valid_target()
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
check_target()
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
# ====================== method
|
||||||
|
|
||||||
|
def check_valid_target():
|
||||||
|
return (len(bpy.context.selected_objects) > 0)
|
||||||
|
|
||||||
|
def check_target():
|
||||||
|
noUVObject = []
|
||||||
|
invalidObjectCount = 0
|
||||||
|
for obj in bpy.context.selected_objects:
|
||||||
|
if obj.type != 'MESH':
|
||||||
|
invalidObjectCount+=1
|
||||||
|
continue
|
||||||
|
if obj.mode != 'OBJECT':
|
||||||
|
invalidObjectCount+=1
|
||||||
|
continue
|
||||||
|
if obj.data.uv_layers.active is None:
|
||||||
|
noUVObject.append(obj.name)
|
||||||
|
|
||||||
|
if len(noUVObject) > 4:
|
||||||
|
print("Following object don't have UV:")
|
||||||
|
for item in noUVObject:
|
||||||
|
print(item)
|
||||||
|
|
||||||
|
utils.ShowMessageBox((
|
||||||
|
"All objects: {}".format(len(bpy.context.selected_objects)),
|
||||||
|
"Skipped: {}".format(invalidObjectCount),
|
||||||
|
"No UV Count: {}".format(len(noUVObject)),
|
||||||
|
"",
|
||||||
|
"Following object don't have UV: "
|
||||||
|
) + tuple(noUVObject[:4]) +
|
||||||
|
(("Too much objects don't have UV. Please open terminal to browse them." if len(noUVObject) > 4 else "") ,), "Check result", 'INFO')
|
@ -1,11 +1,31 @@
|
|||||||
import bpy
|
import bpy
|
||||||
|
import bpy.types
|
||||||
|
|
||||||
|
class MyPropertyGroup(bpy.types.PropertyGroup):
|
||||||
|
material_picker : bpy.props.PointerProperty(
|
||||||
|
type=bpy.types.Material,
|
||||||
|
name="Material",
|
||||||
|
description="The material used for rail"
|
||||||
|
)
|
||||||
|
|
||||||
|
collection_picker : bpy.props.PointerProperty(
|
||||||
|
type=bpy.types.Collection,
|
||||||
|
name="Collection",
|
||||||
|
description="The collection which will be exported"
|
||||||
|
)
|
||||||
|
|
||||||
|
object_picker : bpy.props.PointerProperty(
|
||||||
|
type=bpy.types.Object,
|
||||||
|
name="Object",
|
||||||
|
description="The object which will be exported"
|
||||||
|
)
|
||||||
|
|
||||||
class BallanceBlenderPluginPreferences(bpy.types.AddonPreferences):
|
class BallanceBlenderPluginPreferences(bpy.types.AddonPreferences):
|
||||||
bl_idname = __package__
|
bl_idname = __package__
|
||||||
|
|
||||||
external_folder: bpy.props.StringProperty(
|
external_folder: bpy.props.StringProperty(
|
||||||
name="External texture folder",
|
name="External texture folder",
|
||||||
description="The Ballance texture folder which will be used buy this plugin to get external texture.",
|
description="The Ballance texture folder which will be used by this plugin to get external texture.",
|
||||||
)
|
)
|
||||||
|
|
||||||
no_component_collection: bpy.props.StringProperty(
|
no_component_collection: bpy.props.StringProperty(
|
||||||
|
169
ballance_blender_plugin/rail_uv.py
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
import bpy,bmesh
|
||||||
|
import mathutils
|
||||||
|
import bpy.types
|
||||||
|
from . import utils, preferences
|
||||||
|
|
||||||
|
class BALLANCE_OT_rail_uv(bpy.types.Operator):
|
||||||
|
"""Create a UV for rail"""
|
||||||
|
bl_idname = "ballance.rail_uv"
|
||||||
|
bl_label = "Create Rail UV"
|
||||||
|
bl_options = {'UNDO'}
|
||||||
|
|
||||||
|
uv_type: bpy.props.EnumProperty(
|
||||||
|
name="Type",
|
||||||
|
description="Define how to create UV",
|
||||||
|
items=(
|
||||||
|
("POINT", "Point", "All UV will be created in a specific point"),
|
||||||
|
("UNIFORM", "Uniform", "All UV will be created within 1x1"),
|
||||||
|
("SCALE", "Scale", "Give a scale number to scale UV")
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
projection_axis: bpy.props.EnumProperty(
|
||||||
|
name="Projection axis",
|
||||||
|
description="Projection axis",
|
||||||
|
items=(
|
||||||
|
("X", "X axis", "X axis"),
|
||||||
|
("Y", "Y axis", "Y axis"),
|
||||||
|
("Z", "Z axis", "Z axis")
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
uv_scale : bpy.props.FloatProperty(
|
||||||
|
name="Scale",
|
||||||
|
description="The scale of UV",
|
||||||
|
min=0.0,
|
||||||
|
default=1.0,
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(self, context):
|
||||||
|
return check_rail_target()
|
||||||
|
|
||||||
|
def invoke(self, context, event):
|
||||||
|
wm = context.window_manager
|
||||||
|
return wm.invoke_props_dialog(self)
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
if context.scene.BallanceBlenderPluginProperty.material_picker == None:
|
||||||
|
utils.ShowMessageBox(("No specific material", ), "Lost parameter", 'ERROR')
|
||||||
|
else:
|
||||||
|
create_rail_uv(self.uv_type, context.scene.BallanceBlenderPluginProperty.material_picker, self.uv_scale, self.projection_axis)
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
layout = self.layout
|
||||||
|
layout.prop(self, "uv_type")
|
||||||
|
layout.prop(context.scene.BallanceBlenderPluginProperty, "material_picker")
|
||||||
|
if self.uv_type != 'POINT':
|
||||||
|
layout.prop(self, "projection_axis")
|
||||||
|
if self.uv_type == 'SCALE':
|
||||||
|
layout.prop(self, "uv_scale")
|
||||||
|
|
||||||
|
# ====================== method
|
||||||
|
|
||||||
|
def check_rail_target():
|
||||||
|
for obj in bpy.context.selected_objects:
|
||||||
|
if obj.type != 'MESH':
|
||||||
|
continue
|
||||||
|
if obj.mode != 'OBJECT':
|
||||||
|
continue
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def get_distance(iterator):
|
||||||
|
is_first_min = True
|
||||||
|
is_first_max = True
|
||||||
|
max_value = 0.0
|
||||||
|
min_value = 0.0
|
||||||
|
|
||||||
|
for item in iterator:
|
||||||
|
if is_first_max:
|
||||||
|
is_first_max = False
|
||||||
|
max_value = item
|
||||||
|
else:
|
||||||
|
if item > max_value:
|
||||||
|
max_value = item
|
||||||
|
if is_first_min:
|
||||||
|
is_first_min = False
|
||||||
|
min_value = item
|
||||||
|
else:
|
||||||
|
if item < min_value:
|
||||||
|
min_value = item
|
||||||
|
|
||||||
|
return max_value - min_value
|
||||||
|
|
||||||
|
def create_rail_uv(rail_type, material_pointer, scale_size, projection_axis):
|
||||||
|
objList = []
|
||||||
|
ignoredObj = []
|
||||||
|
for obj in bpy.context.selected_objects:
|
||||||
|
if obj.type != 'MESH':
|
||||||
|
ignoredObj.append(obj.name)
|
||||||
|
continue
|
||||||
|
if obj.mode != 'OBJECT':
|
||||||
|
ignoredObj.append(obj.name)
|
||||||
|
continue
|
||||||
|
if obj.data.uv_layers.active is None:
|
||||||
|
# create a empty uv for it.
|
||||||
|
obj.data.uv_layers.new(do_init=False)
|
||||||
|
|
||||||
|
objList.append(obj)
|
||||||
|
|
||||||
|
for obj in objList:
|
||||||
|
mesh = obj.data
|
||||||
|
|
||||||
|
# clean it material and set rail first
|
||||||
|
obj.data.materials.clear()
|
||||||
|
obj.data.materials.append(material_pointer)
|
||||||
|
|
||||||
|
# copy mesh vec for scale or uniform mode
|
||||||
|
vecList = mesh.vertices[:]
|
||||||
|
real_scale = 1.0
|
||||||
|
if rail_type == 'SCALE':
|
||||||
|
real_scale = scale_size
|
||||||
|
elif rail_type == 'UNIFORM':
|
||||||
|
# calc proper scale
|
||||||
|
if projection_axis == 'X':
|
||||||
|
maxLength = max(
|
||||||
|
get_distance(vec.co[1] for vec in vecList),
|
||||||
|
get_distance(vec.co[2] for vec in vecList)
|
||||||
|
)
|
||||||
|
elif projection_axis == 'Y':
|
||||||
|
maxLength = max(
|
||||||
|
get_distance(vec.co[0] for vec in vecList),
|
||||||
|
get_distance(vec.co[2] for vec in vecList)
|
||||||
|
)
|
||||||
|
elif projection_axis == 'Z':
|
||||||
|
maxLength = max(
|
||||||
|
get_distance(vec.co[0] for vec in vecList),
|
||||||
|
get_distance(vec.co[1] for vec in vecList)
|
||||||
|
)
|
||||||
|
real_scale = 1.0 / maxLength
|
||||||
|
|
||||||
|
uv_layer = mesh.uv_layers.active.data
|
||||||
|
for poly in mesh.polygons:
|
||||||
|
for loop_index in range(poly.loop_start, poly.loop_start + poly.loop_total):
|
||||||
|
# get correspond vec index
|
||||||
|
index = mesh.loops[loop_index].vertex_index
|
||||||
|
if rail_type == 'POINT':
|
||||||
|
# set to 1 point
|
||||||
|
uv_layer[loop_index].uv[0] = 0
|
||||||
|
uv_layer[loop_index].uv[1] = 1
|
||||||
|
else:
|
||||||
|
# following xy -> uv scale
|
||||||
|
#
|
||||||
|
# use Z axis: X->U Y->V
|
||||||
|
# use X axis: Y->U Z->V
|
||||||
|
# use Y axis: X->U Z->V
|
||||||
|
if projection_axis == 'X':
|
||||||
|
uv_layer[loop_index].uv[0] = vecList[index].co[1] * real_scale
|
||||||
|
uv_layer[loop_index].uv[1] = vecList[index].co[2] * real_scale
|
||||||
|
elif projection_axis == 'Y':
|
||||||
|
uv_layer[loop_index].uv[0] = vecList[index].co[0] * real_scale
|
||||||
|
uv_layer[loop_index].uv[1] = vecList[index].co[2] * real_scale
|
||||||
|
elif projection_axis == 'Z':
|
||||||
|
uv_layer[loop_index].uv[0] = vecList[index].co[0] * real_scale
|
||||||
|
uv_layer[loop_index].uv[1] = vecList[index].co[1] * real_scale
|
||||||
|
|
||||||
|
if len(ignoredObj) != 0:
|
||||||
|
utils.ShowMessageBox(("Following objects are not processed due to they are not suit for this function now: ", ) + tuple(ignoredObj), "Execution result", 'INFO')
|
@ -1,10 +1,10 @@
|
|||||||
import bpy,mathutils
|
import bpy,mathutils
|
||||||
from . import utils
|
from . import utils
|
||||||
|
|
||||||
class SuperAlignOperator(bpy.types.Operator):
|
class BALLANCE_OT_super_align(bpy.types.Operator):
|
||||||
"""Align object with 3ds Max way"""
|
"""Align object with 3ds Max way"""
|
||||||
bl_idname = "ballance.super_align"
|
bl_idname = "ballance.super_align"
|
||||||
bl_label = "Super Align"
|
bl_label = "3ds Max Align"
|
||||||
bl_options = {'UNDO'}
|
bl_options = {'UNDO'}
|
||||||
|
|
||||||
align_x: bpy.props.BoolProperty(name="X position")
|
align_x: bpy.props.BoolProperty(name="X position")
|
||||||
@ -12,7 +12,7 @@ class SuperAlignOperator(bpy.types.Operator):
|
|||||||
align_z: bpy.props.BoolProperty(name="Z position")
|
align_z: bpy.props.BoolProperty(name="Z position")
|
||||||
|
|
||||||
current_references: bpy.props.EnumProperty(
|
current_references: bpy.props.EnumProperty(
|
||||||
name="Current",
|
name="Reference",
|
||||||
items=(('MIN', "Min", ""),
|
items=(('MIN', "Min", ""),
|
||||||
('CENTER', "Center (bound box)", ""),
|
('CENTER', "Center (bound box)", ""),
|
||||||
('POINT', "Center (axis)", ""),
|
('POINT', "Center (axis)", ""),
|
@ -1,8 +1,21 @@
|
|||||||
import bpy
|
import bpy
|
||||||
|
from bpy_extras.io_utils import unpack_list
|
||||||
|
|
||||||
def ShowMessageBox(message = "", title = "Message Box", icon = 'INFO'):
|
def ShowMessageBox(message, title, icon):
|
||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
self.layout.label(text=message)
|
layout = self.layout
|
||||||
|
for item in message:
|
||||||
|
layout.label(text=item, translate=False)
|
||||||
|
|
||||||
bpy.context.window_manager.popup_menu(draw, title = title, icon = icon)
|
bpy.context.window_manager.popup_menu(draw, title = title, icon = icon)
|
||||||
|
|
||||||
|
def AddSceneAndMove2Cursor(obj):
|
||||||
|
Move2Cursor(obj)
|
||||||
|
|
||||||
|
view_layer = bpy.context.view_layer
|
||||||
|
collection = view_layer.active_layer_collection.collection
|
||||||
|
collection.objects.link(obj)
|
||||||
|
|
||||||
|
def Move2Cursor(obj):
|
||||||
|
obj.location = bpy.context.scene.cursor.location
|
||||||
|