Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 10 additions & 8 deletions i_scene_cp77_gltf/cyber_prefs.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ class CP77IOSuitePreferences(AddonPreferences):

# Define the depotfolder path property
depotfolder_path: StringProperty(
name="MaterialDepot Path",
description="Path to the material depot folder",
name="Depot Path",
description="Path to your local Depot folder. Used to override the Depot paths contained within json files generated by WolvenKit",
subtype='DIR_PATH',
default="//MaterialDepot"
default=""
)
enable_temperance: BoolProperty(
name= "Enable the Temperance bone heuristic MAY BREAK ANIM EXPORT",
Expand Down Expand Up @@ -76,17 +76,19 @@ class CP77IOSuitePreferences(AddonPreferences):

def draw(self, context):
layout = self.layout

box = layout.box()
box.label(text="Depot Path:")
row = box.row()
row.prop(self, "depotfolder_path", text="")
row = box.row()

box = layout.box()
row = box.row()
row.prop(self, "show_modtools",toggle=1)
row.prop(self, "experimental_features",toggle=1)
row.prop(self, "non_verbose",toggle=1)
if self.experimental_features:
box = layout.box()
box.label(text="Material Depot Path:")
row = box.row()
row.prop(self, "depotfolder_path", text="")
row = box.row()
# Toggle for temperance bone heuristic
box = layout.box()
box.label(text="Enable Temperance Bone Heuristic (MAY BREAK ANIM EXPORT):")
Expand Down
201 changes: 191 additions & 10 deletions i_scene_cp77_gltf/exporters/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import bpy
import bpy.utils.previews
import sys
import re
import string
from pathlib import Path
from bpy.props import (StringProperty, BoolProperty)
from bpy.types import (Operator, Panel, TOPBAR_MT_file_export)
from bpy_extras.io_utils import ExportHelper
Expand All @@ -9,14 +12,17 @@
from .hp_export import *
from .phys_export import *
from .sectors_export import *
from .mi_export import *
from .mlsetup_export import *
from .mlmask_export import *
from .write_rig import *
from ..main.bartmoss_functions import *
from ..main.common import get_classes, show_message
from ..cyber_props import *
from ..cyber_prefs import *
from ..icons.cp77_icons import *


class CP77RigJSONExport(Operator,ExportHelper):
bl_idname = "export_scene.cp77_rig_export"
bl_label = "Export Rig Updates to JSON for Cyberpunk"
Expand Down Expand Up @@ -273,46 +279,221 @@ def execute(self, context):
class CP77HairProfileExport(Operator):
bl_idname = "export_scene.hp"
bl_label = "Export Hair Profile"
bl_description ="Generates a new .hp.json in your mod project folder which can be imported in Wolvenkit"
bl_description ="Generates a new .hp.json in your mod project folder which can be imported in WolvenKit"
bl_parent_id = "CP77_PT_MaterialTools"

filepath: StringProperty(subtype="FILE_PATH")

def execute(self, context):
active_object = context.active_object
if not active_object:
self.report({'ERROR'}, "No active object")
return {'CANCELLED'}
active_material = active_object.active_material
if not active_material:
self.report({'ERROR'}, "Active object has no material.")
return {'CANCELLED'}

cp77_hp_export(self.filepath)
return {"FINISHED"}

class CP77MaterialInstanceExport(Operator, ExportHelper):
"""Export the active material properties to a WolvenKit JSON file"""
bl_idname = "export_scene.mi"
bl_label = "Export Material"
bl_description = "Export selected material as a Material Instance (.mi) json file which can be converted in WolvenKit"

filepath: StringProperty(name="File Path",subtype='FILE_PATH')

filename_ext = ".json"

#filter_glob: StringProperty(default="*.json",options={'HIDDEN'})

def draw(self, context):
layout = self.layout
active_object = context.active_object
active_material = active_object.active_material

box = layout.box()
box.label(text="Material Data", icon='NODE_MATERIAL')

# Create a column and disable it to make everything inside read-only
col = box.column()
col.enabled = False

# Helper to display custom props if they exist
def draw_mat_prop(prop_name, label):
if prop_name in active_material:
col.prop(active_material, f'["{prop_name}"]', text=label)
else:
col.label(text=f"{label}: (Not Set)")

draw_mat_prop('MLSetup', "MLSetup")
draw_mat_prop('MultilayerMask', "MLMask")
draw_mat_prop('GlobalNormal', "Normal Map")

def invoke(self, context, event):
active_object = context.active_object
if not active_object:
self.report({'ERROR'}, "No active object")
return {'CANCELLED'}
active_material = active_object.active_material
if not active_material:
self.report({'ERROR'}, "Active object has no material.")
return {'CANCELLED'}
if str(active_material['MultilayerMask']) == "None":
self.report({'ERROR'}, "Only Multilayered-based materials are currently supported.")
return {'CANCELLED'}

default_name = active_material.name if active_material else "default"
# JATO: probably should do some safety-checks on the name cause bpy material names are wild-west
# this is just something I pulled from web search results. maybe better not to import re/string idk
invalid_chars = re.escape(string.punctuation + string.whitespace)
invalid_chars = r'[<>:"/\\|?*\n\t\r\x00-\x1F]'
safe_string = re.sub(invalid_chars, '_', default_name)
safe_string = safe_string.replace(' ', '_')
safe_string = safe_string.lower()

projpath = active_material['ProjPath']
if projpath == "":
self.filepath = f"{safe_string}.mi.json"
context.window_manager.fileselect_add(self)
return {'RUNNING_MODAL'}

basepath = str(active_material['ProjPath']) + "\\"

self.filepath = basepath + f"{safe_string}.mi.json"
context.window_manager.fileselect_add(self)
return {'RUNNING_MODAL'}

def execute(self, context):
cp77_materialinstance_export(self, context, self.filepath)
return {'FINISHED'}

class CP77MlSetupExport(Operator):
bl_idname = "export_scene.mlsetup"
bl_label = "Export MLSetup"
bl_parent_id = "CP77_PT_MaterialTools"
bl_description = "Export selected material to a mlsetup json file which can be imported in WolvenKit"
bl_description = "Export selected material to a mlsetup json file which can be converted in WolvenKit"

filepath: StringProperty(subtype="FILE_PATH")

filename_ext = ".json"

def draw(self, context):
props = context.scene.cp77_panel_props
layout = self.layout
layout.prop(props, "write_mltemplate")

def invoke(self, context, event):
try:
self.filepath = cp77_mlsetup_getpath(self, context)
context.window_manager.fileselect_add(self)
return {'RUNNING_MODAL'}
except TypeError as e:
show_message(e.args[0])
active_object = context.active_object
if not active_object:
self.report({'ERROR'}, "No active object")
return {'CANCELLED'}
except ValueError as e:
show_message(e.args[0])
active_material = active_object.active_material
if not active_material:
self.report({'ERROR'}, "Active object has no material.")
return {'CANCELLED'}
mlsetup_path = str(active_material['MLSetup'])
if mlsetup_path == "":
self.report({'ERROR'}, "Active material does not contain MLSetup path")
return {'CANCELLED'}

projpath = active_material['ProjPath']
if projpath == "":
self.filepath = f"{Path(mlsetup_path).name}.json"
context.window_manager.fileselect_add(self)
print("mlsetup filepath: ", self.filepath)
return {'RUNNING_MODAL'}

self.filepath = f"{projpath}\\{mlsetup_path}.json"
if not os.path.exists(Path(self.filepath).parent):
os.makedirs(Path(self.filepath).parent)

print("mlsetup filepath: ", self.filepath)

context.window_manager.fileselect_add(self)

return {'RUNNING_MODAL'}

def execute(self, context):
write_mltemplate = context.scene.cp77_panel_props.write_mltemplate
cp77_mlsetup_export(self, context, self.filepath, write_mltemplate)

before,mid,after=self.filepath.partition('source\\raw\\'.replace('\\',os.sep))
if after != '':
active_material = bpy.context.active_object.active_material
active_material['MLSetup'] = after[:-5] # this trims .json from name
else:
print("WolvenKit project path not detected. MLSetup path was not updated")

return {"FINISHED"}

class CP77MlMaskExport(Operator, ExportHelper):
bl_idname = "export_scene.mlmask"
bl_label = "Export MLMask"
bl_description = "Export mask images from selected material and create a masklist file which can be imported in WolvenKit"

filepath: StringProperty(subtype="FILE_PATH")

filename_ext = ".masklist"

#filter_glob: StringProperty(default="*.masklist",options={'HIDDEN'})

export_format: EnumProperty(
name="File Format",
description="Choose the format for exported images",
items=[
('PNG', "PNG", "Save as Portable Network Graphics"),
('JPEG', "JPEG", "Save as Joint Photographic Experts Group"),
('TARGA', "Targa", "Save as Targa graphic"),
('TIFF', "TIFF", "Save as Tagged Image File Format"),
],
default='PNG'
)

def invoke(self, context, event):
active_object = context.active_object
if not active_object:
self.report({'ERROR'}, "No active object")
return {'CANCELLED'}
active_material = active_object.active_material
if not active_material:
self.report({'ERROR'}, "Active object has no material.")
return {'CANCELLED'}

mlmask_path = str(active_material['MultilayerMask'])
if mlmask_path == "":
self.report({'ERROR'}, "Active material does not contain MLMask path")
return {'CANCELLED'}

# JATO: need to convert from .mlmask to .masklist
masklist_path = (mlmask_path.split(".")[0]) + ".masklist"

projpath = str(active_material['ProjPath'])
if projpath != "":
self.filepath = projpath + "\\" + masklist_path
if not os.path.exists(Path(self.filepath).parent):
os.makedirs(Path(self.filepath).parent)
else:
self.filepath = Path(masklist_path).name

context.window_manager.fileselect_add(self)

return {'RUNNING_MODAL'}

def execute(self, context):
cp77_mlmask_export(self,context, self.filepath, self.export_format)

before,mid,after=self.filepath.partition('source\\raw\\'.replace('\\',os.sep))
if after != '':
active_material = bpy.context.active_object.active_material
active_material['MultilayerMask'] = after[:-8] + "mlmask"
else:
print("WolvenKit project path not detected. MLSetup path was not updated")

return {'FINISHED'}

class CP77CollisionExport(Operator):
bl_idname = "export_scene.collisions"
bl_label = "Export Collisions to .JSON"
Expand Down
Loading