Models with basic materials importing correctly
This commit is contained in:
parent
1cc6a8d08d
commit
b8afa1ed10
|
@ -60,6 +60,7 @@ from .msh_scene import create_scene
|
||||||
from .msh_scene_save import save_scene
|
from .msh_scene_save import save_scene
|
||||||
from .msh_scene_read import read_scene
|
from .msh_scene_read import read_scene
|
||||||
from .msh_material_properties import *
|
from .msh_material_properties import *
|
||||||
|
from .msh_to_blend import *
|
||||||
|
|
||||||
class ExportMSH(Operator, ExportHelper):
|
class ExportMSH(Operator, ExportHelper):
|
||||||
""" Export the current scene as a SWBF .msh file. """
|
""" Export the current scene as a SWBF .msh file. """
|
||||||
|
@ -133,7 +134,7 @@ class ImportMSH(Operator, ImportHelper):
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
with open(self.filepath, 'rb') as input_file:
|
with open(self.filepath, 'rb') as input_file:
|
||||||
read_scene(input_file)
|
extract_scene(read_scene(input_file))
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
def menu_func_import(self, context):
|
def menu_func_import(self, context):
|
||||||
|
|
|
@ -32,8 +32,9 @@ class MaterialFlags(Flag):
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Material:
|
class Material:
|
||||||
""" Data class representing a .msh material.
|
""" Data class representing a .msh material."""
|
||||||
Intended to be stored in a dictionary so name is missing. """
|
|
||||||
|
name: str = ""
|
||||||
|
|
||||||
specular_color: Color = Color((1.0, 1.0, 1.0))
|
specular_color: Color = Color((1.0, 1.0, 1.0))
|
||||||
rendertype: Rendertype = Rendertype.NORMAL
|
rendertype: Rendertype = Rendertype.NORMAL
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
""" Contains functions for saving a Scene to a .msh file. """
|
""" Contains functions for extracting a scene from a .msh file"""
|
||||||
|
|
||||||
from itertools import islice
|
from itertools import islice
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
|
@ -14,42 +14,52 @@ def read_scene(input_file) -> Scene:
|
||||||
|
|
||||||
scene = Scene()
|
scene = Scene()
|
||||||
scene.models = []
|
scene.models = []
|
||||||
|
scene.materials = {}
|
||||||
|
|
||||||
with Reader(file=input_file, chunk_id="HEDR") as hedr:
|
with Reader(file=input_file, chunk_id="HEDR") as hedr:
|
||||||
with hedr.read_child("MSH2") as msh2:
|
|
||||||
|
|
||||||
with msh2.read_child("SINF") as sinf:
|
while hedr.could_have_child():
|
||||||
pass
|
|
||||||
|
|
||||||
materials_list: List[str] = []
|
next_header = hedr.peak_next_header()
|
||||||
|
|
||||||
with msh2.read_child("MATL") as matl:
|
if "MSH2" in next_header:
|
||||||
materials_list = _read_matl_and_get_materials_list(matl)
|
|
||||||
|
|
||||||
while ("MODL" in msh2.peak_next_header()):
|
with hedr.read_child("MSH2") as msh2:
|
||||||
with msh2.read_child("MODL") as modl:
|
|
||||||
scene.models.append(_read_modl(modl, materials_list))
|
|
||||||
|
|
||||||
mats_dict = {}
|
materials_list = []
|
||||||
for i,mat in enumerate(materials_list):
|
|
||||||
mats_dict["Material" + str(i)] = mat
|
|
||||||
|
|
||||||
scene.materials = mats_dict
|
while (msh2.could_have_child()):
|
||||||
|
|
||||||
#with hedr.read_child("ANM2") as anm2: #simple for now
|
next_header = msh2.peak_next_header()
|
||||||
# for anim in scene.anims:
|
|
||||||
# _write_anm2(anm2, anim)
|
|
||||||
|
|
||||||
#with hedr.read_child("CL1L"):
|
if "SINF" in next_header:
|
||||||
# pass
|
with msh2.read_child("SINF") as sinf:
|
||||||
|
pass
|
||||||
|
|
||||||
|
elif "MATL" in next_header:
|
||||||
|
with msh2.read_child("MATL") as matl:
|
||||||
|
materials_list += _read_matl_and_get_materials_list(matl)
|
||||||
|
for i,mat in enumerate(materials_list):
|
||||||
|
scene.materials[mat.name] = mat
|
||||||
|
|
||||||
|
elif "MODL" in next_header:
|
||||||
|
while ("MODL" in msh2.peak_next_header()):
|
||||||
|
with msh2.read_child("MODL") as modl:
|
||||||
|
scene.models.append(_read_modl(modl, materials_list))
|
||||||
|
|
||||||
|
else:
|
||||||
|
with hedr.read_child("NULL") as unknown:
|
||||||
|
pass
|
||||||
|
|
||||||
|
else:
|
||||||
|
with hedr.read_child("NULL") as unknown:
|
||||||
|
pass
|
||||||
|
|
||||||
return scene
|
return scene
|
||||||
|
|
||||||
|
|
||||||
def _read_matl_and_get_materials_list(matl: Reader) -> List[str]:
|
def _read_matl_and_get_materials_list(matl: Reader) -> List[Material]:
|
||||||
materials_list: List[str] = []
|
materials_list: List[Material] = []
|
||||||
|
|
||||||
num_mats = matl.read_u32()
|
num_mats = matl.read_u32()
|
||||||
|
|
||||||
|
@ -72,7 +82,6 @@ def _read_matd(matd: Reader) -> Material:
|
||||||
if "NAME" in next_header:
|
if "NAME" in next_header:
|
||||||
with matd.read_child("NAME") as name:
|
with matd.read_child("NAME") as name:
|
||||||
mat.name = name.read_string()
|
mat.name = name.read_string()
|
||||||
print(matd.indent + "Got a new material: " + mat.name)
|
|
||||||
|
|
||||||
elif "DATA" in next_header:
|
elif "DATA" in next_header:
|
||||||
with matd.read_child("DATA") as data:
|
with matd.read_child("DATA") as data:
|
||||||
|
@ -109,7 +118,7 @@ def _read_matd(matd: Reader) -> Material:
|
||||||
return mat
|
return mat
|
||||||
|
|
||||||
|
|
||||||
def _read_modl(modl: Reader, materials_list: List[str]) -> Model:
|
def _read_modl(modl: Reader, materials_list: List[Material]) -> Model:
|
||||||
|
|
||||||
model = Model()
|
model = Model()
|
||||||
|
|
||||||
|
@ -119,7 +128,7 @@ def _read_modl(modl: Reader, materials_list: List[str]) -> Model:
|
||||||
|
|
||||||
if "MTYP" in next_header:
|
if "MTYP" in next_header:
|
||||||
with modl.read_child("MTYP") as mtyp:
|
with modl.read_child("MTYP") as mtyp:
|
||||||
model.model_type = mtyp.read_u32()
|
model.model_type = ModelType(mtyp.read_u32())
|
||||||
|
|
||||||
elif "MNDX" in next_header:
|
elif "MNDX" in next_header:
|
||||||
with modl.read_child("MNDX") as mndx:
|
with modl.read_child("MNDX") as mndx:
|
||||||
|
@ -128,7 +137,6 @@ def _read_modl(modl: Reader, materials_list: List[str]) -> Model:
|
||||||
elif "NAME" in next_header:
|
elif "NAME" in next_header:
|
||||||
with modl.read_child("NAME") as name:
|
with modl.read_child("NAME") as name:
|
||||||
model.name = name.read_string()
|
model.name = name.read_string()
|
||||||
print(modl.indent + "New model: " + model.name)
|
|
||||||
|
|
||||||
elif "PRNT" in next_header:
|
elif "PRNT" in next_header:
|
||||||
with modl.read_child("PRNT") as prnt:
|
with modl.read_child("PRNT") as prnt:
|
||||||
|
@ -151,13 +159,7 @@ def _read_modl(modl: Reader, materials_list: List[str]) -> Model:
|
||||||
if "SEGM" in next_header_modl:
|
if "SEGM" in next_header_modl:
|
||||||
with geom.read_child("SEGM") as segm:
|
with geom.read_child("SEGM") as segm:
|
||||||
model.geometry.append(_read_segm(segm, materials_list))
|
model.geometry.append(_read_segm(segm, materials_list))
|
||||||
'''
|
|
||||||
if model.model_type == ModelType.SKIN:
|
|
||||||
with modl.read_child("ENVL") as envl:
|
|
||||||
envl.write_u32(len(scene.models))
|
|
||||||
for i in range(len(scene.models)):
|
|
||||||
envl.write_u32(i)
|
|
||||||
'''
|
|
||||||
elif "SWCI" in next_header:
|
elif "SWCI" in next_header:
|
||||||
prim = CollisionPrimitive()
|
prim = CollisionPrimitive()
|
||||||
with modl.read_child("SWCI") as swci:
|
with modl.read_child("SWCI") as swci:
|
||||||
|
@ -171,6 +173,7 @@ def _read_modl(modl: Reader, materials_list: List[str]) -> Model:
|
||||||
with modl.read_child("NULL") as unknown:
|
with modl.read_child("NULL") as unknown:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
return model
|
||||||
|
|
||||||
|
|
||||||
def _read_tran(tran: Reader) -> ModelTransform:
|
def _read_tran(tran: Reader) -> ModelTransform:
|
||||||
|
@ -178,14 +181,15 @@ def _read_tran(tran: Reader) -> ModelTransform:
|
||||||
xform = ModelTransform()
|
xform = ModelTransform()
|
||||||
|
|
||||||
tran.skip_bytes(4 * 3) #ignore scale
|
tran.skip_bytes(4 * 3) #ignore scale
|
||||||
xform.rotation = Quaternion(tran.read_f32(4))
|
|
||||||
xform.position = Vector(tran.read_f32(3))
|
rot = tran.read_f32(4)
|
||||||
|
xform.rotation = Quaternion((rot[3], rot[0], rot[1], rot[2]))
|
||||||
|
xform.translation = Vector(tran.read_f32(3))
|
||||||
|
|
||||||
return xform
|
return xform
|
||||||
|
|
||||||
|
|
||||||
|
def _read_segm(segm: Reader, materials_list: List[Material]) -> GeometrySegment:
|
||||||
def _read_segm(segm: Reader, materials_list: List[str]) -> GeometrySegment:
|
|
||||||
|
|
||||||
geometry_seg = GeometrySegment()
|
geometry_seg = GeometrySegment()
|
||||||
|
|
||||||
|
@ -195,7 +199,7 @@ def _read_segm(segm: Reader, materials_list: List[str]) -> GeometrySegment:
|
||||||
|
|
||||||
if "MATI" in next_header:
|
if "MATI" in next_header:
|
||||||
with segm.read_child("MATI") as mati:
|
with segm.read_child("MATI") as mati:
|
||||||
geometry_seg.material_name = materials_list[mati.read_u32()]
|
geometry_seg.material_name = materials_list[mati.read_u32()].name
|
||||||
|
|
||||||
elif "POSL" in next_header:
|
elif "POSL" in next_header:
|
||||||
with segm.read_child("POSL") as posl:
|
with segm.read_child("POSL") as posl:
|
||||||
|
@ -211,15 +215,6 @@ def _read_segm(segm: Reader, materials_list: List[str]) -> GeometrySegment:
|
||||||
for _ in range(num_positions):
|
for _ in range(num_positions):
|
||||||
geometry_seg.normals.append(Vector(nrml.read_f32(3)))
|
geometry_seg.normals.append(Vector(nrml.read_f32(3)))
|
||||||
|
|
||||||
elif "WGHT" in next_header:
|
|
||||||
geometry_seg.weights = []
|
|
||||||
|
|
||||||
with segm.read_child("WGHT") as wght:
|
|
||||||
num_boneweights = wght.read_u32()
|
|
||||||
|
|
||||||
for _ in range(num_boneweights):
|
|
||||||
geometry_seg.weights.append((wght.read_u32(), wght.read_f32()))
|
|
||||||
|
|
||||||
elif "CLRL" in next_header:
|
elif "CLRL" in next_header:
|
||||||
geometry_seg.colors = []
|
geometry_seg.colors = []
|
||||||
|
|
||||||
|
@ -255,7 +250,7 @@ def _read_segm(segm: Reader, materials_list: List[str]) -> GeometrySegment:
|
||||||
with segm.read_child("STRP") as strp:
|
with segm.read_child("STRP") as strp:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if segm.read_u16 != 0: #trailing 0 bug
|
if segm.read_u16 != 0: #trailing 0 bug https://schlechtwetterfront.github.io/ze_filetypes/msh.html#STRP
|
||||||
segm.skip_bytes(-2)
|
segm.skip_bytes(-2)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
@ -265,44 +260,3 @@ def _read_segm(segm: Reader, materials_list: List[str]) -> GeometrySegment:
|
||||||
return geometry_seg
|
return geometry_seg
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
'''
|
|
||||||
|
|
||||||
|
|
||||||
def _write_anm2(anm2: Writer, anim: Animation):
|
|
||||||
|
|
||||||
with anm2.read_child("CYCL") as cycl:
|
|
||||||
|
|
||||||
cycl.write_u32(1)
|
|
||||||
cycl.write_string(anim.name)
|
|
||||||
|
|
||||||
for _ in range(63 - len(anim.name)):
|
|
||||||
cycl.write_u8(0)
|
|
||||||
|
|
||||||
cycl.write_f32(10.0) #test framerate
|
|
||||||
cycl.write_u32(0) #what does play style refer to?
|
|
||||||
cycl.write_u32(0, 20) #first frame indices
|
|
||||||
|
|
||||||
|
|
||||||
with anm2.read_child("KFR3") as kfr3:
|
|
||||||
|
|
||||||
kfr3.write_u32(len(anim.bone_transforms.keys()))
|
|
||||||
|
|
||||||
for boneName in anim.bone_transforms.keys():
|
|
||||||
kfr3.write_u32(crc(boneName))
|
|
||||||
kfr3.write_u32(0) #what is keyframe type?
|
|
||||||
|
|
||||||
kfr3.write_u32(21, 21) #basic testing
|
|
||||||
|
|
||||||
for i, xform in enumerate(anim.bone_transforms[boneName]):
|
|
||||||
kfr3.write_u32(i)
|
|
||||||
kfr3.write_f32(xform.translation.x, xform.translation.y, xform.translation.z)
|
|
||||||
|
|
||||||
for i, xform in enumerate(anim.bone_transforms[boneName]):
|
|
||||||
kfr3.write_u32(i)
|
|
||||||
kfr3.write_f32(xform.rotation.x, xform.rotation.y, xform.rotation.z, xform.rotation.w)
|
|
||||||
|
|
||||||
|
|
||||||
'''
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,124 @@
|
||||||
|
""" Gathers the Blender objects from the current scene and returns them as a list of
|
||||||
|
Model objects. """
|
||||||
|
|
||||||
|
import bpy
|
||||||
|
import bmesh
|
||||||
|
import math
|
||||||
|
from enum import Enum
|
||||||
|
from typing import List, Set, Dict, Tuple
|
||||||
|
from itertools import zip_longest
|
||||||
|
from .msh_scene import Scene
|
||||||
|
from .msh_model import *
|
||||||
|
from .msh_model_utilities import *
|
||||||
|
from .msh_utilities import *
|
||||||
|
from .msh_model_gather import *
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def extract_models(models, materials_map):
|
||||||
|
|
||||||
|
model_map = {}
|
||||||
|
|
||||||
|
for model in sort_by_parent(models):
|
||||||
|
|
||||||
|
if model.model_type != ModelType.STATIC:
|
||||||
|
new_obj = bpy.data.objects.new(model.name, None)
|
||||||
|
new_obj.empty_display_size = 1
|
||||||
|
new_obj.empty_display_type = 'PLAIN_AXES'
|
||||||
|
|
||||||
|
else:
|
||||||
|
new_mesh = bpy.data.meshes.new(model.name)
|
||||||
|
verts = []
|
||||||
|
faces = []
|
||||||
|
offset = 0
|
||||||
|
|
||||||
|
mat_name = ""
|
||||||
|
|
||||||
|
for i,seg in enumerate(model.geometry):
|
||||||
|
|
||||||
|
if i == 0:
|
||||||
|
mat_name = seg.material_name
|
||||||
|
|
||||||
|
verts += [tuple(convert_vector_space(v)) for v in seg.positions]
|
||||||
|
faces += [tuple([ind + offset for ind in tri]) for tri in seg.triangles]
|
||||||
|
|
||||||
|
offset += len(seg.positions)
|
||||||
|
|
||||||
|
new_mesh.from_pydata(verts, [], faces)
|
||||||
|
new_mesh.update()
|
||||||
|
new_mesh.validate()
|
||||||
|
|
||||||
|
|
||||||
|
edit_mesh = bmesh.new()
|
||||||
|
edit_mesh.from_mesh(new_mesh)
|
||||||
|
|
||||||
|
uvlayer = edit_mesh.loops.layers.uv.verify()
|
||||||
|
|
||||||
|
for edit_mesh_face in edit_mesh.faces:
|
||||||
|
mesh_face = faces[edit_mesh_face.index]
|
||||||
|
|
||||||
|
for i,loop in enumerate(edit_mesh_face.loops):
|
||||||
|
texcoord = seg.texcoords[mesh_face[i]]
|
||||||
|
loop[uvlayer].uv = tuple([texcoord.x, texcoord.y])
|
||||||
|
|
||||||
|
edit_mesh.to_mesh(new_mesh)
|
||||||
|
edit_mesh.free()
|
||||||
|
|
||||||
|
|
||||||
|
new_obj = bpy.data.objects.new(new_mesh.name, new_mesh)
|
||||||
|
|
||||||
|
'''
|
||||||
|
Assign Materials - will do per segment later...
|
||||||
|
'''
|
||||||
|
|
||||||
|
if mat_name:
|
||||||
|
material = materials_map[mat_name]
|
||||||
|
|
||||||
|
if new_obj.data.materials:
|
||||||
|
new_obj.data.materials[0] = material
|
||||||
|
else:
|
||||||
|
new_obj.data.materials.append(material)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
model_map[model.name] = new_obj
|
||||||
|
|
||||||
|
if model.parent:
|
||||||
|
new_obj.parent = model_map[model.parent]
|
||||||
|
|
||||||
|
new_obj.location = convert_vector_space(model.transform.translation)
|
||||||
|
new_obj.rotation_mode = "QUATERNION"
|
||||||
|
new_obj.rotation_quaternion = convert_rotation_space(model.transform.rotation)
|
||||||
|
|
||||||
|
bpy.context.collection.objects.link(new_obj)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def extract_materials(scene: Scene) -> Dict[str,bpy.types.Material]:
|
||||||
|
|
||||||
|
extracted_materials = {}
|
||||||
|
|
||||||
|
for material_name in scene.materials.keys():
|
||||||
|
|
||||||
|
new_mat = bpy.data.materials.new(name=material_name)
|
||||||
|
new_mat.use_nodes = True
|
||||||
|
bsdf = new_mat.node_tree.nodes["Principled BSDF"]
|
||||||
|
texImage = new_mat.node_tree.nodes.new('ShaderNodeTexImage')
|
||||||
|
texImage.image = bpy.data.images.load("/Users/will/Desktop/grad.png")
|
||||||
|
new_mat.node_tree.links.new(bsdf.inputs['Base Color'], texImage.outputs['Color'])
|
||||||
|
|
||||||
|
extracted_materials[material_name] = new_mat
|
||||||
|
|
||||||
|
return extracted_materials
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def extract_scene(scene: Scene):
|
||||||
|
|
||||||
|
matmap = extract_materials(scene)
|
||||||
|
extract_models(scene.models, matmap)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue