18 Commits

Author SHA1 Message Date
314284ed94 [feat] finish wide floor and sink platform auto gen.
- add height re-calc in derived block generation for fixing height issue when re-use Flat in sink platform.
- finish all wide floor prototype.
- finish all platform prototype.
2022-12-29 11:28:50 +08:00
0a815f04d6 [feat] change BMERevenge model read method
- divided json into small pieces for easy management.
- change json reading method for change (1)
2022-12-28 21:40:51 +08:00
2bd031784a [feat] update BMERevenge generation method
- use some laggy AST code to update BMERevenge generation method.
- after this change, BMERevenge become more programable and easy to create more complex models
- ready for wide floor generation development
2022-12-27 22:08:35 +08:00
d5cb8eb1ec make Virtools material can be optional 2022-11-20 12:02:15 +08:00
02c11ffe5a [feat] add more virtools group related operators
- support select/filter by virtools group.
- support group/ungroup/clear in object context menu.
- improve virtools group visit functions
2022-11-19 14:30:31 +08:00
240d5612df add new type rail uv: TT_ReflectionMapping 2022-08-29 10:52:34 +08:00
84dd5b76f1 add feature, optimize function argv passing.
- add support of 4 alpha fields intorduced in BM spec recently.
- optimize material creation functions argv passing strategy.
- change related func calls of (2).
- optimize material parameter pick code to reduce useless check.
2022-07-23 17:22:44 +08:00
2950857e3d do some improvement work
- split blender material creation function.
- allow user to apply virtools material to blender nodes in operator.
- add quick group picker in add virtools group operator.
2022-04-12 15:40:06 +08:00
dde95c3e4f finish vt_mtl and vt_group Panel system 2022-04-09 14:41:32 +08:00
4701164a6c add panel code basically. preparing for future dev 2022-04-08 21:50:31 +08:00
6c875d23ae add console output for rename system 2022-04-06 14:18:45 +08:00
3c36b8b9db finish rename system debug 2022-04-05 21:16:53 +08:00
c943264d05 finish rename system, without debug. 2022-04-05 20:45:00 +08:00
ca459c6185 add half code of rename system 2022-04-05 15:55:42 +08:00
1bfae63fe3 preparing rename system development 2022-04-04 22:04:11 +08:00
cb9609ac2c fix various bugs after refactor 2022-04-04 12:45:41 +08:00
c40f956771 fix loading and bm import/export error 2022-04-04 11:30:04 +08:00
e264c85a04 refactor project. preparing v3.0 development. no debug current 2022-04-03 22:48:12 +08:00
49 changed files with 7029 additions and 4359 deletions

6
.gitattributes vendored
View File

@ -1,2 +1,6 @@
# our generated mesh should be save as binary
*.bin binary
*.bin binary
# json is data and not good for human reading(althought I edit it on my own hand.)
# so set it as binary
ballance_blender_plugin/json/basic_blocks/*.json binary
ballance_blender_plugin/json/derived_blocks/*.json binary

View File

@ -50,7 +50,7 @@ In the dialog, you can select the material to be used. You can also choose the u
You can also select the projection axis for better UV distribution.
### Flatten UV
#### 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.

View File

@ -50,7 +50,7 @@ Ballance 3D是一套简单的用于制图3D相关的轻型工具集合可以
还可以选择投影轴以获取更好的UV分布。
### Flatten UV
#### Flatten UV
在物体编辑模式下用于将当前选中面按某一边贴附到V轴上的模式展开到UV上。注意只支持凸边面。

View File

@ -0,0 +1,343 @@
import bpy,bmesh,bpy_extras,mathutils
import pathlib,zipfile,time,os,tempfile,math
import struct, shutil
from bpy_extras import io_utils, node_shader_utils
from . import UTILS_constants, UTILS_functions, UTILS_file_io, UTILS_zip_helper, UTILS_virtools_prop
class BALLANCE_OT_export_bm(bpy.types.Operator, bpy_extras.io_utils.ExportHelper):
"""Save a Ballance Map File (BM file spec 1.4)"""
bl_idname = "ballance.export_bm"
bl_label = 'Export BM'
bl_options = {'PRESET'}
# ExportHelper mixin class uses this
filename_ext = ".bmx"
filter_glob: bpy.props.StringProperty(
default="*.bmx",
options={'HIDDEN'},
maxlen=255, # Max internal buffer length, longer would be clamped.
)
export_mode: bpy.props.EnumProperty(
name="Export mode",
items=(('COLLECTION', "Collection", "Export a collection"),
('OBJECT', "Objects", "Export an objects"),
),
)
def execute(self, context):
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_functions.show_message_box(("No specific target", ), "Lost parameter", 'ERROR')
else:
prefs = bpy.context.preferences.addons[__package__].preferences
if self.export_mode == 'COLLECTION':
export_bm(context, self.filepath,
prefs.no_component_collection,
self.export_mode, context.scene.BallanceBlenderPluginProperty.collection_picker)
elif self.export_mode == 'OBJECT':
export_bm(context, self.filepath,
prefs.no_component_collection,
self.export_mode, context.scene.BallanceBlenderPluginProperty.object_picker)
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")
def export_bm(context, bmx_filepath, prefs_fncg, opts_exportMode, opts_exportTarget):
# ============================================ alloc a temp folder
utils_tempFolderObj = tempfile.TemporaryDirectory()
utils_tempFolder = utils_tempFolderObj.name
utils_tempTextureFolder = os.path.join(utils_tempFolder, "Texture")
os.makedirs(utils_tempTextureFolder)
# ============================================
# find export target.
# do not need check them validation in there.
# just collect them.
if opts_exportMode== "COLLECTION":
objectList = opts_exportTarget.objects
else:
objectList = [opts_exportTarget, ]
# try get fncg collection
# fncg stands with forced non-component group
try:
object_fncgCollection = bpy.data.collections[prefs_fncg]
except:
object_fncgCollection = None
# ============================================ export
with open(os.path.join(utils_tempFolder, "index.bm"), "wb") as finfo:
UTILS_file_io.write_uint32(finfo, UTILS_constants.bmfile_currentVersion)
# ====================== export object
meshSet = set()
meshList = []
meshCount = 0
with open(os.path.join(utils_tempFolder, "object.bm"), "wb") as fobject:
for obj in objectList:
# only export mesh object
if obj.type != 'MESH':
continue
# clean no mesh object
object_blenderMesh = obj.data
if object_blenderMesh is None:
continue
# check component
if (object_fncgCollection is not None) and (obj.name in object_fncgCollection.objects):
# it should be set as normal object forcely
object_isComponent = False
else:
# check isComponent normally
object_isComponent = UTILS_functions.is_component(obj.name)
# triangle first and then group
if not object_isComponent:
if object_blenderMesh not in meshSet:
_mesh_triangulate(object_blenderMesh)
meshSet.add(object_blenderMesh)
meshList.append(object_blenderMesh)
object_meshIndex = meshCount
meshCount += 1
else:
object_meshIndex = meshList.index(object_blenderMesh)
else:
object_meshIndex = UTILS_functions.get_component_id(obj.name)
# get visibility
object_isHidden = not obj.visible_get()
# try get grouping data
object_groupList = UTILS_virtools_prop.get_virtools_group_data(obj)
# =======================
# write to files
# write finfo first
UTILS_file_io.write_string(finfo, obj.name)
UTILS_file_io.write_uint8(finfo, UTILS_constants.BmfileInfoType.OBJECT)
UTILS_file_io.write_uint64(finfo, fobject.tell())
# write fobject
UTILS_file_io.write_bool(fobject, object_isComponent)
UTILS_file_io.write_bool(fobject, object_isHidden)
UTILS_file_io.write_world_matrix(fobject, obj.matrix_world)
UTILS_file_io.write_uint32(fobject, len(object_groupList))
for item in object_groupList:
UTILS_file_io.write_string(fobject, item)
UTILS_file_io.write_uint32(fobject, object_meshIndex)
# ====================== export mesh
materialSet = set()
materialList = []
with open(os.path.join(utils_tempFolder, "mesh.bm"), "wb") as fmesh:
for mesh in meshList:
# split normals
mesh.calc_normals_split()
# write finfo first
UTILS_file_io.write_string(finfo, mesh.name)
UTILS_file_io.write_uint8(finfo, UTILS_constants.BmfileInfoType.MESH)
UTILS_file_io.write_uint64(finfo, fmesh.tell())
# write fmesh
# vertices
mesh_vecList = mesh.vertices[:]
UTILS_file_io.write_uint32(fmesh, len(mesh_vecList))
for vec in mesh_vecList:
#swap yz
UTILS_file_io.write_3vector(fmesh,vec.co[0],vec.co[2],vec.co[1])
# uv
mesh_faceIndexPairs = [(face, index) for index, face in enumerate(mesh.polygons)]
UTILS_file_io.write_uint32(fmesh, len(mesh_faceIndexPairs) * 3)
if mesh.uv_layers.active is not None:
uv_layer = mesh.uv_layers.active.data[:]
for f, f_index in mesh_faceIndexPairs:
# it should be triangle face, otherwise throw a error
if (f.loop_total != 3):
raise Exception("Not a triangle", f.poly.loop_total)
for loop_index in range(f.loop_start, f.loop_start + f.loop_total):
uv = uv_layer[loop_index].uv
# reverse v
UTILS_file_io.write_2vector(fmesh, uv[0], -uv[1])
else:
# no uv data. write garbage
for i in range(len(mesh_faceIndexPairs) * 3):
UTILS_file_io.write_2vector(fmesh, 0.0, 0.0)
# normals
UTILS_file_io.write_uint32(fmesh, len(mesh_faceIndexPairs) * 3)
for f, f_index in mesh_faceIndexPairs:
# no need to check triangle again
for loop_index in range(f.loop_start, f.loop_start + f.loop_total):
nml = mesh.loops[loop_index].normal
# swap yz
UTILS_file_io.write_3vector(fmesh, nml[0], nml[2], nml[1])
# face
# get material first
mesh_usedBlenderMtl = mesh.materials[:]
mesh_noMaterial = len(mesh_usedBlenderMtl) == 0
for mat in mesh_usedBlenderMtl:
if mat not in materialSet:
materialSet.add(mat)
materialList.append(mat)
UTILS_file_io.write_uint32(fmesh, len(mesh_faceIndexPairs))
mesh_vtIndex = []
mesh_vnIndex = []
mesh_vIndex = []
for f, f_index in mesh_faceIndexPairs:
# confirm material use
if mesh_noMaterial:
mesh_materialIndex = 0
else:
mesh_materialIndex = materialList.index(mesh_usedBlenderMtl[f.material_index])
# export face
mesh_vtIndex.clear()
mesh_vnIndex.clear()
mesh_vIndex.clear()
counter = 0
for loop_index in range(f.loop_start, f.loop_start + f.loop_total):
mesh_vIndex.append(mesh.loops[loop_index].vertex_index)
mesh_vnIndex.append(f_index * 3 + counter)
mesh_vtIndex.append(f_index * 3 + counter)
counter += 1
# reverse vertices sort
UTILS_file_io.write_face(fmesh,
mesh_vIndex[2], mesh_vtIndex[2], mesh_vnIndex[2],
mesh_vIndex[1], mesh_vtIndex[1], mesh_vnIndex[1],
mesh_vIndex[0], mesh_vtIndex[0], mesh_vnIndex[0])
# set used material
UTILS_file_io.write_bool(fmesh, not mesh_noMaterial)
UTILS_file_io.write_uint32(fmesh, mesh_materialIndex)
# free splited normals
mesh.free_normals_split()
# ====================== export material
textureSet = set()
textureList = []
textureCount = 0
with open(os.path.join(utils_tempFolder, "material.bm"), "wb") as fmaterial:
for material in materialList:
# write finfo first
UTILS_file_io.write_string(finfo, material.name)
UTILS_file_io.write_uint8(finfo, UTILS_constants.BmfileInfoType.MATERIAL)
UTILS_file_io.write_uint64(finfo, fmaterial.tell())
# try get original written data
(material_enableVirtoolsMat,
material_colAmbient, material_colDiffuse, material_colSpecular, material_colEmissive, material_specularPower,
material_alphaTest, material_alphaBlend, material_zBuffer, material_twoSided,
material_texture) = UTILS_virtools_prop.get_virtools_material_data(material)
# only try get from Principled BSDF when we couldn't get from virtools_material props
if not material_enableVirtoolsMat:
v = UTILS_functions.parse_material_nodes(material)
if v is not None:
(material_enableVirtoolsMat,
material_colAmbient, material_colDiffuse, material_colSpecular, material_colEmissive, material_specularPower,
material_alphaTest, material_alphaBlend, material_zBuffer, material_twoSided,
material_texture) = v
# check texture index
if material_texture is None:
material_useTexture = False
material_textureIndex = 0
else:
# add into texture list
if material_texture not in textureSet:
textureSet.add(material_texture)
textureList.append(material_texture)
textureIndex = textureCount
textureCount += 1
else:
textureIndex = textureList.index(material_texture)
material_useTexture = True
material_textureIndex = textureIndex
UTILS_file_io.write_color(fmaterial, material_colAmbient)
UTILS_file_io.write_color(fmaterial, material_colDiffuse)
UTILS_file_io.write_color(fmaterial, material_colSpecular)
UTILS_file_io.write_color(fmaterial, material_colEmissive)
UTILS_file_io.write_float(fmaterial, material_specularPower)
UTILS_file_io.write_bool(fmaterial, material_alphaTest)
UTILS_file_io.write_bool(fmaterial, material_alphaBlend)
UTILS_file_io.write_bool(fmaterial, material_zBuffer)
UTILS_file_io.write_bool(fmaterial, material_twoSided)
UTILS_file_io.write_bool(fmaterial, material_useTexture)
UTILS_file_io.write_uint32(fmaterial, material_textureIndex)
# ====================== export texture
texture_blenderFilePath = os.path.dirname(bpy.data.filepath)
texture_existedTextureFilepath = set()
with open(os.path.join(utils_tempFolder, "texture.bm"), "wb") as ftexture:
for texture in textureList:
# write finfo first
UTILS_file_io.write_string(finfo, texture.name)
UTILS_file_io.write_uint8(finfo, UTILS_constants.BmfileInfoType.TEXTURE)
UTILS_file_io.write_uint64(finfo, ftexture.tell())
# confirm whether it is internal texture
# get absolute texture path
texture_filepath = io_utils.path_reference(texture.filepath, texture_blenderFilePath, utils_tempTextureFolder,
'ABSOLUTE', "", None, texture.library)
# get file name and write it
texture_filename = os.path.basename(texture_filepath)
UTILS_file_io.write_string(ftexture, texture_filename)
if (_is_external_texture(texture_filename)):
# write directly, use Ballance texture
UTILS_file_io.write_bool(ftexture, True)
else:
# copy internal texture, if this file is copied, do not copy it again
UTILS_file_io.write_bool(ftexture, False)
if texture_filename not in texture_existedTextureFilepath:
shutil.copy(texture_filepath, os.path.join(utils_tempTextureFolder, texture_filename))
texture_existedTextureFilepath.add(texture_filename)
# ============================================
# save zip and clean up folder
UTILS_zip_helper.compress(utils_tempFolder, bmx_filepath)
utils_tempFolderObj.cleanup()
# ==========================================
# blender related functions
def _is_external_texture(name):
if name in UTILS_constants.bmfile_externalTextureSet:
return True
else:
return False
def _mesh_triangulate(me):
bm = bmesh.new()
bm.from_mesh(me)
bmesh.ops.triangulate(bm, faces=bm.faces)
bm.to_mesh(me)
bm.free()
def _set_value_when_none(obj, newValue):
if obj is None:
return newValue
else:
return obj

View File

@ -0,0 +1,372 @@
import bpy,bmesh,bpy_extras,mathutils
import pathlib,zipfile,time,os,tempfile,math
import struct, shutil
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_constants, UTILS_functions, UTILS_file_io, UTILS_zip_helper, UTILS_virtools_prop
class BALLANCE_OT_import_bm(bpy.types.Operator, bpy_extras.io_utils.ImportHelper):
"""Load a Ballance Map File (BM file spec 1.4)"""
bl_idname = "ballance.import_bm"
bl_label = "Import BM "
bl_options = {'PRESET', 'UNDO'}
# ImportHelper mixin class uses this
filename_ext = ".bmx"
filter_glob: bpy.props.StringProperty(
default="*.bmx",
options={'HIDDEN'},
maxlen=255, # Max internal buffer length, longer would be clamped.
)
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
def poll(self, context):
prefs = bpy.context.preferences.addons[__package__].preferences
return (os.path.isdir(prefs.temp_texture_folder) and os.path.isdir(prefs.external_folder))
def execute(self, context):
prefs = bpy.context.preferences.addons[__package__].preferences
import_bm(context, self.filepath,
prefs.no_component_collection, 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'}
def import_bm(context, bmx_filepath, prefs_fncg, prefs_externalTexture, prefs_tempTextureFolder, opts_texture, opts_material, opts_mesh, opts_object):
# ============================================
# alloc a temp folder for decompress
utils_tempFolderObj = tempfile.TemporaryDirectory()
utils_tempFolder = utils_tempFolderObj.name
utils_tempTextureFolder = os.path.join(utils_tempFolder, "Texture")
# decompress
UTILS_zip_helper.decompress(utils_tempFolder, bmx_filepath)
# ============================================
# read bmx file officially
# index.bm
objectList = []
meshList = []
materialList = []
textureList = []
with open(os.path.join(utils_tempFolder, "index.bm"), "rb") as findex:
# check version first
index_gottenVersion = UTILS_file_io.read_uint32(findex)
if (index_gottenVersion != UTILS_constants.bmfile_currentVersion):
# clean temp folder, output error
UTILS_functions.show_message_box(
("Unsupported BM spec. Expect: {} Gotten: {}".format(UTILS_constants.bmfile_currentVersion, index_gottenVersion), ),
"Unsupported BM spec", 'ERROR')
findex.close()
utils_tempFolderObj.cleanup()
return
# collect block header data
while len(UTILS_file_io.peek_stream(findex)) != 0:
# read
index_name = UTILS_file_io.read_string(findex)
index_type = UTILS_file_io.read_uint8(findex)
index_offset = UTILS_file_io.read_uint64(findex)
index_blockCache = _InfoBlockHelper(index_name, index_offset)
# grouping into list
if index_type == UTILS_constants.BmfileInfoType.OBJECT:
objectList.append(index_blockCache)
elif index_type == UTILS_constants.BmfileInfoType.MESH:
meshList.append(index_blockCache)
elif index_type == UTILS_constants.BmfileInfoType.MATERIAL:
materialList.append(index_blockCache)
elif index_type == UTILS_constants.BmfileInfoType.TEXTURE:
textureList.append(index_blockCache)
else:
pass
# texture.bm
with open(os.path.join(utils_tempFolder, "texture.bm"), "rb") as ftexture:
for item in textureList:
# seek to block
ftexture.seek(item.offset, os.SEEK_SET)
# read data
texture_filename = UTILS_file_io.read_string(ftexture)
texture_isExternal = UTILS_file_io.read_bool(ftexture)
if texture_isExternal:
(texture_target, skip_init) = UTILS_functions.create_instance_with_option(
UTILS_constants.BmfileInfoType.TEXTURE, item.name, opts_texture,
extra_texture_filename= texture_filename, extra_texture_path= prefs_externalTexture)
else:
# not external. copy temp file into blender temp. then use it.
# try copy. if fail, don't need to do more
try:
shutil.copy(os.path.join(utils_tempTextureFolder, texture_filename),
os.path.join(prefs_tempTextureFolder, texture_filename))
except:
pass
(texture_target, skip_init) = UTILS_functions.create_instance_with_option(
UTILS_constants.BmfileInfoType.TEXTURE, item.name, opts_texture,
extra_texture_filename= texture_filename, extra_texture_path= prefs_tempTextureFolder)
# setup name and blender data for header
item.blender_data = texture_target
# material.bm
# WARNING: this code is shared with add_floor - create_or_get_material()
with open(os.path.join(utils_tempFolder, "material.bm"), "rb") as fmaterial:
for item in materialList:
# seek to block
fmaterial.seek(item.offset, os.SEEK_SET)
# read data
material_colAmbient = UTILS_file_io.read_3vector(fmaterial)
material_colDiffuse = UTILS_file_io.read_3vector(fmaterial)
material_colSpecular = UTILS_file_io.read_3vector(fmaterial)
material_colEmissive = UTILS_file_io.read_3vector(fmaterial)
material_specularPower = UTILS_file_io.read_float(fmaterial)
material_alphaTest = UTILS_file_io.read_bool(fmaterial)
material_alphaBlend = UTILS_file_io.read_bool(fmaterial)
material_zBuffer = UTILS_file_io.read_bool(fmaterial)
material_twoSided = UTILS_file_io.read_bool(fmaterial)
material_useTexture = UTILS_file_io.read_bool(fmaterial)
material_texture = UTILS_file_io.read_uint32(fmaterial)
# alloc basic material
(material_target, skip_init) = UTILS_functions.create_instance_with_option(
UTILS_constants.BmfileInfoType.MATERIAL, item.name, opts_material)
item.blender_data = material_target
if skip_init:
continue
# try create material nodes
UTILS_functions.create_blender_material(material_target,
(True,
material_colAmbient, material_colDiffuse, material_colSpecular, material_colEmissive, material_specularPower,
material_alphaTest, material_alphaBlend, material_zBuffer, material_twoSided,
textureList[material_texture].blender_data if material_useTexture else None)
)
# mesh.bm
# WARNING: this code is shared with add_floor
with open(os.path.join(utils_tempFolder, "mesh.bm"), "rb") as fmesh:
mesh_vList=[]
mesh_vtList=[]
mesh_vnList=[]
mesh_faceList=[]
mesh_materialSolt = []
for item in meshList:
fmesh.seek(item.offset, os.SEEK_SET)
# create real mesh
(mesh_target, skip_init) = UTILS_functions.create_instance_with_option(
UTILS_constants.BmfileInfoType.MESH, item.name, opts_mesh)
item.blender_data = mesh_target
if skip_init:
continue
mesh_vList.clear()
mesh_vtList.clear()
mesh_vnList.clear()
mesh_faceList.clear()
mesh_materialSolt.clear()
# in first read, store all data into list
mesh_listCount = UTILS_file_io.read_uint32(fmesh)
for i in range(mesh_listCount):
cache = UTILS_file_io.read_3vector(fmesh)
# switch yz
mesh_vList.append((cache[0], cache[2], cache[1]))
mesh_listCount = UTILS_file_io.read_uint32(fmesh)
for i in range(mesh_listCount):
cache = UTILS_file_io.read_2vector(fmesh)
# reverse v
mesh_vtList.append((cache[0], -cache[1]))
mesh_listCount = UTILS_file_io.read_uint32(fmesh)
for i in range(mesh_listCount):
cache = UTILS_file_io.read_3vector(fmesh)
# switch yz
mesh_vnList.append((cache[0], cache[2], cache[1]))
mesh_listCount = UTILS_file_io.read_uint32(fmesh)
for i in range(mesh_listCount):
mesh_faceData = UTILS_file_io.read_face(fmesh)
mesh_useMaterial = UTILS_file_io.read_bool(fmesh)
mesh_materialIndex = UTILS_file_io.read_uint32(fmesh)
if mesh_useMaterial:
mesh_neededMaterial = materialList[mesh_materialIndex].blender_data
if mesh_neededMaterial in mesh_materialSolt:
mesh_blenderMtlIndex = mesh_materialSolt.index(mesh_neededMaterial)
else:
mesh_blenderMtlIndex = len(mesh_materialSolt)
mesh_materialSolt.append(mesh_neededMaterial)
else:
mesh_blenderMtlIndex = -1
# we need invert triangle sort
mesh_faceList.append((
mesh_faceData[6], mesh_faceData[7], mesh_faceData[8],
mesh_faceData[3], mesh_faceData[4], mesh_faceData[5],
mesh_faceData[0], mesh_faceData[1], mesh_faceData[2],
mesh_blenderMtlIndex
))
# and then we need add material solt for this mesh
for mat in mesh_materialSolt:
mesh_target.materials.append(mat)
# then, we need add correspond count for vertices
mesh_target.vertices.add(len(mesh_vList))
mesh_target.loops.add(len(mesh_faceList)*3) # triangle face confirm
mesh_target.polygons.add(len(mesh_faceList))
mesh_target.uv_layers.new(do_init=False)
mesh_target.create_normals_split()
# add vertices data
mesh_target.vertices.foreach_set("co", unpack_list(mesh_vList))
mesh_target.loops.foreach_set("vertex_index", unpack_list(_flat_vertices_index(mesh_faceList)))
mesh_target.loops.foreach_set("normal", unpack_list(_flat_vertices_normal(mesh_faceList, mesh_vnList)))
mesh_target.uv_layers[0].data.foreach_set("uv", unpack_list(_flat_vertices_uv(mesh_faceList, mesh_vtList)))
for i in range(len(mesh_faceList)):
mesh_target.polygons[i].loop_start = i * 3
mesh_target.polygons[i].loop_total = 3
if mesh_faceList[i][9] != -1:
mesh_target.polygons[i].material_index = mesh_faceList[i][9]
mesh_target.polygons[i].use_smooth = True
mesh_target.validate(clean_customdata=False)
mesh_target.update(calc_edges=False, calc_edges_loose=False)
# object
with open(os.path.join(utils_tempFolder, "object.bm"), "rb") as fobject:
# we need get needed collection first
blender_viewLayer = context.view_layer
blender_collection = blender_viewLayer.active_layer_collection.collection
if prefs_fncg == "":
# fncg stands with Forced Non-Component Group
object_fncgCollection = None
else:
try:
# try get collection
object_fncgCollection = bpy.data.collections[prefs_fncg]
except:
# fail to get, create new one under active collection instead
object_fncgCollection = bpy.data.collections.new(prefs_fncg)
blender_collection.children.link(object_fncgCollection)
# start process it
object_groupList = []
for item in objectList:
fobject.seek(item.offset, os.SEEK_SET)
# read data
object_isComponent = UTILS_file_io.read_bool(fobject)
#object_isForcedNoComponent = UTILS_file_io.read_bool(fobject)
object_isHidden = UTILS_file_io.read_bool(fobject)
object_worldMatrix = UTILS_file_io.read_world_materix(fobject)
object_groupListCount = UTILS_file_io.read_uint32(fobject)
object_groupList.clear()
for i in range(object_groupListCount):
object_groupList.append(UTILS_file_io.read_string(fobject))
object_meshIndex = UTILS_file_io.read_uint32(fobject)
# got mesh first
if object_isComponent:
object_neededMesh = UTILS_functions.load_component(object_meshIndex)
else:
object_neededMesh = meshList[object_meshIndex].blender_data
# create real object
(object_target, skip_init) = UTILS_functions.create_instance_with_option(
UTILS_constants.BmfileInfoType.OBJECT, item.name, opts_object,
extra_mesh=object_neededMesh)
if skip_init:
continue
# link to correct collection
if (object_fncgCollection is not None) and (not object_isComponent) and UTILS_functions.is_component(item.name):
# a object should be grouped into fncg should check following requirements
# fncg is not null
# this object is a normal object
# but its name match component format
object_fncgCollection.objects.link(object_target)
else:
# otherwise, group it into normal collection
blender_collection.objects.link(object_target)
object_target.matrix_world = object_worldMatrix
object_target.hide_set(object_isHidden)
# write custom property
if len(object_groupList) != 0:
UTILS_virtools_prop.fill_virtools_group_data(object_target, tuple(object_groupList))
else:
UTILS_virtools_prop.fill_virtools_group_data(object_target, None)
# update view layer after all objects has been imported
blender_viewLayer.update()
# release temp folder
utils_tempFolderObj.cleanup()
# ==========================================
# blender related functions
class _InfoBlockHelper():
def __init__(self, name, offset):
self.name = name
self.offset = offset
self.blender_data = None
def _flat_vertices_index(faceList):
for item in faceList:
yield (item[0], )
yield (item[3], )
yield (item[6], )
def _flat_vertices_normal(faceList, vnList):
for item in faceList:
yield vnList[item[2]]
yield vnList[item[5]]
yield vnList[item[8]]
def _flat_vertices_uv(faceList, vtList):
for item in faceList:
yield vtList[item[1]]
yield vtList[item[4]]
yield vtList[item[7]]

View File

@ -1,8 +1,8 @@
import bpy,mathutils
from . import utils
import bpy, mathutils
from . import UTILS_functions
class BALLANCE_OT_super_align(bpy.types.Operator):
"""Align object with 3ds Max way"""
"""Align object with 3ds Max style"""
bl_idname = "ballance.super_align"
bl_label = "3ds Max Align"
bl_options = {'UNDO'}
@ -12,7 +12,7 @@ class BALLANCE_OT_super_align(bpy.types.Operator):
align_z: bpy.props.BoolProperty(name="Z position")
current_references: bpy.props.EnumProperty(
name="Reference",
name="Reference (Active Object)",
items=(('MIN', "Min", ""),
('CENTER', "Center (bound box)", ""),
('POINT', "Center (axis)", ""),
@ -21,7 +21,7 @@ class BALLANCE_OT_super_align(bpy.types.Operator):
)
target_references: bpy.props.EnumProperty(
name="Target",
name="Target (Other Objects)",
items=(('MIN', "Min", ""),
('CENTER', "Center (bound box)", ""),
('POINT', "Center (axis)", ""),
@ -31,10 +31,10 @@ class BALLANCE_OT_super_align(bpy.types.Operator):
@classmethod
def poll(self, context):
return check_align_target()
return _check_align_target()
def execute(self, context):
align_object(self.align_x, self.align_y, self.align_z, self.current_references, self.target_references)
_align_object(self.align_x, self.align_y, self.align_z, self.current_references, self.target_references)
return {'FINISHED'}
def invoke(self, context, event):
@ -56,7 +56,7 @@ class BALLANCE_OT_super_align(bpy.types.Operator):
# ============================== method
def check_align_target():
def _check_align_target():
if bpy.context.active_object is None:
return False
@ -69,14 +69,14 @@ def check_align_target():
return True
def align_object(use_x, use_y, use_z, currentMode, targetMode):
def _align_object(use_x, use_y, use_z, currentMode, targetMode):
if not (use_x or use_y or use_z):
return
# calc active object data
currentObj = bpy.context.active_object
currentObjBbox = [currentObj.matrix_world @ mathutils.Vector(corner) for corner in currentObj.bound_box]
currentObjRef = provideObjRefPoint(currentObj, currentObjBbox, currentMode)
currentObjRef = _provide_obj_reference_point(currentObj, currentObjBbox, currentMode)
# calc target
targetObjList = bpy.context.selected_objects[:]
@ -86,7 +86,7 @@ def align_object(use_x, use_y, use_z, currentMode, targetMode):
# process each obj
for targetObj in targetObjList:
targetObjBbox = [targetObj.matrix_world @ mathutils.Vector(corner) for corner in targetObj.bound_box]
targetObjRef = provideObjRefPoint(targetObj, targetObjBbox, targetMode)
targetObjRef = _provide_obj_reference_point(targetObj, targetObjBbox, targetMode)
if use_x:
targetObj.location.x += currentObjRef.x - targetObjRef.x
@ -95,7 +95,7 @@ def align_object(use_x, use_y, use_z, currentMode, targetMode):
if use_z:
targetObj.location.z += currentObjRef.z - targetObjRef.z
def provideObjRefPoint(obj, vecList, mode):
def _provide_obj_reference_point(obj, vecList, mode):
refPoint = mathutils.Vector((0, 0, 0))
if (mode == 'MIN'):

View File

@ -1,6 +1,6 @@
import bpy,mathutils
import bmesh
from . import utils
from . import UTILS_functions
class BALLANCE_OT_flatten_uv(bpy.types.Operator):
"""Flatten selected face UV. Only works for convex face"""
@ -9,7 +9,7 @@ class BALLANCE_OT_flatten_uv(bpy.types.Operator):
bl_options = {'UNDO'}
reference_edge : bpy.props.IntProperty(
name="Reference_edge",
name="Reference edge",
description="The references edge of UV. It will be placed in V axis.",
min=0,
soft_min=0,
@ -33,16 +33,19 @@ class BALLANCE_OT_flatten_uv(bpy.types.Operator):
return wm.invoke_props_dialog(self)
def execute(self, context):
no_processed_count = real_flatten_uv(bpy.context.active_object.data, self.reference_edge)
no_processed_count = _real_flatten_uv(bpy.context.active_object.data, self.reference_edge)
if no_processed_count != 0:
utils.ShowMessageBox(("{} faces may not be processed correctly because they have problem.".format(no_processed_count), ), "Warning", 'ERROR')
UTILS_functions.show_message_box(
("{} 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):
def _real_flatten_uv(mesh, reference_edge):
no_processed_count = 0
if mesh.uv_layers.active is None:

View File

@ -1,7 +1,7 @@
import bpy,bmesh
import mathutils
import bpy.types
from . import utils, preferences
from . import UTILS_functions
class BALLANCE_OT_rail_uv(bpy.types.Operator):
"""Create a UV for rail"""
@ -15,7 +15,8 @@ class BALLANCE_OT_rail_uv(bpy.types.Operator):
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")
("SCALE", "Scale", "Give a scale number to scale UV"),
("TT", "TT_ReflectionMapping", "The real internal process of Ballance rail")
),
)
@ -38,7 +39,7 @@ class BALLANCE_OT_rail_uv(bpy.types.Operator):
@classmethod
def poll(self, context):
return check_rail_target()
return _check_rail_target()
def invoke(self, context, event):
wm = context.window_manager
@ -46,23 +47,23 @@ class BALLANCE_OT_rail_uv(bpy.types.Operator):
def execute(self, context):
if context.scene.BallanceBlenderPluginProperty.material_picker == None:
utils.ShowMessageBox(("No specific material", ), "Lost parameter", 'ERROR')
UTILS_functions.show_message_box(("No specific material", ), "Lost parameter", 'ERROR')
else:
create_rail_uv(self.uv_type, context.scene.BallanceBlenderPluginProperty.material_picker, self.uv_scale, self.projection_axis)
_create_rail_uv(self.uv_type, context.scene.BallanceBlenderPluginProperty.material_picker, self.uv_scale, self.projection_axis)
return {'FINISHED'}
def draw(self, context):
layout = self.layout
layout.prop(self, "uv_type")
layout.prop(context.scene.BallanceBlenderPluginProperty, "material_picker")
if self.uv_type != 'POINT':
if self.uv_type == 'SCALE' or self.uv_type == 'UNIFORM':
layout.prop(self, "projection_axis")
if self.uv_type == 'SCALE':
layout.prop(self, "uv_scale")
# ====================== method
def check_rail_target():
def _check_rail_target():
for obj in bpy.context.selected_objects:
if obj.type != 'MESH':
continue
@ -71,7 +72,7 @@ def check_rail_target():
return True
return False
def get_distance(iterator):
def _get_distance(iterator):
is_first_min = True
is_first_max = True
max_value = 0.0
@ -93,7 +94,7 @@ def get_distance(iterator):
return max_value - min_value
def create_rail_uv(rail_type, material_pointer, scale_size, projection_axis):
def _create_rail_uv(rail_type, material_pointer, scale_size, projection_axis):
objList = []
ignoredObj = []
for obj in bpy.context.selected_objects:
@ -125,18 +126,18 @@ def create_rail_uv(rail_type, material_pointer, scale_size, projection_axis):
# 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)
_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)
_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)
_get_distance(vec.co[0] for vec in vecList),
_get_distance(vec.co[1] for vec in vecList)
)
real_scale = 1.0 / maxLength
@ -149,7 +150,7 @@ def create_rail_uv(rail_type, material_pointer, scale_size, projection_axis):
# set to 1 point
uv_layer[loop_index].uv[0] = 0
uv_layer[loop_index].uv[1] = 1
else:
elif rail_type == 'SCALE' or rail_type == 'UNIFORM':
# following xy -> uv scale
#
# use Z axis: X->U Y->V
@ -164,6 +165,27 @@ def create_rail_uv(rail_type, material_pointer, scale_size, projection_axis):
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
elif rail_type == 'TT':
(uv_layer[loop_index].uv[0], uv_layer[loop_index].uv[1]) = _tt_reflection_mapping_compute(
vecList[index].co,
mesh.loops[loop_index].normal,
(0.0, 0.0, 0.0)
)
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')
UTILS_functions.show_message_box(
("Following objects are not processed due to they are not suit for this function now: ", ) + tuple(ignoredObj),
"Execution result", 'INFO'
)
def _tt_reflection_mapping_compute(_point, _n, _refobj):
# switch blender coord to virtools coord for convenient calc
point = mathutils.Vector((_point[0], _point[2], _point[1]))
n = mathutils.Vector((_n[0], _n[2], _n[1])).normalized()
refobj = mathutils.Vector((_refobj[0], _refobj[2], _refobj[1]))
p = (refobj - point).normalized()
b=(((2*(p*n))*n)-p).normalized()
# convert back to blender coord
return ((b.x + 1.0) / 2.0, -(b.z + 1.0) / 2.0)

View File

@ -0,0 +1,496 @@
import bpy
from . import UTILS_constants, UTILS_functions, UTILS_virtools_prop
class rename_system_props(bpy.types.Operator):
name_standard: bpy.props.EnumProperty(
name="Name Standard",
description="Choose your name standard",
items=(
("YYC", "YYC Tools Chains", "YYC Tools Chains name standard."),
("IMENGYU", "Imengyu Ballance", "Auto grouping name standard for Imengyu/Ballance")
),
)
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, "name_standard")
class BALLANCE_OT_rename_by_group(rename_system_props):
"""Rename object by Virtools groups"""
bl_idname = "ballance.rename_by_group"
bl_label = "Rename by Group"
bl_options = {'UNDO'}
def execute(self, context):
_rename_core(_NameStandard.CKGROUP, _NameStandard.cvt_std_from_str_to_int(self.name_standard))
return {'FINISHED'}
class BALLANCE_OT_convert_name(rename_system_props):
"""Convert name from one name standard to another one."""
bl_idname = "ballance.convert_name"
bl_label = "Convert Name"
bl_options = {'UNDO'}
dest_name_standard: bpy.props.EnumProperty(
name="Destination Name Standard",
description="Choose your name standard",
items=(
("YYC", "YYC Tools Chains", "YYC Tools Chains name standard."),
("IMENGYU", "Imengyu Ballance", "Auto grouping name standard for Imengyu/Ballance")
),
)
def execute(self, context):
_rename_core(
_NameStandard.cvt_std_from_str_to_int(self.name_standard),
_NameStandard.cvt_std_from_str_to_int(self.dest_name_standard))
return {'FINISHED'}
# rewrite draw func
def draw(self, context):
layout = self.layout
layout.prop(self, "name_standard")
layout.prop(self, "dest_name_standard")
class BALLANCE_OT_auto_grouping(rename_system_props):
"""Auto Grouping object according to specific name standard."""
bl_idname = "ballance.auto_grouping"
bl_label = "Auto Grouping"
bl_options = {'UNDO'}
def execute(self, context):
_rename_core(_NameStandard.cvt_std_from_str_to_int(self.name_standard), _NameStandard.CKGROUP)
return {'FINISHED'}
# ==========================================
# rename misc funcs
class _ObjectBasicType():
COMPONENT = 0
FLOOR = 1
RAIL = 2
WOOD = 3
STOPPER = 4
DEPTH_CUBE = 5
DECORATION = 6
LEVEL_START = 7
LEVEL_END = 8
CHECKPOINT = 9
RESETPOINT = 10
class _NameStandard():
CKGROUP = 0
YYC = 1
IMENGYU = 2
@staticmethod
def cvt_std_from_str_to_int(std_str):
if std_str == "YYC":
return _NameStandard.YYC
elif std_str == "IMENGYU":
return _NameStandard.IMENGYU
else:
raise Exception("Unknow name standard.")
class _NameInfoHelper():
def __init__(self, _basic_type):
self.basic_type = _basic_type
# extra field notes:
# COMPONENT:
# component_type(string)
# sector(int)
# CHECKPOINT, RESETPOINT:
# sector(int)(following Ballance index, checkpoint starts with 1)
def _get_selected_objects():
return bpy.context.view_layer.active_layer_collection.collection.objects
def _get_sector_from_ckgroup(group_set):
# this counter is served for stupid
# multi-sector-grouping accident.
counter = 0
last_matched_sector = ''
for i in group_set:
regex_result = UTILS_constants.rename_regexCKGroupSector.match(i)
if regex_result is not None:
last_matched_sector = regex_result.group(1)
counter += 1
if counter != 1:
return None
else:
return last_matched_sector
# ==========================================
# rename core funcs
# NOTE: the implement of this function are copied from
# BallanceVirtoolsHelper/bvh/features/mapping/grouping.cpp
# ---
# YYC Tools Chains name standard is Ballance-compatible name standard.
# So this functions also serving for `_get_name_info_from_group` function
# to help get sector field from PC/PR elements. In ordinary call(external call)
# The final error output should be outputed nromally. But in the call from
# `_get_name_info_from_group`, this function should not output any error.
# So parameter `call_internal` is served for this work. In common it is False
# to let function output error str normally. But only set it to True in
# the call from `_get_name_info_from_group` to disable error output.
def _get_name_info_from_yyc_name(obj_name, call_internal = False):
# check component first
regex_result = UTILS_constants.rename_regexYYCComponent.match(obj_name)
if regex_result is not None:
data = _NameInfoHelper(_ObjectBasicType.COMPONENT)
data.component_type = regex_result.group(1)
data.sector = int(regex_result.group(2))
return data
# check PC PR elements
regex_result = UTILS_constants.rename_regexYYCPC.match(obj_name)
if regex_result is not None:
data = _NameInfoHelper(_ObjectBasicType.CHECKPOINT)
data.sector = int(regex_result.group(1))
return data
regex_result = UTILS_constants.rename_regexYYCPR.match(obj_name)
if regex_result is not None:
data = _NameInfoHelper(_ObjectBasicType.RESETPOINT)
data.sector = int(regex_result.group(1))
return data
# check other unique elements
if obj_name == "PS_FourFlames_01":
return _NameInfoHelper(_ObjectBasicType.LEVEL_START)
if obj_name == "PE_Balloon_01":
return _NameInfoHelper(_ObjectBasicType.LEVEL_END)
# process floors
if obj_name.startswith("A_Floor"):
return _NameInfoHelper(_ObjectBasicType.FLOOR)
if obj_name.startswith("A_Wood"):
return _NameInfoHelper(_ObjectBasicType.WOOD)
if obj_name.startswith("A_Rail"):
return _NameInfoHelper(_ObjectBasicType.RAIL)
if obj_name.startswith("A_Stopper"):
return _NameInfoHelper(_ObjectBasicType.STOPPER)
# process others
if obj_name.startswith("DepthCubes"):
return _NameInfoHelper(_ObjectBasicType.DEPTH_CUBE)
if obj_name.startswith("D_"):
return _NameInfoHelper(_ObjectBasicType.DECORATION)
# only output in external calling
if not call_internal:
print("[ERROR]\t{}:\tName match lost.".format(obj_name))
return None
def _get_name_info_from_imengyu_name(obj_name):
# check component first
regex_result = UTILS_constants.rename_regexImengyuComponent.match(obj_name)
if regex_result is not None:
data = _NameInfoHelper(_ObjectBasicType.COMPONENT)
data.component_type = regex_result.group(1)
data.sector = int(regex_result.group(2))
return data
# check PC PR elements
regex_result = UTILS_constants.rename_regexImengyuPCRComp.match(obj_name)
if regex_result is not None:
eles_name = regex_result.group(1)
if eles_name == 'PC_CheckPoint':
data = _NameInfoHelper(_ObjectBasicType.CHECKPOINT)
elif eles_name == 'PR_ResetPoint':
data = _NameInfoHelper(_ObjectBasicType.RESETPOINT)
data.sector = int(regex_result.group(2))
return data
# check other unique elements
if obj_name == "PS_LevelStart":
return _NameInfoHelper(_ObjectBasicType.LEVEL_START)
if obj_name == "PE_LevelEnd":
return _NameInfoHelper(_ObjectBasicType.LEVEL_END)
# process floors
if obj_name.startswith("S_Floors"):
return _NameInfoHelper(_ObjectBasicType.FLOOR)
if obj_name.startswith("S_FloorWoods"):
return _NameInfoHelper(_ObjectBasicType.WOOD)
if obj_name.startswith("S_FloorRails"):
return _NameInfoHelper(_ObjectBasicType.RAIL)
if obj_name.startswith("S_FloorStopper"):
return _NameInfoHelper(_ObjectBasicType.STOPPER)
# process others
if obj_name.startswith("DepthTestCubes"):
return _NameInfoHelper(_ObjectBasicType.DEPTH_CUBE)
if obj_name.startswith("O_"):
return _NameInfoHelper(_ObjectBasicType.DECORATION)
print("[ERROR]\t{}:\tName match lost.".format(obj_name))
return None
def _get_name_info_from_group(obj):
group_list = UTILS_virtools_prop.get_virtools_group_data(obj)
if len(group_list) == 0:
# name it as a decoration
return _NameInfoHelper(_ObjectBasicType.DECORATION)
group_set = set(group_list)
# try to filter unique elements first
set_result = UTILS_constants.rename_uniqueComponentsGroupName.intersection(group_set)
if len(set_result) == 1:
# get it
gotten_group_name = (list(set_result))[0]
if gotten_group_name == 'PS_Levelstart':
return _NameInfoHelper(_ObjectBasicType.LEVEL_START)
elif gotten_group_name == 'PE_Levelende':
return _NameInfoHelper(_ObjectBasicType.LEVEL_END)
elif gotten_group_name == 'PC_Checkpoints' or gotten_group_name == 'PR_Resetpoints':
# these type's data should be gotten from its name
# use _get_name_info_from_yyc_name to get it
# _get_name_info_from_yyc_name is Ballance-compatible name standard
data = _get_name_info_from_yyc_name(obj.name, call_internal=True)
if data is None:
print("[ERROR]\t{}:\tPC_Checkpoints or PR_Resetpoints detected. But couldn't get sector from name.".format(obj.name))
return None
if data.basic_type != _ObjectBasicType.CHECKPOINT and data.basic_type != _ObjectBasicType.RESETPOINT:
# check whether it is checkpoint or resetpoint
# if not, it mean that we got error data from name
# return None instead
print("[ERROR]\t{}:\tPC_Checkpoints or PR_Resetpoints detected. But name is illegal.".format(obj.name))
return None
# otherwise return data
return data
else:
print("[ERROR]\t{}:\tThe match of Unique Component lost.".format(obj.name))
return None
elif len(set_result) != 0:
# must be a weird grouping, report it
print("[ERROR]\t{}:\tA Multi-grouping Unique Component.".format(obj.name))
return None
# distinguish normal elements
set_result = UTILS_constants.rename_normalComponentsGroupName.intersection(group_set)
if len(set_result) == 1:
# get it
# now try get its sector
gotten_elements = (tuple(set_result))[0]
gotten_sector = _get_sector_from_ckgroup(group_set)
if gotten_sector is None:
# fail to get sector
print("[ERROR]\t{}:\tComponent detected. But couldn't get sector from CKGroup data.".format(obj.name))
return None
data = _NameInfoHelper(_ObjectBasicType.COMPONENT)
data.component_type = gotten_elements
data.sector = int(gotten_sector)
return data
elif len(set_result) != 0:
# must be a weird grouping, report it
print("[ERROR]\t{}:\tA Multi-grouping Component.".format(obj.name))
return None
# distinguish road
if 'Phys_FloorRails' in group_set:
# rail
return _NameInfoHelper(_ObjectBasicType.RAIL)
elif 'Phys_Floors' in group_set:
# distinguish it between Floor and Wood
floor_result =UTILS_constants.rename_floorGroupTester.intersection(group_set)
rail_result = UTILS_constants.rename_woodGroupTester.intersection(group_set)
if len(floor_result) > 0 and len(rail_result) == 0:
return _NameInfoHelper(_ObjectBasicType.FLOOR)
elif len(floor_result) == 0 and len(rail_result) > 0:
return _NameInfoHelper(_ObjectBasicType.WOOD)
else:
print("[WARNING]\t{}:\tCan't distinguish between Floors and Rails. Suppose it is Floors".format(obj.name))
return _NameInfoHelper(_ObjectBasicType.FLOOR)
elif 'Phys_FloorStopper' in group_set:
return _NameInfoHelper(_ObjectBasicType.STOPPER)
elif 'DepthTestCubes' in group_set:
return _NameInfoHelper(_ObjectBasicType.DEPTH_CUBE)
# no matched
print("[ERROR]\t{}:\tGroup match lost.".format(obj.name))
return None
def _set_for_yyc_name(obj, name_info):
basic_type = name_info.basic_type
if basic_type == _ObjectBasicType.DECORATION:
obj.name = "D_"
elif basic_type == _ObjectBasicType.LEVEL_START:
obj.name = "PS_FourFlames_01"
elif basic_type == _ObjectBasicType.LEVEL_END:
obj.name = "PE_Balloon_01"
elif basic_type == _ObjectBasicType.RESETPOINT:
obj.name = "PR_Resetpoint_{:0>2d}".format(name_info.sector)
elif basic_type == _ObjectBasicType.CHECKPOINT:
obj.name = "PC_TwoFlames_{:0>2d}".format(name_info.sector)
elif basic_type == _ObjectBasicType.DEPTH_CUBE:
obj.name = "DepthCubes_"
elif basic_type == _ObjectBasicType.FLOOR:
obj.name = "A_Floor_"
elif basic_type == _ObjectBasicType.WOOD:
obj.name = "A_Wood_"
elif basic_type == _ObjectBasicType.RAIL:
obj.name = "A_Rail_"
elif basic_type == _ObjectBasicType.STOPPER:
obj.name = "A_Stopper_"
elif basic_type == _ObjectBasicType.COMPONENT:
obj.name = "{}_{:0>2d}_".format(name_info.component_type, name_info.sector)
def _set_for_imengyu_name(obj, name_info):
basic_type = name_info.basic_type
if basic_type == _ObjectBasicType.DECORATION:
obj.name = "O_"
elif basic_type == _ObjectBasicType.LEVEL_START:
obj.name = "PS_LevelStart"
elif basic_type == _ObjectBasicType.LEVEL_END:
obj.name = "PE_LevelEnd"
elif basic_type == _ObjectBasicType.RESETPOINT:
obj.name = "PR_ResetPoint:{:d}".format(name_info.sector)
elif basic_type == _ObjectBasicType.CHECKPOINT:
obj.name = "PC_CheckPoint:{:d}".format(name_info.sector + 1)
elif basic_type == _ObjectBasicType.DEPTH_CUBE:
obj.name = "DepthTestCubes"
elif basic_type == _ObjectBasicType.FLOOR:
obj.name = "S_Floors"
elif basic_type == _ObjectBasicType.WOOD:
obj.name = "S_FloorWoods"
elif basic_type == _ObjectBasicType.RAIL:
obj.name = "S_FloorRails"
elif basic_type == _ObjectBasicType.STOPPER:
obj.name = "S_FloorStopper"
elif basic_type == _ObjectBasicType.COMPONENT:
obj.name = "{}:{}:{:d}".format(name_info.component_type, obj.name.replace(":", "_"), name_info.sector)
# NOTE: the implement of this function are copied from
# BallanceVirtoolsHelper/bvh/features/mapping/grouping.cpp
def _set_for_group(obj, name_info):
gps = []
basic_type = name_info.basic_type
if basic_type == _ObjectBasicType.DECORATION:
# decoration do not need grouping
pass
elif basic_type == _ObjectBasicType.LEVEL_START:
gps.append("PS_Levelstart")
elif basic_type == _ObjectBasicType.LEVEL_END:
gps.append("PE_Levelende")
elif basic_type == _ObjectBasicType.RESETPOINT:
gps.append("PC_Checkpoints")
elif basic_type == _ObjectBasicType.CHECKPOINT:
gps.append("PR_Resetpoints")
elif basic_type == _ObjectBasicType.DEPTH_CUBE:
gps.append("DepthTestCubes")
elif basic_type == _ObjectBasicType.FLOOR:
gps.append("Phys_Floors")
gps.append("Sound_HitID_01")
gps.append("Sound_RollID_01")
gps.append("Shadow")
elif basic_type == _ObjectBasicType.WOOD:
gps.append("Phys_FloorRails")
gps.append("Sound_HitID_03")
gps.append("Sound_RollID_03")
elif basic_type == _ObjectBasicType.RAIL:
gps.append("Phys_Floors")
gps.append("Sound_HitID_02")
gps.append("Sound_RollID_02")
gps.append("Shadow")
elif basic_type == _ObjectBasicType.STOPPER:
gps.append("Phys_FloorStopper")
elif basic_type == _ObjectBasicType.COMPONENT:
gps.append(name_info.component_type)
# set compabitility for 999 sector loader
if (name_info.sector == 9):
gps.append("Sector_9")
else:
gps.append("Sector_{:0>2d}".format(name_info.sector))
# apply to custom property
UTILS_virtools_prop.fill_virtools_group_data(obj, tuple(gps))
# ==========================================
# assemble funcs
def _get_data(obj, standard):
if standard == _NameStandard.YYC:
return _get_name_info_from_yyc_name(obj.name)
elif standard == _NameStandard.IMENGYU:
return _get_name_info_from_imengyu_name(obj.name)
elif standard == _NameStandard.CKGROUP:
return _get_name_info_from_group(obj)
else:
raise Exception("Unknow standard")
def _set_data(obj, name_info, standard):
if standard == _NameStandard.YYC:
return _set_for_yyc_name(obj, name_info)
elif standard == _NameStandard.IMENGYU:
return _set_for_imengyu_name(obj, name_info)
elif standard == _NameStandard.CKGROUP:
return _set_for_group(obj, name_info)
else:
raise Exception("Unknow standard")
def _rename_core(source_std, dest_std):
if source_std == dest_std:
# if source == dest
# we do not to do anything
return
failed_obj_counter = 0
all_obj_counter = 0
print('============')
print('Rename system report')
print('------------')
for obj in _get_selected_objects():
all_obj_counter += 1
info = _get_data(obj, source_std)
if info is None:
failed_obj_counter += 1
continue
_set_data(obj, info, dest_std)
print('------------')
print('All/failed - {}/{}'.format(all_obj_counter, failed_obj_counter))
print('============')
UTILS_functions.show_message_box(
('Rename system report',
'View console to get more detail',
'All: {}'.format(all_obj_counter),
'Failed: {}'.format(failed_obj_counter)),
"Info",
"INFO"
)

View File

@ -0,0 +1,56 @@
import bpy, mathutils
from . import UTILS_constants, UTILS_functions
# ================================================= actual add
class BALLANCE_OT_add_components(bpy.types.Operator):
"""Add sector related elements"""
bl_idname = "ballance.add_components"
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, ""), UTILS_constants.bmfile_componentList)),
)
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 = UTILS_functions.load_component(
UTILS_constants.bmfile_componentList.index(self.elements_type))
obj = bpy.data.objects.new(finalObjectName, loadedMesh)
UTILS_functions.add_into_scene_and_move_to_cursor(obj)
return {'FINISHED'}
def invoke(self, context, event):
wm = context.window_manager
return wm.invoke_props_dialog(self)
def draw(self, context):
layout = self.layout
layout.prop(self, "elements_type")
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.")

View File

@ -1,20 +1,21 @@
import bpy,mathutils
import os, math
import ast
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
from . import UTILS_constants, UTILS_functions, UTILS_safe_eval
class BALLANCE_OT_add_floor(bpy.types.Operator):
class BALLANCE_OT_add_floors(bpy.types.Operator):
"""Add Ballance floor"""
bl_idname = "ballance.add_floor"
bl_idname = "ballance.add_floors"
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()),
items=tuple((x, x, "") for x in UTILS_constants.floor_blockDict.keys()),
)
expand_length_1 : bpy.props.IntProperty(
@ -39,16 +40,16 @@ class BALLANCE_OT_add_floor(bpy.types.Operator):
)
use_2d_top : bpy.props.BoolProperty(
name="Top side"
name="Top edge"
)
use_2d_right : bpy.props.BoolProperty(
name="Right side"
name="Right edge"
)
use_2d_bottom : bpy.props.BoolProperty(
name="Bottom side"
name="Bottom edge"
)
use_2d_left : bpy.props.BoolProperty(
name="Left side"
name="Left edge"
)
use_3d_top : bpy.props.BoolProperty(
name="Top face"
@ -65,10 +66,14 @@ class BALLANCE_OT_add_floor(bpy.types.Operator):
return os.path.isdir(prefs.external_folder)
def execute(self, context):
# get prefs
prefs = bpy.context.preferences.addons[__package__].preferences
prefs_externalTexture = prefs.external_folder
# load mesh
objmesh = bpy.data.meshes.new('done_')
if self.floor_type in config.floor_basic_block_list:
load_basic_floor(
if self.floor_type in UTILS_constants.floor_basicBlockList:
_load_basic_floor(
objmesh,
self.floor_type,
'R0',
@ -81,9 +86,10 @@ class BALLANCE_OT_add_floor(bpy.types.Operator):
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(
(0.0, 0.0, 0.0),
prefs_externalTexture)
elif self.floor_type in UTILS_constants.floor_derivedBlockList:
_load_derived_floor(
objmesh,
self.floor_type,
self.height_multiplier,
@ -94,7 +100,10 @@ class BALLANCE_OT_add_floor(bpy.types.Operator):
self.use_2d_bottom,
self.use_2d_left,
self.use_3d_top,
self.use_3d_bottom))
self.use_3d_bottom),
prefs_externalTexture)
else:
raise Exception("Fatal error: unknow floor type.")
# normalization mesh
objmesh.validate(clean_customdata=False)
@ -102,7 +111,7 @@ class BALLANCE_OT_add_floor(bpy.types.Operator):
# create object and link it
obj=bpy.data.objects.new('A_Floor_BMERevenge_', objmesh)
utils.AddSceneAndMove2Cursor(obj)
UTILS_functions.add_into_scene_and_move_to_cursor(obj)
return {'FINISHED'}
def invoke(self, context, event):
@ -111,7 +120,7 @@ class BALLANCE_OT_add_floor(bpy.types.Operator):
def draw(self, context):
# get floor prototype
floor_prototype = config.floor_block_dict[self.floor_type]
floor_prototype = UTILS_constants.floor_blockDict[self.floor_type]
# try sync default value
if self.previous_floor_type != self.floor_type:
@ -142,13 +151,13 @@ class BALLANCE_OT_add_floor(bpy.types.Operator):
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.label(text=UTILS_constants.floor_expandDirectionMap[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.label(text=UTILS_constants.floor_expandDirectionMap[floor_prototype['InitColumnDirection']][floor_prototype['ExpandType']][3])
grids.template_icon(icon_value = UTILS_constants.icons_floorDict[self.floor_type])
grids.label(text=UTILS_constants.floor_expandDirectionMap[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.label(text=UTILS_constants.floor_expandDirectionMap[floor_prototype['InitColumnDirection']][floor_prototype['ExpandType']][2])
grids.separator()
col.separator()
@ -164,13 +173,13 @@ class BALLANCE_OT_add_floor(bpy.types.Operator):
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.template_icon(icon_value = UTILS_constants.icons_floorDict[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):
def _face_fallback(normal_face, expand_face, height):
if expand_face == None:
return normal_face
@ -179,66 +188,68 @@ def face_fallback(normal_face, expand_face, height):
else:
return expand_face
def create_or_get_material(material_name):
def _create_or_get_material(material_name, prefs_externalTexture):
# 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])
deconflict_mtl_name = "BMERevenge_" + material_name
inode=m.node_tree.nodes.new(type="ShaderNodeTexImage")
inode.image=txur
m.node_tree.links.new(inode.outputs[0],bnode.inputs[0])
# create or get material
(mtl, skip_init) = UTILS_functions.create_instance_with_option(
UTILS_constants.BmfileInfoType.MATERIAL,
deconflict_mtl_name, 'CURRENT'
)
if skip_init:
return mtl
# 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
# initialize material parameter
# load texture first
texture_filename = UTILS_constants.floor_textureReflactionMap[material_name]
deconflict_texture_name = "BMERevenge_" + texture_filename
(texture, skip_init) = UTILS_functions.create_instance_with_option(
UTILS_constants.BmfileInfoType.TEXTURE,
deconflict_texture_name, 'CURRENT',
extra_texture_path = prefs_externalTexture, extra_texture_filename = texture_filename
)
return m
# iterate material statistic to get corresponding mtl data
for try_item in UTILS_constants.floor_materialStatistic:
if material_name in try_item['member']:
# got it
# set material data
# all floor do not have any transparency props, so we provide 4 False value.
UTILS_functions.create_blender_material(mtl,
(True,
try_item['data']['ambient'], try_item['data']['diffuse'],
try_item['data']['specular'], try_item['data']['emissive'],
try_item['data']['power'],
False, False, False, False,
texture)
)
break
# return mtl
return mtl
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])]
def _solve_vec_data(str_data, d1, d2, d3, unit):
(cmd_x, cmd_y, cmd_z) = map(lambda x: x.strip(), str_data.split(','))
for i in range(3):
symbol = sp[i+1]
if symbol == '':
continue
# calc raw expand data
raw_d1 = d1 * unit
raw_d2 = d2 * unit
raw_d3 = (d3 - 1) * 5.0 # the 3d heigh unit of ballance floor is always 5.0
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
# do safe eval
return (
UTILS_safe_eval.do_vec_calc(cmd_x, raw_d1, raw_d2, raw_d3),
UTILS_safe_eval.do_vec_calc(cmd_y, raw_d1, raw_d2, raw_d3),
UTILS_safe_eval.do_vec_calc(cmd_z, raw_d1, raw_d2, raw_d3)
)
return vec
def rotate_translate_vec(vec, rotation, unit, extra_translate):
vec[0] -= unit / 2
vec[1] -= unit / 2
def _rotate_translate_vec(_vec, rotation, unit, extra_translate):
vec = (
_vec[0] - unit / 2,
_vec[1] - unit / 2,
_vec[2]
)
if rotation == 'R0':
coso=1
@ -256,76 +267,51 @@ def rotate_translate_vec(vec, rotation, unit, extra_translate):
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]
vec[2] + extra_translate[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])]
def _solve_uv_data(str_data, d1, d2, d3, unit):
(cmd_u, cmd_v) = map(lambda x: x.strip(), str_data.split(','))
for i in range(2):
symbol = sp[i+1]
if symbol == '':
continue
# calc raw expand data
raw_d1 = d1 * unit
raw_d2 = d2 * unit
raw_d3 = float(d3 - 1) # the uv heigh unit of ballance floor is always 1.0
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 safe eval
return (
UTILS_safe_eval.do_vec_calc(cmd_u, raw_d1, raw_d2, raw_d3),
UTILS_safe_eval.do_vec_calc(cmd_v, raw_d1, raw_d2, raw_d3)
)
def _solve_normal_data(point1, point2, point3):
p1 = mathutils.Vector(point1)
p2 = mathutils.Vector(point2)
p3 = mathutils.Vector(point3)
vector1 = p2 - p1
vector2 = p3 - p2
# 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]
]
corss_mul = vector1.cross(vector2)
# do a normalization
length = math.sqrt(nor[0] ** 2 + nor[1] ** 2 + nor[2] ** 2)
nor[0] /= length
nor[1] /= length
nor[2] /= length
corss_mul.normalize()
return tuple(nor)
return (corss_mul[0], corss_mul[1], corss_mul[2])
def solve_smashed_position(str_data, d1, d2):
sp=str_data.split(';')
sp_pos = sp[0].split(',')
sp_sync = sp[1].split(',')
def _solve_expand_param(str_data, d1, d2):
(cmd_d1, cmd_d2) = map(lambda x: x.strip(), str_data.split(','))
vec = [int(sp_pos[0]), int(sp_pos[1])]
# do safe eval
return (
UTILS_safe_eval.do_expand_calc(cmd_d1, d1, d2),
UTILS_safe_eval.do_expand_calc(cmd_d2, d1, d2)
)
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):
def _virtual_foreach_set(collection, field, base_num, data):
counter = 0
for i in data:
exec("a[j]." + field + "=q", {}, {
@ -345,11 +331,10 @@ sides_struct should be a tuple and it always have 6 bool items
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]
def _load_basic_floor(mesh, floor_type, rotation, height_multiplier, d1, d2, sides_struct, extra_translate, prefs_externalTexture):
floor_prototype = UTILS_constants.floor_blockDict[floor_type]
# set some unit
height_unit = 5.0
if floor_prototype['UnitSize'] == 'Small':
block_3dworld_unit = 2.5
block_uvworld_unit = 0.5
@ -360,13 +345,13 @@ def load_basic_floor(mesh, floor_type, rotation, height_multiplier, d1, d2, side
# got all needed faces
needCreatedFaces = []
if sides_struct[0]:
needCreatedFaces.append(face_fallback(floor_prototype['TwoDTopSide'], floor_prototype['TwoDTopSideExpand'], height_multiplier))
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))
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))
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))
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]:
@ -382,7 +367,7 @@ def load_basic_floor(mesh, floor_type, rotation, height_multiplier, d1, d2, side
new_texture = face['Textures']
if new_texture not in materialDict.keys():
# try get from existed solt
pending_material = create_or_get_material(new_texture)
pending_material = _create_or_get_material(new_texture, prefs_externalTexture)
if pending_material not in allmat:
# no matched. add it
mesh.materials.append(pending_material)
@ -406,32 +391,22 @@ def load_basic_floor(mesh, floor_type, rotation, height_multiplier, d1, d2, side
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),
vecList.append(_rotate_translate_vec(
_solve_vec_data(vec, d1, d2, height_multiplier, block_3dworld_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)
vec_indices = tuple(map(lambda x: x + base_indices, face['Indices']))
indCount = 4
elif face['Type'] == 'TRIANGLE':
# triangle
vec_indices = (
face['P1'] + base_indices,
face['P2'] + base_indices,
face['P3'] + base_indices)
vec_indices = tuple(map(lambda x: x + base_indices, face['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]])
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)
@ -439,6 +414,10 @@ def load_basic_floor(mesh, floor_type, rotation, height_multiplier, d1, d2, side
for i in range(indCount):
faceList.append(vec_indices[i] + global_offset_vec)
# push uvs into list
for i in range(indCount):
uvList.append(_solve_uv_data(face['UVs'][i], d1, d2, height_multiplier, block_uvworld_unit))
# push material into list
faceMatList.append(materialDict[face['Textures']])
@ -454,10 +433,10 @@ def load_basic_floor(mesh, floor_type, rotation, height_multiplier, d1, d2, side
# 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)
_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)):
@ -469,8 +448,8 @@ def load_basic_floor(mesh, floor_type, rotation, height_multiplier, d1, d2, side
cache_counter += indCount
def load_derived_floor(mesh, floor_type, height_multiplier, d1, d2, sides_struct):
floor_prototype = config.floor_block_dict[floor_type]
def _load_derived_floor(mesh, floor_type, height_multiplier, d1, d2, sides_struct, prefs_externalTexture):
floor_prototype = UTILS_constants.floor_blockDict[floor_type]
# set some unit
if floor_prototype['UnitSize'] == 'Small':
@ -492,21 +471,32 @@ def load_derived_floor(mesh, floor_type, height_multiplier, d1, d2, sides_struct
# 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)
# calc basic parameter
start_pos = _solve_vec_data(blk['StartPosition'], d1, d2, height_multiplier, block_3dworld_unit)
expand_param = _solve_expand_param(blk['ExpandParam'], d1, d2)
# re-calc height multiply for this block.
# if the translation of Z is not zero
if start_pos[2] != 0.0:
raw_height = (height_multiplier - 1) * 5.0 + start_pos[2] # test height
blk_height = max(0, (raw_height / 5.0) + 1) # prevent minus height
else:
blk_height = height_multiplier
# get face data
sides_data = tuple(sides_dict[x] for x in blk['SideSync'].split(';'))
# call basic floor creator
load_basic_floor(
_load_basic_floor(
mesh,
blk['Type'],
blk['Rotation'],
height_multiplier,
expand_pos[0],
expand_pos[1],
blk_height,
expand_param[0],
expand_param[1],
sides_data,
(start_pos[0] * block_3dworld_unit, start_pos[1] * block_3dworld_unit)
start_pos,
prefs_externalTexture
)

View File

@ -1,62 +1,9 @@
import bpy,mathutils
from . import utils, config, bm_import_export
import bpy, mathutils
from . import UTILS_functions
# ================================================= 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):
class BALLANCE_OT_add_rails(bpy.types.Operator):
"""Add rail"""
bl_idname = "ballance.add_rail"
bl_idname = "ballance.add_rails"
bl_label = "Add rail section"
bl_options = {'UNDO'}
@ -113,7 +60,7 @@ class BALLANCE_OT_add_rail(bpy.types.Operator):
bpy.ops.object.join()
# apply 3d cursor
utils.Move2Cursor(firstObj)
UTILS_functions.move_to_cursor(firstObj)
return {'FINISHED'}
@ -127,3 +74,4 @@ class BALLANCE_OT_add_rail(bpy.types.Operator):
layout.prop(self, "rail_radius")
if self.rail_type == 'DOUBLE':
layout.prop(self, "rail_span")

View File

@ -0,0 +1,221 @@
import bpy
from . import UTILS_constants, UTILS_functions, UTILS_virtools_prop
class common_group_name_props(bpy.types.Operator):
use_custom_name: bpy.props.BoolProperty(
name="Use Custom Name",
description="Whether use user defined group name.",
default=False,
)
group_name: bpy.props.EnumProperty(
name="Group Name",
description="Pick vanilla Ballance group name.",
items=tuple((x, x, "") for x in UTILS_constants.propsVtGroups_availableGroups),
)
custom_group_name: bpy.props.StringProperty(
name="Custom Group Name",
description="Input your custom group name.",
default="",
)
def get_group_name_string(self):
return str(self.custom_group_name if self.use_custom_name else self.group_name)
def invoke(self, context, event):
wm = context.window_manager
return wm.invoke_props_dialog(self)
class BALLANCE_OT_select_virtools_group(common_group_name_props):
"""Select objects by Virtools Group."""
bl_idname = "ballance.select_virtools_group"
bl_label = "Select by Virtools Group"
bl_options = {'UNDO'}
merge_selection: bpy.props.BoolProperty(
name="Merge Selection",
description="Merge selection, rather than re-select them.",
default=False,
)
ignore_hide: bpy.props.BoolProperty(
name="Ignore Hide Property",
description="Select objects without considering visibility.",
default=False,
)
def execute(self, context):
# iterate object
for obj in bpy.context.scene.objects:
# ignore hidden objects
if (not self.ignore_hide) and obj.hide_get() == True:
continue
# check group
if UTILS_virtools_prop.check_virtools_group_data(obj, self.get_group_name_string()):
# select object
obj.select_set(True)
else:
# if not in merge mode, deselect them
if not self.merge_selection:
obj.select_set(False)
return {'FINISHED'}
def draw(self, context):
layout = self.layout
row = layout.row()
row.prop(self, 'ignore_hide')
row.prop(self, 'merge_selection')
layout.separator()
layout.prop(self, 'use_custom_name')
if (self.use_custom_name):
layout.prop(self, 'custom_group_name')
else:
layout.prop(self, 'group_name')
class BALLANCE_OT_filter_virtools_group(common_group_name_props):
"""Filter objects by Virtools Group."""
bl_idname = "ballance.filter_virtools_group"
bl_label = "Filter by Virtools Group"
bl_options = {'UNDO'}
reverse_selection: bpy.props.BoolProperty(
name="Reverse",
description="Reverse operation. Remove matched objects.",
default=False,
)
ignore_hide: bpy.props.BoolProperty(
name="Ignore Hide Property",
description="Select objects without considering visibility.",
default=False,
)
def execute(self, context):
# make a copy for all objects, to ensure it is not viotile
# becuase we need deselect some objects in for statement
selected = bpy.context.selected_objects[:]
# iterate object
for obj in selected:
# ignore hidden objects
if (not self.ignore_hide) and obj.hide_get() == True:
continue
# check group and decide select
is_selected = UTILS_virtools_prop.check_virtools_group_data(obj, self.get_group_name_string())
if self.reverse_selection:
is_selected = not is_selected
# select object
obj.select_set(is_selected)
return {'FINISHED'}
def draw(self, context):
layout = self.layout
row = layout.row()
row.prop(self, 'ignore_hide')
row.prop(self, 'reverse_selection')
layout.separator()
layout.prop(self, 'use_custom_name')
if (self.use_custom_name):
layout.prop(self, 'custom_group_name')
else:
layout.prop(self, 'group_name')
class BALLANCE_OT_ctx_set_group(common_group_name_props):
"""Grouping selected objects"""
bl_idname = "ballance.ctx_set_group"
bl_label = "Grouping Objects"
bl_options = {'UNDO'}
@classmethod
def poll(self, context):
return len(bpy.context.selected_objects) != 0
def execute(self, context):
has_duplicated = False
# iterate object
for obj in bpy.context.selected_objects:
# try setting
if not UTILS_virtools_prop.add_virtools_group_data(obj, self.get_group_name_string()):
has_duplicated = True
# throw a warning if some objects have duplicated group
if has_duplicated:
UTILS_functions.show_message_box(("Some objects have duplicated group name.", "These objects have been omitted.", ), "Duplicated Group", 'ERROR')
return {'FINISHED'}
def invoke(self, context, event):
wm = context.window_manager
return wm.invoke_props_dialog(self)
def draw(self, context):
layout = self.layout
layout.prop(self, 'use_custom_name')
if (self.use_custom_name):
layout.prop(self, 'custom_group_name')
else:
layout.prop(self, 'group_name')
class BALLANCE_OT_ctx_unset_group(common_group_name_props):
"""Ungrouping selected objects"""
bl_idname = "ballance.ctx_unset_group"
bl_label = "Ungrouping Objects"
bl_options = {'UNDO'}
@classmethod
def poll(self, context):
return len(bpy.context.selected_objects) != 0
def execute(self, context):
lack_group = False
# iterate object
for obj in bpy.context.selected_objects:
# try unsetting
if not UTILS_virtools_prop.remove_virtools_group_data(obj, self.get_group_name_string()):
lack_group = True
# throw a warning if some objects have duplicated group
if lack_group:
UTILS_functions.show_message_box(("Some objects lack specified group name.", "These objects have been omitted.", ), "Lack Group", 'ERROR')
return {'FINISHED'}
def draw(self, context):
layout = self.layout
layout.prop(self, 'use_custom_name')
if (self.use_custom_name):
layout.prop(self, 'custom_group_name')
else:
layout.prop(self, 'group_name')
class BALLANCE_OT_ctx_clear_group(bpy.types.Operator):
"""Clear Virtools Groups for selected objects"""
bl_idname = "ballance.ctx_clear_group"
bl_label = "Clear Grouping"
bl_options = {'UNDO'}
@classmethod
def poll(self, context):
return len(bpy.context.selected_objects) != 0
def execute(self, context):
# iterate object
for obj in bpy.context.selected_objects:
UTILS_virtools_prop.clear_virtools_group_data(obj)
return {'FINISHED'}

View File

@ -0,0 +1,102 @@
import bpy
from . import UTILS_constants, UTILS_functions, UTILS_virtools_prop
class BALLANCE_OT_add_virtools_group(bpy.types.Operator):
"""Add a Virtools Group for Active Object."""
bl_idname = "ballance.add_virtools_group"
bl_label = "Add Virtools Group"
bl_options = {'UNDO'}
use_custom_name: bpy.props.BoolProperty(
name="Use Custom Name",
description="Whether use user defined group name.",
default=False,
)
group_name: bpy.props.EnumProperty(
name="Group Name",
description="Pick vanilla Ballance group name.",
items=tuple((x, x, "") for x in UTILS_constants.propsVtGroups_availableGroups),
)
custom_group_name: bpy.props.StringProperty(
name="Custom Group Name",
description="Input your custom group name.",
default="",
)
@classmethod
def poll(self, context):
return context.object is not None
def execute(self, context):
# get name first
gotten_group_name = str(self.custom_group_name if self.use_custom_name else self.group_name)
# try adding
obj = context.object
if not UTILS_virtools_prop.add_virtools_group_data(obj, gotten_group_name):
UTILS_functions.show_message_box(("Group name is duplicated!", ), "Duplicated Name", 'ERROR')
return {'FINISHED'}
def invoke(self, context, event):
wm = context.window_manager
return wm.invoke_props_dialog(self)
def draw(self, context):
self.layout.prop(self, 'use_custom_name')
if (self.use_custom_name):
self.layout.prop(self, 'custom_group_name')
else:
self.layout.prop(self, 'group_name')
class BALLANCE_OT_rm_virtools_group(bpy.types.Operator):
"""Remove a Virtools Group for Active Object."""
bl_idname = "ballance.rm_virtools_group"
bl_label = "Remove Virtools Group"
bl_options = {'UNDO'}
@classmethod
def poll(self, context):
if context.object is None:
return False
obj = context.object
gp = UTILS_virtools_prop.get_virtools_group(obj)
active_gp = UTILS_virtools_prop.get_active_virtools_group(obj)
return int(active_gp) >= 0 and int(active_gp) < len(gp)
def execute(self, context):
obj = context.object
UTILS_virtools_prop.remove_virtools_group_data_by_index(obj, int(UTILS_virtools_prop.get_active_virtools_group(obj)))
return {'FINISHED'}
class BALLANCE_UL_virtools_group(bpy.types.UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
layout.prop(item, 'group_name', icon='GROUP', emboss=False, text="")
class BALLANCE_PT_virtools_group(bpy.types.Panel):
"""Show Virtools Group Properties."""
bl_label = "Virtools Group"
bl_idname = "BALLANCE_PT_virtools_group"
bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW'
bl_context = "object"
@classmethod
def poll(cls, context):
return context.object is not None
def draw(self, context):
layout = self.layout
target = bpy.context.active_object
row = layout.row()
row.template_list("BALLANCE_UL_virtools_group", "", target, "virtools_group",
target, "active_virtools_group")
col = row.column(align=True)
col.operator(BALLANCE_OT_add_virtools_group.bl_idname, icon='ADD', text="")
col.operator(BALLANCE_OT_rm_virtools_group.bl_idname, icon='REMOVE', text="")

View File

@ -0,0 +1,91 @@
import bpy
from . import UTILS_constants, UTILS_functions, UTILS_virtools_prop
class BALLANCE_OT_apply_virtools_material(bpy.types.Operator):
"""Apply Virtools Material to Blender Material."""
bl_idname = "ballance.apply_virtools_material"
bl_label = "Apply Virtools Material"
bl_options = {'UNDO'}
@classmethod
def poll(cls, context):
return context.material is not None
def execute(self, context):
mtl = context.material
mtl_data = UTILS_virtools_prop.get_virtools_material_data(mtl)
# check enable, [0] is enable_virtools_material
if mtl_data[0]:
UTILS_functions.create_material_nodes(mtl, mtl_data)
else:
UTILS_functions.show_message_box(("Virtools Material is not enabled.", ), "Apply Failed", 'ERROR')
return {'FINISHED'}
class BALLANCE_OT_parse_virtools_material(bpy.types.Operator):
"""Apply Virtools Material to Blender Material."""
bl_idname = "ballance.parse_virtools_material"
bl_label = "Parse from Blender Principled BSDF"
bl_options = {'UNDO'}
@classmethod
def poll(cls, context):
return context.material is not None
def execute(self, context):
mtl = context.material
mtl_data = UTILS_functions.parse_material_nodes(mtl)
if mtl_data is None:
UTILS_functions.show_message_box(("Fail to parse Principled BSDF.", ), "Parsing Failed", 'ERROR')
else:
UTILS_virtools_prop.set_virtools_material_data(mtl, mtl_data)
return {'FINISHED'}
class BALLANCE_PT_virtools_material(bpy.types.Panel):
"""Show Virtools Material Properties."""
bl_label = "Virtools Material"
bl_idname = "BALLANCE_PT_virtools_material"
bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW'
bl_context = "material"
@classmethod
def poll(cls, context):
return context.material is not None
def draw_header(self, context):
# draw a checkbox in header
target = UTILS_virtools_prop.get_virtools_material(context.material)
self.layout.prop(target, "enable_virtools_material", text="")
def draw(self, context):
# get layout and target
layout = self.layout
target = UTILS_virtools_prop.get_virtools_material(context.material)
# decide visible
layout.enabled = target.enable_virtools_material
# draw layout
layout.label(text="Basic Parameters")
layout.prop(target, 'ambient')
layout.prop(target, 'diffuse')
layout.prop(target, 'specular')
layout.prop(target, 'emissive')
layout.prop(target, 'specular_power')
layout.prop(target, 'texture', emboss=True)
layout.separator()
layout.label(text="Advanced Parameters")
layout.prop(target, 'alpha_test')
layout.prop(target, 'alpha_blend')
layout.prop(target, 'z_buffer')
layout.prop(target, 'two_sided')
layout.separator()
layout.label(text="Operations")
layout.operator("ballance.apply_virtools_material", icon="NODETREE")
layout.operator("ballance.parse_virtools_material", icon="HIDE_OFF")

View File

@ -1,7 +1,19 @@
import json
import os
import re
external_texture_list = set([
bmfile_currentVersion = 14
bmfile_flagUnicode = 0x800
bmfile_flagDeflatedMaximum = 0x2
bmfile_globalComment = 'Use BM Spec 1.4'.encode('utf-8')
class BmfileInfoType():
OBJECT = 0
MESH = 1
MATERIAL = 2
TEXTURE = 3
bmfile_externalTextureSet = set([
"atari.avi",
"atari.bmp",
"Ball_LightningSphere1.bmp",
@ -85,7 +97,7 @@ external_texture_list = set([
"Wood_Raft.bmp"
])
component_list = [
bmfile_componentList = [
"P_Extra_Life",
"P_Extra_Point",
"P_Trafo_Paper",
@ -120,7 +132,7 @@ 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 = {
floor_expandDirectionMap = {
"PositiveX": {
"Static": ("X", "X", "X", "X"),
"Column": ("X", "X", "D1", "X"),
@ -143,7 +155,7 @@ floor_expand_direction_map = {
}
}
floor_texture_corresponding_map = {
floor_textureReflactionMap = {
"FloorSide": "Floor_Side.bmp",
"FloorTopBorder": "Floor_Top_Border.bmp",
"FloorTopBorder_ForSide": "Floor_Top_Border.bmp",
@ -157,8 +169,8 @@ floor_texture_corresponding_map = {
"BallStone": "Ball_Stone.bmp"
}
# WARNING: this data is shared with BallanceVirtoolsPlugin - mapping_BM.cpp - fix_blender_texture
floor_material_statistic = [
# WARNING: this data is shared with `BallanceVirtoolsPlugin/bvh/features/mapping/fix_texture.cpp`
floor_materialStatistic = [
{
"member": [
"FloorSide",
@ -178,6 +190,7 @@ floor_material_statistic = [
"FloorTopBorder",
"FloorTopBorderless",
"FloorTopFlat",
"FloorTopProfil",
"FloorTopProfilFlat"
],
"data": {
@ -215,19 +228,132 @@ floor_material_statistic = [
}
]
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
floor_blockDict = {}
floor_basicBlockList = []
floor_derivedBlockList = []
# read from json
for walk_root, walk_dirs, walk_files in os.walk(os.path.join(os.path.dirname(__file__), "json", "basic_blocks")):
for relfile in walk_files:
if not relfile.endswith('.json'): continue
with open(os.path.join(walk_root, relfile)) as fp:
for item in json.load(fp):
floor_basicBlockList.append(item["Type"])
floor_blockDict[item["Type"]] = item
for walk_root, walk_dirs, walk_files in os.walk(os.path.join(os.path.dirname(__file__), "json", "derived_blocks")):
for relfile in walk_files:
if not relfile.endswith('.json'): continue
with open(os.path.join(walk_root, relfile)) as fp:
for item in json.load(fp):
floor_derivedBlockList.append(item["Type"])
floor_blockDict[item["Type"]] = item
blenderIcon_floor = None
blenderIcon_floor_dict = {}
icons_floor = None
icons_floorDict = {}
# blenderIcon_elements = None
# blenderIcon_elements_dict = {}
# blenderIcon_elements_dict = {}
rename_normalComponentsGroupName = set([
"P_Extra_Life",
"P_Extra_Point",
"P_Trafo_Paper",
"P_Trafo_Stone",
"P_Trafo_Wood",
"P_Ball_Paper",
"P_Ball_Stone",
"P_Ball_Wood",
"P_Box",
"P_Dome",
"P_Modul_01",
"P_Modul_03",
"P_Modul_08",
"P_Modul_17",
"P_Modul_18",
"P_Modul_19",
"P_Modul_25",
"P_Modul_26",
"P_Modul_29",
"P_Modul_30",
"P_Modul_34",
"P_Modul_37",
"P_Modul_41"
])
rename_uniqueComponentsGroupName = set([
"PS_Levelstart",
"PE_Levelende",
"PC_Checkpoints",
"PR_Resetpoints"
])
rename_floorGroupTester = set([
"Sound_HitID_01",
"Sound_RollID_01"
])
rename_woodGroupTester = set([
"Sound_HitID_02",
"Sound_RollID_02"
])
# 61 mark: Sector_(0[1-8]|[1-9][0-9]{1,2}|9) may also work
rename_regexCKGroupSector = re.compile('^Sector_([123456789]{1}[0123456789]{1}[0123456789]{1}|[123456789]{1}[0123456789]{1}|0[12345678]{1}|9)$')
rename_regexYYCComponent = re.compile('^(' + '|'.join(rename_normalComponentsGroupName) + ')_(0[1-9]|[1-9][0-9])_.*$')
rename_regexYYCPC = re.compile('^PC_TwoFlames_(0[1-7])$')
rename_regexYYCPR = re.compile('^PR_Resetpoint_(0[1-8])$')
rename_regexImengyuComponent = re.compile('^(' + '|'.join(rename_normalComponentsGroupName) + '):[^:]*:([1-9]|[1-9][0-9])$')
rename_regexImengyuPCRComp = re.compile('^(PC_CheckPoint|PR_ResetPoint):([0-9]+)$')
propsVtGroups_availableGroups = (
"Sector_01",
"Sector_02",
"Sector_03",
"Sector_04",
"Sector_05",
"Sector_06",
"Sector_07",
"Sector_08",
"P_Extra_Life",
"P_Extra_Point",
"P_Trafo_Paper",
"P_Trafo_Stone",
"P_Trafo_Wood",
"P_Ball_Paper",
"P_Ball_Stone",
"P_Ball_Wood",
"P_Box",
"P_Dome",
"P_Modul_01",
"P_Modul_03",
"P_Modul_08",
"P_Modul_17",
"P_Modul_18",
"P_Modul_19",
"P_Modul_25",
"P_Modul_26",
"P_Modul_29",
"P_Modul_30",
"P_Modul_34",
"P_Modul_37",
"P_Modul_41",
"PS_Levelstart",
"PE_Levelende",
"PC_Checkpoints",
"PR_Resetpoints",
"Sound_HitID_01",
"Sound_RollID_01",
"Sound_HitID_02",
"Sound_RollID_02",
"Sound_HitID_03",
"Sound_RollID_03",
"DepthTestCubes",
"Phys_Floors",
"Phys_FloorRails",
"Phys_FloorStopper"
)

View File

@ -0,0 +1,96 @@
import bpy, bmesh, bpy_extras, mathutils
import struct, shutil, os
# writer
def write_string(fs,str):
count=len(str)
write_uint32(fs,count)
fs.write(str.encode("utf_32_le"))
def write_uint8(fs,num):
fs.write(struct.pack("B", num))
def write_uint32(fs,num):
fs.write(struct.pack("I", num))
def write_uint64(fs,num):
fs.write(struct.pack("Q", num))
def write_bool(fs,boolean):
if boolean:
write_uint8(fs, 1)
else:
write_uint8(fs, 0)
def write_float(fs,fl):
fs.write(struct.pack("f", fl))
def write_world_matrix(fs, matt):
mat = matt.transposed()
fs.write(struct.pack("ffffffffffffffff",
mat[0][0],mat[0][2], mat[0][1], mat[0][3],
mat[2][0],mat[2][2], mat[2][1], mat[2][3],
mat[1][0],mat[1][2], mat[1][1], mat[1][3],
mat[3][0],mat[3][2], mat[3][1], mat[3][3]))
def write_3vector(fs, x, y ,z):
fs.write(struct.pack("fff", x, y ,z))
def write_color(fs, colors):
write_3vector(fs, colors[0], colors[1], colors[2])
def write_2vector(fs, u, v):
fs.write(struct.pack("ff", u, v))
def write_face(fs, v1, vt1, vn1, v2, vt2, vn2, v3, vt3, vn3):
fs.write(struct.pack("IIIIIIIII", v1, vt1, vn1, v2, vt2, vn2, v3, vt3, vn3))
# reader
def peek_stream(fs):
res = fs.read(1)
fs.seek(-1, os.SEEK_CUR)
return res
def read_float(fs):
return struct.unpack("f", fs.read(4))[0]
def read_uint8(fs):
return struct.unpack("B", fs.read(1))[0]
def read_uint32(fs):
return struct.unpack("I", fs.read(4))[0]
def read_uint64(fs):
return struct.unpack("Q", fs.read(8))[0]
def read_string(fs):
count = read_uint32(fs)
return fs.read(count*4).decode("utf_32_le")
def read_bool(fs):
return read_uint8(fs) != 0
def read_world_materix(fs):
p = struct.unpack("ffffffffffffffff", fs.read(4*4*4))
res = mathutils.Matrix((
(p[0], p[2], p[1], p[3]),
(p[8], p[10], p[9], p[11]),
(p[4], p[6], p[5], p[7]),
(p[12], p[14], p[13], p[15])))
return res.transposed()
def read_3vector(fs):
return struct.unpack("fff", fs.read(3*4))
def read_2vector(fs):
return struct.unpack("ff", fs.read(2*4))
def read_face(fs):
return struct.unpack("IIIIIIIII", fs.read(4*9))
def read_component_face(fs):
return struct.unpack("IIIIII", fs.read(4*6))

View File

@ -0,0 +1,265 @@
import bpy, bmesh, bpy_extras, mathutils
import struct, shutil, os
from bpy_extras.io_utils import unpack_list
from bpy_extras.image_utils import load_image
from bpy_extras import io_utils, node_shader_utils
from . import UTILS_file_io, UTILS_constants, UTILS_virtools_prop
# =================================
# scene operation
def show_message_box(message, title, icon):
def draw(self, context):
layout = self.layout
for item in message:
layout.label(text=item, translate=False)
bpy.context.window_manager.popup_menu(draw, title = title, icon = icon)
def add_into_scene_and_move_to_cursor(obj):
move_to_cursor(obj)
view_layer = bpy.context.view_layer
collection = view_layer.active_layer_collection.collection
collection.objects.link(obj)
def move_to_cursor(obj):
obj.location = bpy.context.scene.cursor.location
# =================================
# is compoent
def is_component(name):
return get_component_id(name) != -1
def get_component_id(name):
for ind, comp in enumerate(UTILS_constants.bmfile_componentList):
if name.startswith(comp):
return ind
return -1
# =================================
# create / parse material
def create_blender_material(input_mtl, packed_data):
# adding material nodes
create_material_nodes(input_mtl, packed_data)
# write custom property
UTILS_virtools_prop.set_virtools_material_data(input_mtl, packed_data)
def create_material_nodes(input_mtl, packed_data):
(enable_virtools_material,
ambient, diffuse, specular, emissive, specular_power,
alpha_test, alpha_blend, z_buffer, two_sided,
texture) = packed_data
# enable nodes mode
input_mtl.use_nodes=True
# delete all existed nodes
for node in input_mtl.node_tree.nodes:
input_mtl.node_tree.nodes.remove(node)
# create ballance-style blender material
bnode = input_mtl.node_tree.nodes.new(type="ShaderNodeBsdfPrincipled")
mnode = input_mtl.node_tree.nodes.new(type="ShaderNodeOutputMaterial")
input_mtl.node_tree.links.new(bnode.outputs[0],mnode.inputs[0])
input_mtl.metallic = sum(ambient) / 3
input_mtl.diffuse_color = [i for i in diffuse] + [1]
input_mtl.specular_color = specular
input_mtl.specular_intensity = specular_power
# adding a texture
if texture is not None:
inode = input_mtl.node_tree.nodes.new(type="ShaderNodeTexImage")
inode.image = texture
input_mtl.node_tree.links.new(inode.outputs[0], bnode.inputs[0])
# return None if fail to parse
def parse_material_nodes(mtl):
# get node
mat_wrap = node_shader_utils.PrincipledBSDFWrapper(mtl)
# check existence of Principled BSDF
if mat_wrap:
# we trying get texture data from Principled BSDF
use_mirror = mat_wrap.metallic != 0.0
if use_mirror:
mtl_ambient = (mat_wrap.metallic, mat_wrap.metallic, mat_wrap.metallic)
else:
mtl_ambient = (1.0, 1.0, 1.0)
mtl_diffuse = (mat_wrap.base_color[0], mat_wrap.base_color[1], mat_wrap.base_color[2])
mtl_specular = (mat_wrap.specular, mat_wrap.specular, mat_wrap.specular)
mtl_emissive = mat_wrap.emission_color[:3]
mtl_specularPower = 0.0
# confirm texture
mtl_texture = None
tex_wrap = getattr(mat_wrap, "base_color_texture", None)
if tex_wrap:
image = tex_wrap.image
if image:
mtl_texture = image
# return value
return (True,
mtl_ambient, mtl_diffuse, mtl_specular, mtl_emissive, mtl_specularPower,
False, False, False, False,
mtl_texture
)
else:
return None
# =================================
# load component
def load_component(component_id):
# get file first
component_name = UTILS_constants.bmfile_componentList[component_id]
selected_file = os.path.join(
os.path.dirname(__file__),
'meshes',
component_name + '.bin'
)
# read file. please note this sector is sync with import_bm's mesh's code. when something change, please change each other.
fmesh = open(selected_file, 'rb')
# create real mesh, we don't need to consider name. blender will solve duplicated name
mesh = bpy.data.meshes.new('mesh_' + component_name)
vList = []
vnList = []
faceList = []
# in first read, store all data into list
listCount = UTILS_file_io.read_uint32(fmesh)
for i in range(listCount):
cache = UTILS_file_io.read_3vector(fmesh)
# switch yz
vList.append((cache[0], cache[2], cache[1]))
listCount = UTILS_file_io.read_uint32(fmesh)
for i in range(listCount):
cache = UTILS_file_io.read_3vector(fmesh)
# switch yz
vnList.append((cache[0], cache[2], cache[1]))
listCount = UTILS_file_io.read_uint32(fmesh)
for i in range(listCount):
faceData = UTILS_file_io.read_component_face(fmesh)
# we need invert triangle sort
faceList.append((
faceData[4], faceData[5],
faceData[2], faceData[3],
faceData[0], faceData[1]
))
# then, we need add correspond count for vertices
mesh.vertices.add(len(vList))
mesh.loops.add(len(faceList)*3) # triangle face confirmed
mesh.polygons.add(len(faceList))
mesh.create_normals_split()
# add vertices data
mesh.vertices.foreach_set("co", unpack_list(vList))
mesh.loops.foreach_set("vertex_index", unpack_list(_flat_component_vertices_index(faceList)))
mesh.loops.foreach_set("normal", unpack_list(_flat_component_vertices_normal(faceList, vnList)))
for i in range(len(faceList)):
mesh.polygons[i].loop_start = i * 3
mesh.polygons[i].loop_total = 3
mesh.polygons[i].use_smooth = True
mesh.validate(clean_customdata=False)
mesh.update(calc_edges=False, calc_edges_loose=False)
fmesh.close()
return mesh
def _flat_component_vertices_index(faceList):
for item in faceList:
yield (item[0], )
yield (item[2], )
yield (item[4], )
def _flat_component_vertices_normal(faceList, vnList):
for item in faceList:
yield vnList[item[1]]
yield vnList[item[3]]
yield vnList[item[5]]
# =================================
# create instance with option
def create_instance_with_option(instance_type, instance_name, instance_opt,
extra_mesh = None, extra_texture_path = None, extra_texture_filename = None):
"""
Create instance with opetions
`instance_type`, `instance_name`, `instance_opt` are essential for each type instances.
For object, you should provide `extra_mesh`.
For texture, you should provide `extra_texture_path` and `extra_texture_filename`.
"""
def get_instance():
try:
if instance_type == UTILS_constants.BmfileInfoType.OBJECT:
temp_instance = bpy.data.objects[instance_name]
elif instance_type == UTILS_constants.BmfileInfoType.MESH:
temp_instance = bpy.data.meshes[instance_name]
elif instance_type == UTILS_constants.BmfileInfoType.MATERIAL:
temp_instance = bpy.data.materials[instance_name]
elif instance_type == UTILS_constants.BmfileInfoType.TEXTURE:
temp_instance = bpy.data.images[instance_name]
temp_is_existed = True
except:
temp_instance = None
temp_is_existed = False
return (temp_instance, temp_is_existed)
def create_instance():
if instance_type == UTILS_constants.BmfileInfoType.OBJECT:
instance_obj = bpy.data.objects.new(instance_name, extra_mesh)
#instance_obj.name = instance_name
elif instance_type == UTILS_constants.BmfileInfoType.MESH:
instance_obj = bpy.data.meshes.new(instance_name)
#instance_obj.name = instance_name
elif instance_type == UTILS_constants.BmfileInfoType.MATERIAL:
instance_obj = bpy.data.materials.new(instance_name)
#instance_obj.name = instance_name
elif instance_type == UTILS_constants.BmfileInfoType.TEXTURE:
# this command will also check current available texture
# because `get_instance()` only check texture name
# but this strategy is not based on texture filepath, so this create method will
# correct this problem
instance_obj = load_image(extra_texture_filename, extra_texture_path, check_existing=(instance_opt == 'CURRENT'))
instance_obj.name = instance_name
return instance_obj
# analyze options
if (not isinstance(instance_opt, str)) or instance_opt == 'RENAME':
# create new instance
# or always create new instance if opts is not string
temp_instance = create_instance()
temp_skip_init = False
elif instance_opt == 'CURRENT':
# try get instance
(temp_instance, temp_is_existed) = get_instance()
# if got instance successfully, we do not create new one, otherwise, we should
# create new instance
if not temp_is_existed:
temp_instance = create_instance()
temp_skip_init = False
else:
temp_skip_init = True
return (temp_instance, temp_skip_init)

View File

@ -0,0 +1,68 @@
import ast
class SimpleCalcEnsurance(ast.NodeVisitor):
def __init__(self):
self.is_illegal_syntax: bool = False
self.allow_float: bool = True
self.param_name: tuple = tuple()
def wrapper_visit(self, node: ast.AST, allow_float: bool, param_name: tuple) -> bool:
self.is_illegal_syntax = False
self.allow_float = allow_float
self.param_name = param_name
self.visit(node)
return self.is_illegal_syntax
def generic_visit(node):
self.is_illegal_syntax = True
def visit_Expression(self, node: ast.Expression):
self.visit(node.body)
def visit_BinOp(self, node: ast.BinOp):
if isinstance(node.op, ast.Add) or isinstance(node.op, ast.Sub) or isinstance(node.op, ast.Mult) or isinstance(node.op, ast.Div):
self.visit(node.left)
self.visit(node.right)
else:
self.is_illegal_syntax = True
def visit_UnaryOp(self, node: ast.UnaryOp):
if isinstance(node.op, ast.USub):
self.visit(node.operand)
else:
self.is_illegal_syntax = True
def visit_Constant(self, node: ast.Constant):
if (self.allow_float and isinstance(node.value, float)) or isinstance(node.value, int):
pass
else:
self.is_illegal_syntax = True
def visit_Name(self, node: ast.Name):
if node.id in self.param_name and isinstance(node.ctx, ast.Load):
pass
else:
self.is_illegal_syntax = True
def _do_calc(szEval: str, allow_float: bool, d: dict):
ast_tree = ast.parse(szEval, mode='eval')
walker = SimpleCalcEnsurance()
if walker.wrapper_visit(ast_tree, allow_float, d.keys()):
raise Exception("Illegal AST Tree. Tree contain illegal syntax. Please check BMERevenge.")
return eval(compile(ast_tree, '', mode='eval'), {}, d)
def do_vec_calc(szEval: str, raw_d1: float, raw_d2: float, raw_d3: float) -> float:
return float(_do_calc(szEval, True, {
"d1": raw_d1,
"d2": raw_d2,
"d3": raw_d3
}))
def do_expand_calc(szEval: str, d1: int, d2: int) -> int:
return int(_do_calc(szEval, False, {
"d1": d1,
"d2": d2
}))

View File

@ -0,0 +1,194 @@
import bpy
from . import UTILS_constants, UTILS_functions
class BALLANCE_PG_virtools_material(bpy.types.PropertyGroup):
enable_virtools_material: bpy.props.BoolProperty(
name="Enable Virtools Material",
default=False,
)
ambient: bpy.props.FloatVectorProperty(name="Ambient",
subtype='COLOR',
min=0.0,
max=1.0,
default=[0.0,0.0,0.0])
diffuse: bpy.props.FloatVectorProperty(name="Diffuse",
subtype='COLOR',
min=0.0,
max=1.0,
default=[0.0,0.0,0.0])
specular: bpy.props.FloatVectorProperty(name="Specular",
subtype='COLOR',
min=0.0,
max=1.0,
default=[0.0,0.0,0.0])
emissive: bpy.props.FloatVectorProperty(name="Emissive",
subtype='COLOR',
min=0.0,
max=1.0,
default=[0.0,0.0,0.0])
specular_power: bpy.props.FloatProperty(
name="Specular Power",
min=0.0,
max=100.0,
default=0.0,
)
alpha_test: bpy.props.BoolProperty(
name="Alpha Test",
description="Alpha Func: VXCMP_GREATER. Alpha Ref: 1.",
default=False,
)
alpha_blend: bpy.props.BoolProperty(
name="Alpha Blend",
description="Source Blend: VXBLEND_SRCALPHA. Dest Blend: VXBLEND_INVSRCALPHA.",
default=False,
)
z_buffer: bpy.props.BoolProperty(
name="Z Buffer",
description="ZFunc: VXCMP_LESSEQUAL.",
default=False,
)
two_sided: bpy.props.BoolProperty(
name="Two Sided",
default=False,
)
texture: bpy.props.PointerProperty(
type=bpy.types.Image,
name="Texture",
description="The texture used for Virtools material"
)
class BALLANCE_PG_virtools_group(bpy.types.PropertyGroup):
group_name: bpy.props.StringProperty(
name="Group Name",
default=""
)
def get_virtools_material(mtl):
return mtl.virtools_material
def get_virtools_material_data(mtl):
data = get_virtools_material(mtl)
return (
data.enable_virtools_material,
data.ambient,
data.diffuse,
data.specular,
data.emissive,
data.specular_power,
data.alpha_test,
data.alpha_blend,
data.z_buffer,
data.two_sided,
data.texture
)
def set_virtools_material_data(mtl, packed_data):
data = get_virtools_material(mtl)
# packed_data have the same order with the return value of `get_virtools_material_data`
(data.enable_virtools_material,
data.ambient, data.diffuse, data.specular, data.emissive, data.specular_power,
data.alpha_test, data.alpha_blend, data.z_buffer, data.two_sided,
data.texture) = packed_data
def get_active_virtools_group(obj):
return obj.active_virtools_group
def get_virtools_group(obj):
return obj.virtools_group
def check_virtools_group_data(obj, probe):
for item in get_virtools_group(obj):
if probe == str(item.group_name):
return True
return False
def add_virtools_group_data(obj, new_data):
# check exist
if check_virtools_group_data(obj, new_data):
# existed, give up
return False
# "add" do not need operate active_virtools_group
data = get_virtools_group(obj)
it = data.add()
it.name = ""
it.group_name = new_data
return True
def remove_virtools_group_data(obj, rm_data):
gp = get_virtools_group(obj)
active_gp = get_active_virtools_group(obj)
for idx, item in enumerate(gp):
if rm_data == str(item.group_name):
# decrease active group if removed item is ahead of active group
if idx <= active_gp:
active_gp -= 1
# remove
gp.remove(idx)
# indicate success
return True
return False
def remove_virtools_group_data_by_index(obj, rm_idx):
gp = get_virtools_group(obj)
active_gp = get_active_virtools_group(obj)
# report error
if rm_idx >= len(gp):
return False
# remove
if rm_idx <= active_gp:
active_gp -= 1
gp.remove(rm_idx)
return True
def clear_virtools_group_data(obj):
gp = get_virtools_group(obj)
active_gp = get_active_virtools_group(obj)
gp.clear()
active_gp = 0
def fill_virtools_group_data(obj, data_list):
# clear first
clear_virtools_group_data(obj)
# if no data to add, return
if data_list is None:
return
# add one by one after check duplication
data = get_virtools_group(obj)
for item in set(data_list):
it = data.add()
it.name = ""
it.group_name = item
def get_virtools_group_data(obj):
return tuple(str(item.group_name) for item in get_virtools_group(obj))
def register_props():
bpy.types.Object.virtools_group = bpy.props.CollectionProperty(type=BALLANCE_PG_virtools_group)
bpy.types.Object.active_virtools_group = bpy.props.IntProperty()
bpy.types.Material.virtools_material = bpy.props.PointerProperty(type=BALLANCE_PG_virtools_material)
def unregister_props():
del bpy.types.Material.virtools_material
del bpy.types.Object.virtools_group
del bpy.types.Object.active_virtools_group

View File

@ -0,0 +1,48 @@
import pathlib, zipfile, os, shutil
from . import UTILS_constants
def compress(folder, zip_file):
# remove target file first
if os.path.isfile(zip_file):
os.remove(zip_file)
# compress data
with zipfile.ZipFile(zip_file, mode= 'w', compression= zipfile.ZIP_DEFLATED, compresslevel= 9, allowZip64= False) as zip_obj:
# set global comment
zip_obj.comment = UTILS_constants.bmfile_globalComment
# iterate folder and add files
for folder_name, subfolders, filenames in os.walk(folder):
for filename in filenames:
# construct zip_entry
abstract_filepath = os.path.join(folder_name, filename)
relative_filepath = os.path.relpath(abstract_filepath, folder)
zip_entry = zipfile.ZipInfo.from_file(abstract_filepath, arcname= relative_filepath)
zip_entry.compress_type = zipfile.ZIP_DEFLATED
# compress file
with open(abstract_filepath, 'rb') as fs_in:
with zip_obj.open(zip_entry, mode= 'w') as zip_in:
# References
# https://stackoverflow.com/questions/53254622/zipfile-header-language-encoding-bit-set-differently-between-python2-and-python3
# set unicode flag after opening internal file.
# for the shit implement of python module zipfile, we need set Deflated:Maximum manually
zip_entry.flag_bits |= UTILS_constants.bmfile_flagUnicode
zip_entry.flag_bits |= UTILS_constants.bmfile_flagDeflatedMaximum
# copy file
shutil.copyfileobj(fs_in, zip_in)
def decompress(folder, zip_file):
with zipfile.ZipFile(zip_file, mode= 'r', compression= zipfile.ZIP_DEFLATED, compresslevel= 9, allowZip64= False) as zip_obj:
for zip_entry in zip_obj.infolist():
if (zip_entry.flag_bits & UTILS_constants.bmfile_flagUnicode) == 0:
# lost unicode flag, throw error
raise Exception("Zip Entry lost UNICODE flag.")
# decompress file
zip_obj.extract(zip_entry, path= folder)

View File

@ -2,7 +2,7 @@ bl_info={
"name":"Ballance Blender Plugin",
"description":"Ballance mapping tools for Blender",
"author":"yyc12345",
"version":(2,0),
"version":(3,0),
"blender":(2,83,0),
"category":"Object",
"support":"TESTING",
@ -11,49 +11,91 @@ bl_info={
"tracker_url": "https://github.com/yyc12345/BallanceBlenderHelper/issues"
}
# ============================================= import system
import bpy,bpy_extras
# =============================================
# import system
import bpy, bpy_extras
import bpy.utils.previews
import os
# import my code (with reload)
if "bpy" in locals():
import importlib
if "bm_import_export" in locals():
importlib.reload(bm_import_export)
if "rail_uv" in locals():
importlib.reload(rail_uv)
if "utils" in locals():
importlib.reload(utils)
if "config" in locals():
importlib.reload(config)
if "preferences" in locals():
importlib.reload(preferences)
if "threedsmax_align" in locals():
importlib.reload(threedsmax_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
if "UTILS_constants" in locals():
importlib.reload(UTILS_constants)
if "UTILS_functions" in locals():
importlib.reload(UTILS_functions)
if "UTILS_preferences" in locals():
importlib.reload(UTILS_preferences)
if "UTILS_file_io" in locals():
importlib.reload(UTILS_file_io)
if "UTILS_zip_helper" in locals():
importlib.reload(UTILS_zip_helper)
if "UTILS_virtools_prop" in locals():
importlib.reload(UTILS_virtools_prop)
if "UTILS_safe_eval" in locals():
importlib.reload(UTILS_safe_eval)
# ============================================= menu system
if "BMFILE_export" in locals():
importlib.reload(BMFILE_export)
if "BMFILE_import" in locals():
importlib.reload(BMFILE_import)
if "MODS_rail_uv" in locals():
importlib.reload(MODS_rail_uv)
if "MODS_3dsmax_align" in locals():
importlib.reload(MODS_3dsmax_align)
if "MODS_flatten_uv" in locals():
importlib.reload(MODS_flatten_uv)
if "OBJS_add_components" in locals():
importlib.reload(OBJS_add_components)
if "OBJS_add_floors" in locals():
importlib.reload(OBJS_add_floors)
if "OBJS_add_rails" in locals():
importlib.reload(OBJS_add_rails)
if "OBJS_group_opers" in locals():
importlib.reload(OBJS_group_opers)
if "NAMES_rename_system" in locals():
importlib.reload(NAMES_rename_system)
if "PROPS_virtools_group" in locals():
importlib.reload(PROPS_virtools_group)
if "PROPS_virtools_material" in locals():
importlib.reload(PROPS_virtools_material)
from . import UTILS_constants, UTILS_functions, UTILS_preferences, UTILS_virtools_prop, UTILS_safe_eval
from . import BMFILE_export, BMFILE_import
from . import MODS_3dsmax_align, MODS_flatten_uv, MODS_rail_uv
from . import OBJS_add_components, OBJS_add_floors, OBJS_add_rails, OBJS_group_opers
from . import NAMES_rename_system
from . import PROPS_virtools_group, PROPS_virtools_material
# =============================================
# menu system
class BALLANCE_MT_ThreeDViewerMenu(bpy.types.Menu):
"""Ballance related 3D operator"""
"""Ballance 3D operators"""
bl_idname = "BALLANCE_MT_ThreeDViewerMenu"
bl_label = "Ballance 3D"
bl_label = "Ballance"
def draw(self, context):
layout = self.layout
layout.operator("ballance.super_align")
layout.operator("ballance.rail_uv")
layout.operator("ballance.no_uv_checker")
layout.operator("ballance.flatten_uv")
layout.operator(MODS_3dsmax_align.BALLANCE_OT_super_align.bl_idname)
layout.operator(MODS_rail_uv.BALLANCE_OT_rail_uv.bl_idname)
layout.operator(MODS_flatten_uv.BALLANCE_OT_flatten_uv.bl_idname)
class BALLANCE_MT_OutlinerMenu(bpy.types.Menu):
"""Ballance rename operators"""
bl_idname = "BALLANCE_MT_OutlinerMenu"
bl_label = "Ballance"
def draw(self, context):
layout = self.layout
oprt = layout.operator(NAMES_rename_system.BALLANCE_OT_rename_by_group.bl_idname)
oprt = layout.operator(NAMES_rename_system.BALLANCE_OT_convert_name.bl_idname)
oprt = layout.operator(NAMES_rename_system.BALLANCE_OT_auto_grouping.bl_idname)
class BALLANCE_MT_AddFloorMenu(bpy.types.Menu):
"""Add Ballance floor"""
@ -64,41 +106,68 @@ class BALLANCE_MT_AddFloorMenu(bpy.types.Menu):
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])
for item in UTILS_constants.floor_basicBlockList:
cop = layout.operator(
OBJS_add_floors.BALLANCE_OT_add_floors.bl_idname,
text=item, icon_value = UTILS_constants.icons_floorDict[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])
for item in UTILS_constants.floor_derivedBlockList:
cop = layout.operator(
OBJS_add_floors.BALLANCE_OT_add_floors.bl_idname,
text=item, icon_value = UTILS_constants.icons_floorDict[item])
cop.floor_type = item
# ============================================= blender call system
# =============================================
# blender call system
classes = (
preferences.BallanceBlenderPluginPreferences,
preferences.MyPropertyGroup,
UTILS_preferences.BallanceBlenderPluginPreferences,
UTILS_preferences.MyPropertyGroup,
bm_import_export.BALLANCE_OT_import_bm,
bm_import_export.BALLANCE_OT_export_bm,
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,
BMFILE_import.BALLANCE_OT_import_bm,
BMFILE_export.BALLANCE_OT_export_bm,
MODS_rail_uv.BALLANCE_OT_rail_uv,
MODS_3dsmax_align.BALLANCE_OT_super_align,
MODS_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
OBJS_add_components.BALLANCE_OT_add_components,
OBJS_add_rails.BALLANCE_OT_add_rails,
OBJS_add_floors.BALLANCE_OT_add_floors,
BALLANCE_MT_AddFloorMenu,
NAMES_rename_system.BALLANCE_OT_rename_by_group,
NAMES_rename_system.BALLANCE_OT_convert_name,
NAMES_rename_system.BALLANCE_OT_auto_grouping,
BALLANCE_MT_OutlinerMenu,
UTILS_virtools_prop.BALLANCE_PG_virtools_material,
UTILS_virtools_prop.BALLANCE_PG_virtools_group,
PROPS_virtools_group.BALLANCE_OT_add_virtools_group,
PROPS_virtools_group.BALLANCE_OT_rm_virtools_group,
PROPS_virtools_group.BALLANCE_UL_virtools_group,
PROPS_virtools_group.BALLANCE_PT_virtools_group,
PROPS_virtools_material.BALLANCE_OT_apply_virtools_material,
PROPS_virtools_material.BALLANCE_OT_parse_virtools_material,
PROPS_virtools_material.BALLANCE_PT_virtools_material,
OBJS_group_opers.BALLANCE_OT_select_virtools_group,
OBJS_group_opers.BALLANCE_OT_filter_virtools_group,
OBJS_group_opers.BALLANCE_OT_ctx_set_group,
OBJS_group_opers.BALLANCE_OT_ctx_unset_group,
OBJS_group_opers.BALLANCE_OT_ctx_clear_group,
)
def menu_func_bm_import(self, context):
self.layout.operator(bm_import_export.BALLANCE_OT_import_bm.bl_idname, text="Ballance Map (.bmx)")
self.layout.operator(BMFILE_import.BALLANCE_OT_import_bm.bl_idname, text="Ballance Map (.bmx)")
def menu_func_bm_export(self, context):
self.layout.operator(bm_import_export.BALLANCE_OT_export_bm.bl_idname, text="Ballance Map (.bmx)")
self.layout.operator(BMFILE_export.BALLANCE_OT_export_bm.bl_idname, text="Ballance Map (.bmx)")
def menu_func_ballance_3d(self, context):
layout = self.layout
layout.menu(BALLANCE_MT_ThreeDViewerMenu.bl_idname)
@ -106,42 +175,75 @@ 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.operator_menu_enum(
OBJS_add_components.BALLANCE_OT_add_components.bl_idname,
"elements_type", icon='MESH_ICOSPHERE', text="Elements")
layout.operator(OBJS_add_rails.BALLANCE_OT_add_rails.bl_idname, icon='MESH_CIRCLE', text="Rail section")
layout.menu(BALLANCE_MT_AddFloorMenu.bl_idname, icon='MESH_CUBE')
def menu_func_ballance_rename(self, context):
layout = self.layout
layout.menu(BALLANCE_MT_OutlinerMenu.bl_idname)
def menu_func_ballance_select(self, context):
layout = self.layout
layout.separator()
layout.label(text="Ballance")
layout.operator(OBJS_group_opers.BALLANCE_OT_select_virtools_group.bl_idname, icon='SELECT_SET')
layout.operator(OBJS_group_opers.BALLANCE_OT_filter_virtools_group.bl_idname, icon='FILTER')
def menu_func_ballance_grouping(self, context):
layout = self.layout
layout.separator()
col = layout.column()
col.operator_context = 'INVOKE_DEFAULT'
col.label(text="Ballance")
col.operator(OBJS_group_opers.BALLANCE_OT_ctx_set_group.bl_idname, icon='ADD', text="Group into...")
col.operator(OBJS_group_opers.BALLANCE_OT_ctx_unset_group.bl_idname, icon='REMOVE', text="Ungroup from...")
col.operator(OBJS_group_opers.BALLANCE_OT_ctx_clear_group.bl_idname, icon='TRASH', text="Clear Grouping")
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():
UTILS_constants.icons_floor = bpy.utils.previews.new()
for key, value in UTILS_constants.floor_blockDict.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
UTILS_constants.icons_floor.load(blockIconName, os.path.join(icon_path, "floor", value["BindingDisplayTexture"]), 'IMAGE')
UTILS_constants.icons_floorDict[key] = UTILS_constants.icons_floor[blockIconName].icon_id
for cls in classes:
bpy.utils.register_class(cls)
bpy.types.Scene.BallanceBlenderPluginProperty = bpy.props.PointerProperty(type=preferences.MyPropertyGroup)
bpy.types.Scene.BallanceBlenderPluginProperty = bpy.props.PointerProperty(type=UTILS_preferences.MyPropertyGroup)
UTILS_virtools_prop.register_props()
bpy.types.TOPBAR_MT_file_import.append(menu_func_bm_import)
bpy.types.TOPBAR_MT_file_export.append(menu_func_bm_export)
bpy.types.VIEW3D_HT_header.append(menu_func_ballance_3d)
bpy.types.VIEW3D_MT_editor_menus.prepend(menu_func_ballance_3d)
bpy.types.VIEW3D_MT_add.append(menu_func_ballance_add)
bpy.types.OUTLINER_HT_header.append(menu_func_ballance_rename)
bpy.types.VIEW3D_MT_select_object.append(menu_func_ballance_select)
bpy.types.VIEW3D_MT_object_context_menu.append(menu_func_ballance_grouping)
def unregister():
bpy.types.TOPBAR_MT_file_import.remove(menu_func_bm_import)
bpy.types.TOPBAR_MT_file_export.remove(menu_func_bm_export)
bpy.types.VIEW3D_HT_header.remove(menu_func_ballance_3d)
bpy.types.VIEW3D_MT_editor_menus.remove(menu_func_ballance_3d)
bpy.types.VIEW3D_MT_add.remove(menu_func_ballance_add)
bpy.types.OUTLINER_HT_header.remove(menu_func_ballance_rename)
bpy.types.VIEW3D_MT_select_object.remove(menu_func_ballance_select)
bpy.types.VIEW3D_MT_object_context_menu.remove(menu_func_ballance_grouping)
UTILS_virtools_prop.unregister_props()
del bpy.types.Scene.BallanceBlenderPluginProperty
for cls in classes:
bpy.utils.unregister_class(cls)
# we need uninstall all icon after all classes unregister
bpy.utils.previews.remove(config.blenderIcon_floor)
bpy.utils.previews.remove(UTILS_constants.icons_floor)
if __name__=="__main__":
register()

View File

@ -1,907 +0,0 @@
import bpy,bmesh,bpy_extras,mathutils
import pathlib,zipfile,time,os,tempfile,math
import struct,shutil
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_import_bm(bpy.types.Operator, bpy_extras.io_utils.ImportHelper):
"""Load a Ballance Map File (BM file spec 1.3)"""
bl_idname = "ballance.import_bm"
bl_label = "Import BM "
bl_options = {'PRESET', 'UNDO'}
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
def poll(self, context):
prefs = bpy.context.preferences.addons[__package__].preferences
return (os.path.isdir(prefs.temp_texture_folder) and os.path.isdir(prefs.external_folder))
def execute(self, context):
prefs = bpy.context.preferences.addons[__package__].preferences
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'}
class BALLANCE_OT_export_bm(bpy.types.Operator, bpy_extras.io_utils.ExportHelper):
"""Save a Ballance Map File (BM file spec 1.3)"""
bl_idname = "ballance.export_bm"
bl_label = 'Export BM'
bl_options = {'PRESET'}
filename_ext = ".bmx"
export_mode: bpy.props.EnumProperty(
name="Export mode",
items=(('COLLECTION', "Collection", "Export a collection"),
('OBJECT', "Objects", "Export an objects"),
),
)
def execute(self, context):
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'}
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
bm_current_version = 13
def import_bm(context,filepath,externalTexture,blenderTempFolder, textureOpt, materialOpt, meshOpt, objectOpt):
# ============================================ alloc a temp folder
tempFolderObj = tempfile.TemporaryDirectory()
tempFolder = tempFolderObj.name
# debug
# print(tempFolder)
tempTextureFolder = os.path.join(tempFolder, "Texture")
prefs = bpy.context.preferences.addons[__package__].preferences
blenderTempTextureFolder = prefs.temp_texture_folder
externalTextureFolder = prefs.external_folder
with zipfile.ZipFile(filepath, 'r', zipfile.ZIP_DEFLATED, 9) as zipObj:
zipObj.extractall(tempFolder)
# index.bm
objectList = []
meshList = []
materialList = []
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:
index_name = read_string(findex)
index_type = read_uint8(findex)
index_offset = read_uint64(findex)
blockCache = info_block_helper(index_name, index_offset)
if index_type == info_bm_type.OBJECT:
objectList.append(blockCache)
elif index_type == info_bm_type.MESH:
meshList.append(blockCache)
elif index_type == info_bm_type.MATERIAL:
materialList.append(blockCache)
elif index_type == info_bm_type.TEXTURE:
textureList.append(blockCache)
else:
pass
# texture.bm
with open(os.path.join(tempFolder, "texture.bm"), "rb") as ftexture:
for item in textureList:
ftexture.seek(item.offset, os.SEEK_SET)
texture_filename = read_string(ftexture)
texture_isExternal = read_bool(ftexture)
if texture_isExternal:
txur = load_image(texture_filename, externalTextureFolder, check_existing=(textureOpt == 'CURRENT'))
item.blenderData = txur
else:
# not external. copy temp file into blender temp. then use it.
# try copy. if fail, don't need to do more
try:
shutil.copy(os.path.join(tempTextureFolder, texture_filename), os.path.join(blenderTempTextureFolder, texture_filename))
except:
pass
txur = load_image(texture_filename, blenderTempTextureFolder, check_existing=(textureOpt == 'CURRENT'))
item.blenderData = txur
txur.name = item.name
# material.bm
# 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:
fmaterial.seek(item.offset, os.SEEK_SET)
# read data
material_colAmbient = read_3vector(fmaterial)
material_colDiffuse = read_3vector(fmaterial)
material_colSpecular = read_3vector(fmaterial)
material_colEmissive = read_3vector(fmaterial)
material_specularPower = read_float(fmaterial)
material_useTexture = read_bool(fmaterial)
material_texture = read_uint32(fmaterial)
# create basic material
(m, needSkip) = createInstanceWithOption(info_bm_type.MATERIAL, item.name, materialOpt)
item.blenderData = m
if needSkip:
continue
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])
m.metallic = sum(material_colAmbient) / 3
m.diffuse_color = [i for i in material_colDiffuse] + [1]
m.specular_color = material_colSpecular
m.specular_intensity = material_specularPower
# create a texture
if material_useTexture:
inode=m.node_tree.nodes.new(type="ShaderNodeTexImage")
inode.image=textureList[material_texture].blenderData
m.node_tree.links.new(inode.outputs[0],bnode.inputs[0])
# write custom property
m['virtools-ambient'] = material_colAmbient
m['virtools-diffuse'] = material_colDiffuse
m['virtools-specular'] = material_colSpecular
m['virtools-emissive'] = material_colEmissive
m['virtools-power'] = material_specularPower
# mesh.bm
# WARNING: this code is shared with add_floor
with open(os.path.join(tempFolder, "mesh.bm"), "rb") as fmesh:
vList=[]
vtList=[]
vnList=[]
faceList=[]
materialSolt = []
for item in meshList:
fmesh.seek(item.offset, os.SEEK_SET)
# create real mesh
(mesh, needSkip) = createInstanceWithOption(info_bm_type.MESH, item.name, meshOpt)
item.blenderData = mesh
if needSkip:
continue
vList.clear()
vtList.clear()
vnList.clear()
faceList.clear()
materialSolt.clear()
# in first read, store all data into list
listCount = read_uint32(fmesh)
for i in range(listCount):
cache = read_3vector(fmesh)
# switch yz
vList.append((cache[0], cache[2], cache[1]))
listCount = read_uint32(fmesh)
for i in range(listCount):
cache = read_2vector(fmesh)
# reverse v
vtList.append((cache[0], -cache[1]))
listCount = read_uint32(fmesh)
for i in range(listCount):
cache = read_3vector(fmesh)
# switch yz
vnList.append((cache[0], cache[2], cache[1]))
listCount = read_uint32(fmesh)
for i in range(listCount):
faceData = read_face(fmesh)
mesh_useMaterial = read_bool(fmesh)
mesh_materialIndex = read_uint32(fmesh)
if mesh_useMaterial:
neededMaterial = materialList[mesh_materialIndex].blenderData
if neededMaterial in materialSolt:
neededIndex = materialSolt.index(neededMaterial)
else:
neededIndex = len(materialSolt)
materialSolt.append(neededMaterial)
else:
neededIndex = -1
# we need invert triangle sort
faceList.append((
faceData[6], faceData[7], faceData[8],
faceData[3], faceData[4], faceData[5],
faceData[0], faceData[1], faceData[2],
neededIndex
))
# and then we need add material solt for this mesh
for mat in materialSolt:
mesh.materials.append(mat)
# then, we need add correspond count for vertices
mesh.vertices.add(len(vList))
mesh.loops.add(len(faceList)*3) # triangle face confirm
mesh.polygons.add(len(faceList))
mesh.uv_layers.new(do_init=False)
mesh.create_normals_split()
# add vertices data
mesh.vertices.foreach_set("co", unpack_list(vList))
mesh.loops.foreach_set("vertex_index", unpack_list(flat_vertices_index(faceList)))
mesh.loops.foreach_set("normal", unpack_list(flat_vertices_normal(faceList, vnList)))
mesh.uv_layers[0].data.foreach_set("uv", unpack_list(flat_vertices_uv(faceList, vtList)))
for i in range(len(faceList)):
mesh.polygons[i].loop_start = i * 3
mesh.polygons[i].loop_total = 3
if faceList[i][9] != -1:
mesh.polygons[i].material_index = faceList[i][9]
mesh.polygons[i].use_smooth = True
mesh.validate(clean_customdata=False)
mesh.update(calc_edges=False, calc_edges_loose=False)
# object
with open(os.path.join(tempFolder, "object.bm"), "rb") as fobject:
# we need get needed collection first
view_layer = context.view_layer
collection = view_layer.active_layer_collection.collection
if prefs.no_component_collection == "":
forcedCollection = None
else:
try:
forcedCollection = bpy.data.collections[prefs.no_component_collection]
except:
forcedCollection = bpy.data.collections.new(prefs.no_component_collection)
view_layer.active_layer_collection.collection.children.link(forcedCollection)
# start process it
object_groupList = []
for item in objectList:
fobject.seek(item.offset, os.SEEK_SET)
# read data
object_isComponent = read_bool(fobject)
object_isForcedNoComponent = read_bool(fobject)
object_isHidden = read_bool(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)
# got mesh first
if object_isComponent:
neededMesh = load_component(object_meshIndex)
else:
neededMesh = meshList[object_meshIndex].blenderData
# create real object
(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):
forcedCollection.objects.link(obj)
else:
collection.objects.link(obj)
obj.matrix_world = object_worldMatrix
obj.hide_set(object_isHidden)
# write custom property
if len(object_groupList) != 0:
obj['virtools-group'] = tuple(object_groupList)
view_layer.update()
tempFolderObj.cleanup()
def export_bm(context, filepath, export_mode, export_target):
# ============================================ alloc a temp folder
tempFolderObj = tempfile.TemporaryDirectory()
tempFolder = tempFolderObj.name
# debug
# tempFolder = "G:\\ziptest"
tempTextureFolder = os.path.join(tempFolder, "Texture")
os.makedirs(tempTextureFolder)
prefs = bpy.context.preferences.addons[__package__].preferences
# ============================================ find export target. don't need judge them in there. just collect them
if export_mode== "COLLECTION":
objectList = export_target.objects
else:
objectList = [export_target]
# try get forcedCollection
try:
forcedCollection = bpy.data.collections[prefs.no_component_collection]
except:
forcedCollection = None
# ============================================ export
with open(os.path.join(tempFolder, "index.bm"), "wb") as finfo:
write_uint32(finfo, bm_current_version)
# ====================== export object
meshSet = set()
meshList = []
meshCount = 0
with open(os.path.join(tempFolder, "object.bm"), "wb") as fobject:
for obj in objectList:
# only export mesh object
if obj.type != 'MESH':
continue
# clean no mesh object
currentMesh = obj.data
if currentMesh is None:
continue
# judge component
object_isComponent = is_component(obj.name)
object_isForcedNoComponent = False
if (forcedCollection is not None) and (obj.name in forcedCollection.objects):
# change it to forced no component
object_isComponent = False
object_isForcedNoComponent = True
# triangle first and then group
if not object_isComponent:
if currentMesh not in meshSet:
mesh_triangulate(currentMesh)
meshSet.add(currentMesh)
meshList.append(currentMesh)
meshId = meshCount
meshCount += 1
else:
meshId = meshList.index(currentMesh)
else:
meshId = get_component_id(obj.name)
# get visibility
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_string(finfo, obj.name)
write_uint8(finfo, info_bm_type.OBJECT)
write_uint64(finfo, fobject.tell())
# write fobject
write_bool(fobject, object_isComponent)
write_bool(fobject, object_isForcedNoComponent)
write_bool(fobject, object_isHidden)
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)
# ====================== export mesh
materialSet = set()
materialList = []
with open(os.path.join(tempFolder, "mesh.bm"), "wb") as fmesh:
for mesh in meshList:
mesh.calc_normals_split()
# write finfo first
write_string(finfo, mesh.name)
write_uint8(finfo, info_bm_type.MESH)
write_uint64(finfo, fmesh.tell())
# write fmesh
# vertices
vecList = mesh.vertices[:]
write_uint32(fmesh, len(vecList))
for vec in vecList:
#swap yz
write_3vector(fmesh,vec.co[0],vec.co[2],vec.co[1])
# uv
face_index_pairs = [(face, index) for index, face in enumerate(mesh.polygons)]
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:
# it should be triangle face, otherwise throw a error
if (f.loop_total != 3):
raise Exception("Not a triangle", f.poly.loop_total)
for loop_index in range(f.loop_start, f.loop_start + f.loop_total):
uv = uv_layer[loop_index].uv
# reverse v
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
write_uint32(fmesh, len(face_index_pairs) * 3)
for f, f_index in face_index_pairs:
# no need to check triangle again
for loop_index in range(f.loop_start, f.loop_start + f.loop_total):
nml = mesh.loops[loop_index].normal
# swap yz
write_3vector(fmesh, nml[0], nml[2], nml[1])
# face
# get material first
currentMat = mesh.materials[:]
noMaterial = len(currentMat) == 0
for mat in currentMat:
if mat not in materialSet:
materialSet.add(mat)
materialList.append(mat)
write_uint32(fmesh, len(face_index_pairs))
vtIndex = []
vnIndex = []
vIndex = []
for f, f_index in face_index_pairs:
# confirm material use
if noMaterial:
usedMat = 0
else:
usedMat = materialList.index(currentMat[f.material_index])
# export face
vtIndex.clear()
vnIndex.clear()
vIndex.clear()
counter = 0
for loop_index in range(f.loop_start, f.loop_start + f.loop_total):
vIndex.append(mesh.loops[loop_index].vertex_index)
vnIndex.append(f_index * 3 + counter)
vtIndex.append(f_index * 3 + counter)
counter += 1
# reverse vertices sort
write_face(fmesh,
vIndex[2], vtIndex[2], vnIndex[2],
vIndex[1], vtIndex[1], vnIndex[1],
vIndex[0], vtIndex[0], vnIndex[0])
# set used material
write_bool(fmesh, not noMaterial)
write_uint32(fmesh, usedMat)
mesh.free_normals_split()
# ====================== export material
textureSet = set()
textureList = []
textureCount = 0
with open(os.path.join(tempFolder, "material.bm"), "wb") as fmaterial:
for material in materialList:
# write finfo first
write_string(finfo, material.name)
write_uint8(finfo, info_bm_type.MATERIAL)
write_uint64(finfo, fmaterial.tell())
# try get original written data
material_colAmbient = try_get_custom_property(material, 'virtools-ambient')
material_colDiffuse = try_get_custom_property(material, 'virtools-diffuse')
material_colSpecular = try_get_custom_property(material, 'virtools-specular')
material_colEmissive = try_get_custom_property(material, 'virtools-emissive')
material_specularPower = try_get_custom_property(material, 'virtools-power')
# get basic color
mat_wrap = node_shader_utils.PrincipledBSDFWrapper(material)
if mat_wrap:
use_mirror = mat_wrap.metallic != 0.0
if use_mirror:
material_colAmbient = set_value_when_none(material_colAmbient, (mat_wrap.metallic, mat_wrap.metallic, mat_wrap.metallic))
else:
material_colAmbient = set_value_when_none(material_colAmbient, (1.0, 1.0, 1.0))
material_colDiffuse = set_value_when_none(material_colDiffuse, (mat_wrap.base_color[0], mat_wrap.base_color[1], mat_wrap.base_color[2]))
material_colSpecular = set_value_when_none(material_colSpecular, (mat_wrap.specular, mat_wrap.specular, mat_wrap.specular))
material_colEmissive = set_value_when_none(material_colEmissive, mat_wrap.emission_color[:3])
material_specularPower = set_value_when_none(material_specularPower, 0.0)
# confirm texture
tex_wrap = getattr(mat_wrap, "base_color_texture", None)
if tex_wrap:
image = tex_wrap.image
if image:
# add into texture list
if image not in textureSet:
textureSet.add(image)
textureList.append(image)
currentTexture = textureCount
textureCount += 1
else:
currentTexture = textureList.index(image)
material_useTexture = True
material_texture = currentTexture
else:
# no texture
material_useTexture = False
material_texture = 0
else:
# no texture
material_useTexture = False
material_texture = 0
else:
# no Principled BSDF. write garbage
material_colAmbient = set_value_when_none(material_colAmbient, (0.8, 0.8, 0.8))
material_colDiffuse = set_value_when_none(material_colDiffuse, (0.8, 0.8, 0.8))
material_colSpecular = set_value_when_none(material_colSpecular, (0.8, 0.8, 0.8))
material_colEmissive = set_value_when_none(material_colEmissive, (0.8, 0.8, 0.8))
material_specularPower = set_value_when_none(material_specularPower, 0.0)
material_useTexture = False
material_texture = 0
write_color(fmaterial, material_colAmbient)
write_color(fmaterial, material_colDiffuse)
write_color(fmaterial, material_colSpecular)
write_color(fmaterial, material_colEmissive)
write_float(fmaterial, material_specularPower)
write_bool(fmaterial, material_useTexture)
write_uint32(fmaterial, material_texture)
# ====================== export texture
source_dir = os.path.dirname(bpy.data.filepath)
existed_texture = set()
with open(os.path.join(tempFolder, "texture.bm"), "wb") as ftexture:
for texture in textureList:
# write finfo first
write_string(finfo, texture.name)
write_uint8(finfo, info_bm_type.TEXTURE)
write_uint64(finfo, ftexture.tell())
# confirm internal
texture_filepath = io_utils.path_reference(texture.filepath, source_dir, tempTextureFolder,
'ABSOLUTE', "", None, texture.library)
filename = os.path.basename(texture_filepath)
write_string(ftexture, filename)
if (is_external_texture(filename)):
write_bool(ftexture, True)
else:
# copy internal texture, if this file is copied, do not copy it again
write_bool(ftexture, False)
if filename not in existed_texture:
shutil.copy(texture_filepath, os.path.join(tempTextureFolder, filename))
existed_texture.add(filename)
# ============================================ save zip and clean up folder
if os.path.isfile(filepath):
os.remove(filepath)
with zipfile.ZipFile(filepath, 'w', zipfile.ZIP_DEFLATED, 9) as zipObj:
for folderName, subfolders, filenames in os.walk(tempFolder):
for filename in filenames:
filePath = os.path.join(folderName, filename)
arcname=os.path.relpath(filePath, tempFolder)
zipObj.write(filePath, arcname)
tempFolderObj.cleanup()
# ======================================================================================= export / import assistant
# shared
class info_bm_type():
OBJECT = 0
MESH = 1
MATERIAL = 2
TEXTURE = 3
# import
class info_block_helper():
def __init__(self, name, offset):
self.name = name
self.offset = offset
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):
# get file first
compName = config.component_list[component_id]
selectedFile = os.path.join(
os.path.dirname(__file__),
'meshes',
compName + '.bin'
)
# read file. please note this sector is sync with import_bm's mesh's code. when something change, please change each other.
fmesh = open(selectedFile, 'rb')
# create real mesh, we don't need to consider name. blender will solve duplicated name
mesh = bpy.data.meshes.new('mesh_' + compName)
vList = []
vnList = []
faceList = []
# in first read, store all data into list
listCount = read_uint32(fmesh)
for i in range(listCount):
cache = read_3vector(fmesh)
# switch yz
vList.append((cache[0], cache[2], cache[1]))
listCount = read_uint32(fmesh)
for i in range(listCount):
cache = read_3vector(fmesh)
# switch yz
vnList.append((cache[0], cache[2], cache[1]))
listCount = read_uint32(fmesh)
for i in range(listCount):
faceData = read_component_face(fmesh)
# we need invert triangle sort
faceList.append((
faceData[4], faceData[5],
faceData[2], faceData[3],
faceData[0], faceData[1]
))
# then, we need add correspond count for vertices
mesh.vertices.add(len(vList))
mesh.loops.add(len(faceList)*3) # triangle face confirm
mesh.polygons.add(len(faceList))
mesh.create_normals_split()
# add vertices data
mesh.vertices.foreach_set("co", unpack_list(vList))
mesh.loops.foreach_set("vertex_index", unpack_list(flat_component_vertices_index(faceList)))
mesh.loops.foreach_set("normal", unpack_list(flat_component_vertices_normal(faceList, vnList)))
for i in range(len(faceList)):
mesh.polygons[i].loop_start = i * 3
mesh.polygons[i].loop_total = 3
mesh.polygons[i].use_smooth = True
mesh.validate(clean_customdata=False)
mesh.update(calc_edges=False, calc_edges_loose=False)
fmesh.close()
return mesh
def flat_vertices_index(faceList):
for item in faceList:
yield (item[0], )
yield (item[3], )
yield (item[6], )
def flat_vertices_normal(faceList, vnList):
for item in faceList:
yield vnList[item[2]]
yield vnList[item[5]]
yield vnList[item[8]]
def flat_vertices_uv(faceList, vtList):
for item in faceList:
yield vtList[item[1]]
yield vtList[item[4]]
yield vtList[item[7]]
def flat_component_vertices_index(faceList):
for item in faceList:
yield (item[0], )
yield (item[2], )
yield (item[4], )
def flat_component_vertices_normal(faceList, vnList):
for item in faceList:
yield vnList[item[1]]
yield vnList[item[3]]
yield vnList[item[5]]
# export
def is_component(name):
return get_component_id(name) != -1
def get_component_id(name):
for ind, comp in enumerate(config.component_list):
if name.startswith(comp):
return ind
return -1
def is_external_texture(name):
if name in config.external_texture_list:
return True
else:
return False
def mesh_triangulate(me):
bm = bmesh.new()
bm.from_mesh(me)
bmesh.ops.triangulate(bm, faces=bm.faces)
bm.to_mesh(me)
bm.free()
def try_get_custom_property(obj, field):
try:
return obj[field]
except:
return None
def set_value_when_none(obj, newValue):
if obj is None:
return newValue
else:
return obj
# ======================================================================================= file io assistant
# import
def peek_stream(fs):
res = fs.read(1)
fs.seek(-1, os.SEEK_CUR)
return res
def read_float(fs):
return struct.unpack("f", fs.read(4))[0]
def read_uint8(fs):
return struct.unpack("B", fs.read(1))[0]
def read_uint32(fs):
return struct.unpack("I", fs.read(4))[0]
def read_uint64(fs):
return struct.unpack("Q", fs.read(8))[0]
def read_string(fs):
count = read_uint32(fs)
return fs.read(count*4).decode("utf_32_le")
def read_bool(fs):
return read_uint8(fs) != 0
def read_worldMaterix(fs):
p = struct.unpack("ffffffffffffffff", fs.read(4*4*4))
res = mathutils.Matrix((
(p[0], p[2], p[1], p[3]),
(p[8], p[10], p[9], p[11]),
(p[4], p[6], p[5], p[7]),
(p[12], p[14], p[13], p[15])))
return res.transposed()
def read_3vector(fs):
return struct.unpack("fff", fs.read(3*4))
def read_2vector(fs):
return struct.unpack("ff", fs.read(2*4))
def read_face(fs):
return struct.unpack("IIIIIIIII", fs.read(4*9))
def read_component_face(fs):
return struct.unpack("IIIIII", fs.read(4*6))
# export
def write_string(fs,str):
count=len(str)
write_uint32(fs,count)
fs.write(str.encode("utf_32_le"))
def write_uint8(fs,num):
fs.write(struct.pack("B", num))
def write_uint32(fs,num):
fs.write(struct.pack("I", num))
def write_uint64(fs,num):
fs.write(struct.pack("Q", num))
def write_bool(fs,boolean):
if boolean:
write_uint8(fs, 1)
else:
write_uint8(fs, 0)
def write_float(fs,fl):
fs.write(struct.pack("f", fl))
def write_worldMatrix(fs, matt):
mat = matt.transposed()
fs.write(struct.pack("ffffffffffffffff",
mat[0][0],mat[0][2], mat[0][1], mat[0][3],
mat[2][0],mat[2][2], mat[2][1], mat[2][3],
mat[1][0],mat[1][2], mat[1][1], mat[1][3],
mat[3][0],mat[3][2], mat[3][1], mat[3][3]))
def write_3vector(fs, x, y ,z):
fs.write(struct.pack("fff", x, y ,z))
def write_color(fs, colors):
write_3vector(fs, colors[0], colors[1], colors[2])
def write_2vector(fs, u, v):
fs.write(struct.pack("ff", u, v))
def write_face(fs, v1, vt1, vn1, v2, vt2, vn2, v3, vt3, vn3):
fs.write(struct.pack("IIIIIIIII", v1, vt1, vn1, v2, vt2, vn2, v3, vt3, vn3))

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

File diff suppressed because it is too large Load Diff

View File

@ -1,608 +0,0 @@
[
{
"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"
}
]
}
]

View File

@ -0,0 +1,483 @@
[
{
"Type": "Flat",
"BindingDisplayTexture": "Flat.png",
"UnitSize": "Small",
"ExpandType": "Freedom",
"InitColumnDirection": "PositiveY",
"DefaultSideConfig": {
"UseTwoDTop": false,
"UseTwoDRight": false,
"UseTwoDBottom": false,
"UseTwoDLeft": false,
"UseThreeDTop": true,
"UseThreeDBottom": false
},
"ThreeDTopFace": {
"Vertices": [
"0, 0, 0",
"2.5+d2, 0, 0",
"2.5+d2, 2.5+d1, 0",
"0, 2.5+d1, 0"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "FloorTopBorderless",
"Indices": [
0,
1,
2,
3
],
"UVs": [
"0, 0.5",
"0, -d2",
"0.5+d1, -d2",
"0.5+d1, 0.5"
]
}
]
},
"ThreeDBottomFace": {
"Vertices": [
"0, 0, -5-d3",
"2.5+d2, 0, -5-d3",
"2.5+d2, 2.5+d1, -5-d3",
"0, 2.5+d1, -5-d3"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "FloorTopBorderless_ForSide",
"Indices": [
3,
2,
1,
0
],
"UVs": [
"0, 0.5",
"0, -d2",
"0.5+d1, -d2",
"0.5+d1, 0.5"
]
}
]
},
"TwoDTopSide": {
"Vertices": [
"0, 0, 0",
"0, 2.5+d1, 0",
"0, 2.5+d1, -5-d3",
"0, 0, -5-d3"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "FloorTopBorderless_ForSide",
"Indices": [
0,
1,
2,
3
],
"UVs": [
"0, 0",
"0, 0.5+d1",
"1+d3, 0.5+d1",
"1+d3, 0"
]
}
]
},
"TwoDRightSide": {
"Vertices": [
"2.5+d2, 2.5+d1, 0",
"2.5+d2, 2.5+d1, -5-d3",
"0, 2.5+d1, -5-d3",
"0, 2.5+d1, 0"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "FloorTopBorderless_ForSide",
"Indices": [
0,
1,
2,
3
],
"UVs": [
"0, -d2",
"1+d3, -d2",
"1+d3, 0.5",
"0, 0.5"
]
}
]
},
"TwoDBottomSide": {
"Vertices": [
"2.5+d2, 2.5+d1, 0",
"2.5+d2, 0, 0",
"2.5+d2, 0, -5-d3",
"2.5+d2, 2.5+d1, -5-d3"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "FloorTopBorderless_ForSide",
"Indices": [
0,
1,
2,
3
],
"UVs": [
"0, 0.5+d1",
"0, 0",
"1+d3, 0",
"1+d3, 0.5+d1"
]
}
]
},
"TwoDLeftSide": {
"Vertices": [
"0, 0, 0",
"0, 0, -5-d3",
"2.5+d2, 0, -5-d3",
"2.5+d2, 0, 0"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "FloorTopBorderless_ForSide",
"Indices": [
0,
1,
2,
3
],
"UVs": [
"0, 0.5",
"1+d3, 0.5",
"1+d3, -d2",
"0, -d2"
]
}
]
},
"TwoDTopSideExpand": null,
"TwoDRightSideExpand": null,
"TwoDBottomSideExpand": null,
"TwoDLeftSideExpand": null
},
{
"Type": "NormalSinkTransition",
"BindingDisplayTexture": "NormalSinkTransition.png",
"UnitSize": "Large",
"ExpandType": "Static",
"InitColumnDirection": "PositiveX",
"DefaultSideConfig": {
"UseTwoDTop": false,
"UseTwoDRight": true,
"UseTwoDBottom": false,
"UseTwoDLeft": true,
"UseThreeDTop": true,
"UseThreeDBottom": false
},
"ThreeDTopFace": {
"Vertices": [
"0, 0, 0",
"5, 0, 0",
"0, 2.5, -0.7",
"5, 5, 0",
"0, 5, 0"
],
"Faces": [
{
"Type": "TRIANGLE",
"Textures": "FloorTopProfilFlat",
"Indices": [
0,
1,
2
],
"UVs": [
"0, 1",
"0, 0",
"0.5, 1"
]
},
{
"Type": "TRIANGLE",
"Textures": "FloorTopProfilFlat",
"Indices": [
3,
2,
1
],
"UVs": [
"1, 0",
"0.5, 1",
"0, 0"
]
},
{
"Type": "TRIANGLE",
"Textures": "FloorTopProfilFlat",
"Indices": [
2,
3,
4
],
"UVs": [
"0.5, 1",
"1, 0",
"1, 1"
]
}
]
},
"ThreeDBottomFace": {
"Vertices": [
"5, 0, -5-d3",
"5, 5, -5-d3",
"0, 5, -5-d3",
"0, 0, -5-d3"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "FloorTopBorderless_ForSide",
"Indices": [
3,
2,
1,
0
],
"UVs": [
"0, 1",
"1, 1",
"1, 0",
"0, 0"
]
}
]
},
"TwoDTopSide": {
"Vertices": [
"0, 0, -5-d3",
"0, 0, 0",
"0, 2.5, -0.7",
"0, 2.5, -5-d3",
"0, 5, -5-d3",
"0, 5, 0"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "FloorTopBorderless_ForSide",
"Indices": [
0,
1,
2,
3
],
"UVs": [
"0, -d3",
"0, 1",
"0.5, 0.86",
"0.5, -d3"
]
},
{
"Type": "RECTANGLE",
"Textures": "FloorTopBorderless_ForSide",
"Indices": [
5,
4,
3,
2
],
"UVs": [
"1, 1",
"1, -d3",
"0.5, -d3",
"0.5, 0.86"
]
}
]
},
"TwoDRightSide": {
"Vertices": [
"5, 5, 0",
"5, 5, -5-d3",
"0, 5, -5-d3",
"0, 5, 0"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "FloorSide",
"Indices": [
0,
1,
2,
3
],
"UVs": [
"0, 0",
"1+d3, 0",
"1+d3, 1",
"0, 1"
]
}
]
},
"TwoDBottomSide": {
"Vertices": [
"5, 5, 0",
"5, 0, 0",
"5, 0, -5-d3",
"5, 5, -5-d3"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "FloorTopBorderless_ForSide",
"Indices": [
0,
1,
2,
3
],
"UVs": [
"0, 1",
"0, 0",
"1+d3, 0",
"1+d3, 1"
]
}
]
},
"TwoDLeftSide": {
"Vertices": [
"5, 0, 0",
"5, 0, -5-d3",
"0, 0, -5-d3",
"0, 0, 0"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "FloorSide",
"Indices": [
3,
2,
1,
0
],
"UVs": [
"0, 1",
"1+d3, 1",
"1+d3, 0",
"0, 0"
]
}
]
},
"TwoDTopSideExpand": null,
"TwoDRightSideExpand": {
"Vertices": [
"0, 5, 0",
"5, 5, 0",
"5, 5, -2.5",
"0, 5, -2.5",
"0, 5, -5-d3",
"5, 5, -5-d3"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "FloorTopBorder_ForSide",
"Indices": [
0,
1,
2,
3
],
"UVs": [
"0, 1",
"0, 0",
"0.5, 0",
"0.5, 1"
]
},
{
"Type": "RECTANGLE",
"Textures": "FloorTopBorderless_ForSide",
"Indices": [
5,
4,
3,
2
],
"UVs": [
"0.5+d3, 0",
"0.5+d3, 1",
"0, 1",
"0, 0"
]
}
]
},
"TwoDBottomSideExpand": null,
"TwoDLeftSideExpand": {
"Vertices": [
"0, 0, 0",
"5, 0, 0",
"5, 0, -2.5",
"0, 0, -2.5",
"0, 0, -5-d3",
"5, 0, -5-d3"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "FloorTopBorder_ForSide",
"Indices": [
3,
2,
1,
0
],
"UVs": [
"0.5, 1",
"0.5, 0",
"0, 0",
"0, 1"
]
},
{
"Type": "RECTANGLE",
"Textures": "FloorTopBorderless_ForSide",
"Indices": [
2,
3,
4,
5
],
"UVs": [
"0, 0",
"0, 1",
"0.5+d3, 1",
"0.5+d3, 0"
]
}
]
}
}
]

View File

@ -0,0 +1,680 @@
[
{
"Type": "NormalBorder",
"BindingDisplayTexture": "NormalBorder.png",
"UnitSize": "Small",
"ExpandType": "Column",
"InitColumnDirection": "PositiveX",
"DefaultSideConfig": {
"UseTwoDTop": false,
"UseTwoDRight": false,
"UseTwoDBottom": false,
"UseTwoDLeft": true,
"UseThreeDTop": true,
"UseThreeDBottom": false
},
"ThreeDTopFace": {
"Vertices": [
"0, 0, 0",
"2.5+d1, 0, 0",
"2.5+d1, 2.5, 0",
"0, 2.5, 0"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "FloorTopFlat",
"Indices": [
0,
1,
2,
3
],
"UVs": [
"0, 0.5",
"0, -d1",
"0.5, -d1",
"0.5, 0.5"
]
}
]
},
"ThreeDBottomFace": {
"Vertices": [
"2.5+d1, 0, -5-d3",
"2.5+d1, 2.5, -5-d3",
"0, 2.5, -5-d3",
"0, 0, -5-d3"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "FloorTopBorderless_ForSide",
"Indices": [
3,
2,
1,
0
],
"UVs": [
"0, 0.5",
"0.5, 0.5",
"0.5, -d1",
"0, -d1"
]
}
]
},
"TwoDTopSide": {
"Vertices": [
"0, 0, -5-d3",
"0, 2.5, -5-d3",
"0, 2.5, 0",
"0, 0, 0"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "FloorTopBorderless_ForSide",
"Indices": [
3,
2,
1,
0
],
"UVs": [
"0, 0.5",
"0, 0",
"1+d3, 0",
"1+d3, 0.5"
]
}
]
},
"TwoDRightSide": {
"Vertices": [
"2.5+d1, 2.5, 0",
"2.5+d1, 2.5, -5-d3",
"0, 2.5, -5-d3",
"0, 2.5, 0"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "FloorTopBorderless_ForSide",
"Indices": [
0,
1,
2,
3
],
"UVs": [
"0, -d1",
"1+d3, -d1",
"1+d3, 0.5",
"0, 0.5"
]
}
]
},
"TwoDBottomSide": {
"Vertices": [
"2.5+d1, 0, -5-d3",
"2.5+d1, 2.5, -5-d3",
"2.5+d1, 2.5, 0",
"2.5+d1, 0, 0"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "FloorTopBorderless_ForSide",
"Indices": [
0,
1,
2,
3
],
"UVs": [
"1+d3, 0.5",
"1+d3, 0",
"0, 0",
"0, 0.5"
]
}
]
},
"TwoDLeftSide": {
"Vertices": [
"2.5+d1, 0, 0",
"2.5+d1, 0, -5-d3",
"0, 0, -5-d3",
"0, 0, 0"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "FloorSide",
"Indices": [
3,
2,
1,
0
],
"UVs": [
"0, 0.5",
"1+d3, 0.5",
"1+d3, -d1",
"0, -d1"
]
}
]
},
"TwoDTopSideExpand": null,
"TwoDRightSideExpand": null,
"TwoDBottomSideExpand": null,
"TwoDLeftSideExpand": {
"Vertices": [
"2.5+d1, 0, -5-d3",
"0, 0, -5-d3",
"0, 0, -2.5",
"2.5+d1, 0, -2.5",
"2.5+d1, 0, 0",
"0, 0, 0"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "FloorTopBorder_ForSide",
"Indices": [
2,
3,
4,
5
],
"UVs": [
"0.5, 0.5",
"0.5, -d1",
"0, -d1",
"0, 0.5"
]
},
{
"Type": "RECTANGLE",
"Textures": "FloorTopBorderless_ForSide",
"Indices": [
3,
2,
1,
0
],
"UVs": [
"0, -d1",
"0, 0.5",
"0.5+d3, 0.5",
"0.5+d3, -d1"
]
}
]
}
},
{
"Type": "NormalOutterCorner",
"BindingDisplayTexture": "NormalOutterCorner.png",
"UnitSize": "Small",
"ExpandType": "Static",
"InitColumnDirection": "PositiveX",
"DefaultSideConfig": {
"UseTwoDTop": true,
"UseTwoDRight": false,
"UseTwoDBottom": false,
"UseTwoDLeft": true,
"UseThreeDTop": true,
"UseThreeDBottom": false
},
"ThreeDTopFace": {
"Vertices": [
"0, 0, 0",
"2.5, 0, 0",
"2.5, 2.5, 0",
"0, 2.5, 0"
],
"Faces": [
{
"Type": "TRIANGLE",
"Textures": "FloorTopFlat",
"Indices": [
0,
1,
2
],
"UVs": [
"0, 0.5",
"0, 1",
"0.5, 1"
]
},
{
"Type": "TRIANGLE",
"Textures": "FloorTopFlat",
"Indices": [
0,
2,
3
],
"UVs": [
"1, 0.5",
"0.5, 1",
"1, 1"
]
}
]
},
"ThreeDBottomFace": {
"Vertices": [
"2.5, 0, -5-d3",
"2.5, 2.5, -5-d3",
"0, 2.5, -5-d3",
"0, 0, -5-d3"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "FloorTopBorderless_ForSide",
"Indices": [
3,
2,
1,
0
],
"UVs": [
"0, 0.5",
"0.5, 0.5",
"0.5, 0",
"0, 0"
]
}
]
},
"TwoDTopSide": {
"Vertices": [
"0, 0, -5-d3",
"0, 0, 0",
"0, 2.5, 0",
"0, 2.5, -5-d3"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "FloorSide",
"Indices": [
0,
1,
2,
3
],
"UVs": [
"1+d3, 0",
"0, 0",
"0, 0.5",
"1+d3, 0.5"
]
}
]
},
"TwoDRightSide": {
"Vertices": [
"2.5, 2.5, -5-d3",
"0, 2.5, -5-d3",
"0, 2.5, 0",
"2.5, 2.5, 0"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "FloorTopBorderless_ForSide",
"Indices": [
0,
1,
2,
3
],
"UVs": [
"1+d3, 0",
"1+d3, 0.5",
"0, 0.5",
"0, 0"
]
}
]
},
"TwoDBottomSide": {
"Vertices": [
"2.5, 0, -5-d3",
"2.5, 2.5, -5-d3",
"2.5, 2.5, 0",
"2.5, 0, 0"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "FloorTopBorderless_ForSide",
"Indices": [
0,
1,
2,
3
],
"UVs": [
"1+d3, 0",
"1+d3, 0.5",
"0, 0.5",
"0, 0"
]
}
]
},
"TwoDLeftSide": {
"Vertices": [
"2.5, 0, 0",
"2.5, 0, -5-d3",
"0, 0, -5-d3",
"0, 0, 0"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "FloorSide",
"Indices": [
3,
2,
1,
0
],
"UVs": [
"0, 0.5",
"1+d3, 0.5",
"1+d3, 0",
"0, 0"
]
}
]
},
"TwoDTopSideExpand": {
"Vertices": [
"0, 0, -5-d3",
"0, 2.5, -5-d3",
"0, 2.5, -2.5",
"0, 0, -2.5",
"0, 0, 0",
"0, 2.5, 0"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "FloorTopBorder_ForSide",
"Indices": [
2,
3,
4,
5
],
"UVs": [
"0.5, 0.5",
"0.5, 0",
"0, 0",
"0, 0.5"
]
},
{
"Type": "RECTANGLE",
"Textures": "FloorTopBorderless_ForSide",
"Indices": [
3,
2,
1,
0
],
"UVs": [
"0, 0",
"0, 0.5",
"0.5+d3, 0.5",
"0.5+d3, 0"
]
}
]
},
"TwoDRightSideExpand": null,
"TwoDBottomSideExpand": null,
"TwoDLeftSideExpand": {
"Vertices": [
"2.5, 0, -5-d3",
"0, 0, -5-d3",
"0, 0, -2.5",
"2.5, 0, -2.5",
"2.5, 0, 0",
"0, 0, 0"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "FloorTopBorder_ForSide",
"Indices": [
2,
3,
4,
5
],
"UVs": [
"0.5, 0.5",
"0.5, 0",
"0, 0",
"0, 0.5"
]
},
{
"Type": "RECTANGLE",
"Textures": "FloorTopBorderless_ForSide",
"Indices": [
3,
2,
1,
0
],
"UVs": [
"0, 0",
"0, 0.5",
"0.5+d3, 0.5",
"0.5+d3, 0"
]
}
]
}
},
{
"Type": "NormalInnerCorner",
"BindingDisplayTexture": "NormalInnerCorner.png",
"UnitSize": "Small",
"ExpandType": "Static",
"InitColumnDirection": "PositiveX",
"DefaultSideConfig": {
"UseTwoDTop": false,
"UseTwoDRight": false,
"UseTwoDBottom": false,
"UseTwoDLeft": false,
"UseThreeDTop": true,
"UseThreeDBottom": false
},
"ThreeDTopFace": {
"Vertices": [
"2.5, 0, 0",
"2.5, 2.5, 0",
"0, 0, 0",
"0, 2.5, 0"
],
"Faces": [
{
"Type": "TRIANGLE",
"Textures": "FloorTopFlat",
"Indices": [
0,
1,
2
],
"UVs": [
"0.5, 1",
"0, 1",
"0.5, 0.5"
]
},
{
"Type": "TRIANGLE",
"Textures": "FloorTopFlat",
"Indices": [
3,
2,
1
],
"UVs": [
"0.5, 1",
"0.5, 0.5",
"1, 1"
]
}
]
},
"ThreeDBottomFace": {
"Vertices": [
"2.5, 0, -5-d3",
"2.5, 2.5, -5-d3",
"0, 2.5, -5-d3",
"0, 0, -5-d3"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "FloorTopBorderless_ForSide",
"Indices": [
3,
2,
1,
0
],
"UVs": [
"0, 0.5",
"0.5, 0.5",
"0.5, 0",
"0, 0"
]
}
]
},
"TwoDTopSide": {
"Vertices": [
"0, 0, 0",
"0, 2.5, 0",
"0, 2.5, -5-d3",
"0, 0, -5-d3"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "FloorTopBorderless_ForSide",
"Indices": [
0,
1,
2,
3
],
"UVs": [
"0, 0",
"0, 0.5",
"1+d3, 0.5",
"1+d3, 0"
]
}
]
},
"TwoDRightSide": {
"Vertices": [
"2.5, 2.5, 0",
"2.5, 2.5, -5-d3",
"0, 2.5, -5-d3",
"0, 2.5, 0"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "FloorTopBorderless_ForSide",
"Indices": [
0,
1,
2,
3
],
"UVs": [
"0, 0",
"1+d3, 0",
"1+d3, 0.5",
"0, 0.5"
]
}
]
},
"TwoDBottomSide": {
"Vertices": [
"2.5, 2.5, 0",
"2.5, 0, 0",
"2.5, 0, -5-d3",
"2.5, 2.5, -5-d3"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "FloorTopBorderless_ForSide",
"Indices": [
0,
1,
2,
3
],
"UVs": [
"0, 0.5",
"0, 0",
"1+d3, 0",
"1+d3, 0.5"
]
}
]
},
"TwoDLeftSide": {
"Vertices": [
"0, 0, 0",
"0, 0, -5-d3",
"2.5, 0, -5-d3",
"2.5, 0, 0"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "FloorTopBorderless_ForSide",
"Indices": [
0,
1,
2,
3
],
"UVs": [
"0, 0.5",
"1+d3, 0.5",
"1+d3, 0",
"0, 0"
]
}
]
},
"TwoDTopSideExpand": null,
"TwoDRightSideExpand": null,
"TwoDBottomSideExpand": null,
"TwoDLeftSideExpand": null
}
]

View File

@ -0,0 +1,493 @@
[
{
"Type": "RibbonBorder",
"BindingDisplayTexture": "RibbonBorder.png",
"UnitSize": "Small",
"ExpandType": "Column",
"InitColumnDirection": "PositiveX",
"DefaultSideConfig": {
"UseTwoDTop": false,
"UseTwoDRight": false,
"UseTwoDBottom": false,
"UseTwoDLeft": true,
"UseThreeDTop": true,
"UseThreeDBottom": false
},
"ThreeDTopFace": {
"Vertices": [
"2.5+d1, 0, 0",
"2.5+d1, 2.5, -0.7",
"0, 2.5, -0.7",
"0, 0, 0"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "FloorTopFlat",
"Indices": [
0,
1,
2,
3
],
"UVs": [
"0, -d1",
"1, -d1",
"1, 0.5",
"0, 0.5"
]
}
]
},
"ThreeDBottomFace": {
"Vertices": [
"2.5+d1, 0, -5-d3",
"2.5+d1, 2.5, -5-d3",
"0, 2.5, -5-d3",
"0, 0, -5-d3"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "FloorTopBorderless_ForSide",
"Indices": [
3,
2,
1,
0
],
"UVs": [
"0, 0.5",
"0.5, 0.5",
"0.5, -d1",
"0, -d1"
]
}
]
},
"TwoDTopSide": {
"Vertices": [
"0, 0, -5-d3",
"0, 0, 0",
"0, 2.5, -0.7",
"0, 2.5, -5-d3"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "FloorTopBorderless_ForSide",
"Indices": [
0,
1,
2,
3
],
"UVs": [
"1+d3, 0",
"0, 0",
"0.14, 0.5",
"1+d3, 0.5"
]
}
]
},
"TwoDRightSide": {
"Vertices": [
"2.5+d1, 2.5, -0.7",
"2.5+d1, 2.5, -5-d3",
"0, 2.5, -5-d3",
"0, 2.5, -0.7"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "FloorTopBorderless_ForSide",
"Indices": [
0,
1,
2,
3
],
"UVs": [
"0.14, -d1",
"1+d3, -d1",
"1+d3, 0.5",
"0.14, 0.5"
]
}
]
},
"TwoDBottomSide": {
"Vertices": [
"2.5+d1, 0, 0",
"2.5+d1, 0, -5-d3",
"2.5+d1, 2.5, -5-d3",
"2.5+d1, 2.5, -0.7"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "FloorTopBorderless_ForSide",
"Indices": [
0,
1,
2,
3
],
"UVs": [
"0, 0",
"1+d3, 0",
"1+d3, 0.5",
"0.14, 0.5"
]
}
]
},
"TwoDLeftSide": {
"Vertices": [
"2.5+d1, 0, 0",
"2.5+d1, 0, -5-d3",
"0, 0, -5-d3",
"0, 0, 0"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "FloorSide",
"Indices": [
3,
2,
1,
0
],
"UVs": [
"0, 0.5",
"1+d3, 0.5",
"1+d3, -d1",
"0, -d1"
]
}
]
},
"TwoDTopSideExpand": null,
"TwoDRightSideExpand": null,
"TwoDBottomSideExpand": null,
"TwoDLeftSideExpand": {
"Vertices": [
"2.5+d1, 0, -5-d3",
"0, 0, -5-d3",
"0, 0, -2.5",
"2.5+d1, 0, -2.5",
"2.5+d1, 0, 0",
"0, 0, 0"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "FloorTopBorder_ForSide",
"Indices": [
2,
3,
4,
5
],
"UVs": [
"0.5, 0.5",
"0.5, -d1",
"0, -d1",
"0, 0.5"
]
},
{
"Type": "RECTANGLE",
"Textures": "FloorTopBorderless_ForSide",
"Indices": [
3,
2,
1,
0
],
"UVs": [
"0, -d1",
"0, 0.5",
"0.5+d3, 0.5",
"0.5+d3, -d1"
]
}
]
}
},
{
"Type": "RibbonOutterCorner",
"BindingDisplayTexture": "RibbonOutterCorner.png",
"UnitSize": "Small",
"ExpandType": "Static",
"InitColumnDirection": "PositiveX",
"DefaultSideConfig": {
"UseTwoDTop": true,
"UseTwoDRight": false,
"UseTwoDBottom": false,
"UseTwoDLeft": true,
"UseThreeDTop": true,
"UseThreeDBottom": false
},
"ThreeDTopFace": {
"Vertices": [
"0, 0, 0",
"2.5, 0, 0",
"2.5, 2.5, -0.7",
"0, 2.5, 0"
],
"Faces": [
{
"Type": "TRIANGLE",
"Textures": "FloorTopFlat",
"Indices": [
0,
1,
2
],
"UVs": [
"0, 0",
"0, 1",
"1, 1"
]
},
{
"Type": "TRIANGLE",
"Textures": "FloorTopFlat",
"Indices": [
0,
2,
3
],
"UVs": [
"1, 0",
"0, 1",
"1, 1"
]
}
]
},
"ThreeDBottomFace": {
"Vertices": [
"2.5, 0, -5-d3",
"2.5, 2.5, -5-d3",
"0, 2.5, -5-d3",
"0, 0, -5-d3"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "FloorTopBorderless_ForSide",
"Indices": [
3,
2,
1,
0
],
"UVs": [
"0, 0.5",
"0.5, 0.5",
"0.5, 0",
"0, 0"
]
}
]
},
"TwoDTopSide": {
"Vertices": [
"0, 0, -5-d3",
"0, 0, 0",
"0, 2.5, 0",
"0, 2.5, -5-d3"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "FloorSide",
"Indices": [
0,
1,
2,
3
],
"UVs": [
"1+d3, 0",
"0, 0",
"0, 0.5",
"1+d3, 0.5"
]
}
]
},
"TwoDRightSide": {
"Vertices": [
"2.5, 2.5, -5-d3",
"0, 2.5, -5-d3",
"0, 2.5, 0",
"2.5, 2.5, -0.7"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "FloorTopBorderless_ForSide",
"Indices": [
0,
1,
2,
3
],
"UVs": [
"1+d3, 0",
"1+d3, 0.5",
"0, 0.5",
"0.14, 0"
]
}
]
},
"TwoDBottomSide": {
"Vertices": [
"2.5, 0, -5-d3",
"2.5, 2.5, -5-d3",
"2.5, 2.5, -0.7",
"2.5, 0, 0"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "FloorTopBorderless_ForSide",
"Indices": [
0,
1,
2,
3
],
"UVs": [
"1+d3, 0",
"1+d3, 0.5",
"0.14, 0.5",
"0, 0"
]
}
]
},
"TwoDLeftSide": {
"Vertices": [
"2.5, 0, 0",
"2.5, 0, -5-d3",
"0, 0, -5-d3",
"0, 0, 0"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "FloorSide",
"Indices": [
3,
2,
1,
0
],
"UVs": [
"0, 0.5",
"1+d3, 0.5",
"1+d3, 0",
"0, 0"
]
}
]
},
"TwoDTopSideExpand": {
"Vertices": [
"0, 0, -5-d3",
"0, 2.5, -5-d3",
"0, 2.5, -2.5",
"0, 0, -2.5",
"0, 0, 0",
"0, 2.5, 0"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "FloorTopBorder_ForSide",
"Indices": [
2,
3,
4,
5
],
"UVs": [
"0.5, 0.5",
"0.5, 0",
"0, 0",
"0, 0.5"
]
},
{
"Type": "RECTANGLE",
"Textures": "FloorTopBorderless_ForSide",
"Indices": [
3,
2,
1,
0
],
"UVs": [
"0, 0",
"0, 0.5",
"0.5+d3, 0.5",
"0.5+d3, 0"
]
}
]
},
"TwoDRightSideExpand": null,
"TwoDBottomSideExpand": null,
"TwoDLeftSideExpand": {
"Vertices": [
"2.5, 0, -5-d3",
"0, 0, -5-d3",
"0, 0, -2.5",
"2.5, 0, -2.5",
"2.5, 0, 0",
"0, 0, 0"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "FloorTopBorder_ForSide",
"Indices": [
2,
3,
4,
5
],
"UVs": [
"0.5, 0.5",
"0.5, 0",
"0, 0",
"0, 0.5"
]
},
{
"Type": "RECTANGLE",
"Textures": "FloorTopBorderless_ForSide",
"Indices": [
3,
2,
1,
0
],
"UVs": [
"0, 0",
"0, 0.5",
"0.5+d3, 0.5",
"0.5+d3, 0"
]
}
]
}
}
]

View File

@ -0,0 +1,680 @@
[
{
"Type": "SinkBorder",
"BindingDisplayTexture": "SinkBorder.png",
"UnitSize": "Small",
"ExpandType": "Column",
"InitColumnDirection": "PositiveX",
"DefaultSideConfig": {
"UseTwoDTop": false,
"UseTwoDRight": false,
"UseTwoDBottom": false,
"UseTwoDLeft": true,
"UseThreeDTop": true,
"UseThreeDBottom": false
},
"ThreeDTopFace": {
"Vertices": [
"2.5+d1, 0, 0",
"2.5+d1, 2.5, -0.7",
"0, 2.5, -0.7",
"0, 0, 0"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "FloorTopProfil",
"Indices": [
0,
1,
2,
3
],
"UVs": [
"0, -d1",
"0.5, -d1",
"0.5, 0.5",
"0, 0.5"
]
}
]
},
"ThreeDBottomFace": {
"Vertices": [
"2.5+d1, 0, -5-d3",
"2.5+d1, 2.5, -5-d3",
"0, 2.5, -5-d3",
"0, 0, -5-d3"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "FloorTopBorderless_ForSide",
"Indices": [
3,
2,
1,
0
],
"UVs": [
"0, 0.5",
"0.5, 0.5",
"0.5, -d1",
"0, -d1"
]
}
]
},
"TwoDTopSide": {
"Vertices": [
"0, 0, -5-d3",
"0, 0, 0",
"0, 2.5, -0.7",
"0, 2.5, -5-d3"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "FloorTopBorderless_ForSide",
"Indices": [
0,
1,
2,
3
],
"UVs": [
"1+d3, 0",
"0, 0",
"0.14, 0.5",
"1+d3, 0.5"
]
}
]
},
"TwoDRightSide": {
"Vertices": [
"2.5+d1, 2.5, -0.7",
"2.5+d1, 2.5, -5-d3",
"0, 2.5, -5-d3",
"0, 2.5, -0.7"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "FloorTopBorderless_ForSide",
"Indices": [
0,
1,
2,
3
],
"UVs": [
"0.14, -d1",
"1+d3, -d1",
"1+d3, 0.5",
"0.14, 0.5"
]
}
]
},
"TwoDBottomSide": {
"Vertices": [
"2.5+d1, 0, 0",
"2.5+d1, 0, -5-d3",
"2.5+d1, 2.5, -5-d3",
"2.5+d1, 2.5, -0.7"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "FloorTopBorderless_ForSide",
"Indices": [
0,
1,
2,
3
],
"UVs": [
"0, 0",
"1+d3, 0",
"1+d3, 0.5",
"0.14, 0.5"
]
}
]
},
"TwoDLeftSide": {
"Vertices": [
"2.5+d1, 0, 0",
"2.5+d1, 0, -5-d3",
"0, 0, -5-d3",
"0, 0, 0"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "FloorSide",
"Indices": [
3,
2,
1,
0
],
"UVs": [
"0, 0.5",
"1+d3, 0.5",
"1+d3, -d1",
"0, -d1"
]
}
]
},
"TwoDTopSideExpand": null,
"TwoDRightSideExpand": null,
"TwoDBottomSideExpand": null,
"TwoDLeftSideExpand": {
"Vertices": [
"2.5+d1, 0, -5-d3",
"0, 0, -5-d3",
"0, 0, -2.5",
"2.5+d1, 0, -2.5",
"2.5+d1, 0, 0",
"0, 0, 0"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "FloorTopBorder_ForSide",
"Indices": [
2,
3,
4,
5
],
"UVs": [
"0.5, 0.5",
"0.5, -d1",
"0, -d1",
"0, 0.5"
]
},
{
"Type": "RECTANGLE",
"Textures": "FloorTopBorderless_ForSide",
"Indices": [
3,
2,
1,
0
],
"UVs": [
"0, -d1",
"0, 0.5",
"0.5+d3, 0.5",
"0.5+d3, -d1"
]
}
]
}
},
{
"Type": "SinkOutterCorner",
"BindingDisplayTexture": "SinkOutterCorner.png",
"UnitSize": "Small",
"ExpandType": "Static",
"InitColumnDirection": "PositiveX",
"DefaultSideConfig": {
"UseTwoDTop": true,
"UseTwoDRight": false,
"UseTwoDBottom": false,
"UseTwoDLeft": true,
"UseThreeDTop": true,
"UseThreeDBottom": false
},
"ThreeDTopFace": {
"Vertices": [
"0, 0, 0",
"2.5, 0, 0",
"2.5, 2.5, -0.7",
"0, 2.5, 0"
],
"Faces": [
{
"Type": "TRIANGLE",
"Textures": "FloorTopProfil",
"Indices": [
0,
1,
2
],
"UVs": [
"0, 0.5",
"0, 1",
"0.5, 1"
]
},
{
"Type": "TRIANGLE",
"Textures": "FloorTopProfil",
"Indices": [
0,
2,
3
],
"UVs": [
"1, 0.5",
"0.5, 1",
"1, 1"
]
}
]
},
"ThreeDBottomFace": {
"Vertices": [
"2.5, 0, -5-d3",
"2.5, 2.5, -5-d3",
"0, 2.5, -5-d3",
"0, 0, -5-d3"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "FloorTopBorderless_ForSide",
"Indices": [
3,
2,
1,
0
],
"UVs": [
"0, 0.5",
"0.5, 0.5",
"0.5, 0",
"0, 0"
]
}
]
},
"TwoDTopSide": {
"Vertices": [
"0, 0, -5-d3",
"0, 0, 0",
"0, 2.5, 0",
"0, 2.5, -5-d3"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "FloorSide",
"Indices": [
0,
1,
2,
3
],
"UVs": [
"1+d3, 0",
"0, 0",
"0, 0.5",
"1+d3, 0.5"
]
}
]
},
"TwoDRightSide": {
"Vertices": [
"2.5, 2.5, -5-d3",
"0, 2.5, -5-d3",
"0, 2.5, 0",
"2.5, 2.5, -0.7"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "FloorTopBorderless_ForSide",
"Indices": [
0,
1,
2,
3
],
"UVs": [
"1+d3, 0",
"1+d3, 0.5",
"0, 0.5",
"0.14, 0"
]
}
]
},
"TwoDBottomSide": {
"Vertices": [
"2.5, 0, -5-d3",
"2.5, 2.5, -5-d3",
"2.5, 2.5, -0.7",
"2.5, 0, 0"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "FloorTopBorderless_ForSide",
"Indices": [
0,
1,
2,
3
],
"UVs": [
"1+d3, 0",
"1+d3, 0.5",
"0.14, 0.5",
"0, 0"
]
}
]
},
"TwoDLeftSide": {
"Vertices": [
"2.5, 0, 0",
"2.5, 0, -5-d3",
"0, 0, -5-d3",
"0, 0, 0"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "FloorSide",
"Indices": [
3,
2,
1,
0
],
"UVs": [
"0, 0.5",
"1+d3, 0.5",
"1+d3, 0",
"0, 0"
]
}
]
},
"TwoDTopSideExpand": {
"Vertices": [
"0, 0, -5-d3",
"0, 2.5, -5-d3",
"0, 2.5, -2.5",
"0, 0, -2.5",
"0, 0, 0",
"0, 2.5, 0"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "FloorTopBorder_ForSide",
"Indices": [
2,
3,
4,
5
],
"UVs": [
"0.5, 0.5",
"0.5, 0",
"0, 0",
"0, 0.5"
]
},
{
"Type": "RECTANGLE",
"Textures": "FloorTopBorderless_ForSide",
"Indices": [
3,
2,
1,
0
],
"UVs": [
"0, 0",
"0, 0.5",
"0.5+d3, 0.5",
"0.5+d3, 0"
]
}
]
},
"TwoDRightSideExpand": null,
"TwoDBottomSideExpand": null,
"TwoDLeftSideExpand": {
"Vertices": [
"2.5, 0, -5-d3",
"0, 0, -5-d3",
"0, 0, -2.5",
"2.5, 0, -2.5",
"2.5, 0, 0",
"0, 0, 0"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "FloorTopBorder_ForSide",
"Indices": [
2,
3,
4,
5
],
"UVs": [
"0.5, 0.5",
"0.5, 0",
"0, 0",
"0, 0.5"
]
},
{
"Type": "RECTANGLE",
"Textures": "FloorTopBorderless_ForSide",
"Indices": [
3,
2,
1,
0
],
"UVs": [
"0, 0",
"0, 0.5",
"0.5+d3, 0.5",
"0.5+d3, 0"
]
}
]
}
},
{
"Type": "SinkInnerCorner",
"BindingDisplayTexture": "SinkInnerCorner.png",
"UnitSize": "Small",
"ExpandType": "Static",
"InitColumnDirection": "PositiveX",
"DefaultSideConfig": {
"UseTwoDTop": false,
"UseTwoDRight": false,
"UseTwoDBottom": false,
"UseTwoDLeft": false,
"UseThreeDTop": true,
"UseThreeDBottom": false
},
"ThreeDTopFace": {
"Vertices": [
"2.5, 0, -0.7",
"2.5, 2.5, 0",
"0, 0, -0.7",
"0, 2.5, -0.7"
],
"Faces": [
{
"Type": "TRIANGLE",
"Textures": "FloorTopProfil",
"Indices": [
0,
1,
2
],
"UVs": [
"0.5, 1",
"0, 1",
"0.5, 0.5"
]
},
{
"Type": "TRIANGLE",
"Textures": "FloorTopProfil",
"Indices": [
3,
2,
1
],
"UVs": [
"0.5, 1",
"0.5, 0.5",
"1, 1"
]
}
]
},
"ThreeDBottomFace": {
"Vertices": [
"2.5, 0, -5-d3",
"2.5, 2.5, -5-d3",
"0, 2.5, -5-d3",
"0, 0, -5-d3"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "FloorTopBorderless_ForSide",
"Indices": [
3,
2,
1,
0
],
"UVs": [
"0, 0.5",
"0.5, 0.5",
"0.5, 0",
"0, 0"
]
}
]
},
"TwoDTopSide": {
"Vertices": [
"0, 0, -0.7",
"0, 2.5, -0.7",
"0, 2.5, -5-d3",
"0, 0, -5-d3"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "FloorTopBorderless_ForSide",
"Indices": [
0,
1,
2,
3
],
"UVs": [
"0.14, 0",
"0.14, 0.5",
"1+d3, 0.5",
"1+d3, 0"
]
}
]
},
"TwoDRightSide": {
"Vertices": [
"2.5, 2.5, 0",
"2.5, 2.5, -5-d3",
"0, 2.5, -5-d3",
"0, 2.5, -0.7"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "FloorTopBorderless_ForSide",
"Indices": [
0,
1,
2,
3
],
"UVs": [
"0, 0",
"1+d3, 0",
"1+d3, 0.5",
"0.14, 0.5"
]
}
]
},
"TwoDBottomSide": {
"Vertices": [
"2.5, 2.5, 0",
"2.5, 0, -0.7",
"2.5, 0, -5-d3",
"2.5, 2.5, -5-d3"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "FloorTopBorderless_ForSide",
"Indices": [
0,
1,
2,
3
],
"UVs": [
"0, 0.5",
"0.14, 0",
"1+d3, 0",
"1+d3, 0.5"
]
}
]
},
"TwoDLeftSide": {
"Vertices": [
"0, 0, -0.7",
"0, 0, -5-d3",
"2.5, 0, -5-d3",
"2.5, 0, -0.7"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "FloorTopBorderless_ForSide",
"Indices": [
0,
1,
2,
3
],
"UVs": [
"0.14, 0.5",
"1+d3, 0.5",
"1+d3, 0",
"0.14, 0"
]
}
]
},
"TwoDTopSideExpand": null,
"TwoDRightSideExpand": null,
"TwoDBottomSideExpand": null,
"TwoDLeftSideExpand": null
}
]

View File

@ -0,0 +1,827 @@
[
{
"Type": "WoodTrafo",
"BindingDisplayTexture": "WoodTrafo.png",
"UnitSize": "Large",
"ExpandType": "Static",
"InitColumnDirection": "PositiveX",
"DefaultSideConfig": {
"UseTwoDTop": true,
"UseTwoDRight": true,
"UseTwoDBottom": true,
"UseTwoDLeft": true,
"UseThreeDTop": true,
"UseThreeDBottom": true
},
"ThreeDTopFace": {
"Vertices": [
"0, 5, 0",
"0, 0, 0",
"5, 0, 0",
"5, 5, 0"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "BallWood",
"Indices": [
0,
1,
2,
3
],
"UVs": [
"0, 0",
"1, 0",
"1, 1",
"0, 1"
]
}
]
},
"ThreeDBottomFace": {
"Vertices": [
"0, 0, -5-d3",
"0, 5, -5-d3",
"5, 5, -5-d3",
"5, 0, -5-d3"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "BallWood",
"Indices": [
0,
1,
2,
3
],
"UVs": [
"0, 0",
"1, 0",
"1, 1",
"0, 1"
]
}
]
},
"TwoDTopSide": {
"Vertices": [
"0, 5, -5-d3",
"0, 0, -5-d3",
"0, 0, 0",
"0, 5, 0"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "BallWood",
"Indices": [
0,
1,
2,
3
],
"UVs": [
"0, -d3",
"1, -d3",
"1, 1",
"0, 1"
]
}
]
},
"TwoDRightSide": {
"Vertices": [
"5, 5, -5-d3",
"0, 5, -5-d3",
"0, 5, 0",
"5, 5, 0"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "BallWood",
"Indices": [
0,
1,
2,
3
],
"UVs": [
"0, -d3",
"1, -d3",
"1, 1",
"0, 1"
]
}
]
},
"TwoDBottomSide": {
"Vertices": [
"5, 0, -5-d3",
"5, 5, -5-d3",
"5, 5, 0",
"5, 0, 0"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "BallWood",
"Indices": [
0,
1,
2,
3
],
"UVs": [
"0, -d3",
"1, -d3",
"1, 1",
"0, 1"
]
}
]
},
"TwoDLeftSide": {
"Vertices": [
"0, 0, -5-d3",
"5, 0, -5-d3",
"5, 0, 0",
"0, 0, 0"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "BallWood",
"Indices": [
0,
1,
2,
3
],
"UVs": [
"0, -d3",
"1, -d3",
"1, 1",
"0, 1"
]
}
]
},
"TwoDTopSideExpand": {
"Vertices": [
"0, 5, -5-d3",
"0, 0, -5-d3",
"0, 0, 0",
"0, 5, 0"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "BallWood",
"Indices": [
0,
1,
2,
3
],
"UVs": [
"1+d3, 0",
"1+d3, 1",
"0, 1",
"0, 0"
]
}
]
},
"TwoDRightSideExpand": {
"Vertices": [
"5, 5, -5-d3",
"0, 5, -5-d3",
"0, 5, 0",
"5, 5, 0"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "BallWood",
"Indices": [
0,
1,
2,
3
],
"UVs": [
"1+d3, 0",
"1+d3, 1",
"0, 1",
"0, 0"
]
}
]
},
"TwoDBottomSideExpand": {
"Vertices": [
"5, 0, -5-d3",
"5, 5, -5-d3",
"5, 5, 0",
"5, 0, 0"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "BallWood",
"Indices": [
0,
1,
2,
3
],
"UVs": [
"1+d3, 0",
"1+d3, 1",
"0, 1",
"0, 0"
]
}
]
},
"TwoDLeftSideExpand": {
"Vertices": [
"0, 0, -5-d3",
"5, 0, -5-d3",
"5, 0, 0",
"0, 0, 0"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "BallWood",
"Indices": [
0,
1,
2,
3
],
"UVs": [
"1+d3, 0",
"1+d3, 1",
"0, 1",
"0, 0"
]
}
]
}
},
{
"Type": "StoneTrafo",
"BindingDisplayTexture": "StoneTrafo.png",
"UnitSize": "Large",
"ExpandType": "Static",
"InitColumnDirection": "PositiveX",
"DefaultSideConfig": {
"UseTwoDTop": true,
"UseTwoDRight": true,
"UseTwoDBottom": true,
"UseTwoDLeft": true,
"UseThreeDTop": true,
"UseThreeDBottom": true
},
"ThreeDTopFace": {
"Vertices": [
"0, 5, 0",
"0, 0, 0",
"5, 0, 0",
"5, 5, 0"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "BallStone",
"Indices": [
0,
1,
2,
3
],
"UVs": [
"0, 0",
"1, 0",
"1, 1",
"0, 1"
]
}
]
},
"ThreeDBottomFace": {
"Vertices": [
"0, 0, -5-d3",
"0, 5, -5-d3",
"5, 5, -5-d3",
"5, 0, -5-d3"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "BallStone",
"Indices": [
0,
1,
2,
3
],
"UVs": [
"0, 0",
"1, 0",
"1, 1",
"0, 1"
]
}
]
},
"TwoDTopSide": {
"Vertices": [
"0, 5, -5-d3",
"0, 0, -5-d3",
"0, 0, 0",
"0, 5, 0"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "BallStone",
"Indices": [
0,
1,
2,
3
],
"UVs": [
"0, -d3",
"1, -d3",
"1, 1",
"0, 1"
]
}
]
},
"TwoDRightSide": {
"Vertices": [
"5, 5, -5-d3",
"0, 5, -5-d3",
"0, 5, 0",
"5, 5, 0"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "BallStone",
"Indices": [
0,
1,
2,
3
],
"UVs": [
"0, -d3",
"1, -d3",
"1, 1",
"0, 1"
]
}
]
},
"TwoDBottomSide": {
"Vertices": [
"5, 0, -5-d3",
"5, 5, -5-d3",
"5, 5, 0",
"5, 0, 0"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "BallStone",
"Indices": [
0,
1,
2,
3
],
"UVs": [
"0, -d3",
"1, -d3",
"1, 1",
"0, 1"
]
}
]
},
"TwoDLeftSide": {
"Vertices": [
"0, 0, -5-d3",
"5, 0, -5-d3",
"5, 0, 0",
"0, 0, 0"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "BallStone",
"Indices": [
0,
1,
2,
3
],
"UVs": [
"0, -d3",
"1, -d3",
"1, 1",
"0, 1"
]
}
]
},
"TwoDTopSideExpand": {
"Vertices": [
"0, 5, -5-d3",
"0, 0, -5-d3",
"0, 0, 0",
"0, 5, 0"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "BallStone",
"Indices": [
0,
1,
2,
3
],
"UVs": [
"1+d3, 0",
"1+d3, 1",
"0, 1",
"0, 0"
]
}
]
},
"TwoDRightSideExpand": {
"Vertices": [
"5, 5, -5-d3",
"0, 5, -5-d3",
"0, 5, 0",
"5, 5, 0"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "BallStone",
"Indices": [
0,
1,
2,
3
],
"UVs": [
"1+d3, 0",
"1+d3, 1",
"0, 1",
"0, 0"
]
}
]
},
"TwoDBottomSideExpand": {
"Vertices": [
"5, 0, -5-d3",
"5, 5, -5-d3",
"5, 5, 0",
"5, 0, 0"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "BallStone",
"Indices": [
0,
1,
2,
3
],
"UVs": [
"1+d3, 0",
"1+d3, 1",
"0, 1",
"0, 0"
]
}
]
},
"TwoDLeftSideExpand": {
"Vertices": [
"0, 0, -5-d3",
"5, 0, -5-d3",
"5, 0, 0",
"0, 0, 0"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "BallStone",
"Indices": [
0,
1,
2,
3
],
"UVs": [
"1+d3, 0",
"1+d3, 1",
"0, 1",
"0, 0"
]
}
]
}
},
{
"Type": "PaperTrafo",
"BindingDisplayTexture": "PaperTrafo.png",
"UnitSize": "Large",
"ExpandType": "Static",
"InitColumnDirection": "PositiveX",
"DefaultSideConfig": {
"UseTwoDTop": true,
"UseTwoDRight": true,
"UseTwoDBottom": true,
"UseTwoDLeft": true,
"UseThreeDTop": true,
"UseThreeDBottom": true
},
"ThreeDTopFace": {
"Vertices": [
"0, 5, 0",
"0, 0, 0",
"5, 0, 0",
"5, 5, 0"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "BallPaper",
"Indices": [
0,
1,
2,
3
],
"UVs": [
"0, 0",
"1, 0",
"1, 1",
"0, 1"
]
}
]
},
"ThreeDBottomFace": {
"Vertices": [
"0, 0, -5-d3",
"0, 5, -5-d3",
"5, 5, -5-d3",
"5, 0, -5-d3"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "BallPaper",
"Indices": [
0,
1,
2,
3
],
"UVs": [
"0, 0",
"1, 0",
"1, 1",
"0, 1"
]
}
]
},
"TwoDTopSide": {
"Vertices": [
"0, 5, -5-d3",
"0, 0, -5-d3",
"0, 0, 0",
"0, 5, 0"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "BallPaper",
"Indices": [
0,
1,
2,
3
],
"UVs": [
"0, -d3",
"1, -d3",
"1, 1",
"0, 1"
]
}
]
},
"TwoDRightSide": {
"Vertices": [
"5, 5, -5-d3",
"0, 5, -5-d3",
"0, 5, 0",
"5, 5, 0"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "BallPaper",
"Indices": [
0,
1,
2,
3
],
"UVs": [
"0, -d3",
"1, -d3",
"1, 1",
"0, 1"
]
}
]
},
"TwoDBottomSide": {
"Vertices": [
"5, 0, -5-d3",
"5, 5, -5-d3",
"5, 5, 0",
"5, 0, 0"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "BallPaper",
"Indices": [
0,
1,
2,
3
],
"UVs": [
"0, -d3",
"1, -d3",
"1, 1",
"0, 1"
]
}
]
},
"TwoDLeftSide": {
"Vertices": [
"0, 0, -5-d3",
"5, 0, -5-d3",
"5, 0, 0",
"0, 0, 0"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "BallPaper",
"Indices": [
0,
1,
2,
3
],
"UVs": [
"0, -d3",
"1, -d3",
"1, 1",
"0, 1"
]
}
]
},
"TwoDTopSideExpand": {
"Vertices": [
"0, 5, -5-d3",
"0, 0, -5-d3",
"0, 0, 0",
"0, 5, 0"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "BallPaper",
"Indices": [
0,
1,
2,
3
],
"UVs": [
"1+d3, 0",
"1+d3, 1",
"0, 1",
"0, 0"
]
}
]
},
"TwoDRightSideExpand": {
"Vertices": [
"5, 5, -5-d3",
"0, 5, -5-d3",
"0, 5, 0",
"5, 5, 0"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "BallPaper",
"Indices": [
0,
1,
2,
3
],
"UVs": [
"1+d3, 0",
"1+d3, 1",
"0, 1",
"0, 0"
]
}
]
},
"TwoDBottomSideExpand": {
"Vertices": [
"5, 0, -5-d3",
"5, 5, -5-d3",
"5, 5, 0",
"5, 0, 0"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "BallPaper",
"Indices": [
0,
1,
2,
3
],
"UVs": [
"1+d3, 0",
"1+d3, 1",
"0, 1",
"0, 0"
]
}
]
},
"TwoDLeftSideExpand": {
"Vertices": [
"0, 0, -5-d3",
"5, 0, -5-d3",
"5, 0, 0",
"0, 0, 0"
],
"Faces": [
{
"Type": "RECTANGLE",
"Textures": "BallPaper",
"Indices": [
0,
1,
2,
3
],
"UVs": [
"1+d3, 0",
"1+d3, 1",
"0, 1",
"0, 0"
]
}
]
}
}
]

View File

@ -0,0 +1,317 @@
[
{
"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",
"Rotation": "R0",
"SideSync": "2dTop;False;2dBottom;2dLeft;3dTop;3dBottom",
"StartPosition": "0, 0, 0",
"ExpandParam": "d1, 0"
},
{
"Type": "NormalBorder",
"Rotation": "R180",
"SideSync": "2dBottom;False;2dTop;2dRight;3dTop;3dBottom",
"StartPosition": "d1, (2.5*1), 0",
"ExpandParam": "d1, 0"
}
]
},
{
"Type": "NormalFloorTerminal",
"BindingDisplayTexture": "NormalFloorTerminal.png",
"UnitSize": "Small",
"ExpandType": "Static",
"InitColumnDirection": "PositiveX",
"DefaultSideConfig": {
"UseTwoDTop": true,
"UseTwoDRight": true,
"UseTwoDBottom": false,
"UseTwoDLeft": true,
"UseThreeDTop": true,
"UseThreeDBottom": false
},
"SmashedBlocks": [
{
"Type": "NormalOutterCorner",
"Rotation": "R0",
"SideSync": "2dTop;False;2dBottom;2dLeft;3dTop;3dBottom",
"StartPosition": "0, 0, 0",
"ExpandParam": "0, 0"
},
{
"Type": "NormalOutterCorner",
"Rotation": "R270",
"SideSync": "2dRight;2dBottom;False;2dTop;3dTop;3dBottom",
"StartPosition": "0, (2.5*1), 0",
"ExpandParam": "0, 0"
}
]
},
{
"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",
"Rotation": "R0",
"SideSync": "2dTop;False;False;2dLeft;3dTop;3dBottom",
"StartPosition": "0, 0, 0",
"ExpandParam": "0, 0"
},
{
"Type": "NormalOutterCorner",
"Rotation": "R90",
"SideSync": "2dLeft;False;False;2dBottom;3dTop;3dBottom",
"StartPosition": "(2.5*1), 0, 0",
"ExpandParam": "0, 0"
},
{
"Type": "NormalOutterCorner",
"Rotation": "R180",
"SideSync": "2dBottom;False;False;2dRight;3dTop;3dBottom",
"StartPosition": "(2.5*1), (2.5*1), 0",
"ExpandParam": "0, 0"
},
{
"Type": "NormalOutterCorner",
"Rotation": "R270",
"SideSync": "2dRight;False;False;2dTop;3dTop;3dBottom",
"StartPosition": "0, (2.5*1), 0",
"ExpandParam": "0, 0"
}
]
},
{
"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",
"Rotation": "R0",
"SideSync": "False;False;False;False;3dTop;3dBottom",
"StartPosition": "(2.5*1), (2.5*1), 0",
"ExpandParam": "d1, d2"
},
{
"Type": "NormalBorder",
"Rotation": "R0",
"SideSync": "False;False;False;2dLeft;3dTop;3dBottom",
"StartPosition": "(2.5*1), 0, 0",
"ExpandParam": "d2, 0"
},
{
"Type": "NormalBorder",
"Rotation": "R90",
"SideSync": "False;False;False;2dBottom;3dTop;3dBottom",
"StartPosition": "(2.5*2) +d2, (2.5*1), 0",
"ExpandParam": "d1, 0"
},
{
"Type": "NormalBorder",
"Rotation": "R180",
"SideSync": "False;False;False;2dRight;3dTop;3dBottom",
"StartPosition": "(2.5*1) +d2, (2.5*2) +d1, 0",
"ExpandParam": "d2, 0"
},
{
"Type": "NormalBorder",
"Rotation": "R270",
"SideSync": "False;False;False;2dTop;3dTop;3dBottom",
"StartPosition": "0, (2.5*1) +d1, 0",
"ExpandParam": "d1, 0"
},
{
"Type": "NormalOutterCorner",
"Rotation": "R0",
"SideSync": "2dTop;False;False;2dLeft;3dTop;3dBottom",
"StartPosition": "0, 0, 0",
"ExpandParam": "0, 0"
},
{
"Type": "NormalOutterCorner",
"Rotation": "R90",
"SideSync": "2dLeft;False;False;2dBottom;3dTop;3dBottom",
"StartPosition": "(2.5*2) +d2, 0, 0",
"ExpandParam": "0, 0"
},
{
"Type": "NormalOutterCorner",
"Rotation": "R180",
"SideSync": "2dBottom;False;False;2dRight;3dTop;3dBottom",
"StartPosition": "(2.5*2) +d2, (2.5*2) +d1, 0",
"ExpandParam": "0, 0"
},
{
"Type": "NormalOutterCorner",
"Rotation": "R270",
"SideSync": "2dRight;False;False;2dTop;3dTop;3dBottom",
"StartPosition": "0, (2.5*2) +d1, 0",
"ExpandParam": "0, 0"
}
]
},
{
"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",
"Rotation": "R0",
"SideSync": "2dTop;False;False;2dLeft;3dTop;3dBottom",
"StartPosition": "0, 0, 0",
"ExpandParam": "0, 0"
},
{
"Type": "NormalBorder",
"Rotation": "R0",
"SideSync": "False;False;2dBottom;2dLeft;3dTop;3dBottom",
"StartPosition": "(2.5*1), 0, 0",
"ExpandParam": "0, 0"
},
{
"Type": "NormalInnerCorner",
"Rotation": "R0",
"SideSync": "False;2dRight;2dBottom;False;3dTop;3dBottom",
"StartPosition": "(2.5*1), (2.5*1), 0",
"ExpandParam": "0, 0"
},
{
"Type": "NormalBorder",
"Rotation": "R270",
"SideSync": "2dRight;False;False;2dTop;3dTop;3dBottom",
"StartPosition": "0, (2.5*1), 0",
"ExpandParam": "0, 0"
}
]
},
{
"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",
"Rotation": "R270",
"SideSync": "False;2dBottom;2dLeft;False;3dTop;3dBottom",
"StartPosition": "(2.5*1), 0, 0",
"ExpandParam": "0, 0"
},
{
"Type": "NormalInnerCorner",
"Rotation": "R0",
"SideSync": "False;2dRight;2dBottom;False;3dTop;3dBottom",
"StartPosition": "(2.5*1), (2.5*1), 0",
"ExpandParam": "0, 0"
},
{
"Type": "NormalBorder",
"Rotation": "R270",
"SideSync": "2dRight;False;2dLeft;2dTop;3dTop;3dBottom",
"StartPosition": "0, (2.5*1), 0",
"ExpandParam": "1, 0"
}
]
},
{
"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",
"Rotation": "R0",
"SideSync": "False;2dRight;2dBottom;False;3dTop;3dBottom",
"StartPosition": "(2.5*1), (2.5*1), 0",
"ExpandParam": "0, 0"
},
{
"Type": "NormalInnerCorner",
"Rotation": "R90",
"SideSync": "False;2dTop;2dRight;False;3dTop;3dBottom",
"StartPosition": "0, (2.5*1), 0",
"ExpandParam": "0, 0"
},
{
"Type": "NormalInnerCorner",
"Rotation": "R180",
"SideSync": "False;2dLeft;2dTop;False;3dTop;3dBottom",
"StartPosition": "0, 0, 0",
"ExpandParam": "0, 0"
},
{
"Type": "NormalInnerCorner",
"Rotation": "R270",
"SideSync": "False;2dBottom;2dLeft;False;3dTop;3dBottom",
"StartPosition": "(2.5*1), 0, 0",
"ExpandParam": "0, 0"
}
]
}
]

View File

@ -0,0 +1,397 @@
[
{
"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",
"Rotation": "R0",
"SideSync": "2dTop;False;2dBottom;2dLeft;3dTop;3dBottom",
"StartPosition": "0, 0, 0",
"ExpandParam": "d1, 0"
},
{
"Type": "SinkBorder",
"Rotation": "R180",
"SideSync": "2dBottom;False;2dTop;2dRight;3dTop;3dBottom",
"StartPosition": "d1, (2.5*1), 0",
"ExpandParam": "d1, 0"
}
]
},
{
"Type": "SinkFloorTerminal",
"BindingDisplayTexture": "SinkFloorTerminal.png",
"UnitSize": "Small",
"ExpandType": "Static",
"InitColumnDirection": "PositiveX",
"DefaultSideConfig": {
"UseTwoDTop": true,
"UseTwoDRight": true,
"UseTwoDBottom": false,
"UseTwoDLeft": true,
"UseThreeDTop": true,
"UseThreeDBottom": false
},
"SmashedBlocks": [
{
"Type": "SinkOutterCorner",
"Rotation": "R0",
"SideSync": "2dTop;False;2dBottom;2dLeft;3dTop;3dBottom",
"StartPosition": "0, 0, 0",
"ExpandParam": "0, 0"
},
{
"Type": "SinkOutterCorner",
"Rotation": "R270",
"SideSync": "2dRight;2dBottom;False;2dTop;3dTop;3dBottom",
"StartPosition": "0, (2.5*1), 0",
"ExpandParam": "0, 0"
}
]
},
{
"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",
"Rotation": "R0",
"SideSync": "2dTop;False;False;2dLeft;3dTop;3dBottom",
"StartPosition": "0, 0, 0",
"ExpandParam": "0, 0"
},
{
"Type": "SinkOutterCorner",
"Rotation": "R90",
"SideSync": "2dLeft;False;False;2dBottom;3dTop;3dBottom",
"StartPosition": "(2.5*1), 0, 0",
"ExpandParam": "0, 0"
},
{
"Type": "SinkOutterCorner",
"Rotation": "R180",
"SideSync": "2dBottom;False;False;2dRight;3dTop;3dBottom",
"StartPosition": "(2.5*1), (2.5*1), 0",
"ExpandParam": "0, 0"
},
{
"Type": "SinkOutterCorner",
"Rotation": "R270",
"SideSync": "2dRight;False;False;2dTop;3dTop;3dBottom",
"StartPosition": "0, (2.5*1), 0",
"ExpandParam": "0, 0"
}
]
},
{
"Type": "SinkPlatform",
"BindingDisplayTexture": "SinkPlatform.png",
"UnitSize": "Small",
"ExpandType": "Freedom",
"InitColumnDirection": "PositiveY",
"DefaultSideConfig": {
"UseTwoDTop": true,
"UseTwoDRight": true,
"UseTwoDBottom": true,
"UseTwoDLeft": true,
"UseThreeDTop": true,
"UseThreeDBottom": false
},
"SmashedBlocks": [
{
"Type": "Flat",
"Rotation": "R0",
"SideSync": "False;False;False;False;3dTop;3dBottom",
"StartPosition": "(2.5*1), (2.5*1), -0.7",
"ExpandParam": "d1, d2"
},
{
"Type": "SinkBorder",
"Rotation": "R0",
"SideSync": "False;False;False;2dLeft;3dTop;3dBottom",
"StartPosition": "(2.5*1), 0, 0",
"ExpandParam": "d2, 0"
},
{
"Type": "SinkBorder",
"Rotation": "R90",
"SideSync": "False;False;False;2dBottom;3dTop;3dBottom",
"StartPosition": "(2.5*2) +d2, (2.5*1), 0",
"ExpandParam": "d1, 0"
},
{
"Type": "SinkBorder",
"Rotation": "R180",
"SideSync": "False;False;False;2dRight;3dTop;3dBottom",
"StartPosition": "(2.5*1) +d2, (2.5*2) +d1, 0",
"ExpandParam": "d2, 0"
},
{
"Type": "SinkBorder",
"Rotation": "R270",
"SideSync": "False;False;False;2dTop;3dTop;3dBottom",
"StartPosition": "0, (2.5*1) +d1, 0",
"ExpandParam": "d1, 0"
},
{
"Type": "SinkOutterCorner",
"Rotation": "R0",
"SideSync": "2dTop;False;False;2dLeft;3dTop;3dBottom",
"StartPosition": "0, 0, 0",
"ExpandParam": "0, 0"
},
{
"Type": "SinkOutterCorner",
"Rotation": "R90",
"SideSync": "2dLeft;False;False;2dBottom;3dTop;3dBottom",
"StartPosition": "(2.5*2) +d2, 0, 0",
"ExpandParam": "0, 0"
},
{
"Type": "SinkOutterCorner",
"Rotation": "R180",
"SideSync": "2dBottom;False;False;2dRight;3dTop;3dBottom",
"StartPosition": "(2.5*2) +d2, (2.5*2) +d1, 0",
"ExpandParam": "0, 0"
},
{
"Type": "SinkOutterCorner",
"Rotation": "R270",
"SideSync": "2dRight;False;False;2dTop;3dTop;3dBottom",
"StartPosition": "0, (2.5*2) +d1, 0",
"ExpandParam": "0, 0"
}
]
},
{
"Type": "RibbonPlatform",
"BindingDisplayTexture": "RibbonPlatform.png",
"UnitSize": "Small",
"ExpandType": "Freedom",
"InitColumnDirection": "PositiveY",
"DefaultSideConfig": {
"UseTwoDTop": true,
"UseTwoDRight": true,
"UseTwoDBottom": true,
"UseTwoDLeft": true,
"UseThreeDTop": true,
"UseThreeDBottom": false
},
"SmashedBlocks": [
{
"Type": "Flat",
"Rotation": "R0",
"SideSync": "False;False;False;False;3dTop;3dBottom",
"StartPosition": "(2.5*1), (2.5*1), -0.7",
"ExpandParam": "d1, d2"
},
{
"Type": "RibbonBorder",
"Rotation": "R0",
"SideSync": "False;False;False;2dLeft;3dTop;3dBottom",
"StartPosition": "(2.5*1), 0, 0",
"ExpandParam": "d2, 0"
},
{
"Type": "RibbonBorder",
"Rotation": "R90",
"SideSync": "False;False;False;2dBottom;3dTop;3dBottom",
"StartPosition": "(2.5*2) +d2, (2.5*1), 0",
"ExpandParam": "d1, 0"
},
{
"Type": "RibbonBorder",
"Rotation": "R180",
"SideSync": "False;False;False;2dRight;3dTop;3dBottom",
"StartPosition": "(2.5*1) +d2, (2.5*2) +d1, 0",
"ExpandParam": "d2, 0"
},
{
"Type": "RibbonBorder",
"Rotation": "R270",
"SideSync": "False;False;False;2dTop;3dTop;3dBottom",
"StartPosition": "0, (2.5*1) +d1, 0",
"ExpandParam": "d1, 0"
},
{
"Type": "RibbonOutterCorner",
"Rotation": "R0",
"SideSync": "2dTop;False;False;2dLeft;3dTop;3dBottom",
"StartPosition": "0, 0, 0",
"ExpandParam": "0, 0"
},
{
"Type": "RibbonOutterCorner",
"Rotation": "R90",
"SideSync": "2dLeft;False;False;2dBottom;3dTop;3dBottom",
"StartPosition": "(2.5*2) +d2, 0, 0",
"ExpandParam": "0, 0"
},
{
"Type": "RibbonOutterCorner",
"Rotation": "R180",
"SideSync": "2dBottom;False;False;2dRight;3dTop;3dBottom",
"StartPosition": "(2.5*2) +d2, (2.5*2) +d1, 0",
"ExpandParam": "0, 0"
},
{
"Type": "RibbonOutterCorner",
"Rotation": "R270",
"SideSync": "2dRight;False;False;2dTop;3dTop;3dBottom",
"StartPosition": "0, (2.5*2) +d1, 0",
"ExpandParam": "0, 0"
}
]
},
{
"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",
"Rotation": "R0",
"SideSync": "2dTop;False;False;2dLeft;3dTop;3dBottom",
"StartPosition": "0, 0, 0",
"ExpandParam": "0, 0"
},
{
"Type": "SinkBorder",
"Rotation": "R0",
"SideSync": "False;False;2dBottom;2dLeft;3dTop;3dBottom",
"StartPosition": "(2.5*1), 0, 0",
"ExpandParam": "0, 0"
},
{
"Type": "SinkInnerCorner",
"Rotation": "R0",
"SideSync": "False;2dRight;2dBottom;False;3dTop;3dBottom",
"StartPosition": "(2.5*1), (2.5*1), 0",
"ExpandParam": "0, 0"
},
{
"Type": "SinkBorder",
"Rotation": "R270",
"SideSync": "2dRight;False;False;2dTop;3dTop;3dBottom",
"StartPosition": "0, (2.5*1), 0",
"ExpandParam": "0, 0"
}
]
},
{
"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",
"Rotation": "R270",
"SideSync": "False;2dBottom;2dLeft;False;3dTop;3dBottom",
"StartPosition": "(2.5*1), 0, 0",
"ExpandParam": "0, 0"
},
{
"Type": "SinkInnerCorner",
"Rotation": "R0",
"SideSync": "False;2dRight;2dBottom;False;3dTop;3dBottom",
"StartPosition": "(2.5*1), (2.5*1), 0",
"ExpandParam": "0, 0"
},
{
"Type": "SinkBorder",
"Rotation": "R270",
"SideSync": "2dRight;False;2dLeft;2dTop;3dTop;3dBottom",
"StartPosition": "0, (2.5*1), 0",
"ExpandParam": "1, 0"
}
]
},
{
"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",
"Rotation": "R0",
"SideSync": "False;2dRight;2dBottom;False;3dTop;3dBottom",
"StartPosition": "(2.5*1), (2.5*1), 0",
"ExpandParam": "0, 0"
},
{
"Type": "SinkInnerCorner",
"Rotation": "R90",
"SideSync": "False;2dTop;2dRight;False;3dTop;3dBottom",
"StartPosition": "0, (2.5*1), 0",
"ExpandParam": "0, 0"
},
{
"Type": "SinkInnerCorner",
"Rotation": "R180",
"SideSync": "False;2dLeft;2dTop;False;3dTop;3dBottom",
"StartPosition": "0, 0, 0",
"ExpandParam": "0, 0"
},
{
"Type": "SinkInnerCorner",
"Rotation": "R270",
"SideSync": "False;2dBottom;2dLeft;False;3dTop;3dBottom",
"StartPosition": "(2.5*1), 0, 0",
"ExpandParam": "0, 0"
}
]
}
]

View File

@ -0,0 +1,262 @@
[
{
"Type": "WideFloor",
"BindingDisplayTexture": "WideFloor.png",
"UnitSize": "Small",
"ExpandType": "Freedom",
"InitColumnDirection": "PositiveY",
"DefaultSideConfig": {
"UseTwoDTop": false,
"UseTwoDRight": true,
"UseTwoDBottom": false,
"UseTwoDLeft": true,
"UseThreeDTop": true,
"UseThreeDBottom": false
},
"SmashedBlocks": [
{
"Type": "SinkBorder",
"Rotation": "R0",
"SideSync": "2dTop;False;2dBottom;2dLeft;3dTop;3dBottom",
"StartPosition": "0, 0, 0",
"ExpandParam": "d2, 0"
},
{
"Type": "SinkBorder",
"Rotation": "R180",
"SideSync": "2dBottom;False;2dTop;2dRight;3dTop;3dBottom",
"StartPosition": "d2, (2.5*2)+d1, 0",
"ExpandParam": "d2, 0"
},
{
"Type": "Flat",
"Rotation": "R0",
"SideSync": "2dTop;False;2dBottom;False;3dTop;3dBottom",
"StartPosition": "0, (2.5*1), -0.7",
"ExpandParam": "d1, d2"
}
]
},
{
"Type": "WideFloorTerminal",
"BindingDisplayTexture": "WideFloorTerminal.png",
"UnitSize": "Small",
"ExpandType": "Column",
"InitColumnDirection": "PositiveY",
"DefaultSideConfig": {
"UseTwoDTop": true,
"UseTwoDRight": true,
"UseTwoDBottom": false,
"UseTwoDLeft": true,
"UseThreeDTop": true,
"UseThreeDBottom": false
},
"SmashedBlocks": [
{
"Type": "SinkOutterCorner",
"Rotation": "R0",
"SideSync": "2dTop;False;2dBottom;2dLeft;3dTop;3dBottom",
"StartPosition": "0, 0, 0",
"ExpandParam": "0, 0"
},
{
"Type": "SinkOutterCorner",
"Rotation": "R270",
"SideSync": "2dRight;2dBottom;False;2dTop;3dTop;3dBottom",
"StartPosition": "0, (2.5*2)+d1, 0",
"ExpandParam": "0, 0"
},
{
"Type": "SinkBorder",
"Rotation": "R270",
"SideSync": "False;2dBottom;False;2dTop;3dTop;3dBottom",
"StartPosition": "0, (2.5*1) +d1, 0",
"ExpandParam": "d1, 0"
}
]
},
{
"Type": "WideLConnector",
"BindingDisplayTexture": "WideLConnector.png",
"UnitSize": "Small",
"ExpandType": "Column",
"InitColumnDirection": "PositiveY",
"DefaultSideConfig": {
"UseTwoDTop": true,
"UseTwoDRight": false,
"UseTwoDBottom": false,
"UseTwoDLeft": true,
"UseThreeDTop": true,
"UseThreeDBottom": false
},
"SmashedBlocks": [
{
"Type": "SinkOutterCorner",
"Rotation": "R0",
"SideSync": "2dTop;False;False;2dLeft;3dTop;3dBottom",
"StartPosition": "0, 0, 0",
"ExpandParam": "0, 0"
},
{
"Type": "SinkBorder",
"Rotation": "R0",
"SideSync": "False;False;2dBottom;2dLeft;3dTop;3dBottom",
"StartPosition": "(2.5*1), 0, 0",
"ExpandParam": "d1 + 1, 0"
},
{
"Type": "SinkInnerCorner",
"Rotation": "R0",
"SideSync": "False;2dRight;2dBottom;False;3dTop;3dBottom",
"StartPosition": "(2.5*2) + d1, (2.5*2) + d1, 0",
"ExpandParam": "0, 0"
},
{
"Type": "SinkBorder",
"Rotation": "R270",
"SideSync": "2dRight;False;False;2dTop;3dTop;3dBottom",
"StartPosition": "0, (2.5*2)+d1, 0",
"ExpandParam": "d1 + 1, 0"
},
{
"Type": "Flat",
"Rotation": "R0",
"SideSync": "False;False;2dBottom;False;3dTop;3dBottom",
"StartPosition": "2.5, 2.5, -0.7",
"ExpandParam": "d1, d1 + 1"
},
{
"Type": "Flat",
"Rotation": "R0",
"SideSync": "False;2dRight;False;False;3dTop;3dBottom",
"StartPosition": "2.5, (2.5 * 2) + d1, -0.7",
"ExpandParam": "0, d1"
}
]
},
{
"Type": "WideTConnector",
"BindingDisplayTexture": "WideTConnector.png",
"UnitSize": "Small",
"ExpandType": "Column",
"InitColumnDirection": "PositiveY",
"DefaultSideConfig": {
"UseTwoDTop": true,
"UseTwoDRight": false,
"UseTwoDBottom": false,
"UseTwoDLeft": false,
"UseThreeDTop": true,
"UseThreeDBottom": false
},
"SmashedBlocks": [
{
"Type": "SinkInnerCorner",
"Rotation": "R270",
"SideSync": "False;2dBottom;2dLeft;False;3dTop;3dBottom",
"StartPosition": "(2.5*2) + d1, 0, 0",
"ExpandParam": "0, 0"
},
{
"Type": "SinkInnerCorner",
"Rotation": "R0",
"SideSync": "False;2dRight;2dBottom;False;3dTop;3dBottom",
"StartPosition": "(2.5*2) + d1, (2.5*2) + d1, 0",
"ExpandParam": "0, 0"
},
{
"Type": "SinkBorder",
"Rotation": "R270",
"SideSync": "2dRight;False;2dLeft;2dTop;3dTop;3dBottom",
"StartPosition": "0, (2.5*2) + d1, 0",
"ExpandParam": "d1 + 2, 0"
},
{
"Type": "Flat",
"Rotation": "R0",
"SideSync": "False;False;2dBottom;False;3dTop;3dBottom",
"StartPosition": "2.5, 2.5, -0.7",
"ExpandParam": "d1, d1+1"
},
{
"Type": "Flat",
"Rotation": "R0",
"SideSync": "False;False;False;2dLeft;3dTop;3dBottom",
"StartPosition": "2.5, 0, -0.7",
"ExpandParam": "0, d1"
},
{
"Type": "Flat",
"Rotation": "R0",
"SideSync": "False;2dRight;False;False;3dTop;3dBottom",
"StartPosition": "2.5, (2.5*2)+d1, -0.7",
"ExpandParam": "0, d1"
}
]
},
{
"Type": "WideCrossroad",
"BindingDisplayTexture": "WideCrossroad.png",
"UnitSize": "Small",
"ExpandType": "Column",
"InitColumnDirection": "PositiveY",
"DefaultSideConfig": {
"UseTwoDTop": false,
"UseTwoDRight": false,
"UseTwoDBottom": false,
"UseTwoDLeft": false,
"UseThreeDTop": true,
"UseThreeDBottom": false
},
"SmashedBlocks": [
{
"Type": "SinkInnerCorner",
"Rotation": "R0",
"SideSync": "False;2dRight;2dBottom;False;3dTop;3dBottom",
"StartPosition": "(2.5*2)+d1, (2.5*2)+d1, 0",
"ExpandParam": "0, 0"
},
{
"Type": "SinkInnerCorner",
"Rotation": "R90",
"SideSync": "False;2dTop;2dRight;False;3dTop;3dBottom",
"StartPosition": "0, (2.5*2)+d1, 0",
"ExpandParam": "0, 0"
},
{
"Type": "SinkInnerCorner",
"Rotation": "R180",
"SideSync": "False;2dLeft;2dTop;False;3dTop;3dBottom",
"StartPosition": "0, 0, 0",
"ExpandParam": "0, 0"
},
{
"Type": "SinkInnerCorner",
"Rotation": "R270",
"SideSync": "False;2dBottom;2dLeft;False;3dTop;3dBottom",
"StartPosition": "(2.5*2)+d1, 0, 0",
"ExpandParam": "0, 0"
},
{
"Type": "Flat",
"Rotation": "R0",
"SideSync": "2dTop;False;2dBottom;False;3dTop;3dBottom",
"StartPosition": "0, 2.5, -0.7",
"ExpandParam": "d1, d1+2"
},
{
"Type": "Flat",
"Rotation": "R0",
"SideSync": "False;False;False;2dLeft;3dTop;3dBottom",
"StartPosition": "2.5, 0, -0.7",
"ExpandParam": "0, d1"
},
{
"Type": "Flat",
"Rotation": "R0",
"SideSync": "False;2dRight;False;False;3dTop;3dBottom",
"StartPosition": "2.5, (2.5*2)+d1, -0.7",
"ExpandParam": "0, d1"
}
]
}
]

View File

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

View File

@ -1,21 +0,0 @@
import bpy
from bpy_extras.io_utils import unpack_list
def ShowMessageBox(message, title, icon):
def draw(self, context):
layout = self.layout
for item in message:
layout.label(text=item, translate=False)
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