Added 2. LAY visualization: Instead of bounding box, now actual mesh instances, from pre-imported library

This commit is contained in:
ArthurHeitmann
2022-08-13 09:14:43 +02:00
parent 169c39bc78
commit 8560094fe5
5 changed files with 107 additions and 30 deletions
+2 -6
View File
@@ -52,13 +52,9 @@ class CreateLayBoundingBox(bpy.types.Operator):
bl_options = {'REGISTER', 'UNDO'}
def execute(self, context):
from .lay.importer.lay_importer import getModelBoundingBox, createBoundingBoxObject
from .lay.importer.lay_importer import getModelBoundingBox, createVisualizationObject
for obj in bpy.context.selected_objects:
boundingBox = getModelBoundingBox(obj.name.split("_")[0], ADDON_NAME)
if boundingBox:
createBoundingBoxObject(obj, obj.name + "-BoundingBox", bpy.data.collections.get("lay_layAssets"), boundingBox)
else:
self.report({'WARNING'}, "Couldn't find dtt of " + obj.name)
createVisualizationObject(obj, obj.name + "-BoundingBox", obj.users_collection[0])
return {'FINISHED'}
def menu_func_import(self, context):
+2 -3
View File
@@ -7,7 +7,6 @@ from bpy_extras.io_utils import ImportHelper
from ...col.exporter.col_ui_manager import enableCollisionTools
from ...utils.visibilitySwitcher import enableVisibilitySelector
from ...utils.util import setExportFieldsFromImportFile
from ...consts import ADDON_NAME
def importDat(only_extract, filepath):
@@ -49,7 +48,7 @@ def importDat(only_extract, filepath):
lay_filepath = os.path.join(extract_dir, tailless_tail + '.dat', 'Layout.lay')
if os.path.isfile(lay_filepath):
from ...lay.importer import lay_importer
lay_importer.main(lay_filepath, ADDON_NAME)
lay_importer.main(lay_filepath)
return {'FINISHED'}
@@ -121,7 +120,7 @@ class ImportNierDat(bpy.types.Operator, ImportHelper):
lay_filepath = os.path.join(extract_dir, tailless_tail + '.dat', 'Layout.lay')
if os.path.isfile(lay_filepath):
from ...lay.importer import lay_importer
lay_importer.main(lay_filepath, ADDON_NAME)
lay_importer.main(lay_filepath)
return {'FINISHED'}
+1 -2
View File
@@ -3,7 +3,6 @@ from bpy.props import StringProperty
from bpy_extras.io_utils import ImportHelper
from ...utils.util import setExportFieldsFromImportFile
from ...consts import ADDON_NAME
class ImportNierLay(bpy.types.Operator, ImportHelper):
@@ -19,4 +18,4 @@ class ImportNierLay(bpy.types.Operator, ImportHelper):
setExportFieldsFromImportFile(self.filepath)
return lay_importer.main(self.filepath, ADDON_NAME)
return lay_importer.main(self.filepath)
+65 -15
View File
@@ -1,11 +1,15 @@
from __future__ import annotations
import math
from time import time
from ...utils.ioUtils import to_string, read_float, read_uint32
from .lay import Lay
from ...utils.util import *
def main(layFilePath, addonName):
def main(layFilePath):
t1 = time()
with open(layFilePath, "rb") as layFile:
print("Parsing Lay file...", layFilePath)
lay = Lay(layFile)
@@ -42,18 +46,18 @@ def main(layFilePath, addonName):
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 = createLayObject(assetName, layAssetsCollection, assetRootNode, asset.position, asset.rotation, asset.scale)
assetObj["unknownIndex"] = asset.unknownIndex
assetObj["null1"] = asset.null1
for instance in asset.instances:
instanceName = assetName + "-Instance"
createLayObject(instanceName, layInstancesCollection, instanceRootNode, instance.position, instance.rotation, instance.scale, boundingBox)
createLayObject(instanceName, layInstancesCollection, instanceRootNode, instance.position, instance.rotation, instance.scale)
print('Importing finished. ;)')
tD = time() - t1
print(f"Importing finished in {tD:.1}s ;)")
return {'FINISHED'}
def createLayObject(name, collection, parent, pos, rot, scale, boundingBox):
def createLayObject(name, collection, parent, pos, rot, scale):
obj = bpy.data.objects.new(name, None)
collection.objects.link(obj)
obj.parent = parent
@@ -66,18 +70,24 @@ def createLayObject(name, collection, parent, pos, rot, scale, boundingBox):
obj.show_axis = True
if boundingBox is not None:
createBoundingBoxObject(obj, name + "-BoundingBox", collection, boundingBox)
createVisualizationObject(obj, name + "-BoundingBox", collection)
return obj
def createBoundingBoxObject(obj: bpy.types.Object, name, collection, boundingBox):
boundingBoxObj = bpy.data.objects.new(name, None)
collection.objects.link(boundingBoxObj)
def createVisualizationObject(obj: bpy.types.Object, name, collection):
for child in obj.children:
bpy.data.objects.remove(child, do_unlink=True)
linkAssetModel(obj, obj.name[:6], collection) or createBoundingBoxObject(obj, name, collection)
def createBoundingBoxObject(obj: bpy.types.Object, name, collection):
boundingBox = getModelBoundingBox(obj.name[:6])
if boundingBox is None:
return None
boundingBoxObj = bpy.data.objects.new(name, None)
collection.objects.link(boundingBoxObj)
boundingBoxObj.parent = obj
boundingBoxObj.empty_display_type = 'CUBE'
@@ -86,7 +96,7 @@ def createBoundingBoxObject(obj: bpy.types.Object, name, collection, boundingBox
boundingBoxObj.hide_select = True
def searchDirForModel(dir: str, modelName: str, depth = 0) -> str:
def searchDirForModel(dir: str, modelName: str, depth = 0) -> str | None:
if depth > 0 and dir.endswith("nier2blender_extracted") or depth > 6:
return None
@@ -101,7 +111,7 @@ def searchDirForModel(dir: str, modelName: str, depth = 0) -> str:
return None
def getModelBoundingBox(modelName, addonName):
def getModelBoundingBox(modelName):
searchDirs = getPreferences().assetDirs
searchDirs = [dir.directory for dir in searchDirs]
@@ -119,7 +129,7 @@ def getModelBoundingBox(modelName, addonName):
return None
with open(filePath, "rb") as modelDTTFile:
id = modelDTTFile.read(4)
modelDTTFile.read(4) # id
numFiles = read_uint32(modelDTTFile)
fileOffsetsOffset = read_uint32(modelDTTFile)
fileExtensionsOffset = read_uint32(modelDTTFile)
@@ -137,5 +147,45 @@ def getModelBoundingBox(modelName, addonName):
for i, ext in enumerate(fileExtensions):
if ext == "wmb":
modelDTTFile.seek(fileOffsets[i] + 16)
boundingBox = [read_float(modelDTTFile) for val in range(6)]
boundingBox = [read_float(modelDTTFile) for _ in range(6)]
return boundingBox
def linkAssetModel(obj: bpy.types.Object, modelName: str, collection: bpy.types.Collection) -> bpy.types.Object | None:
global linked
linkedObjName = f"{modelName}-linked-model"
# link file for first object
if modelName not in bpy.data.collections:
searchDirs = getPreferences().assetBlendDirs
searchDirs = [dir.directory for dir in searchDirs]
filePath = ""
for pathName in searchDirs:
if not os.path.isdir(pathName):
continue
modelPath = searchDirForModel(pathName, modelName + ".blend")
if modelPath is not None:
filePath = modelPath
break
if not filePath:
return False
if f"{modelName}.blend" not in bpy.data.libraries:
with bpy.data.libraries.load(filepath = filePath, link = True, relative = True) as (data_from, data_to):
data_to.collections = [modelName]
if modelName in bpy.data.objects and bpy.data.objects[modelName].instance_type == "COLLECTION":
bpy.data.objects.remove(bpy.data.objects[modelName], do_unlink=True)
# create instance object
newObj = bpy.data.objects.new(linkedObjName, None)
newObj.instance_type = "COLLECTION"
newObj.instance_collection = bpy.data.collections[modelName]
collection.objects.link(newObj)
newObj.parent = obj
newObj.name = linkedObjName
newObj.rotation_euler[0] = -math.radians(90)
newObj.hide_select = True
return newObj
+37 -4
View File
@@ -16,9 +16,17 @@ class SelectDirectory(bpy.types.Operator, ImportHelper):
filename_ext = ""
dirpath : bpy.props.StringProperty(name = "", description="Choose directory:", subtype='DIR_PATH')
settingsType: bpy.props.IntProperty()
def execute(self, context):
directory = os.path.dirname(self.filepath)
newDir = getPreferences().assetDirs.add()
if self.settingsType == 0:
newDir = getPreferences().assetDirs.add()
elif self.settingsType == 1:
newDir = getPreferences().assetBlendDirs.add()
else:
print("Invalid settingsType")
return {'CANCELLED'}
newDir.directory = directory
return {'FINISHED'}
@@ -28,10 +36,17 @@ class RemoveDirectory(bpy.types.Operator):
bl_idname = "n2b.remove_asset_dir"
bl_label = "Remove Directory"
settingsType: bpy.props.IntProperty()
index : bpy.props.IntProperty()
def execute(self, context):
getPreferences().assetDirs.remove(self.index)
if self.settingsType == 0:
getPreferences().assetDirs.remove(self.index)
elif self.settingsType == 1:
getPreferences().assetBlendDirs.remove(self.index)
else:
print("Invalid settingsType")
return {'CANCELLED'}
return {'FINISHED'}
ArmatureDisplayTypeEnum = [
@@ -46,6 +61,7 @@ ArmatureDisplayTypeEnum = [
class N2B2NPreferences(bpy.types.AddonPreferences):
bl_idname = ADDON_NAME
assetDirs: bpy.props.CollectionProperty(type=DirectoryProperty)
assetBlendDirs: bpy.props.CollectionProperty(type=DirectoryProperty)
armatureDefaultDisplayType: bpy.props.EnumProperty(name="Armature Display Type", items=ArmatureDisplayTypeEnum, default="DEFAULT")
armatureDefaultInFront: bpy.props.BoolProperty(name="Armature Default In Front", default=False)
@@ -61,13 +77,30 @@ class N2B2NPreferences(bpy.types.AddonPreferences):
row.label(text="In Front:")
row.prop(self, "armatureDefaultInFront", text="")
# asset dirs selection
box = layout.box()
drawMultilineLabel(context, "Assign extracted cpk directories below if you wish to enable bounding box visualization with layout import", box)
for i, assetDir in enumerate(self.assetDirs):
row = box.row(align=True)
row.prop(self.assetDirs[i], "directory", text="")
row.operator(RemoveDirectory.bl_idname, text="", icon="X").index = i
box.operator(SelectDirectory.bl_idname, text="Add Directory", icon="FILE_FOLDER")
remOp = row.operator(RemoveDirectory.bl_idname, text="", icon="X")
remOp.index = i
remOp.settingsType = 0
addOp = box.operator(SelectDirectory.bl_idname, text="Add Directory", icon="FILE_FOLDER")
addOp.settingsType = 0
# asset blend dirs selection
box = layout.box()
drawMultilineLabel(context, "Assign downloaded blend directories below if you wish to enable full model visualization with layout import", box)
for i, assetDir in enumerate(self.assetBlendDirs):
row = box.row(align=True)
row.prop(self.assetBlendDirs[i], "directory", text="")
remOp = row.operator(RemoveDirectory.bl_idname, text="", icon="X")
remOp.index = i
remOp.settingsType = 1
addOp = box.operator(SelectDirectory.bl_idname, text="Add Directory", icon="FILE_FOLDER")
addOp.settingsType = 1
def register():
bpy.utils.register_class(DirectoryProperty)