add some prefs and finish bm import (no debug)

This commit is contained in:
yyc12345 2020-08-05 16:19:38 +08:00
parent dcfc397421
commit 72fe37040c
2 changed files with 297 additions and 8 deletions

View File

@ -2,6 +2,7 @@ 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.image_utils import load_image
from . import utils, config
class ImportBM(bpy.types.Operator, bpy_extras.io_utils.ImportHelper):
@ -11,8 +12,14 @@ class ImportBM(bpy.types.Operator, bpy_extras.io_utils.ImportHelper):
bl_options = {'PRESET', 'UNDO'}
filename_ext = ".bm"
@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):
import_bm(context, self.filepath)
prefs = bpy.context.preferences.addons[__package__].preferences
import_bm(context, self.filepath, prefs.external_folder, prefs.temp_texture_folder)
return {'FINISHED'}
class ExportBM(bpy.types.Operator, bpy_extras.io_utils.ExportHelper):
@ -40,11 +47,226 @@ class ExportBM(bpy.types.Operator, bpy_extras.io_utils.ExportHelper):
# ========================================== method
bm_current_version = 10
bm_current_version = 11
def import_bm(context,filepath):
# todo: finish this
def import_bm(context,filepath,externalTexture,blenderTempFolder):
# ============================================ 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
findex = open(os.path.join(tempFolder, "index.bm"), "rb")
# judge version first
gotten_version = read_uint32(findex)
if (gotten_version != bm_current_version):
utils.ShowMessageBox("Unsupported BM spec. Expect: {} Gotten: {}".format(bm_current_version, gotten_version), "Unsupported BM spec", 'WARNING')
findex.close()
tempFolderObj.cleanup()
return
objectList = []
meshList = []
materialList = []
textureList = []
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
findex.close()
# texture.bm
ftexture = open(os.path.join(tempFolder, "texture.bm"), "rb")
for item in textureList:
ftexture.seek(item.offset, os.SEEK_SET)
texture_filename = read_string(ftexture)
texture_isExternal = read_bool(ftexture)
if texture_isExternal:
item.blenderData = txur = load_image(texture_filename, externalTextureFolder)
else:
# not external. copy temp file into blender temp. then use it.
shutil.copy(os.path.join(tempTextureFolder, texture_filename), os.path.join(blenderTempTextureFolder, texture_filename))
item.blenderData = txur = load_image(texture_filename, blenderTempTextureFolder)
txur.name = item.name
ftexture.close()
# material.bm
fmaterial = open(os.path.join(tempFolder, "material.bm"), "rb")
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
item.blenderData = m = bpy.data.materials.new(item.name)
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])
bnode.metallic = sum(material_colAmbient) / 3
m.diffuse_color = material_colDiffuse
bnode.specular = sum(material_colSpecular) / 3
m.specular_color = material_colEmissive
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
fmaterial.close()
# mesh.bm
fmesh = open(os.path.join(tempFolder, "mesh.bm"), "rb")
for item in meshList:
fmesh.seek(item.name, os.SEEK_SET)
# load a empty mesh first
mesh=bmesh.new()
materialSolt = []
# load vec but don't change it normal. normal will be added in following process
vCount = read_uint32(fmesh)
for i in range(vCount):
cache = read_3vector(fmesh)
# switch yz
mesh.verts.new((cache[0], cache[2], cache[1]))
mesh.verts.ensure_lookup_table()
mesh.verts.index_update()
# load vt and vn into list for following use
vtList = []
vnList = []
vCount = read_uint32(fmesh)
for i in range(vCount):
cache = read_2vector(fmesh)
# reverse v
vtList.append(cache[0], -cache[1])
vCount = read_uint32(fmesh)
for i in range(vCount):
cache = read_3vector(fmesh)
# switch yz
vnList.append((cache[0], cache[2], cache[1]))
# load face
fCount = read_uint32(fmesh)
ftellCache = fmesh.tell()
for i in range(fCount):
faceData = read_face(fmesh)
mesh_useMaterial = read_bool(fmesh)
mesh_materialIndex = read_uint32(fmesh)
# give vec normal
mesh.verts[faceData[6]].normal = vnList[faceData[8]]
mesh.verts[faceData[3]].normal = vnList[faceData[5]]
mesh.verts[faceData[0]].normal = vnList[faceData[2]]
# we need invert triangle sort
nf=mesh.faces.new((
mesh.verts[faceData[6]],
mesh.verts[faceData[3]],
mesh.verts[faceData[0]]
))
if mesh_useMaterial:
neededMaterial = materialList[mesh_materialIndex].blenderData
if neededMaterial in materialSolt:
neededIndex = materialSolt.index(neededMaterial)
else:
neededIndex = len(materialSolt)
materialSolt.append(neededMaterial)
nf.material_index = neededIndex
uv=mesh.loops.layers.uv.new()
# back to face head and run again
fmesh.seek(ftellCache, os.SEEK_SET)
for i in range(fCount):
faceData = read_face(fmesh)
read_bool(fmesh)
read_uint32(fmesh)
# we assume all face's sort is out create sort
nf = mesh.faces[i]
lp = nf.loops[0]
lp[uv].uv=mathutils.Vector(vtList[faceData[7]])
lp = nf.loops[1]
lp[uv].uv=mathutils.Vector(vtList[faceData[4]])
lp = nf.loops[2]
lp[uv].uv=mathutils.Vector(vtList[faceData[1]])
# create real mesh and add material
msh = bpy.data.meshes.new(item.name)
mesh.to_mesh(msh)
mesh.free()
for mat in materialSolt:
msh.materials.append(mat)
item.blenderData = msh
fmesh.close()
# object
fobject = open(os.path.join(tempFolder, "object.bm"), "rb")
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_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 = bpy.data.objects.new(item.name, neededMesh)
obj.matrix_world = object_worldMatrix
obj.hide_viewport = object_isHidden
# todo: finish forced collection grouping
fobject.close()
tempFolderObj.cleanup()
def export_bm(context,filepath,export_mode, export_target, no_component_suffix):
# ============================================ alloc a temp folder
@ -211,7 +433,7 @@ def export_bm(context,filepath,export_mode, export_target, no_component_suffix):
textureSet = set()
textureList = []
textureCount = 0
# todo: texture filter have duplicated name error. fix it later
for material in materialList:
# write finfo first
write_string(finfo, material.name)
@ -307,12 +529,27 @@ def export_bm(context,filepath,export_mode, export_target, no_component_suffix):
# ======================================================================================= 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 load_component(component_id):
return None
# export
def is_component(name):
return get_component_id(name) != -1
@ -334,6 +571,52 @@ def mesh_triangulate(me):
# ======================================================================================= 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(1))[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.Materix((
(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("fff", fs.read(2*4))
def read_face(fs):
return struct.unpack("IIIIIIIII", fs.read(4*9))
# export
def write_string(fs,str):
count=len(str)
write_int(fs,count)

View File

@ -13,6 +13,11 @@ class BallanceBlenderPluginPreferences(bpy.types.AddonPreferences):
description="(Import) The object which stored in this collectiion will not be saved as component. (Export) All forced no component objects will be stored in this collection",
)
temp_texture_folder: bpy.props.StringProperty(
name="Temp texture folder",
description="The folder which will temporarily store the textures which are extracted from bm. Due to system temp folder will be deleted after decoding of bm, so this path should not be blank.",
)
def draw(self, context):
layout = self.layout
@ -21,3 +26,4 @@ class BallanceBlenderPluginPreferences(bpy.types.AddonPreferences):
col.prop(self, "external_folder")
col.prop(self, "no_component_collection")
col.prop(self, "temp_texture_folder")