648 lines
22 KiB
Python
648 lines
22 KiB
Python
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):
|
|
"""Load a Ballance Map File (BM file spec 1.0)"""
|
|
bl_idname = "import_scene.bm"
|
|
bl_label = "Import BM "
|
|
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):
|
|
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):
|
|
"""Save a Ballance Map File (BM file spec 1.0)"""
|
|
bl_idname = "export_scene.bm"
|
|
bl_label = 'Export BM'
|
|
bl_options = {'PRESET'}
|
|
filename_ext = ".bm"
|
|
|
|
export_mode: bpy.props.EnumProperty(
|
|
name="Export mode",
|
|
items=(('COLLECTION', "Selected collection", "Export the selected collection"),
|
|
('OBJECT', "Selected objects", "Export the selected objects"),
|
|
),
|
|
)
|
|
export_target: bpy.props.StringProperty(
|
|
name="Export target",
|
|
description="Which one will be exported",
|
|
)
|
|
|
|
def execute(self, context):
|
|
export_bm(context, self.filepath, self.export_mode, self.export_target, "") #todo: fix no_component_suffix
|
|
return {'FINISHED'}
|
|
|
|
|
|
# ========================================== method
|
|
|
|
bm_current_version = 11
|
|
|
|
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
|
|
tempFolderObj = tempfile.TemporaryDirectory()
|
|
tempFolder = tempFolderObj.name
|
|
# debug
|
|
# tempFolder = "G:\\ziptest"
|
|
tempTextureFolder = os.path.join(tempFolder, "Texture")
|
|
os.makedirs(tempTextureFolder)
|
|
|
|
# ============================================ find export target
|
|
if export_mode== "COLLECTION":
|
|
objectList = bpy.data.collections[export_target].objects
|
|
else:
|
|
objectList = [bpy.data.objects[export_target]]
|
|
|
|
needSuffixChecker = no_component_suffix != ""
|
|
componentObj = set()
|
|
for obj in objectList:
|
|
if needSuffixChecker and obj.name.endwith(no_component_suffix):
|
|
pass # meshObjList.add(obj)
|
|
else:
|
|
if is_component(obj.name):
|
|
componentObj.add(obj)
|
|
else:
|
|
pass # meshObjList.add(obj)
|
|
|
|
# ============================================ export
|
|
finfo = open(os.path.join(tempFolder, "index.bm"), "wb")
|
|
finfo.write(struct.pack("I", bm_current_version))
|
|
|
|
# ====================== export object
|
|
fobject = open(os.path.join(tempFolder, "object.bm"), "wb")
|
|
meshSet = set()
|
|
meshList = []
|
|
meshCount = 0
|
|
for obj in objectList:
|
|
# only export mesh object
|
|
if obj.type != 'MESH':
|
|
continue
|
|
|
|
varis_component = obj in componentObj
|
|
|
|
# clean no mesh object
|
|
currentMesh = obj.data
|
|
if currentMesh == None:
|
|
continue
|
|
# triangle first and then group
|
|
if not varis_component:
|
|
if currentMesh not in meshSet:
|
|
mesh_triangulate(currentMesh)
|
|
meshSet.add(currentMesh)
|
|
meshList.append(currentMesh)
|
|
meshId = meshCount
|
|
meshCount += 1
|
|
else:
|
|
meshId = meshList.index(currentMesh)
|
|
|
|
# write finfo first
|
|
write_string(finfo, obj.name)
|
|
write_int(finfo, info_bm_type.OBJECT)
|
|
write_long(finfo, fobject.tell())
|
|
|
|
# write fobject
|
|
write_int(fobject, 1 if varis_component else 0)
|
|
write_worldMatrix(fobject, obj.matrix_world)
|
|
if varis_component:
|
|
write_int(fobject, get_component_id(obj.name))
|
|
else:
|
|
write_int(fobject, meshId)
|
|
|
|
fobject.close()
|
|
|
|
# ====================== export mesh
|
|
fmesh = open(os.path.join(tempFolder, "mesh.bm"), "wb")
|
|
materialSet = set()
|
|
materialList = []
|
|
for mesh in meshList:
|
|
mesh.calc_normals_split()
|
|
|
|
# write finfo first
|
|
write_string(finfo, mesh.name)
|
|
write_int(finfo, info_bm_type.MESH)
|
|
write_long(finfo, fmesh.tell())
|
|
|
|
# write fmesh
|
|
# vertices
|
|
vecList = mesh.vertices[:]
|
|
write_int(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)]
|
|
uv_layer = mesh.uv_layers.active.data[:]
|
|
write_int(fmesh, len(face_index_pairs) * 3)
|
|
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])
|
|
|
|
# normals
|
|
write_int(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_int(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_int(fmesh, 0 if noMaterial else 1)
|
|
write_int(fmesh, usedMat)
|
|
|
|
mesh.free_normals_split()
|
|
|
|
fmesh.close()
|
|
|
|
# ====================== export material
|
|
fmaterial = open(os.path.join(tempFolder, "material.bm"), "wb")
|
|
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)
|
|
write_int(finfo, info_bm_type.MATERIAL)
|
|
write_long(finfo, fmaterial.tell())
|
|
|
|
# write basic color
|
|
mat_wrap = node_shader_utils.PrincipledBSDFWrapper(material)
|
|
if mat_wrap:
|
|
use_mirror = mat_wrap.metallic != 0.0
|
|
if use_mirror:
|
|
write_3vector(fmaterial, mat_wrap.metallic, mat_wrap.metallic, mat_wrap.metallic)
|
|
else:
|
|
write_3vector(fmaterial, 1, 1, 1)
|
|
write_3vector(fmaterial, mat_wrap.base_color[0], mat_wrap.base_color[1], mat_wrap.base_color[2])
|
|
write_3vector(fmaterial, mat_wrap.specular, mat_wrap.specular, mat_wrap.specular)
|
|
|
|
# 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)
|
|
|
|
write_int(fmaterial, 1)
|
|
write_int(fmaterial, currentTexture)
|
|
else:
|
|
# no texture
|
|
write_int(fmaterial, 0)
|
|
write_int(fmaterial, 0)
|
|
else:
|
|
# no texture
|
|
write_int(fmaterial, 0)
|
|
write_int(fmaterial, 0)
|
|
|
|
else:
|
|
# no Principled BSDF. write garbage
|
|
write_3vector(fmaterial, 0.8, 0.8, 0.8)
|
|
write_3vector(fmaterial, 0.8, 0.8, 0.8)
|
|
write_3vector(fmaterial, 0.8, 0.8, 0.8)
|
|
write_int(fmaterial, 0)
|
|
write_int(fmaterial, 0)
|
|
|
|
fmaterial.close()
|
|
|
|
# ====================== export texture
|
|
ftexture = open(os.path.join(tempFolder, "texture.bm"), "wb")
|
|
source_dir = os.path.dirname(bpy.data.filepath)
|
|
existed_texture = set()
|
|
|
|
for texture in textureList:
|
|
# write finfo first
|
|
write_string(finfo, texture.name)
|
|
write_int(finfo, info_bm_type.TEXTURE)
|
|
write_long(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_int(ftexture, 1)
|
|
else:
|
|
# copy internal texture, if this file is copied, do not copy it again
|
|
write_int(ftexture, 0)
|
|
if filename not in existed_texture:
|
|
shutil.copy(texture_filepath, os.path.join(tempTextureFolder, filename))
|
|
existed_texture.add(filename)
|
|
|
|
ftexture.close()
|
|
|
|
# close info fs
|
|
finfo.close()
|
|
|
|
# ============================================ 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 load_component(component_id):
|
|
return None
|
|
|
|
# export
|
|
|
|
def is_component(name):
|
|
return get_component_id(name) != -1
|
|
|
|
def get_component_id(name):
|
|
return -1 # todo: finish this, -1 mean not a component
|
|
|
|
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()
|
|
|
|
# ======================================================================================= 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)
|
|
fs.write(str.encode("utf_32_le"))
|
|
|
|
def write_int(fs,num):
|
|
fs.write(struct.pack("I", num))
|
|
|
|
def write_long(fs,num):
|
|
fs.write(struct.pack("Q", num))
|
|
|
|
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_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))
|
|
|