Initial commit and push
This commit is contained in:
228
src/blender_addon/io_mesh_msh2/__init__.py
Normal file
228
src/blender_addon/io_mesh_msh2/__init__.py
Normal file
@@ -0,0 +1,228 @@
|
||||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License version 2
|
||||
# as published by the Free Software Foundation.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
|
||||
|
||||
from .msh2 import msh2
|
||||
|
||||
|
||||
msh = msh2.MSH2(None)
|
||||
bl_info = {
|
||||
"name": "Zero Editor MSH2 format",
|
||||
"author": "Maxim Stewart",
|
||||
"version": (0, 0, 1),
|
||||
"blender": (2, 74, 0),
|
||||
"location": "File > Import-Export",
|
||||
"description": "Import-Export Zero Editor MSH2 models, meshs, and textures",
|
||||
"warning": "",
|
||||
"wiki_url": "https://schlechtwetterfront.github.io/ze_filetypes/msh.html",
|
||||
"category": "Import-Export"}
|
||||
|
||||
if "bpy" in locals():
|
||||
import importlib
|
||||
if "import_msh2" in locals():
|
||||
importlib.reload(import_msh2)
|
||||
if "export_msh2" in locals():
|
||||
importlib.reload(export_msh2)
|
||||
|
||||
|
||||
import bpy
|
||||
from bpy.props import (
|
||||
CollectionProperty,
|
||||
BoolProperty,
|
||||
FloatProperty,
|
||||
StringProperty,
|
||||
EnumProperty,
|
||||
)
|
||||
from bpy_extras.io_utils import (
|
||||
ImportHelper,
|
||||
ExportHelper,
|
||||
orientation_helper_factory,
|
||||
path_reference_mode,
|
||||
axis_conversion,
|
||||
)
|
||||
|
||||
from bpy.types import OperatorFileListElement
|
||||
|
||||
# NOTE: NEED to see what ZERO Engine uses as forward
|
||||
IOOBJOrientationHelper = orientation_helper_factory("IOOBJOrientationHelper", axis_forward='-Z', axis_up='Y')
|
||||
|
||||
|
||||
class ImportMSH2(bpy.types.Operator, ImportHelper, IOOBJOrientationHelper):
|
||||
"""Load a MSH2 File"""
|
||||
bl_idname = "import_zero_editor_msh2.msh"
|
||||
bl_label = "Zero Editor Import MSH2"
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
filename_ext = ".msh"
|
||||
filter_glob = StringProperty(
|
||||
default = "*.msh;*.tga",
|
||||
options = {'HIDDEN'},
|
||||
)
|
||||
|
||||
files = CollectionProperty(
|
||||
name="File Path",
|
||||
type=OperatorFileListElement,
|
||||
)
|
||||
directory = StringProperty(
|
||||
subtype='DIR_PATH',
|
||||
)
|
||||
|
||||
|
||||
use_image_search = BoolProperty(
|
||||
name="Image Search",
|
||||
description="Search subdirs for any associated images "
|
||||
"(Warning, may be slow)",
|
||||
default=True,
|
||||
)
|
||||
|
||||
def execute(self, context):
|
||||
keywords = self.as_keywords(ignore=("axis_forward",
|
||||
"axis_up",
|
||||
"filter_glob",
|
||||
"split_mode",
|
||||
))
|
||||
|
||||
keywords["use_cycles"] = (context.scene.render.engine == 'CYCLES')
|
||||
if bpy.data.is_saved and context.user_preferences.filepaths.use_relative_paths:
|
||||
import os
|
||||
keywords["relpath"] = os.path.dirname(bpy.data.filepath)
|
||||
|
||||
data = {**keywords}
|
||||
msh.import_file(data["filepath"])
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
# def draw(self, context):
|
||||
# layout = self.layout
|
||||
#
|
||||
# row = layout.row(align=True)
|
||||
# row.prop(self, "use_smooth_groups")
|
||||
# row.prop(self, "use_edges")
|
||||
#
|
||||
# box = layout.box()
|
||||
# row = box.row()
|
||||
# row.prop(self, "split_mode", expand=True)
|
||||
#
|
||||
# row = box.row()
|
||||
# if self.split_mode == 'ON':
|
||||
# row.label(text="Split by:")
|
||||
# row.prop(self, "use_split_objects")
|
||||
# row.prop(self, "use_split_groups")
|
||||
# else:
|
||||
# row.prop(self, "use_groups_as_vgroups")
|
||||
#
|
||||
# row = layout.split(percentage=0.67)
|
||||
# row.prop(self, "global_clamp_size")
|
||||
# layout.prop(self, "axis_forward")
|
||||
# layout.prop(self, "axis_up")
|
||||
#
|
||||
# layout.prop(self, "use_image_search")
|
||||
|
||||
|
||||
class ExportMSH2(bpy.types.Operator, ExportHelper, IOOBJOrientationHelper):
|
||||
"""Save a MSH2 File"""
|
||||
|
||||
bl_idname = "export_zero_editor_msh2.msh"
|
||||
bl_label = 'Zero Editor Export MSH2'
|
||||
|
||||
# # context group
|
||||
filename_ext = ".msh"
|
||||
filter_glob = StringProperty(
|
||||
default = "*.msh;*.tga",
|
||||
options = {'HIDDEN'},
|
||||
)
|
||||
|
||||
use_selection = BoolProperty(
|
||||
name = "Selection Only",
|
||||
description = "Export selected objects only",
|
||||
default = False,
|
||||
)
|
||||
|
||||
use_animation = BoolProperty(
|
||||
name="Animation",
|
||||
description="Write out an OBJ for each frame",
|
||||
default=False,
|
||||
)
|
||||
|
||||
use_uvs = BoolProperty(
|
||||
name="Include UVs",
|
||||
description="Write out the active UV coordinates",
|
||||
default=True,
|
||||
)
|
||||
use_materials = BoolProperty(
|
||||
name="Write Materials",
|
||||
description="Write out the MTL file",
|
||||
default=True,
|
||||
)
|
||||
use_triangles = BoolProperty(
|
||||
name="Triangulate Faces",
|
||||
description="Convert all faces to triangles",
|
||||
default=False,
|
||||
)
|
||||
|
||||
def execute(self, context):
|
||||
keywords = self.as_keywords(ignore=("axis_forward",
|
||||
"axis_up",
|
||||
"global_scale",
|
||||
"check_existing",
|
||||
"filter_glob",
|
||||
))
|
||||
data = {**keywords}
|
||||
# msh.export_file()
|
||||
# print("")
|
||||
# print("")
|
||||
# print(context)
|
||||
# print("")
|
||||
# print("")
|
||||
msh.export_file(data["filepath"])
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
classes = (
|
||||
ImportMSH2,
|
||||
ExportMSH2,
|
||||
)
|
||||
|
||||
|
||||
def menu_import(self, context):
|
||||
self.layout.operator(ImportMSH2.bl_idname, text="Zero Editor MSH2 (.msh)")
|
||||
|
||||
|
||||
def menu_export(self, context):
|
||||
self.layout.operator(ExportMSH2.bl_idname, text="Zero Editor MSH2 (.msh)")
|
||||
|
||||
|
||||
def register():
|
||||
for cls in classes:
|
||||
bpy.utils.register_class(cls)
|
||||
|
||||
bpy.types.INFO_MT_file_import.append(menu_import)
|
||||
bpy.types.INFO_MT_file_export.append(menu_export)
|
||||
|
||||
|
||||
def unregister():
|
||||
bpy.types.INFO_MT_file_import.remove(menu_import)
|
||||
bpy.types.INFO_MT_file_export.remove(menu_export)
|
||||
|
||||
for cls in classes:
|
||||
bpy.utils.unregister_class(cls)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
register()
|
13
src/blender_addon/io_mesh_msh2/cli.py
Normal file
13
src/blender_addon/io_mesh_msh2/cli.py
Normal file
@@ -0,0 +1,13 @@
|
||||
# from msh2.MSH2 import MSH2
|
||||
#
|
||||
# msh2 = MSH2(None)
|
||||
|
||||
|
||||
from msh2 import msh2
|
||||
|
||||
|
||||
msh = msh2.MSH2(None)
|
||||
|
||||
msh.import_file("../../msh/all_weap_inf_lightsabre.msh")
|
||||
|
||||
# msh.export_file("all_weap_inf_lightsabre.msh")
|
74
src/blender_addon/io_mesh_msh2/export_msh2.py
Normal file
74
src/blender_addon/io_mesh_msh2/export_msh2.py
Normal file
@@ -0,0 +1,74 @@
|
||||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License version 2
|
||||
# as published by the Free Software Foundation.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
|
||||
"""
|
||||
This script exports a MSH2 file from Blender.
|
||||
|
||||
Usage:
|
||||
Run this script from "File->Export" menu and then MSH2 file.
|
||||
Note, This exports mesh objects and materials only, nurbs and curves are not supported.
|
||||
|
||||
https://schlechtwetterfront.github.io/ze_filetypes/msh.html
|
||||
"""
|
||||
|
||||
|
||||
# def save(context,
|
||||
# filepath,
|
||||
# *,
|
||||
# use_triangles=False,
|
||||
# use_edges=True,
|
||||
# use_normals=False,
|
||||
# use_smooth_groups=False,
|
||||
# use_smooth_groups_bitflags=False,
|
||||
# use_uvs=True,
|
||||
# use_materials=True,
|
||||
# use_mesh_modifiers=True,
|
||||
# use_mesh_modifiers_render=False,
|
||||
# use_blen_objects=True,
|
||||
# group_by_object=False,
|
||||
# group_by_material=False,
|
||||
# keep_vertex_order=False,
|
||||
# use_vertex_groups=False,
|
||||
# use_nurbs=True,
|
||||
# use_selection=True,
|
||||
# use_animation=False,
|
||||
# global_matrix=None,
|
||||
# path_mode='AUTO'
|
||||
# ):
|
||||
#
|
||||
# _write(context, filepath,
|
||||
# EXPORT_TRI=use_triangles,
|
||||
# EXPORT_EDGES=use_edges,
|
||||
# EXPORT_SMOOTH_GROUPS=use_smooth_groups,
|
||||
# EXPORT_SMOOTH_GROUPS_BITFLAGS=use_smooth_groups_bitflags,
|
||||
# EXPORT_NORMALS=use_normals,
|
||||
# EXPORT_UV=use_uvs,
|
||||
# EXPORT_MTL=use_materials,
|
||||
# EXPORT_APPLY_MODIFIERS=use_mesh_modifiers,
|
||||
# EXPORT_APPLY_MODIFIERS_RENDER=use_mesh_modifiers_render,
|
||||
# EXPORT_BLEN_OBS=use_blen_objects,
|
||||
# EXPORT_GROUP_BY_OB=group_by_object,
|
||||
# EXPORT_GROUP_BY_MAT=group_by_material,
|
||||
# EXPORT_KEEP_VERT_ORDER=keep_vertex_order,
|
||||
# EXPORT_POLYGROUPS=use_vertex_groups,
|
||||
# EXPORT_CURVE_AS_NURBS=use_nurbs,
|
||||
# EXPORT_SEL_ONLY=use_selection,
|
||||
# EXPORT_ANIMATION=use_animation,
|
||||
# EXPORT_GLOBAL_MATRIX=global_matrix,
|
||||
# EXPORT_PATH_MODE=path_mode,
|
||||
# )
|
||||
#
|
||||
# return {'FINISHED'}
|
48
src/blender_addon/io_mesh_msh2/import_msh2.py
Normal file
48
src/blender_addon/io_mesh_msh2/import_msh2.py
Normal file
@@ -0,0 +1,48 @@
|
||||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License version 2
|
||||
# as published by the Free Software Foundation.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
|
||||
"""
|
||||
This script imports a MSH2 files to Blender.
|
||||
|
||||
Usage:
|
||||
Run this script from "File->Import" menu and then load the desired MSH2 file.
|
||||
Note, This loads mesh objects and materials only, nurbs and curves are not supported.
|
||||
|
||||
https://schlechtwetterfront.github.io/ze_filetypes/msh.html
|
||||
"""
|
||||
|
||||
|
||||
|
||||
# def load(context,
|
||||
# filepath,
|
||||
# *,
|
||||
# global_clamp_size=0.0,
|
||||
# use_smooth_groups=True,
|
||||
# use_edges=True,
|
||||
# use_split_objects=True,
|
||||
# use_split_groups=True,
|
||||
# use_image_search=True,
|
||||
# use_groups_as_vgroups=False,
|
||||
# use_cycles=True,
|
||||
# relpath=None,
|
||||
# global_matrix=None
|
||||
# ):
|
||||
# """
|
||||
# Called by the user interface or another script.
|
||||
# load_msh2(path) - should give acceptable results.
|
||||
# This function passes the file and sends the data off
|
||||
# to be split into objects and then converted into mesh objects
|
||||
# """
|
59
src/blender_addon/io_mesh_msh2/msh2/Logger.py
Normal file
59
src/blender_addon/io_mesh_msh2/msh2/Logger.py
Normal file
@@ -0,0 +1,59 @@
|
||||
# Python imports
|
||||
import os, logging
|
||||
|
||||
# Application imports
|
||||
|
||||
|
||||
class Logger:
|
||||
def __init__(self, name = "NO_LOGGER_NAME_PASSED_ON_INIT"):
|
||||
self.logger = self.create_logger(name)
|
||||
|
||||
def get_logger(self):
|
||||
return self.logger
|
||||
|
||||
def create_logger(self, loggerName, createFile = True):
|
||||
"""
|
||||
Create a new logging object and return it.
|
||||
:note:
|
||||
NOSET # Don't know the actual log level of this... (defaulting or literally none?)
|
||||
Log Levels (From least to most)
|
||||
Type Value
|
||||
CRITICAL 50
|
||||
ERROR 40
|
||||
WARNING 30
|
||||
INFO 20
|
||||
DEBUG 10
|
||||
:param loggerName: Sets the name of the logger object. (Used in log lines)
|
||||
:param createFile: Whether we create a log file or just pump to terminal
|
||||
|
||||
:return: the logging object we created
|
||||
"""
|
||||
|
||||
globalLogLvl = logging.DEBUG # Keep this at highest so that handlers can filter to their desired levels
|
||||
chLogLevel = logging.CRITICAL # Prety musch the only one we change ever
|
||||
fhLogLevel = logging.DEBUG
|
||||
log = logging.getLogger(loggerName)
|
||||
log.setLevel(globalLogLvl)
|
||||
|
||||
# Set our log output styles
|
||||
fFormatter = logging.Formatter('[%(asctime)s] %(pathname)s:%(lineno)d %(levelname)s - %(message)s', '%m-%d %H:%M:%S')
|
||||
cFormatter = logging.Formatter('%(pathname)s:%(lineno)d] %(levelname)s - %(message)s')
|
||||
|
||||
ch = logging.StreamHandler()
|
||||
ch.setLevel(level=chLogLevel)
|
||||
ch.setFormatter(cFormatter)
|
||||
log.addHandler(ch)
|
||||
|
||||
if createFile:
|
||||
folder = "logs"
|
||||
file = folder + "/flask-application.log"
|
||||
|
||||
if not os.path.exists(folder):
|
||||
os.mkdir(folder)
|
||||
|
||||
fh = logging.FileHandler(file)
|
||||
fh.setLevel(level=fhLogLevel)
|
||||
fh.setFormatter(fFormatter)
|
||||
log.addHandler(fh)
|
||||
|
||||
return log
|
1
src/blender_addon/io_mesh_msh2/msh2/__init__.py
Normal file
1
src/blender_addon/io_mesh_msh2/msh2/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from . import *
|
0
src/blender_addon/io_mesh_msh2/msh2/__main__.py
Normal file
0
src/blender_addon/io_mesh_msh2/msh2/__main__.py
Normal file
37
src/blender_addon/io_mesh_msh2/msh2/msh2.py
Normal file
37
src/blender_addon/io_mesh_msh2/msh2/msh2.py
Normal file
@@ -0,0 +1,37 @@
|
||||
# Python imports
|
||||
import faulthandler
|
||||
import traceback
|
||||
|
||||
# Gtk imports
|
||||
|
||||
# Application imports
|
||||
from . import msh2_unpack
|
||||
|
||||
|
||||
class MSH2():
|
||||
"""docstring for MSH2."""
|
||||
|
||||
def __init__(self, arg):
|
||||
faulthandler.enable() # For better debug info
|
||||
self.arg = arg
|
||||
self.msh_config = {'do_logging': True, 'ignore_geo': False, 'triangulate': False}
|
||||
self.msh = None
|
||||
|
||||
|
||||
def import_file(self, file_name):
|
||||
try:
|
||||
self.msh = msh2_unpack.MSHUnpack(file_name, self.msh_config).unpack()
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
|
||||
def export_file(self, file_name):
|
||||
try:
|
||||
self.msh.save(file_name)
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
|
||||
def export_file_to_binary_json(self, file_name):
|
||||
try:
|
||||
self.msh.save_json(file_name)
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
154
src/blender_addon/io_mesh_msh2/msh2/msh2_crc.py
Normal file
154
src/blender_addon/io_mesh_msh2/msh2/msh2_crc.py
Normal file
@@ -0,0 +1,154 @@
|
||||
'''
|
||||
ZeroEngine .msh model format CRC algorithm.
|
||||
|
||||
Refer to
|
||||
schlechtwetterfront.github.io/ze_filetypes/msh.html
|
||||
for more information regarding the file format.
|
||||
'''
|
||||
|
||||
# Python imports
|
||||
from struct import pack
|
||||
|
||||
# Gtk imports
|
||||
|
||||
# Application imports
|
||||
|
||||
|
||||
class CRCError(Exception):
|
||||
def __init__(self, val):
|
||||
self.val = val
|
||||
|
||||
def __str__(self):
|
||||
return '{0}'.format(self.val)
|
||||
|
||||
# CRC lookup table.
|
||||
TABLE_32 = (
|
||||
0x00000000, 0x04C11DB7, 0x09823B6E, 0x0D4326D9,
|
||||
0x130476DC, 0x17C56B6B, 0x1A864DB2, 0x1E475005,
|
||||
0x2608EDB8, 0x22C9F00F, 0x2F8AD6D6, 0x2B4BCB61,
|
||||
0x350C9B64, 0x31CD86D3, 0x3C8EA00A, 0x384FBDBD,
|
||||
0x4C11DB70, 0x48D0C6C7, 0x4593E01E, 0x4152FDA9,
|
||||
0x5F15ADAC, 0x5BD4B01B, 0x569796C2, 0x52568B75,
|
||||
0x6A1936C8, 0x6ED82B7F, 0x639B0DA6, 0x675A1011,
|
||||
0x791D4014, 0x7DDC5DA3, 0x709F7B7A, 0x745E66CD,
|
||||
0x9823B6E0, 0x9CE2AB57, 0x91A18D8E, 0x95609039,
|
||||
0x8B27C03C, 0x8FE6DD8B, 0x82A5FB52, 0x8664E6E5,
|
||||
0xBE2B5B58, 0xBAEA46EF, 0xB7A96036, 0xB3687D81,
|
||||
0xAD2F2D84, 0xA9EE3033, 0xA4AD16EA, 0xA06C0B5D,
|
||||
0xD4326D90, 0xD0F37027, 0xDDB056FE, 0xD9714B49,
|
||||
0xC7361B4C, 0xC3F706FB, 0xCEB42022, 0xCA753D95,
|
||||
0xF23A8028, 0xF6FB9D9F, 0xFBB8BB46, 0xFF79A6F1,
|
||||
0xE13EF6F4, 0xE5FFEB43, 0xE8BCCD9A, 0xEC7DD02D,
|
||||
0x34867077, 0x30476DC0, 0x3D044B19, 0x39C556AE,
|
||||
0x278206AB, 0x23431B1C, 0x2E003DC5, 0x2AC12072,
|
||||
0x128E9DCF, 0x164F8078, 0x1B0CA6A1, 0x1FCDBB16,
|
||||
0x018AEB13, 0x054BF6A4, 0x0808D07D, 0x0CC9CDCA,
|
||||
0x7897AB07, 0x7C56B6B0, 0x71159069, 0x75D48DDE,
|
||||
0x6B93DDDB, 0x6F52C06C, 0x6211E6B5, 0x66D0FB02,
|
||||
0x5E9F46BF, 0x5A5E5B08, 0x571D7DD1, 0x53DC6066,
|
||||
0x4D9B3063, 0x495A2DD4, 0x44190B0D, 0x40D816BA,
|
||||
0xACA5C697, 0xA864DB20, 0xA527FDF9, 0xA1E6E04E,
|
||||
0xBFA1B04B, 0xBB60ADFC, 0xB6238B25, 0xB2E29692,
|
||||
0x8AAD2B2F, 0x8E6C3698, 0x832F1041, 0x87EE0DF6,
|
||||
0x99A95DF3, 0x9D684044, 0x902B669D, 0x94EA7B2A,
|
||||
0xE0B41DE7, 0xE4750050, 0xE9362689, 0xEDF73B3E,
|
||||
0xF3B06B3B, 0xF771768C, 0xFA325055, 0xFEF34DE2,
|
||||
0xC6BCF05F, 0xC27DEDE8, 0xCF3ECB31, 0xCBFFD686,
|
||||
0xD5B88683, 0xD1799B34, 0xDC3ABDED, 0xD8FBA05A,
|
||||
0x690CE0EE, 0x6DCDFD59, 0x608EDB80, 0x644FC637,
|
||||
0x7A089632, 0x7EC98B85, 0x738AAD5C, 0x774BB0EB,
|
||||
0x4F040D56, 0x4BC510E1, 0x46863638, 0x42472B8F,
|
||||
0x5C007B8A, 0x58C1663D, 0x558240E4, 0x51435D53,
|
||||
0x251D3B9E, 0x21DC2629, 0x2C9F00F0, 0x285E1D47,
|
||||
0x36194D42, 0x32D850F5, 0x3F9B762C, 0x3B5A6B9B,
|
||||
0x0315D626, 0x07D4CB91, 0x0A97ED48, 0x0E56F0FF,
|
||||
0x1011A0FA, 0x14D0BD4D, 0x19939B94, 0x1D528623,
|
||||
0xF12F560E, 0xF5EE4BB9, 0xF8AD6D60, 0xFC6C70D7,
|
||||
0xE22B20D2, 0xE6EA3D65, 0xEBA91BBC, 0xEF68060B,
|
||||
0xD727BBB6, 0xD3E6A601, 0xDEA580D8, 0xDA649D6F,
|
||||
0xC423CD6A, 0xC0E2D0DD, 0xCDA1F604, 0xC960EBB3,
|
||||
0xBD3E8D7E, 0xB9FF90C9, 0xB4BCB610, 0xB07DABA7,
|
||||
0xAE3AFBA2, 0xAAFBE615, 0xA7B8C0CC, 0xA379DD7B,
|
||||
0x9B3660C6, 0x9FF77D71, 0x92B45BA8, 0x9675461F,
|
||||
0x8832161A, 0x8CF30BAD, 0x81B02D74, 0x857130C3,
|
||||
0x5D8A9099, 0x594B8D2E, 0x5408ABF7, 0x50C9B640,
|
||||
0x4E8EE645, 0x4A4FFBF2, 0x470CDD2B, 0x43CDC09C,
|
||||
0x7B827D21, 0x7F436096, 0x7200464F, 0x76C15BF8,
|
||||
0x68860BFD, 0x6C47164A, 0x61043093, 0x65C52D24,
|
||||
0x119B4BE9, 0x155A565E, 0x18197087, 0x1CD86D30,
|
||||
0x029F3D35, 0x065E2082, 0x0B1D065B, 0x0FDC1BEC,
|
||||
0x3793A651, 0x3352BBE6, 0x3E119D3F, 0x3AD08088,
|
||||
0x2497D08D, 0x2056CD3A, 0x2D15EBE3, 0x29D4F654,
|
||||
0xC5A92679, 0xC1683BCE, 0xCC2B1D17, 0xC8EA00A0,
|
||||
0xD6AD50A5, 0xD26C4D12, 0xDF2F6BCB, 0xDBEE767C,
|
||||
0xE3A1CBC1, 0xE760D676, 0xEA23F0AF, 0xEEE2ED18,
|
||||
0xF0A5BD1D, 0xF464A0AA, 0xF9278673, 0xFDE69BC4,
|
||||
0x89B8FD09, 0x8D79E0BE, 0x803AC667, 0x84FBDBD0,
|
||||
0x9ABC8BD5, 0x9E7D9662, 0x933EB0BB, 0x97FFAD0C,
|
||||
0xAFB010B1, 0xAB710D06, 0xA6322BDF, 0xA2F33668,
|
||||
0xBCB4666D, 0xB8757BDA, 0xB5365D03, 0xB1F740B4
|
||||
)
|
||||
|
||||
# Used to calculate the lowercase CRC.
|
||||
TOLOWER = (
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
||||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
|
||||
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
|
||||
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
|
||||
0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
|
||||
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
|
||||
0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
|
||||
0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
|
||||
0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
|
||||
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
|
||||
0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
|
||||
0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
|
||||
0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
|
||||
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
|
||||
0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
|
||||
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
|
||||
0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
|
||||
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
|
||||
0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
|
||||
0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
|
||||
0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
|
||||
0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
|
||||
0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
|
||||
0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
|
||||
0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
|
||||
0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
|
||||
0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
|
||||
0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
|
||||
0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
|
||||
0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
|
||||
0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
|
||||
)
|
||||
|
||||
|
||||
def return_lowest_bits(n):
|
||||
'''Simulate unsigned behavior.'''
|
||||
return n & 0xFFFFFFFF
|
||||
|
||||
|
||||
def crc(string):
|
||||
'''Calculate the Zero CRC from string and return it as number.'''
|
||||
crc_ = 0
|
||||
crc_ = return_lowest_bits(~crc_)
|
||||
if string:
|
||||
for char in string:
|
||||
ind = (crc_ >> 24)
|
||||
ind = ind ^ TOLOWER[ord(char)]
|
||||
crc_ = return_lowest_bits(crc_ << 8) ^ TABLE_32[ind]
|
||||
return return_lowest_bits(~crc_)
|
||||
|
||||
|
||||
def strcrc(string):
|
||||
'''Calculate the Zero CRC and return it in a structure usable in .msh files.'''
|
||||
return pack('<I', crc(string))
|
||||
|
||||
|
||||
def compare_crc_adv(possible_strings, crc_):
|
||||
for string in possible_strings:
|
||||
if crc_ == strcrc(string):
|
||||
return string
|
2765
src/blender_addon/io_mesh_msh2/msh2/msh2_data.py
Normal file
2765
src/blender_addon/io_mesh_msh2/msh2/msh2_data.py
Normal file
File diff suppressed because it is too large
Load Diff
593
src/blender_addon/io_mesh_msh2/msh2/msh2_unpack.py
Normal file
593
src/blender_addon/io_mesh_msh2/msh2/msh2_unpack.py
Normal file
@@ -0,0 +1,593 @@
|
||||
'''
|
||||
ZeroEngine .msh model format parser.
|
||||
|
||||
Refer to
|
||||
schlechtwetterfront.github.io/ze_filetypes/msh.html
|
||||
for more information regarding the file format.
|
||||
'''
|
||||
from . import msh2_data as msh2
|
||||
from struct import unpack as unpack
|
||||
import logging
|
||||
|
||||
from .Logger import Logger
|
||||
logging = Logger("Msh").get_logger()
|
||||
|
||||
|
||||
|
||||
STR_CODEC = 'utf-8'
|
||||
CHUNK_LIST = ['HEDR', 'SHVO', 'MSH2',
|
||||
'SINF', 'FRAM', 'CAMR',
|
||||
'MATL', 'DATA', 'MATD', 'ATRB', 'TX0D', 'TX1D', 'TX2D', 'TX3D',
|
||||
'MODL', 'MTYP', 'MNDX', 'PRNT', 'FLGS', 'TRAN', 'ENVL', 'SWCI',
|
||||
'GEOM', 'SEGM', 'SHDW', 'MATI', 'POSL', 'NRML', 'UV0L', 'UV1L',
|
||||
'UV2L', 'CLRL', 'CLRB', 'WGHT', 'NDXL', 'NDXT', 'STRP',
|
||||
'CLTH', 'CTEX', 'CPOS', 'CUV0', 'FIDX', 'FWGT', 'SPRS', 'CPRS',
|
||||
'BPRS', 'COLL',
|
||||
'SKL2', 'BLN2', 'ANM2', 'CYCL', 'KFR3',
|
||||
'NAME', 'BBOX', 'CL1L']
|
||||
|
||||
|
||||
class UnpackError(Exception):
|
||||
def __init__(self, val):
|
||||
self.val = val
|
||||
|
||||
def __str__(self):
|
||||
return str(self.val)
|
||||
|
||||
|
||||
class Unpacker(object):
|
||||
def unpack_header(self, data):
|
||||
return data[:4].decode(), unpack('<L', data[4:])[0]
|
||||
|
||||
def unpack_str(self, data):
|
||||
pass
|
||||
|
||||
# def log(self, text):
|
||||
# logging.debug(text)
|
||||
|
||||
# def dont_log(self, *text):
|
||||
# pass
|
||||
|
||||
def long(self, data):
|
||||
return unpack('<L', data)[0]
|
||||
|
||||
def short(self, data):
|
||||
return unpack('<H', data)[0]
|
||||
|
||||
def float(self, data):
|
||||
return unpack('<f', data)[0]
|
||||
|
||||
|
||||
class BBoxUnpacker(Unpacker):
|
||||
def __init__(self, data):
|
||||
self.bbox = msh2.BBox()
|
||||
self.data = data
|
||||
|
||||
def unpack(self):
|
||||
self.bbox.rotation = unpack('<ffff', self.data[:16])
|
||||
self.bbox.center = unpack('<fff', self.data[16:28])
|
||||
self.bbox.extents = unpack('<fff', self.data[28:40])
|
||||
self.bbox.radius = unpack('<f', self.data[40:])[0]
|
||||
return self.bbox
|
||||
|
||||
|
||||
class MSHUnpack(Unpacker):
|
||||
def __init__(self, mshfile, config={'do_logging': False,
|
||||
'ignore_geo': False,
|
||||
'triangulate': False}):
|
||||
self.mshfile = mshfile
|
||||
self.msh = msh2.Msh()
|
||||
self.msh.animation = msh2.Animation(self.msh, 'maybe_empty')
|
||||
self.config = config
|
||||
# if not config['do_logging']:
|
||||
# self.log = self.dont_log
|
||||
|
||||
def unpack(self):
|
||||
with open(self.mshfile, 'rb') as mf:
|
||||
# HEDR
|
||||
bin, self.size = self.unpack_header(mf.read(8))
|
||||
# MSH2 or SHVO
|
||||
bin, self.msh2size = self.unpack_header(mf.read(8))
|
||||
if bin == 'SHVO':
|
||||
logging.debug('Skipping SHVO chunk.')
|
||||
# Read the 4 content bytes of SHVO.
|
||||
mf.read(4)
|
||||
# that's definitely MSH2
|
||||
bin, self.msh2size = self.unpack_header(mf.read(8))
|
||||
mdlcoll = msh2.ModelCollection(self.msh)
|
||||
self.msh.models = mdlcoll
|
||||
while True:
|
||||
hdr, size = self.unpack_header(mf.read(8))
|
||||
logging.debug('Header, Size: {0}, {1}'.format(hdr, size))
|
||||
if hdr == 'SINF':
|
||||
si = InfoUnpacker(self, size, mf)
|
||||
self.msh.info = si.unpack()
|
||||
elif hdr == 'MATL':
|
||||
num_materials = unpack('<L', mf.read(4))[0]
|
||||
matcoll = msh2.MaterialCollection(self.msh)
|
||||
for n in range(num_materials):
|
||||
mat, matsize = self.unpack_header(mf.read(8))
|
||||
logging.debug('Material, Size: {0}, {1}'.format(mat, matsize))
|
||||
mat = MaterialUnpacker(self, matsize, mf)
|
||||
matcoll.add(mat.unpack())
|
||||
matcoll.assign_indices()
|
||||
self.msh.materials = matcoll
|
||||
elif hdr == 'MODL':
|
||||
mdl = ModelUnpacker(self, size, mf)
|
||||
mdlcoll.add(mdl.unpack())
|
||||
elif hdr == 'CAMR':
|
||||
mf.read(size)
|
||||
elif hdr == 'LGHT':
|
||||
mf.read(size)
|
||||
elif hdr == 'SKL2':
|
||||
numbones = unpack('<L', mf.read(4))[0]
|
||||
bonecoll = msh2.BoneCollection(self.msh.animation)
|
||||
for n in range(numbones):
|
||||
bone = msh2.Bone(bonecoll)
|
||||
bone.CRC = mf.read(4)
|
||||
bone.set_name_from_crc()
|
||||
bone.bone_type = unpack('<L', mf.read(4))[0]
|
||||
bone.constrain = unpack('<f', mf.read(4))[0]
|
||||
bone.bone1len = unpack('<f', mf.read(4))[0]
|
||||
bone.bone2len = unpack('<f', mf.read(4))[0]
|
||||
bonecoll.add(bone)
|
||||
self.msh.animation.bones = bonecoll
|
||||
elif hdr == 'BLN2':
|
||||
# Ignore BLN2 as this only has one attribute(blend_factor).
|
||||
# It's not clear which type this is and it's not important.
|
||||
mf.read(size)
|
||||
elif hdr == 'ANM2':
|
||||
# Continue as this is only a header.
|
||||
continue
|
||||
elif hdr == 'CYCL':
|
||||
num_cycles = unpack('<L', mf.read(4))[0]
|
||||
cycles = []
|
||||
for n in range(num_cycles):
|
||||
cycle = msh2.Cycle(self.msh.animation)
|
||||
cycle.name = mf.read(64).strip(b'\x00')
|
||||
cycle.fps = unpack('<f', mf.read(4))[0]
|
||||
cycle.style = unpack('<L', mf.read(4))[0]
|
||||
cycle.frames = unpack('<LL', mf.read(8))
|
||||
cycles.append(cycle)
|
||||
self.msh.animation.cycles = cycles
|
||||
self.msh.animation.cycle = cycles[0]
|
||||
elif hdr == 'KFR3':
|
||||
numbones = unpack('<L', mf.read(4))[0]
|
||||
for n in range(numbones):
|
||||
# Ingore the crc, usually the same order as before.
|
||||
mf.read(4)
|
||||
bone = self.msh.animation.bones[n]
|
||||
bone.keyframe_type = unpack('<L', mf.read(4))[0]
|
||||
numtran, numrot = unpack('<LL', mf.read(8))
|
||||
tran = []
|
||||
translation_frame_indices = []
|
||||
rot = []
|
||||
rotation_frame_indices = []
|
||||
for i in range(numtran):
|
||||
# Ignore the frame index.
|
||||
translation_frame_indices.append(unpack('<L', mf.read(4))[0])
|
||||
# Add X, Y, Z position tuple.
|
||||
tran.append(unpack('<fff', mf.read(12)))
|
||||
for i in range(numrot):
|
||||
# Again, ignore the frame index.
|
||||
rotation_frame_indices.append(unpack('<L', mf.read(4))[0])
|
||||
# X, Y, Z, W quaternion translation tuple.
|
||||
rot.append(unpack('<ffff', mf.read(16)))
|
||||
bone.pos_keyframes = tran
|
||||
bone.pos_keyframe_indices = translation_frame_indices
|
||||
bone.rot_keyframes = rot
|
||||
bone.rot_keyframe_indices = rotation_frame_indices
|
||||
elif hdr == 'CL1L':
|
||||
break
|
||||
else:
|
||||
logging.debug('Unrecognized chunk {0} in MSHUnpack.'.format(hdr))
|
||||
raise UnpackError('Unrecognized chunk {0} in MSHUnpack (can be a valid chunk but doesnt fit into this unpack level).'.format(hdr))
|
||||
break
|
||||
for model in self.msh.models:
|
||||
model.set_deformers_from_indices()
|
||||
return self.msh
|
||||
|
||||
|
||||
class ModelUnpacker(Unpacker):
|
||||
def __init__(self, up, size, fh):
|
||||
self.size = size
|
||||
self.fh = fh
|
||||
self.mdl = msh2.Model(up.msh)
|
||||
self.up = up
|
||||
|
||||
def unpack(self):
|
||||
logging.debug('Unpacking Model at {0}.'.format(self.fh.tell() - 8))
|
||||
while True:
|
||||
hdr, size = self.unpack_header(self.fh.read(8))
|
||||
logging.debug('Model: Header, Size: {0}, {1}'.format(hdr, size))
|
||||
if hdr == 'MTYP':
|
||||
self.mdl.model_type = msh2.MODEL_TYPES_INT[unpack('<L', self.fh.read(4))[0]]
|
||||
# RepSharpshooters MshEx exports MTYP as MYTP. Should work since the format update.
|
||||
elif hdr == 'MYTP':
|
||||
logging.info('Found RepSharpshooter MshEx type .msh. Continuing the import.')
|
||||
self.mdl.model_type = msh2.MODEL_TYPES_INT[unpack('<L', self.fh.read(4))[0]]
|
||||
elif hdr == 'NAME':
|
||||
self.mdl.name = self.fh.read(size).strip(b'\x00')
|
||||
elif hdr == 'MNDX':
|
||||
self.mdl.index = unpack('<L', self.fh.read(4))[0]
|
||||
elif hdr == 'PRNT':
|
||||
self.mdl.parent_name = self.fh.read(size).decode().encode(STR_CODEC).strip(b'\x00')
|
||||
elif hdr == 'FLGS':
|
||||
self.mdl.vis = unpack('<L', self.fh.read(4))[0]
|
||||
elif hdr == 'TRAN':
|
||||
scl = unpack('<fff', self.fh.read(12))
|
||||
rot = unpack('<ffff', self.fh.read(16))
|
||||
tra = unpack('<fff', self.fh.read(12))
|
||||
self.mdl.transform = msh2.Transform(tra, rot, scl)
|
||||
elif hdr == 'GEOM':
|
||||
if self.up.config['ignore_geo']:
|
||||
self.fh.read(size)
|
||||
continue
|
||||
# If this is a bone(MTYP 3) then set model type to geobone as
|
||||
# it has geometry, too.
|
||||
if self.mdl.model_type == 'bone':
|
||||
self.mdl.model_type = 'geobone'
|
||||
# Ignore the bbox header and size indicator. We dont need it.
|
||||
self.fh.read(8)
|
||||
bb = BBoxUnpacker(self.fh.read(44))
|
||||
self.mdl.bbox = bb.unpack()
|
||||
self.mdl.segments = msh2.SegmentCollection(self.mdl)
|
||||
while True:
|
||||
soc, ssize = self.unpack_header(self.fh.read(8))
|
||||
logging.debug('Model Segments: Header, Size: {0}, {1}'.format(soc, ssize))
|
||||
if soc == 'SEGM':
|
||||
segm = GeometryUnpacker(self, ssize, self.fh)
|
||||
self.mdl.segments.add(segm.unpack())
|
||||
elif soc == 'CLTH':
|
||||
segm = ClothUnpacker(self, ssize, self.fh)
|
||||
self.mdl.segments.add(segm.unpack())
|
||||
elif soc == 'ENVL':
|
||||
num = unpack('<L', self.fh.read(4))[0]
|
||||
inds = []
|
||||
for n in range(num):
|
||||
inds.append(unpack('<L', self.fh.read(4))[0])
|
||||
self.mdl.deformer_indices = inds
|
||||
else:
|
||||
logging.debug('Unrecognized chunk {0} in ModelUnpack(Geometry).'.format(soc))
|
||||
self.fh.seek(self.fh.tell() - 8)
|
||||
break
|
||||
elif hdr == 'SWCI':
|
||||
self.mdl.collprim = True
|
||||
prim_data = unpack('<Lfff', self.fh.read(16))
|
||||
self.mdl.primitive = (prim_data[0], prim_data[1],
|
||||
prim_data[2], prim_data[3])
|
||||
else:
|
||||
logging.debug('Unrecognized chunk {0} in ModelUnpack.'.format(hdr))
|
||||
# Return to the position before the header.
|
||||
self.fh.seek(self.fh.tell() - 8)
|
||||
break
|
||||
return self.mdl
|
||||
|
||||
|
||||
class ClothUnpacker(Unpacker):
|
||||
def __init__(self, up, size, fh):
|
||||
self.up = up
|
||||
self.size = size
|
||||
self.fh = fh
|
||||
self.seg = msh2.ClothGeometry(up.mdl)
|
||||
|
||||
def unpack(self):
|
||||
logging.debug('---- Unpacking Cloth ----')
|
||||
fixed = []
|
||||
while True:
|
||||
hdr, size = self.unpack_header(self.fh.read(8))
|
||||
logging.debug('Cloth: Header, Size: {0}, {1}'.format(hdr, size))
|
||||
if hdr == 'CTEX':
|
||||
self.seg.texture = self.fh.read(size).decode().encode(STR_CODEC).strip(b'\x00')
|
||||
elif hdr == 'CPOS':
|
||||
vertcoll = msh2.ClothVertexCollection(self.seg)
|
||||
num = unpack('<L', self.fh.read(4))[0]
|
||||
for n in range(num):
|
||||
pos = unpack('<fff', self.fh.read(12))
|
||||
vert = msh2.ClothVertex(pos)
|
||||
vertcoll.add(vert)
|
||||
self.seg.vertices = vertcoll
|
||||
elif hdr == 'CUV0':
|
||||
num = unpack('<L', self.fh.read(4))[0]
|
||||
for n in range(num):
|
||||
self.seg.vertices[n].uv = unpack('<ff', self.fh.read(8))
|
||||
elif hdr == 'FIDX':
|
||||
num = unpack('<L', self.fh.read(4))[0]
|
||||
for n in range(num):
|
||||
index = unpack('<L', self.fh.read(4))[0]
|
||||
fixed.append(index)
|
||||
self.seg.vertices[index].is_fixed = True
|
||||
elif hdr == 'FWGT':
|
||||
self.fh.read(4) # Number of points.
|
||||
weights = self.fh.read(size - 4).split(b'\x00')
|
||||
logging.debug('Cloth Deformers: {0}'.format(weights))
|
||||
logging.debug('Cloth Fixed Points: {0}'.format(len(fixed)))
|
||||
weights.remove('')
|
||||
if len(weights) > 0:
|
||||
for index, vertindex in enumerate(fixed):
|
||||
self.seg.vertices[vertindex].deformer = weights[index]
|
||||
elif hdr == 'CMSH':
|
||||
num = unpack('<L', self.fh.read(4))[0]
|
||||
facecoll = msh2.FaceCollection(self.seg)
|
||||
for n in range(num):
|
||||
face = msh2.Face()
|
||||
face.vertices = unpack('<LLL', self.fh.read(12))
|
||||
facecoll.add(face)
|
||||
self.seg.faces = facecoll
|
||||
elif hdr == 'SPRS':
|
||||
num_constraints = self.long(self.fh.read(4))
|
||||
s_constraints = []
|
||||
for n in range(num_constraints):
|
||||
s_constraints.append(unpack('<HH', self.fh.read(4)))
|
||||
self.seg.stretch_constraints = s_constraints
|
||||
elif hdr == 'CPRS':
|
||||
num_constraints = self.long(self.fh.read(4))
|
||||
c_constraints = []
|
||||
for n in range(num_constraints):
|
||||
c_constraints.append(unpack('<HH', self.fh.read(4)))
|
||||
self.seg.cross_constraints = c_constraints
|
||||
elif hdr == 'BPRS':
|
||||
num_constraints = self.long(self.fh.read(4))
|
||||
b_constraints = []
|
||||
for n in range(num_constraints):
|
||||
b_constraints.append(unpack('<HH', self.fh.read(4)))
|
||||
self.seg.bend_constraints = b_constraints
|
||||
elif hdr == 'COLL':
|
||||
num_colls = self.long(self.fh.read(4))
|
||||
for n in range(num_colls):
|
||||
collision = msh2.ClothCollision()
|
||||
collision.cloth = self.seg
|
||||
name = []
|
||||
while 1:
|
||||
char = self.fh.read(1)
|
||||
if char == b'\x00':
|
||||
break
|
||||
name.append(char)
|
||||
collision.name = ''.join(name)
|
||||
parent = []
|
||||
while 1:
|
||||
char = self.fh.read(1)
|
||||
if char == b'\x00':
|
||||
break
|
||||
parent.append(char)
|
||||
collision.parent = ''.join(parent)
|
||||
collision.primitive_type = unpack('<L', self.fh.read(4))[0]
|
||||
collision.primitive_data = unpack('<fff', self.fh.read(12))
|
||||
self.seg.collisions.append(collision)
|
||||
# COLL chunk seems to be padded with \x00 at the end to get an even size indicator.
|
||||
while True:
|
||||
if self.fh.read(1) == b'\x00':
|
||||
continue
|
||||
else:
|
||||
self.fh.seek(self.fh.tell() - 1)
|
||||
break
|
||||
else:
|
||||
logging.debug('Unrecognized chunk {0} in ClothUnpack.'.format(hdr))
|
||||
# Return to the position before the header.
|
||||
self.fh.seek(self.fh.tell() - 8)
|
||||
break
|
||||
return self.seg
|
||||
|
||||
|
||||
class GeometryUnpacker(Unpacker):
|
||||
def __init__(self, up, size, fh):
|
||||
self.up = up
|
||||
self.size = size
|
||||
self.fh = fh
|
||||
# Either SegmentGeometry or ShadowGeometry.
|
||||
# Usually it's SegmentGeometry so we'll set it as default.
|
||||
# Cloth doesn't have the SEGM chunk we read before we got here.
|
||||
self.seg = msh2.SegmentGeometry(up.mdl)
|
||||
|
||||
def unpack(self):
|
||||
# Now we have to check which type of segment it is.
|
||||
# Most common will be static meshes, then maybe shadows, then enveloped.
|
||||
# Enveloped and static meshes share most of the chunks, however,
|
||||
# shadows meshes only have one chunk.
|
||||
logging.debug('---- Unpacking Geometry ----')
|
||||
self.up.up.last_chunk = None
|
||||
while True:
|
||||
# The SEGM header is already gone so read the next one.
|
||||
hdr, size = self.unpack_header(self.fh.read(8))
|
||||
logging.debug('Geo: Header, Size: {0}, {1}'.format(hdr, size))
|
||||
if hdr == 'SHDW':
|
||||
self.up.up.last_chunk = 'SHDW'
|
||||
self.seg = msh2.ShadowGeometry(self.up.mdl)
|
||||
# self.seg.data = self.fh.read(size)
|
||||
num_pos = self.long(self.fh.read(4))
|
||||
positions = []
|
||||
for n in range(num_pos):
|
||||
positions.append((self.float(self.fh.read(4)), self.float(self.fh.read(4)), self.float(self.fh.read(4))))
|
||||
num_edges = self.long(self.fh.read(4))
|
||||
edges = []
|
||||
for n in range(num_edges):
|
||||
edge = self.short(self.fh.read(2)), self.short(self.fh.read(2)), self.short(self.fh.read(2)), self.short(self.fh.read(2))
|
||||
edges.append(edge)
|
||||
self.seg.positions = positions
|
||||
self.seg.edges = edges
|
||||
elif hdr == 'MATI':
|
||||
self.up.up.last_chunk = 'MATI'
|
||||
# 1st up: ModelUnpacker, 2nd up: MSHUnpack
|
||||
self.seg.material = self.up.up.msh.get_mat_by_index(unpack('<L', self.fh.read(4))[0])
|
||||
self.seg.mat_name = self.seg.material.name
|
||||
elif hdr == 'POSL':
|
||||
self.up.up.last_chunk = 'POSL'
|
||||
num_positions = unpack('<L', self.fh.read(4))[0]
|
||||
vertcoll = msh2.VertexCollection(self.seg)
|
||||
for n in range(num_positions):
|
||||
pos = self.fh.read(12)
|
||||
pos = unpack('<fff', pos)
|
||||
vert = msh2.Vertex(pos)
|
||||
vertcoll.add(vert)
|
||||
self.seg.vertices = vertcoll
|
||||
elif hdr == 'NRML':
|
||||
self.up.up.last_chunk = 'NRML'
|
||||
num_normals = unpack('<L', self.fh.read(4))[0]
|
||||
for n in range(num_normals):
|
||||
self.seg.vertices[n].normal = unpack('<fff', self.fh.read(12))
|
||||
elif hdr == 'UV0L':
|
||||
self.up.up.last_chunk = 'UV0L'
|
||||
num_uvs = unpack('<L', self.fh.read(4))[0]
|
||||
for n in range(num_uvs):
|
||||
self.seg.vertices[n].uv = unpack('<ff', self.fh.read(8))
|
||||
self.seg.vertices.uved = True
|
||||
elif hdr in ('UV1L', 'UV2L', 'UV3L'):
|
||||
# Never seen UV2L + but just to be sure.
|
||||
self.fh.read(size)
|
||||
elif hdr == 'CLRL':
|
||||
self.up.up.last_chunk = 'CLRL'
|
||||
num_colors = unpack('<L', self.fh.read(4))[0]
|
||||
for n in range(num_colors):
|
||||
bgra_color = unpack('<BBBB', self.fh.read(4))
|
||||
self.seg.vertices[n].color = msh2.Color((bgra_color[2], bgra_color[1], bgra_color[0], bgra_color[3]))
|
||||
self.seg.vertices.colored = True
|
||||
elif hdr == 'CLRB':
|
||||
self.up.up.last_chunk = 'CLRB'
|
||||
bgra_color = unpack('<BBBB', self.fh.read(4))
|
||||
color = (bgra_color[2], bgra_color[1], bgra_color[0], bgra_color[3])
|
||||
for vert in self.seg.vertices:
|
||||
vert.color = msh2.Color(color)
|
||||
self.seg.vertices.colored = True
|
||||
elif hdr == 'WGHT':
|
||||
last_chunk = 'WGHT'
|
||||
num = unpack('<L', self.fh.read(4))[0]
|
||||
for n in range(num):
|
||||
indices = []
|
||||
vals = []
|
||||
for i in range(4):
|
||||
indices.append(unpack('<L', self.fh.read(4))[0])
|
||||
vals.append(unpack('<f', self.fh.read(4))[0])
|
||||
self.seg.vertices[n].deformer_indices = indices
|
||||
self.seg.vertices[n].weights = vals
|
||||
self.seg.vertices.weighted = True
|
||||
elif hdr == 'STRP':
|
||||
last_chunk = 'STRP'
|
||||
num = unpack('<L', self.fh.read(4))[0]
|
||||
facecoll = msh2.FaceCollection(self.seg)
|
||||
num_begins = 0
|
||||
for n in range(num):
|
||||
val = unpack('<H', self.fh.read(2))[0]
|
||||
if (val - 0x8000) > -1:
|
||||
if num_begins == 0:
|
||||
num_begins += 1
|
||||
face = msh2.Face()
|
||||
facecoll.add(face)
|
||||
elif num_begins == 1:
|
||||
num_begins += 1
|
||||
if face.sides > 2:
|
||||
face = msh2.Face()
|
||||
facecoll.add(face)
|
||||
num_begins = 0
|
||||
elif num_begins == 2:
|
||||
num_begins = 1
|
||||
face = msh2.Face()
|
||||
facecoll.add(face)
|
||||
face.add(val - 0x8000)
|
||||
else:
|
||||
face.add(val)
|
||||
facecoll.de_ngonize(self.up.up.config['triangulate'])
|
||||
self.seg.faces = facecoll
|
||||
elif hdr == 'NDXL':
|
||||
last_chunk = 'NDXL'
|
||||
self.fh.read(size)
|
||||
elif hdr == 'NDXT':
|
||||
last_chunk = 'NDXT'
|
||||
|
||||
# If the faces are already set (for example from the STRP chunk)
|
||||
# then ignore this as it's usually deprecated.
|
||||
if self.seg.faces:
|
||||
continue
|
||||
|
||||
num_triangles = unpack('<L', self.fh.read(4))[0]
|
||||
|
||||
logging.debug('Sizes: %d == %d', num_triangles * 6 + 4, size)
|
||||
|
||||
faces = msh2.FaceCollection(self.seg)
|
||||
|
||||
for n in range(num_triangles):
|
||||
face = msh2.Face(list(unpack('<HHH', self.fh.read(2 * 3))))
|
||||
faces.add(face)
|
||||
|
||||
self.seg.faces = faces
|
||||
|
||||
# Skip any bytes left (which seems to happen in some .msh files),
|
||||
used_bytes = len(faces) * 6 + 4
|
||||
if used_bytes < size:
|
||||
self.fh.read(size - used_bytes)
|
||||
else:
|
||||
if hdr not in CHUNK_LIST:
|
||||
if last_chunk == 'STRP':
|
||||
logging.debug('Fixing STRP chunk import.')
|
||||
self.fh.seek(self.fh.tell() - 8)
|
||||
self.fh.read(2)
|
||||
continue
|
||||
logging.debug('Unrecognized chunk {0} in GeometryUnpack.'.format(hdr))
|
||||
# Return to the position before the header.
|
||||
self.fh.seek(self.fh.tell() - 8)
|
||||
break
|
||||
return self.seg
|
||||
|
||||
|
||||
class MaterialUnpacker(Unpacker):
|
||||
def __init__(self, up, size, fh):
|
||||
self.size = size
|
||||
self.fh = fh
|
||||
self.mat = msh2.Material(up.msh)
|
||||
|
||||
def unpack(self):
|
||||
logging.debug('---- Unpacking Material ----')
|
||||
# NAME chunk.
|
||||
bin, size = self.unpack_header(self.fh.read(8))
|
||||
self.mat.name = self.fh.read(size).strip(b'\x00')
|
||||
# DATA chunk.
|
||||
bin, size = self.unpack_header(self.fh.read(8))
|
||||
logging.debug('header, size: {0}, {1}'.format(bin, size))
|
||||
self.mat.diff_color = msh2.Color(unpack('<ffff', self.fh.read(16)))
|
||||
self.mat.spec_color = msh2.Color(unpack('<ffff', self.fh.read(16)))
|
||||
self.mat.ambt_color = msh2.Color(unpack('<ffff', self.fh.read(16)))
|
||||
self.mat.gloss = unpack('<f', self.fh.read(4))[0]
|
||||
# ATRB chunk.
|
||||
bin, size = self.unpack_header(self.fh.read(8))
|
||||
logging.debug('header, size: {0}, {1}'.format(bin, size))
|
||||
self.mat.flags = self.mat.flags_from_int(unpack('<B', self.fh.read(1))[0])
|
||||
self.mat.render_type, self.mat.data0, self.mat.data1 = unpack('<BBB', self.fh.read(3))
|
||||
# Textures.
|
||||
for n in range(4):
|
||||
hdr, size = self.unpack_header(self.fh.read(8))
|
||||
logging.debug('TX header, size: {0}, {1}'.format(hdr, size))
|
||||
if hdr[:2] == 'TX':
|
||||
if hdr[2] == '0':
|
||||
self.mat.tex0 = self.fh.read(size).decode().encode(STR_CODEC).strip(b'\x00')
|
||||
elif hdr[2] == '1':
|
||||
self.mat.tex1 = self.fh.read(size).decode().encode(STR_CODEC).strip(b'\x00')
|
||||
elif hdr[2] == '2':
|
||||
self.mat.tex2 = self.fh.read(size).decode().encode(STR_CODEC).strip(b'\x00')
|
||||
elif hdr[2] == '3':
|
||||
self.mat.tex3 = self.fh.read(size).decode().encode(STR_CODEC).strip(b'\x00')
|
||||
else:
|
||||
logging.debug('Unrecognized chunk {0} in MaterialUnpacker.'.format(hdr))
|
||||
self.fh.seek(self.fh.tell() - 8)
|
||||
break
|
||||
return self.mat
|
||||
|
||||
|
||||
class InfoUnpacker(Unpacker):
|
||||
def __init__(self, up, size, fh):
|
||||
self.size = size
|
||||
self.fh = fh
|
||||
self.info = msh2.SceneInfo(up.msh)
|
||||
|
||||
def unpack(self):
|
||||
logging.debug('---- Unpacking Scene Info ----')
|
||||
# Name chunk.
|
||||
bin, size = self.unpack_header(self.fh.read(8))
|
||||
self.info.name = self.fh.read(size).decode().encode(STR_CODEC).strip(b'\x00')
|
||||
# FRAM chunk.
|
||||
bin, size = self.unpack_header(self.fh.read(8))
|
||||
self.info.frame_range = unpack('<LL', self.fh.read(8))
|
||||
self.info.fps = unpack('<f', self.fh.read(4))[0]
|
||||
# BBOX chunk.
|
||||
self.fh.read(8) # Just read those, size is always the same.
|
||||
bb = BBoxUnpacker(self.fh.read(44))
|
||||
self.info.bbox = bb.unpack()
|
||||
return self.info
|
12
src/clear_pycache_dirs.sh
Executable file
12
src/clear_pycache_dirs.sh
Executable file
@@ -0,0 +1,12 @@
|
||||
#!/bin/bash
|
||||
|
||||
# set -o xtrace ## To debug scripts
|
||||
# set -o errexit ## To exit on error
|
||||
# set -o errunset ## To exit if a variable is referenced but not set
|
||||
|
||||
|
||||
function main() {
|
||||
find . -name "__pycache__" -exec rm -rf $1 {} \;
|
||||
find . -name "*.pyc" -exec rm -rf $1 {} \;
|
||||
}
|
||||
main
|
Reference in New Issue
Block a user