3.0.0 pre-release

This commit is contained in:
ArthurHeitmann
2022-06-03 20:49:12 +02:00
parent 829d3a7eba
commit 25b8c24444
12 changed files with 876 additions and 219 deletions
+165 -48
View File
@@ -1,11 +1,11 @@
bl_info = {
"name": "NieR2Blender (NieR:Automata Model Importer)",
"name": "NieR2Blender (NieR:Automata Data Importer)",
"author": "Woeful_Wolf (Original by C4nf3ng)",
"version": (2, 2),
"version": (3, 0),
"blender": (2, 80, 0),
"api": 38019,
"location": "File > Import",
"description": "Import Nier:Automata Model Data",
"description": "Import Nier:Automata Data",
"warning": "",
"wiki_url": "",
"tracker_url": "",
@@ -32,6 +32,45 @@ class ImportNier2blender(bpy.types.Operator, ImportHelper):
wmb_importer.reset_blend()
return wmb_importer.main(False, self.filepath)
def importDat(only_extract, filepath):
head = os.path.split(filepath)[0]
tail = os.path.split(filepath)[1]
tailless_tail = tail[:-4]
dat_filepath = head + '\\' + tailless_tail + '.dat'
extract_dir = head + '\\nier2blender_extracted'
from . import dat_unpacker
if os.path.isfile(dat_filepath):
dat_unpacker.main(dat_filepath, extract_dir + '\\' + tailless_tail + '.dat', dat_filepath) # dat
else:
print('DAT not found. Only extracting DTT. (No materials, collisions or layouts will automatically be imported)')
last_filename = dat_unpacker.main(filepath, extract_dir + '\\' + tailless_tail + '.dtt', filepath) # dtt
wmb_filepath = extract_dir + '\\' + tailless_tail + '.dtt\\' + last_filename[:-4] + '.wmb'
if not os.path.exists(wmb_filepath):
wmb_filepath = extract_dir + '\\' + tailless_tail + '.dat\\' + last_filename[:-4] + '.wmb' # if not in dtt, then must be in dat
# WMB
from . import wmb_importer
wmb_importer.main(only_extract, wmb_filepath)
if only_extract:
return {'FINISHED'}
# COL
col_filepath = extract_dir + '\\' + tailless_tail + '.dat\\' + tailless_tail + '.col'
if os.path.isfile(col_filepath):
from . import col_importer
col_importer.main(col_filepath)
# LAY
lay_filepath = extract_dir + '\\' + tailless_tail + '.dat\\' + 'Layout.lay'
if os.path.isfile(lay_filepath):
from . import lay_importer
lay_importer.main(lay_filepath, __name__)
return {'FINISHED'}
class ImportDATNier2blender(bpy.types.Operator, ImportHelper):
'''Load a Nier:Automata DTT (and DAT) File.'''
bl_idname = "import_scene.dtt_data"
@@ -54,69 +93,147 @@ class ImportDATNier2blender(bpy.types.Operator, ImportHelper):
if filename[-4:] == '.dtt':
try:
filepath = folder + '\\' + filename
head = os.path.split(filepath)[0]
tail = os.path.split(filepath)[1]
tailless_tail = tail[:-4]
dat_filepath = head + '\\' + tailless_tail + '.dat'
extract_dir = head + '\\nier2blender_extracted'
from . import dat_unpacker
if os.path.isfile(dat_filepath):
dat_unpacker.main(dat_filepath, extract_dir + '\\' + tailless_tail + '.dat', dat_filepath) # dat
else:
print('DAT not found. Only extracting DTT. (No materials will automatically be imported)')
wtp_filename = dat_unpacker.main(filepath, extract_dir + '\\' + tailless_tail + '.dtt', filepath) # dtt
wmb_filepath = extract_dir + '\\' + tailless_tail + '.dtt\\' + wtp_filename[:-4] + '.wmb'
if not os.path.exists(wmb_filepath):
wmb_filepath = extract_dir + '\\' + tailless_tail + '.dat\\' + wtp_filename[:-4] + '.wmb' # if not in dtt, then must be in dat
wmb_importer.main(self.only_extract, wmb_filepath)
importDat(self.only_extract, filepath)
except:
print('ERROR: FAILED TO IMPORT', filename)
return {'FINISHED'}
else:
head = os.path.split(self.filepath)[0]
tail = os.path.split(self.filepath)[1]
tailless_tail = tail[:-4]
dat_filepath = head + '\\' + tailless_tail + '.dat'
extract_dir = head + '\\nier2blender_extracted'
from . import dat_unpacker
if os.path.isfile(dat_filepath):
dat_unpacker.main(dat_filepath, extract_dir + '\\' + tailless_tail + '.dat', dat_filepath) # dat
else:
print('DAT not found. Only extracting DTT. (No materials will automatically be imported)')
return importDat(self.only_extract, self.filepath)
wtp_filename = dat_unpacker.main(self.filepath, extract_dir + '\\' + tailless_tail + '.dtt', self.filepath) # dtt
class ImportColNier2Blender(bpy.types.Operator, ImportHelper):
'''Load a Nier:Automata Col (Collision) File.'''
bl_idname = "import_scene.col_data"
bl_label = "Import Col Data"
bl_options = {'PRESET'}
filename_ext = ".col"
filter_glob: StringProperty(default="*.col", options={'HIDDEN'})
wmb_filepath = extract_dir + '\\' + tailless_tail + '.dtt\\' + wtp_filename[:-4] + '.wmb'
if not os.path.exists(wmb_filepath):
wmb_filepath = extract_dir + '\\' + tailless_tail + '.dat\\' + wtp_filename[:-4] + '.wmb' # if not in dtt, then must be in dat
def execute(self, context):
from . import col_importer
return col_importer.main(self.filepath)
from . import wmb_importer
return wmb_importer.main(self.only_extract, wmb_filepath)
class ImportLayNier2Blender(bpy.types.Operator, ImportHelper):
'''Load a Nier:Automata Lay (Layout) File.'''
bl_idname = "import_scene.lay_data"
bl_label = "Import Lay Data"
bl_options = {'PRESET'}
filename_ext = ".lay"
filter_glob: StringProperty(default="*.lay", options={'HIDDEN'})
# Registration
def execute(self, context):
from . import lay_importer
return lay_importer.main(self.filepath, __name__)
class SelectDirectory(bpy.types.Operator, ImportHelper):
'''Select Directory'''
bl_idname = "n2b.folder_select"
bl_label = "Select Directory"
filename_ext = ""
dirpath : StringProperty(name = "", description="Choose directory:", subtype='DIR_PATH')
target : bpy.props.StringProperty(options={'HIDDEN'})
def execute(self, context):
directory = os.path.dirname(self.filepath)
if self.target == "data005":
context.preferences.addons[__name__].preferences.data005_dir = directory
elif self.target == "data015":
context.preferences.addons[__name__].preferences.data015_dir = directory
else:
print("Invalid target", self.target)
return {"CANCELLED"}
return {'FINISHED'}
class NieR2BlenderPreferences(bpy.types.AddonPreferences):
bl_idname = __package__
data005_dir : StringProperty(options={'HIDDEN'})
data015_dir : StringProperty(options={'HIDDEN'})
def draw(self, context):
layout = self.layout
layout.label(text="Assign Directories Below If You Wish To Enable Bounding Box Visualization With Layout Import:")
box = layout.box()
box.label(text="Path To Extracted data005.cpk Directory:")
row = box.row(align=True)
row.prop(self, "data005_dir", text="")
row.operator("n2b.folder_select", icon="FILE_FOLDER", text="").target = "data005"
box.label(text="Path To Extracted data015.cpk Directory:")
row = box.row(align=True)
row.prop(self, "data015_dir", text="")
row.operator("n2b.folder_select", icon="FILE_FOLDER", text="").target = "data015"
class NieR2BlenderCreateObjBBox(bpy.types.Operator):
"""Create Layout Object Bounding Box"""
bl_idname = "n2b.create_lay_bb"
bl_label = "Create Layout Object Bounding Box"
bl_options = {'REGISTER', 'UNDO'}
def execute(self, context):
from .lay_importer import getModelBoundingBox, createBoundingBoxObject
for obj in bpy.context.selected_objects:
boundingBox = getModelBoundingBox(obj.name.split("_")[0], __name__)
createBoundingBoxObject(obj, obj.name + "-BoundingBox", bpy.data.collections.get("lay_layAssets"), boundingBox)
return {'FINISHED'}
class N2BLayoutObjectMenu(bpy.types.Menu):
bl_idname = 'OBJECT_MT_n2blayout'
bl_label = 'NieR2Blender'
def draw(self, context):
self.layout.operator(NieR2BlenderCreateObjBBox.bl_idname, icon="CUBE")
def menu_func_utils(self, context):
pcoll = preview_collections["main"]
yorha_icon = pcoll["yorha"]
self.layout.menu(N2BLayoutObjectMenu.bl_idname, icon_value=yorha_icon.icon_id)
def menu_func_import(self, context):
self.layout.operator(ImportNier2blender.bl_idname, text="WMB File for Nier:Automata (.wmb)")
pcoll = preview_collections["main"]
yorha_icon = pcoll["yorha"]
self.layout.operator(ImportDATNier2blender.bl_idname, text="DTT File for Nier:Automata (.dtt)", icon_value=yorha_icon.icon_id)
self.layout.operator(ImportNier2blender.bl_idname, text="WMB File for Nier:Automata (.wmb)", icon_value=yorha_icon.icon_id)
self.layout.operator(ImportColNier2Blender.bl_idname, text="Collision File for Nier:Automata (.col)", icon_value=yorha_icon.icon_id)
self.layout.operator(ImportLayNier2Blender.bl_idname, text="Layout File for Nier:Automata (.lay)", icon_value=yorha_icon.icon_id)
def menu_func_import_dat(self, context):
self.layout.operator(ImportDATNier2blender.bl_idname, text="DTT File for Nier:Automata (.dtt)")
classes = (
ImportNier2blender,
ImportDATNier2blender,
ImportColNier2Blender,
ImportLayNier2Blender,
SelectDirectory,
NieR2BlenderPreferences,
NieR2BlenderCreateObjBBox,
N2BLayoutObjectMenu
)
preview_collections = {}
def register():
bpy.utils.register_class(ImportNier2blender)
bpy.utils.register_class(ImportDATNier2blender)
# Custom icons
import bpy.utils.previews
pcoll = bpy.utils.previews.new()
my_icons_dir = os.path.join(os.path.dirname(__file__), "icons")
pcoll.load("yorha", os.path.join(my_icons_dir, "yorha-filled.png"), 'IMAGE')
preview_collections["main"] = pcoll
for cls in classes:
bpy.utils.register_class(cls)
bpy.types.TOPBAR_MT_file_import.append(menu_func_import)
bpy.types.TOPBAR_MT_file_import.append(menu_func_import_dat)
bpy.types.VIEW3D_MT_object.append(menu_func_utils)
def unregister():
bpy.utils.unregister_class(ImportNier2blender)
bpy.utils.unregister_class(ImportDATNier2blender)
bpy.types.TOPBAR_MT_file_import.remove(menu_func_import)
bpy.types.TOPBAR_MT_file_import.remove(menu_func_import_dat)
for pcoll in preview_collections.values():
bpy.utils.previews.remove(pcoll)
preview_collections.clear()
for cls in classes:
bpy.utils.unregister_class(cls)
bpy.types.TOPBAR_MT_file_import.remove(menu_func_import)
bpy.types.VIEW3D_MT_object.remove(menu_func_utils)
if __name__ == '__main__':
register()
+159
View File
@@ -0,0 +1,159 @@
# https://github.com/Kerilk/bayonetta_tools/blob/master/binary_templates/Nier%20Automata%20col.bt
from .util import *
class Header:
def __init__(self, colFile):
self.id = colFile.read(4)
self.version = "%08x" % (to_uint(colFile.read(4)))
self.offsetNames = to_uint(colFile.read(4))
self.nameCount = to_uint(colFile.read(4))
self.offsetMeshes = to_uint(colFile.read(4))
self.meshCount = to_uint(colFile.read(4))
self.offsetBoneMap = to_uint(colFile.read(4))
self.boneMapCount = to_uint(colFile.read(4))
self.offsetBoneMap2 = to_uint(colFile.read(4))
self.boneMap2Count = to_uint(colFile.read(4))
self.offsetMeshMap = to_uint(colFile.read(4))
self.meshMapCount = to_uint(colFile.read(4))
self.offsetColTreeNodes = to_uint(colFile.read(4))
self.colTreeNodesCount = to_uint(colFile.read(4))
class NameGroups:
def __init__(self, colFile, header):
self.offsetNames = []
for i in range(header.nameCount):
self.offsetNames.append(to_uint(colFile.read(4)))
self.names = []
for offsetName in self.offsetNames:
colFile.seek(offsetName)
self.names.append(to_string(colFile.read(256)))
class Batch:
def __init__(self, colFile, batchType):
if batchType == 2:
self.boneIndex = to_int(colFile.read(4))
self.offsetVertices = to_uint(colFile.read(4))
self.vertexCount = to_uint(colFile.read(4))
self.offsetIndices = to_uint(colFile.read(4))
self.indexCount = to_uint(colFile.read(4))
returnPos = colFile.tell()
colFile.seek(self.offsetVertices)
self.vertices = []
self.vec4Vertices = []
for i in range(self.vertexCount):
x = to_float(colFile.read(4))
y = to_float(colFile.read(4))
z = to_float(colFile.read(4))
w = to_float(colFile.read(4))
self.vertices.append([x, y, z])
self.vec4Vertices.append([x, y, z, w])
colFile.seek(self.offsetIndices)
self.indices = []
self.rawIndices = []
for i in range(round(self.indexCount / 3)):
v0 = to_ushort(colFile.read(2))
v1 = to_ushort(colFile.read(2))
v2 = to_ushort(colFile.read(2))
self.rawIndices.append(v0)
self.rawIndices.append(v1)
self.rawIndices.append(v2)
self.indices.append([v2, v1, v0])
colFile.seek(returnPos)
elif batchType == 3:
self.offsetVertices = to_uint(colFile.read(4))
self.vertexCount = to_uint(colFile.read(4))
self.offsetIndices = to_uint(colFile.read(4))
self.indexCount = to_uint(colFile.read(4))
returnPos = colFile.tell()
else:
print("UNKNOWN BATCH TYPE!")
class Mesh:
def __init__(self, colFile):
self.collisionType = to_uint(colFile.read(1))
self.slidable = to_uint(colFile.read(1))
self.unknownByte = to_uint(colFile.read(1))
self.surfaceType = to_uint(colFile.read(1))
self.nameIndex = to_uint(colFile.read(4))
self.batchType = to_uint(colFile.read(4))
self.offsetBatches = to_uint(colFile.read(4))
self.batchCount = to_uint(colFile.read(4))
returnPos = colFile.tell()
colFile.seek(self.offsetBatches)
self.batches = []
for i in range(self.batchCount):
self.batches.append(Batch(colFile, self.batchType))
colFile.seek(returnPos)
class ColTreeNode:
def __init__(self, colFile):
self.p1 = [to_float(colFile.read(4)), to_float(colFile.read(4)), to_float(colFile.read(4))]
self.p2 = [to_float(colFile.read(4)), to_float(colFile.read(4)), to_float(colFile.read(4))]
self.left = to_int(colFile.read(4))
self.right = to_int(colFile.read(4))
self.offsetMeshIndices = to_uint(colFile.read(4))
self.meshIndexCount = to_uint(colFile.read(4))
self.meshIndices = []
if self.offsetMeshIndices != 0 and self.meshIndexCount != 0:
returnPos = colFile.tell()
colFile.seek(self.offsetMeshIndices)
for i in range(self.meshIndexCount):
self.meshIndices.append(to_uint(colFile.read(4)))
colFile.seek(returnPos)
class Col:
def __init__(self, colFile):
self.header = Header(colFile)
colFile.seek(self.header.offsetNames)
self.nameGroups = NameGroups(colFile, self.header)
colFile.seek(self.header.offsetMeshes)
self.meshes = []
for i in range(self.header.meshCount):
self.meshes.append(Mesh(colFile))
self.meshMaps = []
if self.header.meshMapCount > 0:
colFile.seek(self.header.offsetMeshMap)
for i in range(self.header.meshMapCount):
self.meshMaps.append(to_uint(colFile.read(4)))
self.boneMaps = []
if self.header.boneMapCount > 0:
colFile.seek(self.header.offsetBoneMap)
for i in range(self.header.boneMapCount):
self.boneMaps.append(to_uint(colFile.read(4)))
self.boneMaps2 = []
if self.header.boneMap2Count > 0:
colFile.seek(self.header.offsetBoneMap2)
for i in range(self.header.boneMap2Count):
self.boneMaps2.append(to_uint(colFile.read(4)))
self.colTreeNodes = []
if self.header.colTreeNodesCount > 0:
colFile.seek(self.header.offsetColTreeNodes)
for i in range(self.header.colTreeNodesCount):
self.colTreeNodes.append(ColTreeNode(colFile))
+141
View File
@@ -0,0 +1,141 @@
import bpy, math
from .col import Col
collisionTypes = [
("-1", "UNKNOWN", ""),
("3", "Block Actors", "If modifier is enabled, this will not block players who are jumping (e.g. to prevent accidentally walking off ledges)."),
("88", "Water", ""),
("127", "Grabbable Block All", ""),
("255", "Block All", "")
]
# Identified by NSA Cloud
surfaceTypes = [
("-1", "UNKNOWN", ""),
("0", "Concrete1", ""),
("1", "Dirt", ""),
("2", "Concrete2", ""),
("3", "Metal Floor", ""),
("4", "Rubble", ""),
("5", "Metal Grate", ""),
("6", "Gravel", ""),
("7", "Rope Bridge", ""),
("8", "Grass", ""),
("9", "Wood Plank", ""),
("11", "Water", ""),
("12", "Sand", ""),
("13", "Rocky Gravel 1", ""),
("15", "Mud", ""),
("16", "Rocky Gravel 2", ""),
("17", "Concrete 3", ""),
("18", "Bunker Floor", ""),
("22", "Concrete 4", ""),
("23", "Car", ""),
("24", "Flowers", "")
]
def setColourByCollisionType(obj):
opacity = 0.95
collisionType = int(obj.collisionType)
if collisionType == 127:
obj.color = [0.0, 1.0, 0.0, opacity]
elif collisionType == 88:
obj.color = [0.0, 0.5, 1.0, opacity]
elif collisionType == 3:
obj.color = [1.0, 0.5, 0.0, opacity]
elif collisionType == 255:
obj.color = [1.0, 0.0, 0.0, opacity]
else:
obj.color = [1.0, 0.45, 1.0, opacity]
def updateCollisionType(self, context):
setColourByCollisionType(self)
def main(colFilePath):
bpy.types.Object.collisionType = bpy.props.EnumProperty(name="Collision Type", items=collisionTypes, update=updateCollisionType)
bpy.types.Object.slidable = bpy.props.BoolProperty(name="Slidable/Modifier")
bpy.types.Object.surfaceType = bpy.props.EnumProperty(name="Surface Type", items=surfaceTypes)
# Setup Viewport
for window in bpy.context.window_manager.windows:
for area in window.screen.areas:
if area.type == 'VIEW_3D':
for space in area.spaces:
if space.type == 'VIEW_3D':
space.shading.type = "SOLID"
space.shading.color_type = "OBJECT"
space.shading.show_backface_culling = True
colFile = open(colFilePath, "rb")
print("Parsing Col file...", colFilePath)
col = Col(colFile)
colFile.close()
# Create COL Collection
colCollection = bpy.data.collections.get("COL")
if not colCollection:
colCollection = bpy.data.collections.new("COL")
bpy.context.scene.collection.children.link(colCollection)
# Create meshes
for meshIdx, mesh in enumerate(col.meshes):
meshName = col.nameGroups.names[mesh.nameIndex]
# Create batches
for batchIdx, batch in enumerate(mesh.batches):
objName = str(meshIdx) + "-" + meshName + "-" + str(batchIdx)
objMesh = bpy.data.meshes.new(objName)
obj = bpy.data.objects.new(objName, objMesh)
colCollection.objects.link(obj)
#obj.display_type = 'WIRE'
obj.show_wire = True
objMesh.from_pydata(batch.vertices, [], batch.indices)
objMesh.update(calc_edges=True)
try:
obj.collisionType = str(mesh.collisionType)
except:
print("[!] Collision mesh flagged with unknown collsionType:", mesh.collisionType)
obj.collisionType = "-1"
obj["UNKNOWN_collisionType"] = mesh.collisionType
obj.slidable = bool(mesh.slidable)
obj["unknownByte"] = mesh.unknownByte
try:
obj.surfaceType = str(mesh.surfaceType)
except:
print("[!] Collision mesh flagged with unknown surfaceType:", mesh.surfaceType)
obj.surfaceType = "-1"
obj["UNKNOWN_surfaceType"] = mesh.surfaceType
obj.rotation_euler = (math.radians(90),0,0)
# Create colTreeNodes Sub-Collection
colTreeNodesCollection = bpy.data.collections.get("col_colTreeNodes")
if not colTreeNodesCollection:
colTreeNodesCollection = bpy.data.collections.new("col_colTreeNodes")
colCollection.children.link(colTreeNodesCollection)
bpy.context.view_layer.active_layer_collection.children["COL"].children["col_colTreeNodes"].hide_viewport = True
# Create colTreeNodes
rootNode = bpy.data.objects.new("Root_col", None)
rootNode.hide_viewport = True
colTreeNodesCollection.objects.link(rootNode)
rootNode.rotation_euler = (math.radians(90),0,0)
for nodeIdx, node in enumerate(col.colTreeNodes):
objName = str(nodeIdx) + "_" + str(node.left) + "_" + str(node.right) + "_col"
obj = bpy.data.objects.new(objName, None)
colTreeNodesCollection.objects.link(obj)
obj.parent = rootNode
obj.empty_display_type = 'CUBE'
obj.location = node.p1
obj.scale = node.p2
if len(node.meshIndices) > 0:
obj["meshIndices"] = node.meshIndices
print('Importing finished. ;)')
return {'FINISHED'}
+7 -7
View File
@@ -2,7 +2,7 @@
import os
import sys
import struct
from .util import to_int
from .util import to_uint
def little_endian_to_float(bs):
return struct.unpack("<f", bs)[0]
@@ -120,16 +120,16 @@ def extract_hashes(fp, extract_dir, FileCount, hashMapOffset, fileNamesOffset):
# hash_data.metadata
# Header
fp.seek(hashMapOffset)
preHashShift = to_int(fp.read(4))
bucketOffsetsOffset = to_int(fp.read(4))
hashesOffset = to_int(fp.read(4))
fileIndicesOffset = to_int(fp.read(4))
preHashShift = to_uint(fp.read(4))
bucketOffsetsOffset = to_uint(fp.read(4))
hashesOffset = to_uint(fp.read(4))
fileIndicesOffset = to_uint(fp.read(4))
# Bucket Offsets
fp.seek(hashMapOffset + bucketOffsetsOffset)
bucketOffsets = []
while fp.tell() < (hashMapOffset + hashesOffset):
bucketOffsets.append(to_int(fp.read(2)))
bucketOffsets.append(to_uint(fp.read(2)))
# Hashes
fp.seek(hashMapOffset + hashesOffset)
@@ -141,7 +141,7 @@ def extract_hashes(fp, extract_dir, FileCount, hashMapOffset, fileNamesOffset):
fp.seek(hashMapOffset + fileIndicesOffset)
fileIndices = []
for i in range(FileCount):
fileIndices.append(to_int(fp.read(2)))
fileIndices.append(to_uint(fp.read(2)))
# Extraction
filename = 'hash_data.metadata'
Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

+69
View File
@@ -0,0 +1,69 @@
from .util import *
# Based on binary template by NSA Cloud
class Header:
def __init__(self, layFile):
self.id = layFile.read(4)
self.unknownVer = to_float(layFile.read(4))
self.modelListOffset = to_uint(layFile.read(4))
self.modelListCount = to_uint(layFile.read(4))
self.assetsOffset = to_uint(layFile.read(4))
self.assetsCount = to_uint(layFile.read(4))
self.instancesOffset = to_uint(layFile.read(4))
self.instancesCount = to_uint(layFile.read(4))
class ModelEntry:
def __init__(self, layFile):
self.dir = layFile.read(2)
self.id = layFile.read(2)
class Asset:
def __init__(self, layFile):
self.name = to_string(layFile.read(32))
self.position = [to_float(layFile.read(4)) for val in range(3)]
self.rotation = [to_float(layFile.read(4)) for val in range(3)]
self.scale = [to_float(layFile.read(4)) for val in range(3)]
self.null0 = to_uint(layFile.read(4))
self.unknownIndex = to_uint(layFile.read(4))
self.null1 = [to_uint(layFile.read(4)) for val in range(8)]
self.instanceCount = to_uint(layFile.read(4))
self.instances = []
class Instance:
def __init__(self, layFile):
self.position = [to_float(layFile.read(4)) for val in range(3)]
self.rotation = [to_float(layFile.read(4)) for val in range(3)]
self.scale = [to_float(layFile.read(4)) for val in range(3)]
class Lay:
def __init__(self, layFile):
self.header = Header(layFile)
layFile.seek(self.header.modelListOffset)
self.modelList = []
for i in range(self.header.modelListCount):
self.modelList.append(ModelEntry(layFile))
layFile.seek(self.header.assetsOffset)
self.assets = []
for i in range(self.header.assetsCount):
self.assets.append(Asset(layFile))
layFile.seek(self.header.instancesOffset)
self.instances = []
for i in range(self.header.instancesCount):
self.instances.append(Instance(layFile))
currentInstanceIndx = 0
for asset in self.assets:
for i in range(asset.instanceCount):
asset.instances.append(self.instances[currentInstanceIndx])
currentInstanceIndx = currentInstanceIndx + 1
+153
View File
@@ -0,0 +1,153 @@
import bpy, math, os
from .lay import Lay
from .util import *
def main(layFilePath, addonName):
layFile = open(layFilePath, "rb")
print("Parsing Lay file...", layFilePath)
lay = Lay(layFile)
layFile.close()
# Create LAY Collection
layCollection = bpy.data.collections.get("LAY")
if not layCollection:
layCollection = bpy.data.collections.new("LAY")
bpy.context.scene.collection.children.link(layCollection)
# Create layAssets Sub-Collection
layAssetsCollection = bpy.data.collections.get("lay_layAssets")
if not layAssetsCollection:
layAssetsCollection = bpy.data.collections.new("lay_layAssets")
layCollection.children.link(layAssetsCollection)
# Create layInstances Sub-Collection
layInstancesCollection = bpy.data.collections.get("lay_layInstances")
if not layInstancesCollection:
layInstancesCollection = bpy.data.collections.new("lay_layInstances")
layCollection.children.link(layInstancesCollection)
# Create layAssets and layInstances
assetRootNode = bpy.data.objects.new("Root_layAsset", None)
assetRootNode.hide_viewport = True
layAssetsCollection.objects.link(assetRootNode)
assetRootNode.rotation_euler = (math.radians(90),0,0)
instanceRootNode = bpy.data.objects.new("Root_layInstance", None)
instanceRootNode.hide_viewport = True
layInstancesCollection.objects.link(instanceRootNode)
instanceRootNode.rotation_euler = (math.radians(90),0,0)
for asset in lay.assets:
assetName = asset.name
print("Placing asset", assetName)
boundingBox = getModelBoundingBox(assetName.split("_")[0], addonName)
assetObj = createLayObject(assetName, layAssetsCollection, assetRootNode, asset.position, asset.rotation, asset.scale, boundingBox)
assetObj["unknownIndex"] = asset.unknownIndex
for instance in asset.instances:
instanceName = assetName + "-Instance"
createLayObject(instanceName, layInstancesCollection, instanceRootNode, instance.position, instance.rotation, instance.scale, boundingBox)
print('Importing finished. ;)')
return {'FINISHED'}
def createLayObject(name, collection, parent, pos, rot, scale, boundingBox):
obj = bpy.data.objects.new(name, None)
collection.objects.link(obj)
obj.parent = parent
obj.empty_display_type = 'SPHERE'
obj.empty_display_size = 0.5
obj.location = pos
obj.rotation_euler = rot
obj.scale = scale
obj.show_axis = True
if boundingBox != None:
createBoundingBoxObject(obj, name + "-BoundingBox", collection, boundingBox)
return obj
def createBoundingBoxObject(obj, name, collection, boundingBox):
boundingBoxObj = bpy.data.objects.new(name, None)
collection.objects.link(boundingBoxObj)
boundingBoxObj.parent = obj
boundingBoxObj.empty_display_type = 'CUBE'
boundingBoxObj.location = boundingBox[:3]
boundingBoxObj.scale = boundingBox[-3:]
boundingBoxObj.hide_select = True
def getModelBoundingBox(modelName, addonName):
data005_dir = bpy.context.preferences.addons[addonName].preferences.data005_dir
data015_dir = bpy.context.preferences.addons[addonName].preferences.data015_dir
if not os.path.isdir(data005_dir) or not os.path.isdir(data015_dir):
return None
#print("Model To Find", modelName)
fileFound = False
filePath = ""
# Search data005.cpk
for pathName in os.listdir(data005_dir):
if fileFound:
break
fullPathName = os.path.join(data005_dir, pathName)
if os.path.isdir(fullPathName):
for file in os.listdir(fullPathName):
if file == (modelName + ".dtt"):
filePath = fullPathName + "\\" + file
fileFound = True
break
else:
if pathName == (modelName + ".dtt"):
filePath = fullPathName + "\\" + pathName
fileFound = True
break
# Search data015.cpk
for pathName in os.listdir(data015_dir):
if fileFound:
break
fullPathName = os.path.join(data015_dir, pathName)
if os.path.isdir(fullPathName):
for file in os.listdir(fullPathName):
if file == (modelName + ".dtt"):
filePath = fullPathName + "\\" + file
fileFound = True
break
else:
if pathName == (modelName + ".dtt"):
filePath = fullPathName + "\\" + pathName
fileFound = True
break
if not fileFound:
return None
modelDTTFile = open(filePath, "rb")
id = modelDTTFile.read(4)
numFiles = to_uint(modelDTTFile.read(4))
fileOffsetsOffset = to_uint(modelDTTFile.read(4))
fileExtensionsOffset = to_uint(modelDTTFile.read(4))
fileOffsets = []
modelDTTFile.seek(fileOffsetsOffset)
for i in range(numFiles):
fileOffsets.append(to_uint(modelDTTFile.read(4)))
fileExtensions = []
modelDTTFile.seek(fileExtensionsOffset)
for i in range(numFiles):
fileExtensions.append(to_string(modelDTTFile.read(4)))
for i, ext in enumerate(fileExtensions):
if ext == "wmb":
modelDTTFile.seek(fileOffsets[i] + 16)
boundingBox = [to_float(modelDTTFile.read(4)) for val in range(6)]
modelDTTFile.close()
return boundingBox
+6 -1
View File
@@ -10,12 +10,17 @@ def to_float(bs):
def to_float16(bs):
return float(np.frombuffer(bs, np.float16)[0])
def to_uint(bs):
return (int.from_bytes(bs, byteorder='little', signed=False))
def to_int(bs):
return (int.from_bytes(bs, byteorder='little'))
return (int.from_bytes(bs, byteorder='little', signed=True))
def to_string(bs, encoding = 'utf8'):
return bs.split(b'\x00')[0].decode(encoding)
def to_ushort(bs):
return struct.unpack("<H", bs)[0]
def create_dir(dirpath):
if not os.path.exists(dirpath):
+117 -117
View File
@@ -9,57 +9,57 @@ class WMB_Header(object):
super(WMB_Header, self).__init__()
self.magicNumber = wmb_fp.read(4) # ID
if self.magicNumber == b'WMB3':
self.version = "%08x" % (to_int(wmb_fp.read(4))) # Version
self.unknown08 = to_int(wmb_fp.read(4)) # UnknownA
self.flags = to_int(wmb_fp.read(4)) # flags & referenceBone
self.version = "%08x" % (to_uint(wmb_fp.read(4))) # Version
self.unknown08 = to_uint(wmb_fp.read(4)) # UnknownA
self.flags = to_uint(wmb_fp.read(4)) # flags & referenceBone
self.bounding_box1 = to_float(wmb_fp.read(4)) # bounding_box
self.bounding_box2 = to_float(wmb_fp.read(4))
self.bounding_box3 = to_float(wmb_fp.read(4))
self.bounding_box4 = to_float(wmb_fp.read(4))
self.bounding_box5 = to_float(wmb_fp.read(4))
self.bounding_box6 = to_float(wmb_fp.read(4))
self.boneArrayOffset = to_int(wmb_fp.read(4)) # offsetBones
self.boneCount = to_int(wmb_fp.read(4)) # numBones
self.offsetBoneIndexTranslateTable = to_int(wmb_fp.read(4)) # offsetBoneIndexTranslateTable
self.boneIndexTranslateTableSize = to_int(wmb_fp.read(4)) # boneIndexTranslateTableSize
self.vertexGroupArrayOffset = to_int(wmb_fp.read(4)) # offsetVertexGroups
self.vertexGroupCount = to_int(wmb_fp.read(4)) # numVertexGroups
self.meshArrayOffset = to_int(wmb_fp.read(4)) # offsetBatches
self.meshCount = to_int(wmb_fp.read(4)) # numBatches
self.meshGroupInfoArrayHeaderOffset = to_int(wmb_fp.read(4)) # offsetLODS
self.meshGroupInfoArrayCount = to_int(wmb_fp.read(4)) # numLODS
self.colTreeNodesOffset = to_int(wmb_fp.read(4)) # offsetColTreeNodes
self.colTreeNodesCount = to_int(wmb_fp.read(4)) # numColTreeNodes
self.boneMapOffset = to_int(wmb_fp.read(4)) # offsetBoneMap
self.boneMapCount = to_int(wmb_fp.read(4)) # numBoneMap
self.bonesetOffset = to_int(wmb_fp.read(4)) # offsetBoneSets
self.bonesetCount = to_int(wmb_fp.read(4)) # numBoneSets
self.materialArrayOffset = to_int(wmb_fp.read(4)) # offsetMaterials
self.materialCount = to_int(wmb_fp.read(4)) # numMaterials
self.meshGroupOffset = to_int(wmb_fp.read(4)) # offsetMeshes
self.meshGroupCount = to_int(wmb_fp.read(4)) # numMeshes
self.offsetMeshMaterials = to_int(wmb_fp.read(4)) # offsetMeshMaterials
self.numMeshMaterials = to_int(wmb_fp.read(4)) # numMeshMaterials
self.unknownWorldDataArrayOffset = to_int(wmb_fp.read(4)) # offsetUnknown0 World Model Stuff
self.unknownWorldDataArrayCount = to_int(wmb_fp.read(4)) # numUnknown0 World Model Stuff
self.unknown8C = to_int(wmb_fp.read(4))
self.boneArrayOffset = to_uint(wmb_fp.read(4)) # offsetBones
self.boneCount = to_uint(wmb_fp.read(4)) # numBones
self.offsetBoneIndexTranslateTable = to_uint(wmb_fp.read(4)) # offsetBoneIndexTranslateTable
self.boneIndexTranslateTableSize = to_uint(wmb_fp.read(4)) # boneIndexTranslateTableSize
self.vertexGroupArrayOffset = to_uint(wmb_fp.read(4)) # offsetVertexGroups
self.vertexGroupCount = to_uint(wmb_fp.read(4)) # numVertexGroups
self.meshArrayOffset = to_uint(wmb_fp.read(4)) # offsetBatches
self.meshCount = to_uint(wmb_fp.read(4)) # numBatches
self.meshGroupInfoArrayHeaderOffset = to_uint(wmb_fp.read(4)) # offsetLODS
self.meshGroupInfoArrayCount = to_uint(wmb_fp.read(4)) # numLODS
self.colTreeNodesOffset = to_uint(wmb_fp.read(4)) # offsetColTreeNodes
self.colTreeNodesCount = to_uint(wmb_fp.read(4)) # numColTreeNodes
self.boneMapOffset = to_uint(wmb_fp.read(4)) # offsetBoneMap
self.boneMapCount = to_uint(wmb_fp.read(4)) # numBoneMap
self.bonesetOffset = to_uint(wmb_fp.read(4)) # offsetBoneSets
self.bonesetCount = to_uint(wmb_fp.read(4)) # numBoneSets
self.materialArrayOffset = to_uint(wmb_fp.read(4)) # offsetMaterials
self.materialCount = to_uint(wmb_fp.read(4)) # numMaterials
self.meshGroupOffset = to_uint(wmb_fp.read(4)) # offsetMeshes
self.meshGroupCount = to_uint(wmb_fp.read(4)) # numMeshes
self.offsetMeshMaterials = to_uint(wmb_fp.read(4)) # offsetMeshMaterials
self.numMeshMaterials = to_uint(wmb_fp.read(4)) # numMeshMaterials
self.unknownWorldDataArrayOffset = to_uint(wmb_fp.read(4)) # offsetUnknown0 World Model Stuff
self.unknownWorldDataArrayCount = to_uint(wmb_fp.read(4)) # numUnknown0 World Model Stuff
self.unknown8C = to_uint(wmb_fp.read(4))
class wmb3_vertexHeader(object):
"""docstring for wmb3_vertexHeader"""
def __init__(self, wmb_fp):
super(wmb3_vertexHeader, self).__init__()
self.vertexArrayOffset = to_int(wmb_fp.read(4))
self.vertexExDataArrayOffset = to_int(wmb_fp.read(4))
self.unknown08 = to_int(wmb_fp.read(4))
self.unknown0C = to_int(wmb_fp.read(4))
self.vertexStride = to_int(wmb_fp.read(4))
self.vertexExDataStride = to_int(wmb_fp.read(4))
self.unknown18 = to_int(wmb_fp.read(4))
self.unknown1C = to_int(wmb_fp.read(4))
self.vertexCount = to_int(wmb_fp.read(4))
self.vertexFlags = to_int(wmb_fp.read(4))
self.faceArrayOffset = to_int(wmb_fp.read(4))
self.faceCount = to_int(wmb_fp.read(4))
self.vertexArrayOffset = to_uint(wmb_fp.read(4))
self.vertexExDataArrayOffset = to_uint(wmb_fp.read(4))
self.unknown08 = to_uint(wmb_fp.read(4))
self.unknown0C = to_uint(wmb_fp.read(4))
self.vertexStride = to_uint(wmb_fp.read(4))
self.vertexExDataStride = to_uint(wmb_fp.read(4))
self.unknown18 = to_uint(wmb_fp.read(4))
self.unknown1C = to_uint(wmb_fp.read(4))
self.vertexCount = to_uint(wmb_fp.read(4))
self.vertexFlags = to_uint(wmb_fp.read(4))
self.faceArrayOffset = to_uint(wmb_fp.read(4))
self.faceCount = to_uint(wmb_fp.read(4))
class wmb3_vertex(object):
"""docstring for wmb3_vertex"""
@@ -68,22 +68,22 @@ class wmb3_vertex(object):
self.positionX = to_float(wmb_fp.read(4))
self.positionY = to_float(wmb_fp.read(4))
self.positionZ = to_float(wmb_fp.read(4))
self.normalX = to_int(wmb_fp.read(1)) * 2 / 255
self.normalY = to_int(wmb_fp.read(1)) * 2 / 255
self.normalZ = to_int(wmb_fp.read(1)) * 2 / 255
self.normalX = to_uint(wmb_fp.read(1)) * 2 / 255
self.normalY = to_uint(wmb_fp.read(1)) * 2 / 255
self.normalZ = to_uint(wmb_fp.read(1)) * 2 / 255
wmb_fp.read(1)
self.textureU = to_float16(wmb_fp.read(2))
self.textureV = to_float16(wmb_fp.read(2))
if vertex_flags in [0]:
self.normal = hex(to_int(wmb_fp.read(8)))
self.normal = hex(to_uint(wmb_fp.read(8)))
if vertex_flags in [1, 4, 5, 12, 14]:
self.textureU2 = to_float16(wmb_fp.read(2))
self.textureV2 = to_float16(wmb_fp.read(2))
if vertex_flags in [7, 10, 11]:
self.boneIndices = [to_int(wmb_fp.read(1)) for i in range(4)]
self.boneWeights = [to_int(wmb_fp.read(1))/255 for i in range(4)]
self.boneIndices = [to_uint(wmb_fp.read(1)) for i in range(4)]
self.boneWeights = [to_uint(wmb_fp.read(1))/255 for i in range(4)]
if vertex_flags in [4, 5, 12, 14]:
self.color = [to_int(wmb_fp.read(1)) for i in range(4)]
self.color = [to_uint(wmb_fp.read(1)) for i in range(4)]
class wmb3_vertexExData(object):
"""docstring for wmb3_vertexExData"""
@@ -93,34 +93,34 @@ class wmb3_vertexExData(object):
#0x0 has no ExVertexData
if vertex_flags in [1, 4]: #0x1, 0x4
self.normal = hex(to_int(wmb_fp.read(8)))
self.normal = hex(to_uint(wmb_fp.read(8)))
elif vertex_flags in [5]: #0x5
self.normal = hex(to_int(wmb_fp.read(8)))
self.normal = hex(to_uint(wmb_fp.read(8)))
self.textureU3 = to_float16(wmb_fp.read(2))
self.textureV3 = to_float16(wmb_fp.read(2))
elif vertex_flags in [7]: #0x7
self.textureU2 = to_float16(wmb_fp.read(2))
self.textureV2 = to_float16(wmb_fp.read(2))
self.normal = hex(to_int(wmb_fp.read(8)))
self.normal = hex(to_uint(wmb_fp.read(8)))
elif vertex_flags in [10]: #0xa
self.textureU2 = to_float16(wmb_fp.read(2))
self.textureV2 = to_float16(wmb_fp.read(2))
self.color = [to_int(wmb_fp.read(1)) for i in range(4)]
self.normal = hex(to_int(wmb_fp.read(8)))
self.color = [to_uint(wmb_fp.read(1)) for i in range(4)]
self.normal = hex(to_uint(wmb_fp.read(8)))
elif vertex_flags in [11]: #0xb
self.textureU2 = to_float16(wmb_fp.read(2))
self.textureV2 = to_float16(wmb_fp.read(2))
self.color = [to_int(wmb_fp.read(1)) for i in range(4)]
self.normal = hex(to_int(wmb_fp.read(8)))
self.color = [to_uint(wmb_fp.read(1)) for i in range(4)]
self.normal = hex(to_uint(wmb_fp.read(8)))
self.textureU3 = to_float16(wmb_fp.read(2))
self.textureV3 = to_float16(wmb_fp.read(2))
elif vertex_flags in [12]: #0xc
self.normal = hex(to_int(wmb_fp.read(8)))
self.normal = hex(to_uint(wmb_fp.read(8)))
self.textureU3 = to_float16(wmb_fp.read(2))
self.textureV3 = to_float16(wmb_fp.read(2))
self.textureU4 = to_float16(wmb_fp.read(2))
@@ -129,7 +129,7 @@ class wmb3_vertexExData(object):
self.textureV5 = to_float16(wmb_fp.read(2))
elif vertex_flags in [14]: #0xe
self.normal = hex(to_int(wmb_fp.read(8)))
self.normal = hex(to_uint(wmb_fp.read(8)))
self.textureU3 = to_float16(wmb_fp.read(2))
self.textureV3 = to_float16(wmb_fp.read(2))
self.textureU4 = to_float16(wmb_fp.read(2))
@@ -159,29 +159,29 @@ class wmb3_vertexGroup(object):
wmb_fp.seek(self.vertexGroupHeader.faceArrayOffset)
for face_index in range(self.vertexGroupHeader.faceCount):
if faceSize == 2:
self.faceRawArray.append(to_int(wmb_fp.read(2)) + 1)
self.faceRawArray.append(to_uint(wmb_fp.read(2)) + 1)
else:
self.faceRawArray.append(to_int(wmb_fp.read(4)) + 1)
self.faceRawArray.append(to_uint(wmb_fp.read(4)) + 1)
class wmb3_mesh(object):
"""docstring for wmb3_mesh"""
def __init__(self, wmb_fp):
super(wmb3_mesh, self).__init__()
self.vertexGroupIndex = to_int(wmb_fp.read(4))
self.bonesetIndex = to_int(wmb_fp.read(4))
self.vertexStart = to_int(wmb_fp.read(4))
self.faceStart = to_int(wmb_fp.read(4))
self.vertexCount = to_int(wmb_fp.read(4))
self.faceCount = to_int(wmb_fp.read(4))
self.unknown18 = to_int(wmb_fp.read(4))
self.vertexGroupIndex = to_uint(wmb_fp.read(4))
self.bonesetIndex = to_uint(wmb_fp.read(4))
self.vertexStart = to_uint(wmb_fp.read(4))
self.faceStart = to_uint(wmb_fp.read(4))
self.vertexCount = to_uint(wmb_fp.read(4))
self.faceCount = to_uint(wmb_fp.read(4))
self.unknown18 = to_uint(wmb_fp.read(4))
class wmb3_bone(object):
"""docstring for wmb3_bone"""
def __init__(self, wmb_fp,index):
super(wmb3_bone, self).__init__()
self.boneIndex = index
self.boneNumber = to_int(wmb_fp.read(2))
self.parentIndex = to_int(wmb_fp.read(2))
self.boneNumber = to_uint(wmb_fp.read(2))
self.parentIndex = to_uint(wmb_fp.read(2))
local_positionX = to_float(wmb_fp.read(4))
local_positionY = to_float(wmb_fp.read(4))
@@ -223,8 +223,8 @@ class wmb3_boneMap(object):
"""docstring for wmb3_boneMap"""
def __init__(self, wmb_fp):
super(wmb3_boneMap, self).__init__()
self.boneMapOffset = to_int(wmb_fp.read(4))
self.boneMapCount = to_int(wmb_fp.read(4))
self.boneMapOffset = to_uint(wmb_fp.read(4))
self.boneMapCount = to_uint(wmb_fp.read(4))
class wmb3_boneSet(object):
"""docstring for wmb3_boneSet"""
@@ -234,34 +234,34 @@ class wmb3_boneSet(object):
self.boneSetCount = boneSetCount
boneSetInfoArray = []
for index in range(boneSetCount):
offset = to_int(wmb_fp.read(4))
count = to_int(wmb_fp.read(4))
offset = to_uint(wmb_fp.read(4))
count = to_uint(wmb_fp.read(4))
boneSetInfoArray.append([offset, count])
for boneSetInfo in boneSetInfoArray:
wmb_fp.seek(boneSetInfo[0])
boneSet = []
for index in range(boneSetInfo[1]):
boneSet.append(to_int(wmb_fp.read(2)))
boneSet.append(to_uint(wmb_fp.read(2)))
self.boneSetArray.append(boneSet)
class wmb3_material(object):
"""docstring for wmb3_material"""
def __init__(self, wmb_fp):
super(wmb3_material, self).__init__()
to_int(wmb_fp.read(2))
to_int(wmb_fp.read(2))
to_int(wmb_fp.read(2))
to_int(wmb_fp.read(2))
materialNameOffset = to_int(wmb_fp.read(4))
effectNameOffset = to_int(wmb_fp.read(4))
techniqueNameOffset = to_int(wmb_fp.read(4))
to_int(wmb_fp.read(4))
textureOffset = to_int(wmb_fp.read(4))
textureNum = to_int(wmb_fp.read(4))
paramterGroupsOffset = to_int(wmb_fp.read(4))
numParameterGroups = to_int(wmb_fp.read(4))
varOffset = to_int(wmb_fp.read(4))
varNum = to_int(wmb_fp.read(4))
to_uint(wmb_fp.read(2))
to_uint(wmb_fp.read(2))
to_uint(wmb_fp.read(2))
to_uint(wmb_fp.read(2))
materialNameOffset = to_uint(wmb_fp.read(4))
effectNameOffset = to_uint(wmb_fp.read(4))
techniqueNameOffset = to_uint(wmb_fp.read(4))
to_uint(wmb_fp.read(4))
textureOffset = to_uint(wmb_fp.read(4))
textureNum = to_uint(wmb_fp.read(4))
paramterGroupsOffset = to_uint(wmb_fp.read(4))
numParameterGroups = to_uint(wmb_fp.read(4))
varOffset = to_uint(wmb_fp.read(4))
varNum = to_uint(wmb_fp.read(4))
wmb_fp.seek(materialNameOffset)
self.materialName = to_string(wmb_fp.read(256))
wmb_fp.seek(effectNameOffset)
@@ -288,8 +288,8 @@ class wmb3_material(object):
# Append textures to materials in the dictionary
for i in range(textureNum):
wmb_fp.seek(textureOffset + i * 8)
offset = to_int(wmb_fp.read(4))
identifier = "%08x"%to_int(wmb_fp.read(4))
offset = to_uint(wmb_fp.read(4))
identifier = "%08x"%to_uint(wmb_fp.read(4))
wmb_fp.seek(offset)
textureTypeName = to_string(wmb_fp.read(256))
self.textureArray[textureTypeName] = identifier
@@ -305,9 +305,9 @@ class wmb3_material(object):
for i in range(numParameterGroups):
wmb_fp.seek(paramterGroupsOffset + i * 12)
parameters = []
index = to_int(wmb_fp.read(4))
offset = to_int(wmb_fp.read(4))
num = to_int(wmb_fp.read(4))
index = to_uint(wmb_fp.read(4))
offset = to_uint(wmb_fp.read(4))
num = to_uint(wmb_fp.read(4))
wmb_fp.seek(offset)
for k in range(num):
param = to_float(wmb_fp.read(4))
@@ -318,7 +318,7 @@ class wmb3_material(object):
self.uniformArray = {}
for i in range(varNum):
wmb_fp.seek(varOffset + i * 8)
offset = to_int(wmb_fp.read(4))
offset = to_uint(wmb_fp.read(4))
value = to_float(wmb_fp.read(4))
wmb_fp.seek(offset)
self.uniformArray [to_string(wmb_fp.read(256))] = value
@@ -328,38 +328,38 @@ class wmb3_meshGroup(object):
"""docstring for wmb3_meshGroupInfo"""
def __init__(self, wmb_fp):
super(wmb3_meshGroup, self).__init__()
nameOffset = to_int(wmb_fp.read(4))
nameOffset = to_uint(wmb_fp.read(4))
self.boundingBox = []
for i in range(6):
self.boundingBox.append(to_float(wmb_fp.read(4)))
materialIndexArrayOffset = to_int(wmb_fp.read(4))
materialIndexArrayCount = to_int(wmb_fp.read(4))
boneIndexArrayOffset =to_int(wmb_fp.read(4))
boneIndexArrayCount = to_int(wmb_fp.read(4))
materialIndexArrayOffset = to_uint(wmb_fp.read(4))
materialIndexArrayCount = to_uint(wmb_fp.read(4))
boneIndexArrayOffset =to_uint(wmb_fp.read(4))
boneIndexArrayCount = to_uint(wmb_fp.read(4))
wmb_fp.seek(nameOffset)
self.meshGroupname = to_string(wmb_fp.read(256))
self.materialIndexArray = []
self.boneIndexArray = []
wmb_fp.seek(materialIndexArrayOffset)
for i in range(materialIndexArrayCount):
self.materialIndexArray.append(to_int(wmb_fp.read(2)))
self.materialIndexArray.append(to_uint(wmb_fp.read(2)))
wmb_fp.seek(boneIndexArrayOffset)
for i in range(boneIndexArrayCount):
self.boneIndexArray.append(to_int(wmb_fp.read(2)))
self.boneIndexArray.append(to_uint(wmb_fp.read(2)))
class wmb3_groupedMesh(object):
"""docstring for wmb3_groupedMesh"""
def __init__(self, wmb_fp):
super(wmb3_groupedMesh, self).__init__()
self.vertexGroupIndex = to_int(wmb_fp.read(4))
self.meshGroupIndex = to_int(wmb_fp.read(4))
self.materialIndex = to_int(wmb_fp.read(4))
self.colTreeNodeIndex = to_int(wmb_fp.read(4))
self.vertexGroupIndex = to_uint(wmb_fp.read(4))
self.meshGroupIndex = to_uint(wmb_fp.read(4))
self.materialIndex = to_uint(wmb_fp.read(4))
self.colTreeNodeIndex = to_uint(wmb_fp.read(4))
if self.colTreeNodeIndex == 4294967295:
self.colTreeNodeIndex = -1
self.meshGroupInfoMaterialPair = to_int(wmb_fp.read(4))
self.unknownWorldDataIndex = to_int(wmb_fp.read(4))
self.meshGroupInfoMaterialPair = to_uint(wmb_fp.read(4))
self.unknownWorldDataIndex = to_uint(wmb_fp.read(4))
if self.unknownWorldDataIndex == 4294967295:
self.unknownWorldDataIndex = -1
@@ -368,13 +368,13 @@ class wmb3_meshGroupInfo(object):
"""docstring for wmb3_meshGroupInfo"""
def __init__(self, wmb_fp):
super(wmb3_meshGroupInfo, self).__init__()
self.nameOffset = to_int(wmb_fp.read(4))
self.lodLevel = to_int(wmb_fp.read(4))
self.nameOffset = to_uint(wmb_fp.read(4))
self.lodLevel = to_uint(wmb_fp.read(4))
if self.lodLevel == 4294967295:
self.lodLevel = -1
self.meshStart = to_int(wmb_fp.read(4))
meshGroupInfoOffset = to_int(wmb_fp.read(4))
self.meshCount = to_int(wmb_fp.read(4))
self.meshStart = to_uint(wmb_fp.read(4))
meshGroupInfoOffset = to_uint(wmb_fp.read(4))
self.meshCount = to_uint(wmb_fp.read(4))
wmb_fp.seek(self.nameOffset)
self.meshGroupInfoname = to_string(wmb_fp.read(256))
wmb_fp.seek(meshGroupInfoOffset)
@@ -396,11 +396,11 @@ class wmb3_colTreeNode(object):
p2_z = to_float(wmb_fp.read(4))
self.p2 = (p2_x, p2_y, p2_z)
self.left = to_int(wmb_fp.read(4))
self.left = to_uint(wmb_fp.read(4))
if self.left == 4294967295:
self.left = -1
self.right = to_int(wmb_fp.read(4))
self.right = to_uint(wmb_fp.read(4))
if self.right == 4294967295:
self.right = -1
@@ -459,7 +459,7 @@ class WMB3(object):
wmb_fp.seek(self.wmb3_header.offsetBoneIndexTranslateTable)
self.firstLevel = []
for entry in range(16):
self.firstLevel.append(to_int(wmb_fp.read(2)))
self.firstLevel.append(to_uint(wmb_fp.read(2)))
if self.firstLevel[-1] == 65535:
self.firstLevel[-1] = -1
@@ -470,7 +470,7 @@ class WMB3(object):
self.secondLevel = []
for entry in range(firstLevel_Entry_Count * 16):
self.secondLevel.append(to_int(wmb_fp.read(2)))
self.secondLevel.append(to_uint(wmb_fp.read(2)))
if self.secondLevel[-1] == 65535:
self.secondLevel[-1] = -1
@@ -481,7 +481,7 @@ class WMB3(object):
self.thirdLevel = []
for entry in range(secondLevel_Entry_Count * 16):
self.thirdLevel.append(to_int(wmb_fp.read(2)))
self.thirdLevel.append(to_uint(wmb_fp.read(2)))
if self.thirdLevel[-1] == 65535:
self.thirdLevel[-1] = -1
@@ -489,7 +489,7 @@ class WMB3(object):
wmb_fp.seek(self.wmb3_header.offsetBoneIndexTranslateTable)
unknownData1Array = []
for i in range(self.wmb3_header.boneIndexTranslateTableSize):
unknownData1Array.append(to_int(wmb_fp.read(1)))
unknownData1Array.append(to_uint(wmb_fp.read(1)))
self.vertexGroupArray = []
for vertexGroupIndex in range(self.wmb3_header.vertexGroupCount):
@@ -526,7 +526,7 @@ class WMB3(object):
wmb_fp.seek(self.wmb3_header.boneMapOffset)
self.boneMap = []
for index in range(self.wmb3_header.boneMapCount):
self.boneMap.append(to_int(wmb_fp.read(4)))
self.boneMap.append(to_uint(wmb_fp.read(4)))
wmb_fp.seek(self.wmb3_header.bonesetOffset)
self.boneSetArray = wmb3_boneSet(wmb_fp, self.wmb3_header.bonesetCount).boneSetArray
+47 -34
View File
@@ -25,17 +25,17 @@ def reset_blend():
bpy.data.objects.remove(obj)
obj.user_clear()
def construct_armature(name, bone_data_array, firstLevel, secondLevel, thirdLevel, boneMap, boneSetArray): # bone_data =[boneIndex, boneName, parentIndex, parentName, bone_pos, optional, boneNumber, localPos, local_rotation, world_rotation, world_position_tpose]
def construct_armature(name, bone_data_array, firstLevel, secondLevel, thirdLevel, boneMap, boneSetArray, collection_name): # bone_data =[boneIndex, boneName, parentIndex, parentName, bone_pos, optional, boneNumber, localPos, local_rotation, world_rotation, world_position_tpose]
print('[+] importing armature')
bpy.ops.object.add(
type='ARMATURE',
enter_editmode=True,
location=(0,0,0))
ob = bpy.context.active_object
amt = bpy.data.armatures.new(name +'Amt')
ob = bpy.data.objects.new(name, amt)
#ob = bpy.context.active_object
ob.show_in_front = False
ob.name = name
amt = ob.data
amt.name = name +'Amt'
bpy.data.collections.get(collection_name).objects.link(ob)
bpy.context.view_layer.objects.active = ob
bpy.ops.object.mode_set(mode='EDIT')
amt['firstLevel'] = firstLevel
amt['secondLevel'] = secondLevel
@@ -119,7 +119,7 @@ def construct_mesh(mesh_data, collection_name): # [meshName, vertices, faces,
else:
obj = bpy.data.objects[name]
obj.location = Vector((0,0,0))
bpy.context.collection.objects.link(obj)
bpy.data.collections.get(collection_name).objects.link(obj)
objmesh.from_pydata(vertices, [], faces)
objmesh.update(calc_edges=True)
@@ -155,8 +155,6 @@ def construct_mesh(mesh_data, collection_name): # [meshName, vertices, faces,
obj['LOD_Level'] = mesh_data[9]
obj['colTreeNodeIndex'] = mesh_data[10]
obj['unknownWorldDataIndex'] = mesh_data[11]
obj['boundingBoxXYZ'] = [mesh_data[12][0], mesh_data[12][1], mesh_data[12][2]]
obj['boundingBoxUVW'] = [mesh_data[12][3], mesh_data[12][4], mesh_data[12][5]]
obj.data.flip_normals()
return obj
@@ -559,22 +557,39 @@ def import_colTreeNodes(wmb, collection):
colTreeNodesDict = {}
#collision_col = bpy.data.collections.new("CollisionNodes")
#collection.children.link(collision_col)
for index, node in enumerate(wmb.colTreeNodes):
colTreeNodeName = 'colTreeNode' + str(index)
colTreeNodesCollection = bpy.data.collections.get("wmb_colTreeNodes")
if not colTreeNodesCollection:
colTreeNodesCollection = bpy.data.collections.new("wmb_colTreeNodes")
collection.children.link(colTreeNodesCollection)
bpy.context.view_layer.active_layer_collection.children["WMB"].children[collection.name].children["wmb_colTreeNodes"].hide_viewport = True
rootNode = bpy.data.objects.new("Root_wmb", None)
rootNode.hide_viewport = True
colTreeNodesCollection.objects.link(rootNode)
rootNode.rotation_euler = (math.radians(90),0,0)
for nodeIdx, node in enumerate(wmb.colTreeNodes):
colTreeNodeName = 'colTreeNode' + str(nodeIdx)
objName = str(nodeIdx) + "_" + str(node.left) + "_" + str(node.right) + "_wmb"
obj = bpy.data.objects.new(objName, None)
colTreeNodesCollection.objects.link(obj)
obj.parent = rootNode
obj.empty_display_type = 'CUBE'
obj.location = node.p1
obj.scale = node.p2
meshIndices = []
for bObj in (x for x in bpy.data.collections['WMB'].all_objects if x.type == "MESH"):
if bObj["colTreeNodeIndex"] == nodeIdx:
idx = int(bObj.name.split("-")[0])
meshIndices.append(idx)
if len(meshIndices) > 0:
obj["meshIndices"] = meshIndices
colTreeNode = [node.p1[0], node.p1[1], node.p1[2], node.p2[0], node.p2[1], node.p2[2], node.left, node.right]
colTreeNodesDict[colTreeNodeName] = colTreeNode
"""
bpy.ops.mesh.primitive_cube_add(enter_editmode=False, align='WORLD', location=(node.p1[0], node.p1[1], node.p1[2]))
col_Bound = bpy.context.active_object
col_Bound.name = str(index) + "-" + str(node.left) + "-" + str(node.right)
bpy.ops.transform.resize(value=(node.p2[0], node.p2[1], node.p2[2]))
bpy.ops.transform.rotate(value=math.radians(-90), orient_axis='X', center_override=(0, 0, 0))
bpy.context.object.display_type = 'BOUNDS'
collision_col.objects.link(col_Bound)
collection.objects.unlink(col_Bound)
"""
bpy.context.scene['colTreeNodes'] = colTreeNodesDict
def import_unknowWorldDataArray(wmb):
@@ -584,12 +599,6 @@ def import_unknowWorldDataArray(wmb):
unknownWorldDataDict[unknownWorldDataName] = unknownWorldData.unknownWorldData
bpy.context.scene['unknownWorldData'] = unknownWorldDataDict
def import_wmb_boundingbox(wmb):
boundingBoxXYZ = [wmb.wmb3_header.bounding_box1, wmb.wmb3_header.bounding_box2, wmb.wmb3_header.bounding_box3]
boundingBoxUVW = [wmb.wmb3_header.bounding_box4, wmb.wmb3_header.bounding_box5, wmb.wmb3_header.bounding_box6]
bpy.context.scene['boundingBoxXYZ'] = boundingBoxXYZ
bpy.context.scene['boundingBoxUVW'] = boundingBoxUVW
def main(only_extract = False, wmb_file = os.path.split(os.path.realpath(__file__))[0] + '\\test\\pl0000.dtt\\pl0000.wmb'):
#reset_blend()
wmb = WMB3(wmb_file)
@@ -601,19 +610,23 @@ def main(only_extract = False, wmb_file = os.path.split(os.path.realpath(__file_
print('Extraction finished. ;)')
return {'FINISHED'}
wmbCollection = bpy.data.collections.get("WMB")
if not wmbCollection:
wmbCollection = bpy.data.collections.new("WMB")
bpy.context.scene.collection.children.link(wmbCollection)
collection_name = wmbname[:-4]
col = bpy.data.collections.new(collection_name)
bpy.context.scene.collection.children.link(col)
bpy.context.view_layer.active_layer_collection = bpy.context.view_layer.layer_collection.children[-1]
wmbCollection.children.link(col)
#bpy.context.view_layer.active_layer_collection = bpy.context.view_layer.layer_collection.children[-1]
texture_dir = wmb_file.replace(wmbname, '\\textures\\')
import_wmb_boundingbox(wmb)
if wmb.hasBone:
boneArray = [[bone.boneIndex, "bone%d"%bone.boneIndex, bone.parentIndex,"bone%d"%bone.parentIndex, bone.world_position, bone.world_rotation, bone.boneNumber, bone.local_position, bone.local_rotation, bone.world_rotation, bone.world_position_tpose] for bone in wmb.boneArray]
armature_no_wmb = wmbname.replace('.wmb','')
armature_name_split = armature_no_wmb.split('/')
armature_name = armature_name_split[len(armature_name_split)-1] # THIS IS SPAGHETT I KNOW. I WAS TIRED
construct_armature(armature_name, boneArray, wmb.firstLevel, wmb.secondLevel, wmb.thirdLevel, wmb.boneMap, wmb.boneSetArray)
construct_armature(armature_name, boneArray, wmb.firstLevel, wmb.secondLevel, wmb.thirdLevel, wmb.boneMap, wmb.boneSetArray, collection_name)
meshes, uvs, usedVerticeIndexArrays = format_wmb_mesh(wmb, collection_name)
wmb_materials = get_wmb_material(wmb, texture_dir)
materials = []
+12 -12
View File
@@ -7,13 +7,13 @@ class WTA(object):
super(WTA, self).__init__()
self.magicNumber = wta_fp.read(4)
if self.magicNumber == b'WTB\x00':
self.unknown04 = to_int(wta_fp.read(4))
self.textureCount = to_int(wta_fp.read(4))
self.textureOffsetArrayOffset = to_int(wta_fp.read(4))
self.textureSizeArrayOffset = to_int(wta_fp.read(4))
self.unknownArrayOffset1 = to_int(wta_fp.read(4))
self.textureIdentifierArrayOffset = to_int(wta_fp.read(4))
self.unknownArrayOffset2 = to_int(wta_fp.read(4))
self.unknown04 = to_uint(wta_fp.read(4))
self.textureCount = to_uint(wta_fp.read(4))
self.textureOffsetArrayOffset = to_uint(wta_fp.read(4))
self.textureSizeArrayOffset = to_uint(wta_fp.read(4))
self.unknownArrayOffset1 = to_uint(wta_fp.read(4))
self.textureIdentifierArrayOffset = to_uint(wta_fp.read(4))
self.unknownArrayOffset2 = to_uint(wta_fp.read(4))
self.wtaTextureOffset = [0] * self.textureCount
self.wtaTextureSize = [0] * self.textureCount
self.wtaTextureIdentifier = [0] * self.textureCount
@@ -21,17 +21,17 @@ class WTA(object):
self.unknownArray2 = []
for i in range(self.textureCount):
wta_fp.seek(self.textureOffsetArrayOffset + i * 4)
self.wtaTextureOffset[i] = to_int(wta_fp.read(4))
self.wtaTextureOffset[i] = to_uint(wta_fp.read(4))
wta_fp.seek(self.textureSizeArrayOffset + i * 4)
self.wtaTextureSize[i] = to_int(wta_fp.read(4))
self.wtaTextureSize[i] = to_uint(wta_fp.read(4))
wta_fp.seek(self.textureIdentifierArrayOffset + i * 4)
self.wtaTextureIdentifier[i] = "%08x"%to_int(wta_fp.read(4))
self.wtaTextureIdentifier[i] = "%08x"%to_uint(wta_fp.read(4))
wta_fp.seek(self.unknownArrayOffset1 + i * 4)
self.unknownArray1[i] = "%08x"%to_int(wta_fp.read(4))
self.unknownArray1[i] = "%08x"%to_uint(wta_fp.read(4))
wta_fp.seek(self.unknownArrayOffset2 )
unknownval = (wta_fp.read(4))
while unknownval:
self.unknownArray2.append(to_int(unknownval))
self.unknownArray2.append(to_uint(unknownval))
unknownval = (wta_fp.read(4))
self.pointer2 = hex(wta_fp.tell())
def getTextureByIndex(self, texture_index, texture_fp):