New Addon: Import Autodesk .max #105013

Closed
Sebastian Sille wants to merge 136 commits from (deleted):nrgsille-import_max into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
22 changed files with 216 additions and 806 deletions
Showing only changes of commit 97f78a5b95 - Show all commits

View File

@ -5,8 +5,8 @@
bl_info = { bl_info = {
"name": "Add Camera Rigs", "name": "Add Camera Rigs",
"author": "Wayne Dixon, Brian Raschko, Kris Wittig, Damien Picard, Flavio Perez", "author": "Wayne Dixon, Brian Raschko, Kris Wittig, Damien Picard, Flavio Perez",
"version": (1, 5, 1), "version": (1, 6, 0),
"blender": (3, 3, 0), "blender": (4, 0, 0),
"location": "View3D > Add > Camera > Dolly or Crane Rig", "location": "View3D > Add > Camera > Dolly or Crane Rig",
"description": "Adds a Camera Rig with UI", "description": "Adds a Camera Rig with UI",
"doc_url": "{BLENDER_MANUAL_URL}/addons/camera/camera_rigs.html", "doc_url": "{BLENDER_MANUAL_URL}/addons/camera/camera_rigs.html",

View File

@ -36,26 +36,35 @@ def create_dolly_bones(rig):
root = bones.new("Root") root = bones.new("Root")
root.tail = (0.0, 1.0, 0.0) root.tail = (0.0, 1.0, 0.0)
root.show_wire = True root.show_wire = True
rig.data.collections.new(name="Controls")
rig.data.collections['Controls'].assign(root)
ctrl_aim_child = bones.new("Aim_shape_rotation-MCH") ctrl_aim_child = bones.new("MCH-Aim_shape_rotation")
ctrl_aim_child.head = (0.0, 10.0, 1.7) ctrl_aim_child.head = (0.0, 10.0, 1.7)
ctrl_aim_child.tail = (0.0, 11.0, 1.7) ctrl_aim_child.tail = (0.0, 11.0, 1.7)
ctrl_aim_child.layers = tuple(i == 1 for i in range(32)) # Create bone collection and assign bone
rig.data.collections.new(name="MCH")
rig.data.collections['MCH'].assign(ctrl_aim_child)
rig.data.collections['MCH'].is_visible = False
ctrl_aim = bones.new("Aim") ctrl_aim = bones.new("Aim")
ctrl_aim.head = (0.0, 10.0, 1.7) ctrl_aim.head = (0.0, 10.0, 1.7)
ctrl_aim.tail = (0.0, 11.0, 1.7) ctrl_aim.tail = (0.0, 11.0, 1.7)
ctrl_aim.show_wire = True ctrl_aim.show_wire = True
rig.data.collections['Controls'].assign(ctrl_aim)
ctrl = bones.new("Camera") ctrl = bones.new("Camera")
ctrl.head = (0.0, 0.0, 1.7) ctrl.head = (0.0, 0.0, 1.7)
ctrl.tail = (0.0, 1.0, 1.7) ctrl.tail = (0.0, 1.0, 1.7)
ctrl.show_wire = True ctrl.show_wire = True
rig.data.collections['Controls'].assign(ctrl)
ctrl_offset = bones.new("Camera_offset") ctrl_offset = bones.new("Camera_Offset")
ctrl_offset.head = (0.0, 0.0, 1.7) ctrl_offset.head = (0.0, 0.0, 1.7)
ctrl_offset.tail = (0.0, 1.0, 1.7) ctrl_offset.tail = (0.0, 1.0, 1.7)
ctrl_offset.show_wire = True ctrl_offset.show_wire = True
rig.data.collections['Controls'].assign(ctrl_offset)
# Setup hierarchy # Setup hierarchy
ctrl.parent = root ctrl.parent = root
@ -67,7 +76,7 @@ def create_dolly_bones(rig):
bpy.ops.object.mode_set(mode='OBJECT') bpy.ops.object.mode_set(mode='OBJECT')
pose_bones = rig.pose.bones pose_bones = rig.pose.bones
# Lock the relevant scale channels of the Camera_offset bone # Lock the relevant scale channels of the Camera_offset bone
pose_bones["Camera_offset"].lock_scale = (True,) * 3 pose_bones["Camera_Offset"].lock_scale = (True,) * 3
def create_crane_bones(rig): def create_crane_bones(rig):
@ -78,42 +87,51 @@ def create_crane_bones(rig):
root = bones.new("Root") root = bones.new("Root")
root.tail = (0.0, 1.0, 0.0) root.tail = (0.0, 1.0, 0.0)
root.show_wire = True root.show_wire = True
rig.data.collections.new(name="Controls")
rig.data.collections['Controls'].assign(root)
ctrl_aim_child = bones.new("Aim_shape_rotation-MCH") ctrl_aim_child = bones.new("MCH-Aim_shape_rotation")
ctrl_aim_child.head = (0.0, 10.0, 1.7) ctrl_aim_child.head = (0.0, 10.0, 1.7)
ctrl_aim_child.tail = (0.0, 11.0, 1.7) ctrl_aim_child.tail = (0.0, 11.0, 1.7)
ctrl_aim_child.layers = tuple(i == 1 for i in range(32)) rig.data.collections.new(name="MCH")
rig.data.collections['MCH'].assign(ctrl_aim_child)
rig.data.collections['MCH'].is_visible = False
ctrl_aim = bones.new("Aim") ctrl_aim = bones.new("Aim")
ctrl_aim.head = (0.0, 10.0, 1.7) ctrl_aim.head = (0.0, 10.0, 1.7)
ctrl_aim.tail = (0.0, 11.0, 1.7) ctrl_aim.tail = (0.0, 11.0, 1.7)
ctrl_aim.show_wire = True ctrl_aim.show_wire = True
rig.data.collections['Controls'].assign(ctrl_aim)
ctrl = bones.new("Camera") ctrl = bones.new("Camera")
ctrl.head = (0.0, 1.0, 1.7) ctrl.head = (0.0, 1.0, 1.7)
ctrl.tail = (0.0, 2.0, 1.7) ctrl.tail = (0.0, 2.0, 1.7)
rig.data.collections['Controls'].assign(ctrl)
ctrl_offset = bones.new("Camera_offset") ctrl_offset = bones.new("Camera_Offset")
ctrl_offset.head = (0.0, 1.0, 1.7) ctrl_offset.head = (0.0, 1.0, 1.7)
ctrl_offset.tail = (0.0, 2.0, 1.7) ctrl_offset.tail = (0.0, 2.0, 1.7)
rig.data.collections['Controls'].assign(ctrl_offset)
arm = bones.new("Crane_arm") arm = bones.new("Crane_Arm")
arm.head = (0.0, 0.0, 1.7) arm.head = (0.0, 0.0, 1.7)
arm.tail = (0.0, 1.0, 1.7) arm.tail = (0.0, 1.0, 1.7)
rig.data.collections['Controls'].assign(arm)
height = bones.new("Crane_height") height = bones.new("Crane_Height")
height.head = (0.0, 0.0, 0.0) height.head = (0.0, 0.0, 0.0)
height.tail = (0.0, 0.0, 1.7) height.tail = (0.0, 0.0, 1.7)
rig.data.collections['Controls'].assign(height)
# Setup hierarchy # Setup hierarchy
ctrl.parent = arm ctrl.parent = arm
ctrl_offset.parent = ctrl ctrl_offset.parent = ctrl
ctrl.use_inherit_rotation = False ctrl.use_inherit_rotation = False
ctrl.use_inherit_scale = False ctrl.inherit_scale = "NONE"
ctrl.show_wire = True ctrl.show_wire = True
arm.parent = height arm.parent = height
arm.use_inherit_scale = False arm.inherit_scale = "NONE"
height.parent = root height.parent = root
ctrl_aim.parent = root ctrl_aim.parent = root
@ -124,12 +142,12 @@ def create_crane_bones(rig):
pose_bones = rig.pose.bones pose_bones = rig.pose.bones
# Lock the relevant loc, rot and scale # Lock the relevant loc, rot and scale
pose_bones["Crane_arm"].lock_rotation = (False, True, False) pose_bones["Crane_Arm"].lock_rotation = (False, True, False)
pose_bones["Crane_arm"].lock_scale = (True, False, True) pose_bones["Crane_Arm"].lock_scale = (True, False, True)
pose_bones["Crane_height"].lock_location = (True,) * 3 pose_bones["Crane_Height"].lock_location = (True,) * 3
pose_bones["Crane_height"].lock_rotation = (True,) * 3 pose_bones["Crane_Height"].lock_rotation = (True,) * 3
pose_bones["Crane_height"].lock_scale = (True, False, True) pose_bones["Crane_Height"].lock_scale = (True, False, True)
pose_bones["Camera_offset"].lock_scale = (True,) * 3 pose_bones["Camera_Offset"].lock_scale = (True,) * 3
def setup_3d_rig(rig, cam): def setup_3d_rig(rig, cam):
@ -149,20 +167,20 @@ def setup_3d_rig(rig, cam):
# Build the widgets # Build the widgets
root_widget = create_root_widget("Camera_Root") root_widget = create_root_widget("Camera_Root")
camera_widget = create_camera_widget("Camera") camera_widget = create_camera_widget("Camera")
camera_offset_widget = create_camera_offset_widget("Camera_offset") camera_offset_widget = create_camera_offset_widget("Camera_Offset")
aim_widget = create_aim_widget("Aim") aim_widget = create_aim_widget("Aim")
# Add the custom bone shapes # Add the custom bone shapes
pose_bones["Root"].custom_shape = root_widget pose_bones["Root"].custom_shape = root_widget
pose_bones["Aim"].custom_shape = aim_widget pose_bones["Aim"].custom_shape = aim_widget
pose_bones["Camera"].custom_shape = camera_widget pose_bones["Camera"].custom_shape = camera_widget
pose_bones["Camera_offset"].custom_shape = camera_offset_widget pose_bones["Camera_Offset"].custom_shape = camera_offset_widget
# Set the "Override Transform" field to the mechanism position # Set the "Override Transform" field to the mechanism position
pose_bones["Aim"].custom_shape_transform = pose_bones["Aim_shape_rotation-MCH"] pose_bones["Aim"].custom_shape_transform = pose_bones["MCH-Aim_shape_rotation"]
# Add constraints to bones # Add constraints to bones
con = pose_bones['Aim_shape_rotation-MCH'].constraints.new('COPY_ROTATION') con = pose_bones['MCH-Aim_shape_rotation'].constraints.new('COPY_ROTATION')
con.target = rig con.target = rig
con.subtarget = "Camera" con.subtarget = "Camera"
@ -189,29 +207,38 @@ def create_2d_bones(context, rig, cam):
root = bones.new("Root") root = bones.new("Root")
root.tail = Vector((0.0, 0.0, 1.0)) root.tail = Vector((0.0, 0.0, 1.0))
root.show_wire = True root.show_wire = True
rig.data.collections.new(name="Controls")
rig.data.collections['Controls'].assign(root)
ctrl = bones.new('Camera') ctrl = bones.new('Camera')
ctrl.tail = Vector((0.0, 0.0, 1.0)) ctrl.tail = Vector((0.0, 0.0, 1.0))
ctrl.show_wire = True ctrl.show_wire = True
rig.data.collections['Controls'].assign(ctrl)
left_corner = bones.new("Left_corner") left_corner = bones.new("Left_Corner")
left_corner.head = (-3, 10, -2) left_corner.head = (-3, 10, -2)
left_corner.tail = left_corner.head + Vector((0.0, 0.0, 1.0)) left_corner.tail = left_corner.head + Vector((0.0, 0.0, 1.0))
left_corner.show_wire = True left_corner.show_wire = True
rig.data.collections['Controls'].assign(left_corner)
right_corner = bones.new("Right_corner") right_corner = bones.new("Right_Corner")
right_corner.head = (3, 10, -2) right_corner.head = (3, 10, -2)
right_corner.tail = right_corner.head + Vector((0.0, 0.0, 1.0)) right_corner.tail = right_corner.head + Vector((0.0, 0.0, 1.0))
right_corner.show_wire = True right_corner.show_wire = True
rig.data.collections['Controls'].assign(right_corner)
corner_distance_x = (left_corner.head - right_corner.head).length corner_distance_x = (left_corner.head - right_corner.head).length
corner_distance_y = -left_corner.head.z corner_distance_y = -left_corner.head.z
corner_distance_z = left_corner.head.y corner_distance_z = left_corner.head.y
rig.data.collections['Controls'].assign(root)
center = bones.new("Center-MCH") center = bones.new("MCH-Center")
center.head = ((right_corner.head + left_corner.head) / 2.0) center.head = ((right_corner.head + left_corner.head) / 2.0)
center.tail = center.head + Vector((0.0, 0.0, 1.0)) center.tail = center.head + Vector((0.0, 0.0, 1.0))
center.layers = tuple(i == 1 for i in range(32)) center.show_wire = True
rig.data.collections.new(name="MCH")
rig.data.collections['MCH'].assign(center)
rig.data.collections['MCH'].is_visible = False
center.show_wire = True center.show_wire = True
# Setup hierarchy # Setup hierarchy
@ -227,7 +254,7 @@ def create_2d_bones(context, rig, cam):
bone.rotation_mode = 'XYZ' bone.rotation_mode = 'XYZ'
# Bone drivers # Bone drivers
center_drivers = pose_bones["Center-MCH"].driver_add("location") center_drivers = pose_bones["MCH-Center"].driver_add("location")
# Center X driver # Center X driver
driver = center_drivers[0].driver driver = center_drivers[0].driver
@ -238,7 +265,7 @@ def create_2d_bones(context, rig, cam):
var.name = corner var.name = corner
var.type = 'TRANSFORMS' var.type = 'TRANSFORMS'
var.targets[0].id = rig var.targets[0].id = rig
var.targets[0].bone_target = corner.capitalize() + '_corner' var.targets[0].bone_target = corner.capitalize() + '_Corner'
var.targets[0].transform_type = 'LOC_X' var.targets[0].transform_type = 'LOC_X'
var.targets[0].transform_space = 'TRANSFORM_SPACE' var.targets[0].transform_space = 'TRANSFORM_SPACE'
@ -255,7 +282,7 @@ def create_2d_bones(context, rig, cam):
var.name = '%s_%s' % (corner, direction) var.name = '%s_%s' % (corner, direction)
var.type = 'TRANSFORMS' var.type = 'TRANSFORMS'
var.targets[0].id = rig var.targets[0].id = rig
var.targets[0].bone_target = corner.capitalize() + '_corner' var.targets[0].bone_target = corner.capitalize() + '_Corner'
var.targets[0].transform_type = 'LOC_' + direction.upper() var.targets[0].transform_type = 'LOC_' + direction.upper()
var.targets[0].transform_space = 'TRANSFORM_SPACE' var.targets[0].transform_space = 'TRANSFORM_SPACE'
@ -275,31 +302,31 @@ def create_2d_bones(context, rig, cam):
var.name = corner var.name = corner
var.type = 'TRANSFORMS' var.type = 'TRANSFORMS'
var.targets[0].id = rig var.targets[0].id = rig
var.targets[0].bone_target = corner.capitalize() + '_corner' var.targets[0].bone_target = corner.capitalize() + '_Corner'
var.targets[0].transform_type = 'LOC_Z' var.targets[0].transform_type = 'LOC_Z'
var.targets[0].transform_space = 'TRANSFORM_SPACE' var.targets[0].transform_space = 'TRANSFORM_SPACE'
# Bone constraints # Bone constraints
con = pose_bones["Camera"].constraints.new('DAMPED_TRACK') con = pose_bones["Camera"].constraints.new('DAMPED_TRACK')
con.target = rig con.target = rig
con.subtarget = "Center-MCH" con.subtarget = "MCH-Center"
con.track_axis = 'TRACK_NEGATIVE_Z' con.track_axis = 'TRACK_NEGATIVE_Z'
# Build the widgets # Build the widgets
left_widget = create_corner_widget("Left_corner", reverse=True) left_widget = create_corner_widget("Left_Corner", reverse=True)
right_widget = create_corner_widget("Right_corner") right_widget = create_corner_widget("Right_Corner")
parent_widget = create_circle_widget("Root", radius=0.5) parent_widget = create_circle_widget("Root", radius=0.5)
camera_widget = create_circle_widget("Camera_2D", radius=0.3) camera_widget = create_circle_widget("Camera_2D", radius=0.3)
# Add the custom bone shapes # Add the custom bone shapes
pose_bones["Left_corner"].custom_shape = left_widget pose_bones["Left_Corner"].custom_shape = left_widget
pose_bones["Right_corner"].custom_shape = right_widget pose_bones["Right_Corner"].custom_shape = right_widget
pose_bones["Root"].custom_shape = parent_widget pose_bones["Root"].custom_shape = parent_widget
pose_bones["Camera"].custom_shape = camera_widget pose_bones["Camera"].custom_shape = camera_widget
# Lock the relevant loc, rot and scale # Lock the relevant loc, rot and scale
pose_bones["Left_corner"].lock_rotation = (True,) * 3 pose_bones["Left_Corner"].lock_rotation = (True,) * 3
pose_bones["Right_corner"].lock_rotation = (True,) * 3 pose_bones["Right_Corner"].lock_rotation = (True,) * 3
pose_bones["Camera"].lock_rotation = (True,) * 3 pose_bones["Camera"].lock_rotation = (True,) * 3
pose_bones["Camera"].lock_scale = (True,) * 3 pose_bones["Camera"].lock_scale = (True,) * 3
@ -331,10 +358,10 @@ def create_2d_bones(context, rig, cam):
var.name = 'frame_width' var.name = 'frame_width'
var.type = 'LOC_DIFF' var.type = 'LOC_DIFF'
var.targets[0].id = rig var.targets[0].id = rig
var.targets[0].bone_target = "Left_corner" var.targets[0].bone_target = "Left_Corner"
var.targets[0].transform_space = 'WORLD_SPACE' var.targets[0].transform_space = 'WORLD_SPACE'
var.targets[1].id = rig var.targets[1].id = rig
var.targets[1].bone_target = "Right_corner" var.targets[1].bone_target = "Right_Corner"
var.targets[1].transform_space = 'WORLD_SPACE' var.targets[1].transform_space = 'WORLD_SPACE'
for corner in ('left', 'right'): for corner in ('left', 'right'):
@ -342,7 +369,7 @@ def create_2d_bones(context, rig, cam):
var.name = corner + '_z' var.name = corner + '_z'
var.type = 'TRANSFORMS' var.type = 'TRANSFORMS'
var.targets[0].id = rig var.targets[0].id = rig
var.targets[0].bone_target = corner.capitalize() + '_corner' var.targets[0].bone_target = corner.capitalize() + '_Corner'
var.targets[0].transform_type = 'LOC_Z' var.targets[0].transform_type = 'LOC_Z'
var.targets[0].transform_space = 'TRANSFORM_SPACE' var.targets[0].transform_space = 'TRANSFORM_SPACE'
@ -363,7 +390,7 @@ def create_2d_bones(context, rig, cam):
var.name = corner + '_x' var.name = corner + '_x'
var.type = 'TRANSFORMS' var.type = 'TRANSFORMS'
var.targets[0].id = rig var.targets[0].id = rig
var.targets[0].bone_target = corner.capitalize() + '_corner' var.targets[0].bone_target = corner.capitalize() + '_Corner'
var.targets[0].transform_type = 'LOC_X' var.targets[0].transform_type = 'LOC_X'
var.targets[0].transform_space = 'TRANSFORM_SPACE' var.targets[0].transform_space = 'TRANSFORM_SPACE'
@ -385,7 +412,7 @@ def create_2d_bones(context, rig, cam):
var.name = '%s_%s' % (corner, direction) var.name = '%s_%s' % (corner, direction)
var.type = 'TRANSFORMS' var.type = 'TRANSFORMS'
var.targets[0].id = rig var.targets[0].id = rig
var.targets[0].bone_target = corner.capitalize() + '_corner' var.targets[0].bone_target = corner.capitalize() + '_Corner'
var.targets[0].transform_type = 'LOC_' + direction.upper() var.targets[0].transform_type = 'LOC_' + direction.upper()
var.targets[0].transform_space = 'TRANSFORM_SPACE' var.targets[0].transform_space = 'TRANSFORM_SPACE'
@ -422,7 +449,7 @@ def create_2d_bones(context, rig, cam):
var.name = '%s_%s' % (corner, direction) var.name = '%s_%s' % (corner, direction)
var.type = 'TRANSFORMS' var.type = 'TRANSFORMS'
var.targets[0].id = rig var.targets[0].id = rig
var.targets[0].bone_target = corner.capitalize() + '_corner' var.targets[0].bone_target = corner.capitalize() + '_Corner'
var.targets[0].transform_type = 'LOC_' + direction.upper() var.targets[0].transform_type = 'LOC_' + direction.upper()
var.targets[0].transform_space = 'TRANSFORM_SPACE' var.targets[0].transform_space = 'TRANSFORM_SPACE'
@ -484,7 +511,7 @@ def build_camera_rig(context, mode):
if mode == "2D": if mode == "2D":
cam.parent_bone = "Camera" cam.parent_bone = "Camera"
else: else:
cam.parent_bone = "Camera_offset" cam.parent_bone = "Camera_Offset"
# Change display to BBone: it just looks nicer # Change display to BBone: it just looks nicer
rig.data.display_type = 'BBONE' rig.data.display_type = 'BBONE'

View File

@ -63,7 +63,7 @@ class ExportUVLayout(bpy.types.Operator):
items=( items=(
('SVG', "Scalable Vector Graphic (.svg)", ('SVG', "Scalable Vector Graphic (.svg)",
"Export the UV layout to a vector SVG file"), "Export the UV layout to a vector SVG file"),
('EPS', "Encapsulate PostScript (.eps)", ('EPS', "Encapsulated PostScript (.eps)",
"Export the UV layout to a vector EPS file"), "Export the UV layout to a vector EPS file"),
('PNG', "PNG Image (.png)", ('PNG', "PNG Image (.png)",
"Export the UV layout to a bitmap image"), "Export the UV layout to a bitmap image"),

View File

@ -1651,7 +1651,7 @@ def save(operator, context, filepath="", scale_factor=1.0, use_scene_unit=False,
gradient = next((lk.from_node.color_ramp.elements for lk in ntree if lk.from_node.type == 'VALTORGB' and lk.to_node.type in bgmixer), False) gradient = next((lk.from_node.color_ramp.elements for lk in ntree if lk.from_node.type == 'VALTORGB' and lk.to_node.type in bgmixer), False)
background_color_chunk.add_variable("color", _3ds_float_color(bg_color)) background_color_chunk.add_variable("color", _3ds_float_color(bg_color))
background_chunk.add_subchunk(background_color_chunk) background_chunk.add_subchunk(background_color_chunk)
if bg_image: if bg_image and bg_image is not None:
background_image = _3ds_chunk(BITMAP) background_image = _3ds_chunk(BITMAP)
background_flag = _3ds_chunk(USE_BITMAP) background_flag = _3ds_chunk(USE_BITMAP)
background_image.add_variable("image", _3ds_string(sane_name(bg_image.name))) background_image.add_variable("image", _3ds_string(sane_name(bg_image.name)))
@ -1938,11 +1938,10 @@ def save(operator, context, filepath="", scale_factor=1.0, use_scene_unit=False,
obj_hierarchy_chunk = _3ds_chunk(OBJECT_HIERARCHY) obj_hierarchy_chunk = _3ds_chunk(OBJECT_HIERARCHY)
obj_parent_chunk = _3ds_chunk(OBJECT_PARENT) obj_parent_chunk = _3ds_chunk(OBJECT_PARENT)
obj_hierarchy_chunk.add_variable("hierarchy", _3ds_ushort(object_id[ob.name])) obj_hierarchy_chunk.add_variable("hierarchy", _3ds_ushort(object_id[ob.name]))
if ob.parent is None or (ob.parent.name not in object_id): if ob.parent is not None and (ob.parent.name in object_id):
obj_parent_chunk.add_variable("parent", _3ds_ushort(ROOT_OBJECT)) obj_parent_chunk = _3ds_chunk(OBJECT_PARENT)
else: # Get the parent ID from the object_id dict
obj_parent_chunk.add_variable("parent", _3ds_ushort(object_id[ob.parent.name])) obj_parent_chunk.add_variable("parent", _3ds_ushort(object_id[ob.parent.name]))
obj_hierarchy_chunk.add_subchunk(obj_parent_chunk) obj_hierarchy_chunk.add_subchunk(obj_parent_chunk)
object_chunk.add_subchunk(obj_hierarchy_chunk) object_chunk.add_subchunk(obj_hierarchy_chunk)
# Add light object and hierarchy chunks to object info # Add light object and hierarchy chunks to object info
@ -1976,11 +1975,10 @@ def save(operator, context, filepath="", scale_factor=1.0, use_scene_unit=False,
obj_hierarchy_chunk = _3ds_chunk(OBJECT_HIERARCHY) obj_hierarchy_chunk = _3ds_chunk(OBJECT_HIERARCHY)
obj_parent_chunk = _3ds_chunk(OBJECT_PARENT) obj_parent_chunk = _3ds_chunk(OBJECT_PARENT)
obj_hierarchy_chunk.add_variable("hierarchy", _3ds_ushort(object_id[ob.name])) obj_hierarchy_chunk.add_variable("hierarchy", _3ds_ushort(object_id[ob.name]))
if ob.parent is None or (ob.parent.name not in object_id): if ob.parent is not None and (ob.parent.name in object_id):
obj_parent_chunk.add_variable("parent", _3ds_ushort(ROOT_OBJECT)) obj_parent_chunk = _3ds_chunk(OBJECT_PARENT)
else: # Get the parent ID from the object_id dict
obj_parent_chunk.add_variable("parent", _3ds_ushort(object_id[ob.parent.name])) obj_parent_chunk.add_variable("parent", _3ds_ushort(object_id[ob.parent.name]))
obj_hierarchy_chunk.add_subchunk(obj_parent_chunk) obj_hierarchy_chunk.add_subchunk(obj_parent_chunk)
object_chunk.add_subchunk(obj_hierarchy_chunk) object_chunk.add_subchunk(obj_hierarchy_chunk)
# Add light object and hierarchy chunks to object info # Add light object and hierarchy chunks to object info

View File

@ -5,7 +5,7 @@
bl_info = { bl_info = {
'name': 'glTF 2.0 format', 'name': 'glTF 2.0 format',
'author': 'Julien Duroure, Scurest, Norbert Nopper, Urs Hanselmann, Moritz Becher, Benjamin Schmithüsen, Jim Eckerlein, and many external contributors', 'author': 'Julien Duroure, Scurest, Norbert Nopper, Urs Hanselmann, Moritz Becher, Benjamin Schmithüsen, Jim Eckerlein, and many external contributors',
"version": (4, 0, 31), "version": (4, 0, 32),
'blender': (4, 0, 0), 'blender': (4, 0, 0),
'location': 'File > Import-Export', 'location': 'File > Import-Export',
'description': 'Import-Export as glTF 2.0', 'description': 'Import-Export as glTF 2.0',
@ -326,14 +326,6 @@ class ExportGLTF2_Base(ConvertGLTF2_Base):
default='EXPORT' default='EXPORT'
) )
export_original_specular: BoolProperty(
name='Export original PBR Specular',
description=(
'Export original glTF PBR Specular, instead of Blender Principled Shader Specular'
),
default=False,
)
export_colors: BoolProperty( export_colors: BoolProperty(
name='Vertex Colors', name='Vertex Colors',
description='Export vertex colors with meshes', description='Export vertex colors with meshes',
@ -680,6 +672,15 @@ class ExportGLTF2_Base(ConvertGLTF2_Base):
default=True default=True
) )
# Keep for back compatibility, but no more used
export_original_specular: BoolProperty(
name='Export original PBR Specular',
description=(
'Export original glTF PBR Specular, instead of Blender Principled Shader Specular'
),
default=False,
)
will_save_settings: BoolProperty( will_save_settings: BoolProperty(
name='Remember Export Settings', name='Remember Export Settings',
description='Store glTF export settings in the Blender project', description='Store glTF export settings in the Blender project',
@ -801,8 +802,6 @@ class ExportGLTF2_Base(ConvertGLTF2_Base):
export_settings['gltf_attributes'] = self.export_attributes export_settings['gltf_attributes'] = self.export_attributes
export_settings['gltf_cameras'] = self.export_cameras export_settings['gltf_cameras'] = self.export_cameras
export_settings['gltf_original_specular'] = self.export_original_specular
export_settings['gltf_visible'] = self.use_visible export_settings['gltf_visible'] = self.use_visible
export_settings['gltf_renderable'] = self.use_renderable export_settings['gltf_renderable'] = self.use_renderable
@ -1125,29 +1124,6 @@ class GLTF_PT_export_data_material(bpy.types.Panel):
col.active = operator.export_image_format != "WEBP" col.active = operator.export_image_format != "WEBP"
col.prop(operator, "export_image_webp_fallback") col.prop(operator, "export_image_webp_fallback")
class GLTF_PT_export_data_original_pbr(bpy.types.Panel):
bl_space_type = 'FILE_BROWSER'
bl_region_type = 'TOOL_PROPS'
bl_label = "PBR Extensions"
bl_parent_id = "GLTF_PT_export_data_material"
bl_options = {'DEFAULT_CLOSED'}
@classmethod
def poll(cls, context):
sfile = context.space_data
operator = sfile.active_operator
return operator.bl_idname == "EXPORT_SCENE_OT_gltf"
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False # No animation.
sfile = context.space_data
operator = sfile.active_operator
layout.prop(operator, 'export_original_specular')
class GLTF_PT_export_data_lighting(bpy.types.Panel): class GLTF_PT_export_data_lighting(bpy.types.Panel):
bl_space_type = 'FILE_BROWSER' bl_space_type = 'FILE_BROWSER'
bl_region_type = 'TOOL_PROPS' bl_region_type = 'TOOL_PROPS'
@ -1864,7 +1840,6 @@ classes = (
GLTF_PT_export_data_scene, GLTF_PT_export_data_scene,
GLTF_PT_export_data_mesh, GLTF_PT_export_data_mesh,
GLTF_PT_export_data_material, GLTF_PT_export_data_material,
GLTF_PT_export_data_original_pbr,
GLTF_PT_export_data_shapekeys, GLTF_PT_export_data_shapekeys,
GLTF_PT_export_data_sk_optimize, GLTF_PT_export_data_sk_optimize,
GLTF_PT_export_data_armature, GLTF_PT_export_data_armature,

View File

@ -18,9 +18,5 @@ def create_settings_group(name):
thicknessFactor.default_value = 0.0 thicknessFactor.default_value = 0.0
gltf_node_group.nodes.new('NodeGroupOutput') gltf_node_group.nodes.new('NodeGroupOutput')
gltf_node_group_input = gltf_node_group.nodes.new('NodeGroupInput') gltf_node_group_input = gltf_node_group.nodes.new('NodeGroupInput')
specular = gltf_node_group.interface.new_socket("Specular", socket_type="NodeSocketFloat")
specular.default_value = 1.0
specularColor = gltf_node_group.interface.new_socket("Specular Color", socket_type="NodeSocketColor")
specularColor.default_value = [1.0,1.0,1.0,1.0]
gltf_node_group_input.location = -200, 0 gltf_node_group_input.location = -200, 0
return gltf_node_group return gltf_node_group

View File

@ -119,7 +119,7 @@ def __fix_json(obj):
def __should_include_json_value(key, value): def __should_include_json_value(key, value):
allowed_empty_collections = ["KHR_materials_unlit"] allowed_empty_collections = ["KHR_materials_unlit", "KHR_materials_specular"]
if value is None: if value is None:
return False return False

View File

@ -71,10 +71,6 @@ def get_socket(blender_material: bpy.types.Material, name: str, volume=False):
elif name == "Background": elif name == "Background":
type = bpy.types.ShaderNodeBackground type = bpy.types.ShaderNodeBackground
name = "Color" name = "Color"
elif name == "sheenColor":
return get_node_socket(blender_material, bpy.types.ShaderNodeBsdfSheen, "Color")
elif name == "sheenRoughness":
return get_node_socket(blender_material, bpy.types.ShaderNodeBsdfSheen, "Roughness")
else: else:
if volume is False: if volume is False:
type = bpy.types.ShaderNodeBsdfPrincipled type = bpy.types.ShaderNodeBsdfPrincipled

View File

@ -11,34 +11,43 @@ from ...material import gltf2_blender_gather_texture_info
def export_sheen(blender_material, export_settings): def export_sheen(blender_material, export_settings):
sheen_extension = {} sheen_extension = {}
sheenColor_socket = gltf2_blender_get.get_socket(blender_material, "sheenColor") sheenTint_socket = gltf2_blender_get.get_socket(blender_material, "Sheen Tint")
sheenRoughness_socket = gltf2_blender_get.get_socket(blender_material, "sheenRoughness") sheenRoughness_socket = gltf2_blender_get.get_socket(blender_material, "Sheen Roughness")
sheen_socket = gltf2_blender_get.get_socket(blender_material, "Sheen Weight")
if sheenColor_socket is None or sheenRoughness_socket is None: if sheenTint_socket is None or sheenRoughness_socket is None or sheen_socket is None:
return None, {}
if sheen_socket.is_linked is False and sheen_socket.default_value == 0.0:
return None, {} return None, {}
uvmap_infos = {} uvmap_infos = {}
sheenColor_non_linked = isinstance(sheenColor_socket, bpy.types.NodeSocket) and not sheenColor_socket.is_linked #TODOExt : What to do if sheen_socket is linked? or is not between 0 and 1?
sheenTint_non_linked = isinstance(sheenTint_socket, bpy.types.NodeSocket) and not sheenTint_socket.is_linked
sheenRoughness_non_linked = isinstance(sheenRoughness_socket, bpy.types.NodeSocket) and not sheenRoughness_socket.is_linked sheenRoughness_non_linked = isinstance(sheenRoughness_socket, bpy.types.NodeSocket) and not sheenRoughness_socket.is_linked
if sheenColor_non_linked is True:
color = sheenColor_socket.default_value[:3] use_actives_uvmaps = []
if sheenTint_non_linked is True:
color = sheenTint_socket.default_value[:3]
if color != (0.0, 0.0, 0.0): if color != (0.0, 0.0, 0.0):
sheen_extension['sheenColorFactor'] = color sheen_extension['sheenColorFactor'] = color
else: else:
# Factor # Factor
fac = gltf2_blender_get.get_factor_from_socket(sheenColor_socket, kind='RGB') fac = gltf2_blender_get.get_factor_from_socket(sheenTint_socket, kind='RGB')
if fac is None: if fac is None:
fac = [1.0, 1.0, 1.0] # Default is 0.0/0.0/0.0, so we need to set it to 1 if no factor fac = [1.0, 1.0, 1.0] # Default is 0.0/0.0/0.0, so we need to set it to 1 if no factor
if fac is not None and fac != [0.0, 0.0, 0.0]: if fac is not None and fac != [0.0, 0.0, 0.0]:
sheen_extension['sheenColorFactor'] = fac sheen_extension['sheenColorFactor'] = fac
# Texture # Texture
if gltf2_blender_get.has_image_node_from_socket(sheenColor_socket): if gltf2_blender_get.has_image_node_from_socket(sheenTint_socket):
original_sheenColor_texture, uvmap_info, _ = gltf2_blender_gather_texture_info.gather_texture_info( original_sheenColor_texture, uvmap_info, _ = gltf2_blender_gather_texture_info.gather_texture_info(
sheenColor_socket, sheenTint_socket,
(sheenColor_socket,), (sheenTint_socket,),
(), (),
export_settings, export_settings,
) )

View File

@ -4,41 +4,43 @@
import bpy import bpy
from .....io.com.gltf2_io_extensions import Extension from .....io.com.gltf2_io_extensions import Extension
from .....io.com.gltf2_io_constants import GLTF_IOR
from ....exp import gltf2_blender_get from ....exp import gltf2_blender_get
from ....com.gltf2_blender_default import BLENDER_SPECULAR, BLENDER_SPECULAR_TINT
from ...material.gltf2_blender_gather_texture_info import gather_texture_info from ...material.gltf2_blender_gather_texture_info import gather_texture_info
from ...gltf2_blender_get import image_tex_is_valid_from_socket
def export_original_specular(blender_material, export_settings): def export_specular(blender_material, export_settings):
specular_extension = {} specular_extension = {}
original_specular_socket = gltf2_blender_get.get_socket_old(blender_material, 'Specular') specular_socket = gltf2_blender_get.get_socket(blender_material, 'Specular IOR Level')
original_specularcolor_socket = gltf2_blender_get.get_socket_old(blender_material, 'Specular Color') speculartint_socket = gltf2_blender_get.get_socket(blender_material, 'Specular Tint')
if original_specular_socket is None or original_specularcolor_socket is None: if specular_socket is None or speculartint_socket is None:
return None, {} return None, {}
uvmap_infos = {} uvmap_infos = {}
specular_non_linked = isinstance(original_specular_socket, bpy.types.NodeSocket) and not original_specular_socket.is_linked specular_non_linked = isinstance(specular_socket, bpy.types.NodeSocket) and not specular_socket.is_linked
specularcolor_non_linked = isinstance(original_specularcolor_socket, bpy.types.NodeSocket) and not original_specularcolor_socket.is_linked specularcolor_non_linked = isinstance(speculartint_socket, bpy.types.NodeSocket) and not speculartint_socket.is_linked
if specular_non_linked is True: if specular_non_linked is True:
fac = original_specular_socket.default_value fac = specular_socket.default_value
if fac != 1.0: if fac != 1.0:
specular_extension['specularFactor'] = fac specular_extension['specularFactor'] = fac
if fac == 0.0:
return None, {}
else: else:
# Factor # Factor
fac = gltf2_blender_get.get_factor_from_socket(original_specular_socket, kind='VALUE') fac = gltf2_blender_get.get_factor_from_socket(specular_socket, kind='VALUE')
if fac is not None and fac != 1.0: if fac is not None and fac != 1.0:
specular_extension['specularFactor'] = fac specular_extension['specularFactor'] = fac
if fac == 0.0:
return None, {}
# Texture # Texture
if gltf2_blender_get.has_image_node_from_socket(original_specular_socket): if gltf2_blender_get.has_image_node_from_socket(specular_socket):
original_specular_texture, uvmap_info, _ = gather_texture_info( original_specular_texture, uvmap_info, _ = gather_texture_info(
original_specular_socket, specular_socket,
(original_specular_socket,), (specular_socket,),
(), (),
export_settings, export_settings,
) )
@ -46,20 +48,20 @@ def export_original_specular(blender_material, export_settings):
uvmap_infos.update({'specularTexture': uvmap_info}) uvmap_infos.update({'specularTexture': uvmap_info})
if specularcolor_non_linked is True: if specularcolor_non_linked is True:
color = original_specularcolor_socket.default_value[:3] color = speculartint_socket.default_value[:3]
if color != [1.0, 1.0, 1.0]: if color != (1.0, 1.0, 1.0):
specular_extension['specularColorFactor'] = color specular_extension['specularColorFactor'] = color
else: else:
# Factor # Factor
fac = gltf2_blender_get.get_factor_from_socket(original_specularcolor_socket, kind='RGB') fac = gltf2_blender_get.get_factor_from_socket(speculartint_socket, kind='RGB')
if fac is not None and fac != [1.0, 1.0, 1.0]: if fac is not None and fac != (1.0, 1.0, 1.0):
specular_extension['specularColorFactor'] = fac specular_extension['specularColorFactor'] = fac
# Texture # Texture
if gltf2_blender_get.has_image_node_from_socket(original_specularcolor_socket): if gltf2_blender_get.has_image_node_from_socket(speculartint_socket):
original_specularcolor_texture, uvmap_info, _ = gather_texture_info( original_specularcolor_texture, uvmap_info, _ = gather_texture_info(
original_specularcolor_socket, speculartint_socket,
(original_specularcolor_socket,), (speculartint_socket,),
(), (),
export_settings, export_settings,
) )
@ -67,103 +69,3 @@ def export_original_specular(blender_material, export_settings):
uvmap_infos.update({'specularColorTexture': uvmap_info}) uvmap_infos.update({'specularColorTexture': uvmap_info})
return Extension('KHR_materials_specular', specular_extension, False), uvmap_infos return Extension('KHR_materials_specular', specular_extension, False), uvmap_infos
def export_specular(blender_material, export_settings):
if export_settings['gltf_original_specular'] is True:
return export_original_specular(blender_material, export_settings)
specular_extension = {}
specular_ext_enabled = False
specular_socket = gltf2_blender_get.get_socket(blender_material, 'Specular IOR Level')
specular_tint_socket = gltf2_blender_get.get_socket(blender_material, 'Specular Tint')
base_color_socket = gltf2_blender_get.get_socket(blender_material, 'Base Color')
transmission_socket = gltf2_blender_get.get_socket(blender_material, 'Transmission Weight')
ior_socket = gltf2_blender_get.get_socket(blender_material, 'IOR')
if base_color_socket is None:
return None, {}
specular_not_linked = not image_tex_is_valid_from_socket(specular_socket)
specular_tint_not_linked = not image_tex_is_valid_from_socket(specular_tint_socket)
base_color_not_linked = not image_tex_is_valid_from_socket(base_color_socket)
transmission_not_linked = not image_tex_is_valid_from_socket(transmission_socket)
ior_not_linked = not image_tex_is_valid_from_socket(ior_socket)
specular = specular_socket.default_value if specular_not_linked else None
specular_tint = specular_tint_socket.default_value if specular_tint_not_linked else None
if specular_tint is not None:
specular_tint = (specular_tint[0] + specular_tint[1] + specular_tint[2]) / 3 # TODO tmp fix to avoid crash
transmission = transmission_socket.default_value if transmission_not_linked else None
ior = ior_socket.default_value if ior_not_linked else GLTF_IOR # textures not supported #TODOExt add warning?
base_color = base_color_socket.default_value[0:3]
no_texture = (transmission_not_linked and specular_not_linked and specular_tint_not_linked and
(specular_tint == 0.0 or (specular_tint != 0.0 and base_color_not_linked)))
if no_texture:
uvmap_info = {}
if specular != BLENDER_SPECULAR or specular_tint != BLENDER_SPECULAR_TINT:
import numpy as np
# See https://gist.github.com/proog128/d627c692a6bbe584d66789a5a6437a33
specular_ext_enabled = True
def normalize(c):
luminance = lambda c: 0.3 * c[0] + 0.6 * c[1] + 0.1 * c[2]
assert(len(c) == 3)
l = luminance(c)
if l == 0:
return np.array(c)
return np.array([c[0] / l, c[1] / l, c[2] / l])
f0_from_ior = ((ior - 1)/(ior + 1))**2
if f0_from_ior == 0:
specular_color = [1.0, 1.0, 1.0]
else:
tint_strength = (1 - specular_tint) + normalize(base_color) * specular_tint
specular_color = (1 - transmission) * (1 / f0_from_ior) * 0.08 * specular * tint_strength + transmission * tint_strength
specular_color = list(specular_color)
specular_extension['specularColorFactor'] = specular_color
else:
if specular_not_linked and specular == BLENDER_SPECULAR and specular_tint_not_linked and specular_tint == BLENDER_SPECULAR_TINT:
return None, {}
# Trying to identify cases where exporting a texture will not be needed
if specular_not_linked and transmission_not_linked and \
specular == 0.0 and transmission == 0.0:
specular_extension['specularColorFactor'] = [0.0, 0.0, 0.0]
return specular_extension, {}
# There will be a texture, with a complex calculation (no direct channel mapping)
sockets = (specular_socket, specular_tint_socket, base_color_socket, transmission_socket, ior_socket)
# Set primary socket having a texture
primary_socket = specular_socket
if specular_not_linked:
primary_socket = specular_tint_socket
if specular_tint_not_linked:
primary_socket = base_color_socket
if base_color_not_linked:
primary_socket = transmission_socket
specularColorTexture, uvmap_info, specularColorFactor = gather_texture_info(
primary_socket,
sockets,
(),
export_settings,
filter_type='ANY')
if specularColorTexture is None:
return None, {}
specular_ext_enabled = True
specular_extension['specularColorTexture'] = specularColorTexture
if specularColorFactor is not None:
specular_extension['specularColorFactor'] = specularColorFactor
specular_extension = Extension('KHR_materials_specular', specular_extension, False) if specular_ext_enabled else None
return specular_extension, {'specularColorTexture': uvmap_info}

View File

@ -147,7 +147,6 @@ class ExportImage:
# Unhappy path = we need to create the image self.fills describes or self.stores describes # Unhappy path = we need to create the image self.fills describes or self.stores describes
if self.numpy_calc is None: if self.numpy_calc is None:
print(">2")
return self.__encode_unhappy(export_settings), None return self.__encode_unhappy(export_settings), None
else: else:
pixels, width, height, factor = self.numpy_calc(self.stored) pixels, width, height, factor = self.numpy_calc(self.stored)

View File

@ -1,97 +0,0 @@
# SPDX-FileCopyrightText: 2018-2022 The glTF-Blender-IO authors
#
# SPDX-License-Identifier: Apache-2.0
import bpy
import numpy as np
from .gltf2_blender_image import TmpImageGuard, make_temp_image_copy, StoreImage
def specular_calculation(stored):
# See https://gist.github.com/proog128/d627c692a6bbe584d66789a5a6437a33
# Find all Blender images used
images = []
for fill in stored.values():
if isinstance(fill, StoreImage):
if fill.image not in images:
images.append(fill.image)
if not images:
# No ImageFills; use a 1x1 white pixel
pixels = np.array([1.0, 1.0, 1.0, 1.0], np.float32)
return pixels, 1, 1
width = max(image.size[0] for image in images)
height = max(image.size[1] for image in images)
buffers = {}
for identifier, image in [(ident, store.image) for (ident, store) in stored.items() if isinstance(store, StoreImage)]:
tmp_buf = np.empty(width * height * 4, np.float32)
if image.size[0] == width and image.size[1] == height:
image.pixels.foreach_get(tmp_buf)
else:
# Image is the wrong size; make a temp copy and scale it.
with TmpImageGuard() as guard:
make_temp_image_copy(guard, src_image=image)
tmp_image = guard.image
tmp_image.scale(width, height)
tmp_image.pixels.foreach_get(tmp_buf)
buffers[identifier] = np.reshape(tmp_buf, [width, height, 4])
# keep only needed channels
## scalar
for i in ['specular', 'transmission']:
if i in buffers.keys():
buffers[i] = buffers[i][:,:,stored[i + "_channel"].data]
else:
buffers[i] = np.full((width, height, 1), stored[i].data)
# Vector 3
for i in ['base_color', 'specular_tint']:
if i in buffers.keys():
if i + "_channel" not in stored.keys():
buffers[i] = buffers[i][:,:,:3]
else:
# keep only needed channel
for c in range(3):
if c != stored[i+"_channel"].data:
buffers[i][:, :, c] = 0.0
buffers[i] = buffers[i][:,:,:3]
else:
buffers[i] = np.full((width, height, 3), stored[i].data[0:3])
# TODO tmp Merge last dimension to avoid crash
buffers['specular_tint'] = np.average(buffers['specular_tint'], axis=2)
ior = stored['ior'].data
# calculation
stack3 = lambda v: np.dstack([v]*3)
def normalize(c):
luminance = lambda c: 0.3 * c[:,:,0] + 0.6 * c[:,:,1] + 0.1 * c[:,:,2]
l = luminance(c)
# TODOExt Manage all 0
return c / stack3(l)
f0_from_ior = ((ior - 1)/(ior + 1))**2
tint_strength = (1 - stack3(buffers['specular_tint'])) + normalize(buffers['base_color']) * stack3(buffers['specular_tint'])
out_buf = (1 - stack3(buffers['transmission'])) * (1 / f0_from_ior) * 0.08 * stack3(buffers['specular']) * tint_strength + stack3(buffers['transmission']) * tint_strength
# Manage values > 1.0 -> Need to apply factor
factor = None
factors = [np.amax(out_buf[:, :, i]) for i in range(3)]
if any([f > 1.0 for f in factors]):
factor = [1.0 if f < 1.0 else f for f in factors]
out_buf /= factor
out_buf = np.dstack((out_buf, np.ones((width, height)))) # Set alpha (glTF specular) to 1
out_buf = np.reshape(out_buf, (width * height * 4))
return np.float32(out_buf), width, height, [float(f) for f in factor] if factor else None

View File

@ -194,10 +194,8 @@ def __get_image_data(sockets, default_sockets, export_settings) -> ExportImage:
results = [get_tex_from_socket(socket) for socket in sockets] results = [get_tex_from_socket(socket) for socket in sockets]
# Check if we need a simple mapping or more complex calculation # Check if we need a simple mapping or more complex calculation
if any([socket.name == "Specular IOR Level" and socket.node.type == "BSDF_PRINCIPLED" for socket in sockets]): # There is currently no complex calculation for any textures
return __get_image_data_specular(sockets, results, export_settings) return __get_image_data_mapping(sockets, default_sockets, results, export_settings)
else:
return __get_image_data_mapping(sockets, default_sockets, results, export_settings)
def __get_image_data_mapping(sockets, default_sockets, results, export_settings) -> ExportImage: def __get_image_data_mapping(sockets, default_sockets, results, export_settings) -> ExportImage:
""" """
@ -240,7 +238,7 @@ def __get_image_data_mapping(sockets, default_sockets, results, export_settings)
# some sockets need channel rewriting (gltf pbr defines fixed channels for some attributes) # some sockets need channel rewriting (gltf pbr defines fixed channels for some attributes)
if socket.name == 'Metallic': if socket.name == 'Metallic':
dst_chan = Channel.B dst_chan = Channel.B
elif socket.name == 'Roughness' and socket.node.type == "BSDF_PRINCIPLED": elif socket.name == 'Roughness':
dst_chan = Channel.G dst_chan = Channel.G
elif socket.name == 'Occlusion': elif socket.name == 'Occlusion':
dst_chan = Channel.R dst_chan = Channel.R
@ -252,9 +250,9 @@ def __get_image_data_mapping(sockets, default_sockets, results, export_settings)
dst_chan = Channel.G dst_chan = Channel.G
elif socket.name == 'Thickness': # For KHR_materials_volume elif socket.name == 'Thickness': # For KHR_materials_volume
dst_chan = Channel.G dst_chan = Channel.G
elif socket.name == "Specular": # For original KHR_material_specular elif socket.name == "Specular IOR Level": # For KHR_material_specular
dst_chan = Channel.A dst_chan = Channel.A
elif socket.name == "Roughness" and socket.node.type == "BSDF_SHEEN": # For KHR_materials_sheen elif socket.name == "Sheen Roughness": # For KHR_materials_sheen
dst_chan = Channel.A dst_chan = Channel.A
if dst_chan is not None: if dst_chan is not None:
@ -288,54 +286,6 @@ def __get_image_data_mapping(sockets, default_sockets, results, export_settings)
return composed_image return composed_image
def __get_image_data_specular(sockets, results, export_settings) -> ExportImage:
"""
calculating Specular Texture, settings needed data
"""
from .extensions.gltf2_blender_texture_specular import specular_calculation
composed_image = ExportImage()
composed_image.set_calc(specular_calculation)
composed_image.store_data("ior", sockets[4].default_value, type="Data")
results = [get_tex_from_socket(socket) for socket in sockets[:-1]] #Do not retrieve IOR --> No texture allowed
mapping = {
0: "specular",
1: "specular_tint",
2: "base_color",
3: "transmission"
}
for idx, result in enumerate(results):
if get_tex_from_socket(sockets[idx]):
composed_image.store_data(mapping[idx], result.shader_node.image, type="Image")
# rudimentarily try follow the node tree to find the correct image data.
src_chan = None if idx == 2 else Channel.R
for elem in result.path:
if isinstance(elem.from_node, bpy.types.ShaderNodeSeparateColor):
src_chan = {
'Red': Channel.R,
'Green': Channel.G,
'Blue': Channel.B,
}[elem.from_socket.name]
if elem.from_socket.name == 'Alpha':
src_chan = Channel.A
# For base_color, keep all channels, as this is a Vec, not scalar
if idx != 2:
composed_image.store_data(mapping[idx] + "_channel", src_chan, type="Data")
else:
if src_chan is not None:
composed_image.store_data(mapping[idx] + "_channel", src_chan, type="Data")
else:
composed_image.store_data(mapping[idx], sockets[idx].default_value, type="Data")
return composed_image
def __is_blender_image_a_jpeg(image: bpy.types.Image) -> bool: def __is_blender_image_a_jpeg(image: bpy.types.Image) -> bool:
if image.source != 'FILE': if image.source != 'FILE':
return False return False

View File

@ -23,15 +23,14 @@ def pbr_specular_glossiness(mh):
mh.node_tree.links.new(add_node.inputs[0], glossy_node.outputs[0]) mh.node_tree.links.new(add_node.inputs[0], glossy_node.outputs[0])
mh.node_tree.links.new(add_node.inputs[1], diffuse_node.outputs[0]) mh.node_tree.links.new(add_node.inputs[1], diffuse_node.outputs[0])
emission_socket, alpha_socket, _, _ = make_output_nodes( emission_socket, alpha_socket, _ = make_output_nodes(
mh, mh,
location=(370, 250), location=(370, 250),
additional_location=None, #No additional location needed for SpecGloss additional_location=None, #No additional location needed for SpecGloss
shader_socket=add_node.outputs[0], shader_socket=add_node.outputs[0],
make_emission_socket=mh.needs_emissive(), make_emission_socket=mh.needs_emissive(),
make_alpha_socket=not mh.is_opaque(), make_alpha_socket=not mh.is_opaque(),
make_volume_socket=None, # No possible to have KHR_materials_volume with specular/glossiness make_volume_socket=None # No possible to have KHR_materials_volume with specular/glossiness
make_sheen_socket=None # No possible to have KHR_materials_volume with specular/glossiness
) )
if emission_socket: if emission_socket:

View File

@ -6,13 +6,14 @@ from ...io.com.gltf2_io import TextureInfo
from .gltf2_blender_texture import texture from .gltf2_blender_texture import texture
def sheen( mh, def sheen( mh,
location_sheenColor, location_sheenTint,
location_sheenRoughness, location_sheenRoughness,
sheenColor_socket, sheen_socket,
sheenTint_socket,
sheenRoughness_socket sheenRoughness_socket
): ):
x_sheenColor, y_sheenColor = location_sheenColor x_sheenTint, y_sheenTint = location_sheenTint
x_sheenRoughness, y_sheenRoughness = location_sheenRoughness x_sheenRoughness, y_sheenRoughness = location_sheenRoughness
try: try:
@ -20,7 +21,8 @@ def sheen( mh,
except Exception: except Exception:
return return
sheenColorFactor = ext.get('sheenColorFactor', [0.0, 0.0, 0.0]) sheen_socket.default_value = 1.0
sheenTintFactor = ext.get('sheenColorFactor', [0.0, 0.0, 0.0])
tex_info_color = ext.get('sheenColorTexture') tex_info_color = ext.get('sheenColorTexture')
if tex_info_color is not None: if tex_info_color is not None:
tex_info_color = TextureInfo.from_dict(tex_info_color) tex_info_color = TextureInfo.from_dict(tex_info_color)
@ -31,31 +33,31 @@ def sheen( mh,
tex_info_roughness = TextureInfo.from_dict(tex_info_roughness) tex_info_roughness = TextureInfo.from_dict(tex_info_roughness)
if tex_info_color is None: if tex_info_color is None:
sheenColorFactor.extend([1.0]) sheenTintFactor.extend([1.0])
sheenColor_socket.default_value = sheenColorFactor sheenTint_socket.default_value = sheenTintFactor
else: else:
# Mix sheenColor factor # Mix sheenTint factor
sheenColorFactor = sheenColorFactor + [1.0] sheenTintFactor = sheenTintFactor + [1.0]
if sheenColorFactor != [1.0, 1.0, 1.0, 1.0]: if sheenTintFactor != [1.0, 1.0, 1.0, 1.0]:
node = mh.node_tree.nodes.new('ShaderNodeMix') node = mh.node_tree.nodes.new('ShaderNodeMix')
node.label = 'sheenColor Factor' node.label = 'sheenTint Factor'
node.data_type = 'RGBA' node.data_type = 'RGBA'
node.location = x_sheenColor - 140, y_sheenColor node.location = x_sheenTint - 140, y_sheenTint
node.blend_type = 'MULTIPLY' node.blend_type = 'MULTIPLY'
# Outputs # Outputs
mh.node_tree.links.new(sheenColor_socket, node.outputs[2]) mh.node_tree.links.new(sheenTint_socket, node.outputs[2])
# Inputs # Inputs
node.inputs['Factor'].default_value = 1.0 node.inputs['Factor'].default_value = 1.0
sheenColor_socket = node.inputs[6] sheenTint_socket = node.inputs[6]
node.inputs[7].default_value = sheenColorFactor node.inputs[7].default_value = sheenTintFactor
x_sheenColor -= 200 x_sheenTint -= 200
texture( texture(
mh, mh,
tex_info=tex_info_color, tex_info=tex_info_color,
label='SHEEN COLOR', label='SHEEN COLOR',
location=(x_sheenColor, y_sheenColor), location=(x_sheenTint, y_sheenTint),
color_socket=sheenColor_socket color_socket=sheenTint_socket
) )
if tex_info_roughness is None: if tex_info_roughness is None:

View File

@ -12,13 +12,7 @@ from ..exp.material.extensions.gltf2_blender_image import TmpImageGuard, make_te
def specular(mh, location_specular, def specular(mh, location_specular,
location_specular_tint, location_specular_tint,
specular_socket, specular_socket,
specular_tint_socket, specular_tint_socket):
original_specular_socket,
original_specularcolor_socket,
location_original_specular,
location_original_specularcolor):
x_specular, y_specular = location_specular
x_tint, y_tint = location_specular_tint
if specular_socket is None: if specular_socket is None:
return return
@ -30,316 +24,22 @@ def specular(mh, location_specular,
except Exception: except Exception:
return return
import numpy as np
# Retrieve image names
try:
tex_info = mh.pymat.pbr_metallic_roughness.base_color_texture
pytexture = mh.gltf.data.textures[tex_info.index]
if mh.gltf.import_settings['import_webp_texture'] is True \
and pytexture.extensions is not None \
and "EXT_texture_webp" in pytexture.extensions:
pyimg = mh.gltf.data.images[pytexture.extensions["EXT_texture_webp"]["source"]]
base_color_image_name = pyimg.blender_image_name
else:
pyimg = mh.gltf.data.images[pytexture.source]
base_color_image_name = pyimg.blender_image_name
except:
base_color_image_name = None
# First check if we need a texture or not -> retrieve all info needed # First check if we need a texture or not -> retrieve all info needed
specular_factor = ext.get('specularFactor', 1.0) specular_factor = ext.get('specularFactor', 1.0)
tex_specular_info = ext.get('specularTexture') tex_specular_info = ext.get('specularTexture')
if tex_specular_info is not None: if tex_specular_info is not None:
tex_specular_info = TextureInfo.from_dict(tex_specular_info) tex_specular_info = TextureInfo.from_dict(tex_specular_info)
specular_color_factor = np.array(ext.get('specularColorFactor', [1.0, 1.0, 1.0])[:3]) specular_tint_factor = ext.get('specularColorFactor', [1.0, 1.0, 1.0])[:3]
tex_specular_color_info = ext.get('specularColorTexture') tex_specular_tint_info = ext.get('specularColorTexture')
if tex_specular_color_info is not None: if tex_specular_tint_info is not None:
tex_specular_color_info = TextureInfo.from_dict(tex_specular_color_info) tex_specular_tint_info = TextureInfo.from_dict(tex_specular_tint_info)
base_color_not_linked = base_color_image_name is None x_specular, y_specular = location_specular
base_color = np.array(mh.pymat.pbr_metallic_roughness.base_color_factor or [1, 1, 1]) x_specularcolor, y_specularcolor = location_specular_tint
tex_base_color = mh.pymat.pbr_metallic_roughness.base_color_texture
base_color = base_color[:3]
try:
ext_transmission = mh.pymat.extensions['KHR_materials_transmission']
transmission_factor = ext_transmission.get('transmissionFactor', 0)
tex_transmission_info = ext_transmission.get('transmissionTexture')
if tex_transmission_info is not None:
tex_transmission_info = TextureInfo.from_dict(tex_transmission_info)
pytexture = mh.gltf.data.textures[tex_transmission_info.index]
if mh.gltf.import_settings['import_webp_texture'] is True \
and pytexture.extensions is not None \
and "EXT_texture_webp" in pytexture.extensions:
pyimg = mh.gltf.data.images[pytexture.extensions["EXT_texture_webp"]["source"]]
transmission_image_name = pyimg.blender_image_name
else:
pyimg = mh.gltf.data.images[pytexture.source]
transmission_image_name = pyimg.blender_image_name
else:
transmission_image_name = None
except Exception:
transmission_factor = 0
tex_transmission_info = None
transmission_image_name = None
transmission_not_linked = transmission_image_name is None
try:
ext_ior = mh.pymat.extensions['KHR_materials_ior']
ior = ext_ior.get('ior', GLTF_IOR)
except:
ior = GLTF_IOR
use_texture = tex_specular_info is not None or tex_specular_color_info is not None \
or transmission_not_linked is False or base_color_not_linked is False
# Before creating converted textures,
# Also plug non converted data into glTF PBR Non Converted Extensions node
original_specular( mh,
specular_factor,
tex_specular_info,
specular_color_factor,
tex_specular_color_info,
original_specular_socket,
original_specularcolor_socket,
location_original_specular,
location_original_specularcolor
)
if not use_texture:
def luminance(c):
return 0.3 * c[0] + 0.6 * c[1] + 0.1 * c[2]
def normalize(c):
assert(len(c) == 3)
l = luminance(c)
if l == 0:
return c
return np.array([c[0] / l, c[1] / l, c[2] / l])
f0_from_ior = ((ior - 1)/(ior + 1))**2
lum_specular_color = luminance(specular_color_factor)
if transmission_factor != 1.0:
blender_specular = ((lum_specular_color - transmission_factor) / (1 - transmission_factor)) * (1 / 0.08) * f0_from_ior
else:
# Avoid division by 0
blender_specular = 1.0
if not all([i == 0 for i in normalize(base_color) - 1]):
blender_specular_tint = luminance((normalize(specular_color_factor) - 1) / (normalize(base_color) - 1))
if blender_specular_tint < 0 or blender_specular_tint > 1:
# TODOExt Warning clamping
blender_specular_tint = np.maximum(np.minimum(blender_specular_tint, 1), 0)
else:
blender_specular_tint = 1.0
specular_socket.default_value = blender_specular
specular_tint_socket.default_value = [blender_specular_tint]*3 + [1.0] #TODO tmp fix to avoid crash
# Note: blender_specular can be greater 1. The Blender documentation permits this.
return
else:
# Need to create a texture
# First, retrieve and create all images needed
# Base Color is already created
# Transmission is already created
# specularTexture is just created by original specular function
specular_image_name = None
try:
pytexture = mh.gltf.data.textures[tex_specular_info.index]
if mh.gltf.import_settings['import_webp_texture'] is True \
and pytexture.extensions is not None \
and "EXT_texture_webp" in pytexture.extensions:
pyimg = mh.gltf.data.images[pytexture.extensions["EXT_texture_webp"]["source"]]
specular_image_name = pyimg.blender_image_name
else:
pyimg = mh.gltf.data.images[pytexture.source]
specular_image_name = pyimg.blender_image_name
except:
specular_image_name = None
# specularColorTexture is just created by original specular function
specularcolor_image_name = None
try:
pytexture = mh.gltf.data.textures[tex_specular_color_info.index]
if mh.gltf.import_settings['import_webp_texture'] is True \
and pytexture.extensions is not None \
and "EXT_texture_webp" in pytexture.extensions:
pyimg = mh.gltf.data.images[pytexture.extensions["EXT_texture_webp"]["source"]]
specularcolor_image_name = pyimg.blender_image_name
else:
pyimg = mh.gltf.data.images[pytexture.source]
specularcolor_image_name = pyimg.blender_image_name
except:
specularcolor_image_name = None
stack3 = lambda v: np.dstack([v]*3)
texts = {
base_color_image_name : 'basecolor',
transmission_image_name : 'transmission',
specularcolor_image_name : 'speccolor',
specular_image_name: 'spec'
}
print("--")
print(base_color_image_name)
print(transmission_image_name)
print(specularcolor_image_name)
print(specular_image_name)
images = [(name, bpy.data.images[name]) for name in [base_color_image_name, transmission_image_name, specularcolor_image_name, specular_image_name] if name is not None]
width = max(image[1].size[0] for image in images)
height = max(image[1].size[1] for image in images)
buffers = {}
for name, image in images:
tmp_buf = np.empty(width * height * 4, np.float32)
if image.size[0] == width and image.size[1] == height:
image.pixels.foreach_get(tmp_buf)
else:
# Image is the wrong size; make a temp copy and scale it.
with TmpImageGuard() as guard:
make_temp_image_copy(guard, src_image=image)
tmp_image = guard.image
tmp_image.scale(width, height)
tmp_image.pixels.foreach_get(tmp_buf)
buffers[texts[name]] = np.reshape(tmp_buf, [width, height, 4])
buffers[texts[name]] = buffers[texts[name]][:,:,:3]
# Manage factors
if name == transmission_image_name:
buffers[texts[name]] = stack3(buffers[texts[name]][:,:,0]) # Transmission : keep only R channel
buffers[texts[name]] *= stack3(transmission_factor)
elif name == base_color_image_name:
buffers[texts[name]] *= base_color
elif name == specularcolor_image_name:
buffers[texts[name]] *= specular_color_factor
# Create buffer if there is no image
if 'basecolor' not in buffers.keys():
buffers['basecolor'] = np.full((width, height, 3), base_color)
if 'transmission' not in buffers.keys():
buffers['transmission'] = np.full((width, height, 3), transmission_factor)
if 'speccolor' not in buffers.keys():
buffers['speccolor'] = np.full((width, height, 3), specular_color_factor)
# Calculation
luminance = lambda c: 0.3 * c[:,:,0] + 0.6 * c[:,:,1] + 0.1 * c[:,:,2]
def normalize(c):
l = luminance(c)
if np.all(l == 0.0):
return np.array(c)
return c / stack3(l)
f0_from_ior = ((ior - 1)/(ior + 1))**2
lum_specular_color = stack3(luminance(buffers['speccolor']))
if transmission_factor != 1.0:
blender_specular = ((lum_specular_color - buffers['transmission']) / (1 - buffers['transmission'])) * (1 / 0.08) * f0_from_ior
else:
# Avoid division by 0
blender_specular = np.full((width, height, 3), 1.0)
if not np.all(normalize(buffers['basecolor']) - 1 == 0.0):
blender_specular_tint = luminance((normalize(buffers['speccolor']) - 1) / (normalize(buffers['basecolor']) - 1))
np.nan_to_num(blender_specular_tint, copy=False)
blender_specular_tint = np.clip(blender_specular_tint, 0.0, 1.0)
blender_specular_tint = stack3(blender_specular_tint)
else:
blender_specular_tint = stack3(np.ones((width, height)))
blender_specular = np.dstack((blender_specular, np.ones((width, height)))) # Set alpha to 1
blender_specular_tint = np.dstack((blender_specular_tint, np.ones((width, height)))) # Set alpha to 1
# Check if we really need to create a texture
blender_specular_tex_not_needed = np.all(np.isclose(blender_specular, blender_specular[0][0]))
blender_specular_tint_tex_not_needed = np.all(np.isclose(blender_specular_tint, blender_specular_tint[0][0]))
if blender_specular_tex_not_needed == True:
lum = lambda c: 0.3 * c[0] + 0.6 * c[1] + 0.1 * c[2]
specular_socket.default_value = lum(blender_specular[0][0][:3])
else:
blender_specular = np.reshape(blender_specular, width * height * 4)
# Create images in Blender, width and height are dummy values, then set packed file data
blender_image_spec = bpy.data.images.new('Specular', width, height)
blender_image_spec.pixels.foreach_set(np.float32(blender_specular))
blender_image_spec.pack()
# Create Textures in Blender
tex_info = tex_specular_info
if tex_info is None:
tex_info = tex_specular_color_info
if tex_info is None:
tex_info = tex_transmission_info
if tex_info is None:
tex_info = tex_base_color
texture(
mh,
tex_info=tex_info,
label='SPECULAR',
location=(x_specular, y_specular),
is_data=True,
color_socket=specular_socket,
forced_image=blender_image_spec
)
if blender_specular_tint_tex_not_needed == True:
lum = lambda c: 0.3 * c[0] + 0.6 * c[1] + 0.1 * c[2]
specular_tint_socket.default_value = [lum(blender_specular_tint[0][0])]*3 + [1.0] #TODO tmp fix to avoid crash
else:
blender_specular_tint = np.reshape(blender_specular_tint, width * height * 4)
# Create images in Blender, width and height are dummy values, then set packed file data
blender_image_tint = bpy.data.images.new('Specular Tint', width, height)
blender_image_tint.pixels.foreach_set(np.float32(blender_specular_tint))
blender_image_tint.pack()
# Create Textures in Blender
tex_info = tex_specular_color_info
if tex_info is None:
tex_info = tex_specular_info
if tex_info is None:
tex_info = tex_transmission_info
if tex_info is None:
tex_info = tex_base_color
texture(
mh,
tex_info=tex_info,
label='SPECULAR TINT',
location=(x_tint, y_tint),
is_data=True,
color_socket=specular_tint_socket,
forced_image=blender_image_tint
)
def original_specular( mh,
specular_factor,
tex_specular_info,
specular_color_factor,
tex_specular_color_info,
original_specular_socket,
original_specularcolor_socket,
location_original_specular,
location_original_specularcolor
):
x_specular, y_specular = location_original_specular
x_specularcolor, y_specularcolor = location_original_specularcolor
if tex_specular_info is None: if tex_specular_info is None:
original_specular_socket.default_value = specular_factor specular_socket.default_value = specular_factor
else: else:
# Mix specular factor # Mix specular factor
if specular_factor != 1.0: if specular_factor != 1.0:
@ -348,9 +48,9 @@ def original_specular( mh,
node.location = x_specular - 140, y_specular node.location = x_specular - 140, y_specular
node.operation = 'MULTIPLY' node.operation = 'MULTIPLY'
# Outputs # Outputs
mh.node_tree.links.new(original_specular_socket, node.outputs[0]) mh.node_tree.links.new(specular_socket, node.outputs[0])
# Inputs # Inputs
original_specular_socket = node.inputs[0] specular_socket = node.inputs[0]
node.inputs[1].default_value = specular_factor node.inputs[1].default_value = specular_factor
x_specular -= 200 x_specular -= 200
@ -361,16 +61,16 @@ def original_specular( mh,
location=(x_specular, y_specular), location=(x_specular, y_specular),
is_data=True, is_data=True,
color_socket=None, color_socket=None,
alpha_socket=original_specular_socket alpha_socket=specular_socket
) )
if tex_specular_color_info is None: if tex_specular_tint_info is None:
specular_color_factor = list(specular_color_factor) specular_tint_factor = list(specular_tint_factor)
specular_color_factor.extend([1.0]) specular_tint_factor.extend([1.0])
original_specularcolor_socket.default_value = specular_color_factor specular_tint_socket.default_value = specular_tint_factor
else: else:
specular_color_factor = list(specular_color_factor) + [1.0] specular_tint_factor = list(specular_tint_factor) + [1.0]
if specular_color_factor != [1.0, 1.0, 1.0, 1.0]: if specular_tint_factor != [1.0, 1.0, 1.0, 1.0]:
# Mix specularColorFactor # Mix specularColorFactor
node = mh.node_tree.nodes.new('ShaderNodeMix') node = mh.node_tree.nodes.new('ShaderNodeMix')
node.label = 'SpecularColor Factor' node.label = 'SpecularColor Factor'
@ -378,17 +78,17 @@ def original_specular( mh,
node.location = x_specularcolor - 140, y_specularcolor node.location = x_specularcolor - 140, y_specularcolor
node.blend_type = 'MULTIPLY' node.blend_type = 'MULTIPLY'
# Outputs # Outputs
mh.node_tree.links.new(original_specularcolor_socket, node.outputs[2]) mh.node_tree.links.new(specular_tint_socket, node.outputs[2])
# Inputs # Inputs
node.inputs['Factor'].default_value = 1.0 node.inputs['Factor'].default_value = 1.0
original_specularcolor_socket = node.inputs[6] specular_tint_socket = node.inputs[6]
node.inputs[7].default_value = specular_color_factor node.inputs[7].default_value = specular_tint_factor
x_specularcolor -= 200 x_specularcolor -= 200
texture( texture(
mh, mh,
tex_info=tex_specular_color_info, tex_info=tex_specular_tint_info,
label='SPECULAR COLOR', label='SPECULAR COLOR',
location=(x_specularcolor, y_specularcolor), location=(x_specularcolor, y_specularcolor),
color_socket=original_specularcolor_socket, color_socket=specular_tint_socket,
) )

View File

@ -25,15 +25,14 @@ def unlit(mh):
mh.node_tree.links.new(mix_node.inputs[1], transparent_node.outputs[0]) mh.node_tree.links.new(mix_node.inputs[1], transparent_node.outputs[0])
mh.node_tree.links.new(mix_node.inputs[2], emission_node.outputs[0]) mh.node_tree.links.new(mix_node.inputs[2], emission_node.outputs[0])
_emission_socket, alpha_socket, _, _ = make_output_nodes( _emission_socket, alpha_socket, _ = make_output_nodes(
mh, mh,
location=(420, 280) if mh.is_opaque() else (150, 130), location=(420, 280) if mh.is_opaque() else (150, 130),
additional_location=None, #No additional location needed for Unlit additional_location=None, #No additional location needed for Unlit
shader_socket=mix_node.outputs[0], shader_socket=mix_node.outputs[0],
make_emission_socket=False, make_emission_socket=False,
make_alpha_socket=not mh.is_opaque(), make_alpha_socket=not mh.is_opaque(),
make_volume_socket=None, # Not possible to have KHR_materials_volume with unlit make_volume_socket=None # Not possible to have KHR_materials_volume with unlit
make_sheen_socket=None #Not possible to have KHR_materials_sheen with unlit
) )
base_color( base_color(

View File

@ -50,7 +50,10 @@ def pbr_metallic_roughness(mh: MaterialHelper):
# This value may be overridden later if IOR extension is set on file # This value may be overridden later if IOR extension is set on file
pbr_node.inputs['IOR'].default_value = GLTF_IOR pbr_node.inputs['IOR'].default_value = GLTF_IOR
if mh.pymat.occlusion_texture is not None or (mh.pymat.extensions and 'KHR_materials_specular' in mh.pymat.extensions): pbr_node.inputs['Specular IOR Level'].default_value = 0.0 # Will be overridden by KHR_materials_specular if set
pbr_node.inputs['Specular Tint'].default_value = [0.0]*3 + [1.0] # Will be overridden by KHR_materials_specular if set
if mh.pymat.occlusion_texture is not None:
if mh.settings_node is None: if mh.settings_node is None:
mh.settings_node = make_settings_node(mh) mh.settings_node = make_settings_node(mh)
mh.settings_node.location = additional_location mh.settings_node.location = additional_location
@ -69,28 +72,19 @@ def pbr_metallic_roughness(mh: MaterialHelper):
mh.settings_node = make_settings_node(mh) mh.settings_node = make_settings_node(mh)
mh.settings_node.location = additional_location mh.settings_node.location = additional_location
mh.settings_node.width = 180 mh.settings_node.width = 180
volume_location = additional_location
additional_location = additional_location[0], additional_location[1] - 150 additional_location = additional_location[0], additional_location[1] - 150
need_sheen_node = False _, _, volume_socket = make_output_nodes(
if mh.pymat.extensions and 'KHR_materials_sheen' in mh.pymat.extensions:
need_sheen_node = True
_, _, volume_socket, sheen_node = make_output_nodes(
mh, mh,
location=(250, 260), location=(250, 260),
additional_location=additional_location, additional_location=additional_location,
shader_socket=pbr_node.outputs[0], shader_socket=pbr_node.outputs[0],
make_emission_socket=False, # is managed by Principled shader node make_emission_socket=False, # is managed by Principled shader node
make_alpha_socket=False, # is managed by Principled shader node make_alpha_socket=False, # is managed by Principled shader node
make_volume_socket=need_volume_node, make_volume_socket=need_volume_node
make_sheen_socket=need_sheen_node
) )
if mh.pymat.extensions and 'KHR_materials_sheen':
pass #TOTOEXT
locs = calc_locations(mh) locs = calc_locations(mh)
emission( emission(
@ -164,21 +158,17 @@ def pbr_metallic_roughness(mh: MaterialHelper):
location_specular=locs['specularTexture'], location_specular=locs['specularTexture'],
location_specular_tint=locs['specularColorTexture'], location_specular_tint=locs['specularColorTexture'],
specular_socket=pbr_node.inputs['Specular IOR Level'], specular_socket=pbr_node.inputs['Specular IOR Level'],
specular_tint_socket=pbr_node.inputs['Specular Tint'], specular_tint_socket=pbr_node.inputs['Specular Tint']
original_specular_socket=mh.settings_node.inputs[2] if mh.settings_node else None,
original_specularcolor_socket=mh.settings_node.inputs[3] if mh.settings_node else None,
location_original_specular=locs['original_specularTexture'],
location_original_specularcolor=locs['original_specularColorTexture']
) )
if need_sheen_node: sheen(
sheen( mh,
mh, location_sheenTint=locs['sheenColorTexture'],
location_sheenColor=locs['sheenColorTexture'], location_sheenRoughness=locs['sheenRoughnessTexture'],
location_sheenRoughness=locs['sheenRoughnessTexture'], sheen_socket=pbr_node.inputs['Sheen Weight'],
sheenColor_socket=sheen_node.inputs[0], sheenTint_socket=pbr_node.inputs['Sheen Tint'],
sheenRoughness_socket=sheen_node.inputs[1] sheenRoughness_socket=pbr_node.inputs['Sheen Roughness']
) )
ior( ior(
mh, mh,
@ -219,12 +209,6 @@ def calc_locations(mh):
except: except:
sheen_ext = {} sheen_ext = {}
locs['sheenColorTexture'] = (x, y)
if 'sheenColorTexture' in sheen_ext:
y -= height
locs['sheenRoughnessTexture'] = (x, y)
if 'sheenRoughnessTexture' in sheen_ext:
y -= height
locs['base_color'] = (x, y) locs['base_color'] = (x, y)
if mh.pymat.pbr_metallic_roughness.base_color_texture is not None or mh.vertex_color: if mh.pymat.pbr_metallic_roughness.base_color_texture is not None or mh.vertex_color:
y -= height y -= height
@ -243,6 +227,12 @@ def calc_locations(mh):
locs['specularColorTexture'] = (x, y) locs['specularColorTexture'] = (x, y)
if 'specularColorTexture' in specular_ext: if 'specularColorTexture' in specular_ext:
y -= height y -= height
locs['sheenRoughnessTexture'] = (x, y)
if 'sheenRoughnessTexture' in sheen_ext:
y -= height
locs['sheenColorTexture'] = (x, y)
if 'sheenColorTexture' in sheen_ext:
y -= height
locs['clearcoat'] = (x, y) locs['clearcoat'] = (x, y)
if 'clearcoatTexture' in clearcoat_ext: if 'clearcoatTexture' in clearcoat_ext:
y -= height y -= height
@ -261,19 +251,6 @@ def calc_locations(mh):
locs['volume_thickness'] = (x, y) locs['volume_thickness'] = (x, y)
if 'thicknessTexture' in volume_ext: if 'thicknessTexture' in volume_ext:
y -= height y -= height
locs['original_specularTexture'] = (x, y)
if 'specularTexture' in specular_ext:
y -= height
locs['original_specularColorTexture'] = (x, y)
if 'specularColorTexture' in specular_ext:
y -= height
locs['original_sheenColorTexture'] = (x, y)
if 'sheenColorTexture' in sheen_ext:
y -= height
locs['original_sheenRoughnessTexture'] = (x, y)
if 'sheenRoughnessTexture' in sheen_ext:
y -= height
# Center things # Center things
total_height = -y total_height = -y
@ -306,6 +283,11 @@ def emission(mh: MaterialHelper, location, color_socket, strength_socket):
return return
if mh.pymat.emissive_texture is None: if mh.pymat.emissive_texture is None:
if emissive_factor == [0, 0, 0]:
# Keep as close as possible to the default Blender value when there is no emission
color_socket.default_value = [1,1,1,1]
strength_socket.default_value = 0
return
color_socket.default_value = emissive_factor + [1] color_socket.default_value = emissive_factor + [1]
strength_socket.default_value = strength strength_socket.default_value = strength
return return
@ -624,12 +606,11 @@ def make_output_nodes(
shader_socket, shader_socket,
make_emission_socket, make_emission_socket,
make_alpha_socket, make_alpha_socket,
make_volume_socket, make_volume_socket
make_sheen_socket,
): ):
""" """
Creates the Material Output node and connects shader_socket to it. Creates the Material Output node and connects shader_socket to it.
If requested, it can also create places to hookup the emission/alpha.sheen If requested, it can also create places to hookup the emission/alpha
in between shader_socket and the Output node too. in between shader_socket and the Output node too.
:return: a pair containing the sockets you should put emission and alpha :return: a pair containing the sockets you should put emission and alpha
@ -637,7 +618,6 @@ def make_output_nodes(
""" """
x, y = location x, y = location
emission_socket = None emission_socket = None
sheen_node = None
alpha_socket = None alpha_socket = None
# Create an Emission node and add it to the shader. # Create an Emission node and add it to the shader.
@ -666,31 +646,6 @@ def make_output_nodes(
x += 380 x += 380
y += 125 y += 125
# Create an Sheen node add add it to the shader
# Note that you can not have Emission & Sheen at the same time
if make_sheen_socket:
# Sheen
node = mh.node_tree.nodes.new("ShaderNodeBsdfSheen")
node.location = x + 50, y + 250
# Node
sheen_node = node
# Outputs
sheen_output = node.outputs[0]
# Add
node = mh.node_tree.nodes.new('ShaderNodeAddShader')
node.location = x + 250, y + 160
# Inputs
mh.node_tree.links.new(node.inputs[0], sheen_output)
mh.node_tree.links.new(node.inputs[1], shader_socket)
# Outputs
shader_socket = node.outputs[0]
x += 380
y += 125
# Mix with a Transparent BSDF. Mixing factor is the alpha value. # Mix with a Transparent BSDF. Mixing factor is the alpha value.
if make_alpha_socket: if make_alpha_socket:
# Transparent BSDF # Transparent BSDF
@ -730,7 +685,7 @@ def make_output_nodes(
volume_socket = node.outputs[0] volume_socket = node.outputs[0]
return emission_socket, alpha_socket, volume_socket, sheen_node return emission_socket, alpha_socket, volume_socket
def make_settings_node(mh): def make_settings_node(mh):

View File

@ -539,7 +539,7 @@ class NWPreviewNode(Operator, NWBase):
if not viewer_socket: if not viewer_socket:
# create viewer socket # create viewer socket
viewer_socket = node.node_tree.interface.new_socket(viewer_socket_name, in_out={'OUTPUT'}, socket_type=socket_type) viewer_socket = node.node_tree.interface.new_socket(viewer_socket_name, in_out='OUTPUT', socket_type=socket_type)
viewer_socket.NWViewerSocket = True viewer_socket.NWViewerSocket = True
return viewer_socket return viewer_socket

View File

@ -164,7 +164,7 @@ def create_path(scene):
class SVGExporterLinesetPanel(bpy.types.Panel): class SVGExporterLinesetPanel(bpy.types.Panel):
"""Creates a Panel in the Render Layers context of the properties editor""" """Creates a panel in the View Layer context of the properties editor"""
bl_idname = "RENDER_PT_SVGExporterLinesetPanel" bl_idname = "RENDER_PT_SVGExporterLinesetPanel"
bl_space_type = 'PROPERTIES' bl_space_type = 'PROPERTIES'
bl_label = "Freestyle Line Style SVG Export" bl_label = "Freestyle Line Style SVG Export"

View File

@ -46,7 +46,7 @@ PROMOTED_FEATURE_SETS = [
"name": "Cessen's Rigify Extensions", "name": "Cessen's Rigify Extensions",
"author": "Nathan Vegdahl", "author": "Nathan Vegdahl",
"description": "Collection of original legacy Rigify rigs minimally ported to the modern Rigify", "description": "Collection of original legacy Rigify rigs minimally ported to the modern Rigify",
"warning": "This feature set is maintaned at the bare minimal level", "warning": "This feature set is maintained at the bare minimal level",
"link": "https://github.com/cessen/cessen_rigify_ext", "link": "https://github.com/cessen/cessen_rigify_ext",
} }
] ]

View File

@ -163,7 +163,7 @@ def i18n_updatetranslation_blender_repo_callback(lng, settings):
class UI_OT_i18n_updatetranslation_blender_repo(Operator): class UI_OT_i18n_updatetranslation_blender_repo(Operator):
"""Update i18n data (po files) in Blneder source code repository""" """Update i18n data (po files) in Blender source code repository"""
bl_idname = "ui.i18n_updatetranslation_blender_repo" bl_idname = "ui.i18n_updatetranslation_blender_repo"
bl_label = "Update I18n Blender Repo" bl_label = "Update I18n Blender Repo"