6 Commits

2 changed files with 139 additions and 11 deletions

View File

@@ -10,6 +10,8 @@ from math import sqrt
from bpy.props import BoolProperty, EnumProperty, StringProperty
from bpy.types import Operator, Menu
from .option_file_parser import MungeOptions
import os
@@ -33,28 +35,67 @@ class FillSWBFMaterialProperties(bpy.types.Operator):
slots = sum([list(ob.material_slots) for ob in bpy.context.selected_objects if ob.type == 'MESH'],[])
mats = [slot.material for slot in slots if (slot.material and slot.material.node_tree)]
mats_visited = set()
for mat in mats:
if mat.name in mats_visited or not mat.swbf_msh_mat:
continue
else:
mats_visited.add(mat.name)
mat.swbf_msh_mat.doublesided = not mat.use_backface_culling
mat.swbf_msh_mat.hardedged_transparency = (mat.blend_method == "CLIP")
mat.swbf_msh_mat.blended_transparency = (mat.blend_method == "BLEND")
# Below is all for filling the diffuse map/texture_0 fields
try:
for BSDF_node in [n for n in mat.node_tree.nodes if n.type == 'BSDF_PRINCIPLED']:
base_col = BSDF_node.inputs['Base Color']
for link in base_col.links :
link_node = link.from_node
stack = []
if link_node.type != 'TEX_IMAGE':
continue
texture_node = None
tex_name = link_node.image.filepath
print(tex_name)
current_socket = base_col
if base_col.is_linked:
stack.append(base_col.links[0].from_node)
i = tex_name.find(".tga")
while stack:
curr_node = stack.pop()
if curr_node.type == 'TEX_IMAGE':
texture_node = curr_node
break
else:
next_nodes = []
for node_input in curr_node.inputs:
for link in node_input.links:
next_nodes.append(link.from_node)
# reversing it so we go from up to down
stack += reversed(next_nodes)
if texture_node is not None:
tex_path = texture_node.image.filepath
tex_name = os.path.basename(tex_path)
i = tex_name.find('.')
# Get rid of trailing number in case one is present
if i > 0:
tex_name = tex_name[0:i+4]
tex_name = tex_name[0:i] + ".tga"
refined_tex_path = os.path.join(os.path.dirname(tex_path), tex_name)
mat.swbf_msh_mat.diffuse_map = refined_tex_path
mat.swbf_msh_mat.texture_0 = refined_tex_path
mat.swbf_msh_mat.rendertype = 'NORMAL_BF2'
mat.swbf_msh_mat.diffuse_map = tex_name
break
except:
# Many chances for null ref exceptions. None if user reads doc section...
@@ -118,7 +159,9 @@ to provide an exact emulation"""
texture_input_nodes = []
surface_output_nodes = []
# Op will give up if no diffuse map is present.
# Eventually more nuance will be added for different
# rtypes
diffuse_texture_path = mat_props.diffuse_map
if diffuse_texture_path and os.path.exists(diffuse_texture_path):
@@ -147,6 +190,7 @@ to provide an exact emulation"""
surface_output_nodes.append(tuple(('BSDF', bsdf)))
# Glow (adds another shader output)
if mat_props.glow:
emission = material.node_tree.nodes.new("ShaderNodeEmission")
@@ -172,11 +216,49 @@ to provide an exact emulation"""
surfaces_output = mix
# Normal/bump mapping (needs more rendertype support!)
if "NORMALMAP" in mat_props.rendertype and mat_props.normal_map and os.path.exists(mat_props.normal_map):
normalMapTexImage = material.node_tree.nodes.new('ShaderNodeTexImage')
normalMapTexImage.image = bpy.data.images.load(mat_props.normal_map)
normalMapTexImage.image.alpha_mode = 'CHANNEL_PACKED'
normalMapTexImage.image.colorspace_settings.name = 'Non-Color'
texture_input_nodes.append(normalMapTexImage)
options = MungeOptions(mat_props.normal_map + ".option")
if options.get_bool("bumpmap"):
# First we must convert the RGB data to brightness
rgb_to_bw_node = material.node_tree.nodes.new("ShaderNodeRGBToBW")
material.node_tree.links.new(rgb_to_bw_node.inputs["Color"], normalMapTexImage.outputs["Color"])
# Now create a bump map node (perhaps we could also use this with normals and just plug color into normal input?)
bumpMapNode = material.node_tree.nodes.new('ShaderNodeBump')
bumpMapNode.inputs["Distance"].default_value = options.get_float("bumpscale", default=1.0)
material.node_tree.links.new(bumpMapNode.inputs["Height"], rgb_to_bw_node.outputs["Val"])
normalsOutputNode = bumpMapNode
else:
normalMapNode = material.node_tree.nodes.new('ShaderNodeNormalMap')
material.node_tree.links.new(normalMapNode.inputs["Color"], normalMapTexImage.outputs["Color"])
normalsOutputNode = normalMapNode
material.node_tree.links.new(bsdf.inputs['Normal'], normalsOutputNode.outputs["Normal"])
output = material.node_tree.nodes.new("ShaderNodeOutputMaterial")
material.node_tree.links.new(output.inputs['Surface'], surfaces_output.outputs[0])
# Scrolling
# This approach works 90% of the time, but notably produces very incorrect results
# on mus1_bldg_world_1,2,3
# Clear all anims in all cases
if material.node_tree.animation_data:
material.node_tree.animation_data.action.fcurves.clear()

View File

@@ -0,0 +1,46 @@
""" Parses .tga.option and .msh.option files. Only used with the former as of now. """
import os
class MungeOptions:
def __init__(self, path_to_option_file):
self.options = {}
if os.path.exists(path_to_option_file):
with open(path_to_option_file, 'r') as option_file:
option_text = option_file.read()
option_parts = option_text.split()
current_parameter = ""
for part in option_parts:
if part.startswith("-"):
current_parameter = part[1:]
self.options[current_parameter] = ""
elif current_parameter:
current_value = self.options[current_parameter]
# Keep adding to value in case there are vector options
self.options[current_parameter] += part if not current_value else (" " + part)
def is_option_present(self, param):
return param in self.options
def get_bool(self, param, default=False):
return True if param in self.options else default
def get_float(self, param, default=0.0):
if param in self.options:
try:
result = float(self.options[param])
except:
result = default
finally:
return result
else:
return default
def get_string(self, param, default=""):
return self.options.get(param, default)