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 = {
"name": "Add Camera Rigs",
"author": "Wayne Dixon, Brian Raschko, Kris Wittig, Damien Picard, Flavio Perez",
"version": (1, 5, 1),
"blender": (3, 3, 0),
"version": (1, 6, 0),
"blender": (4, 0, 0),
"location": "View3D > Add > Camera > Dolly or Crane Rig",
"description": "Adds a Camera Rig with UI",
"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.tail = (0.0, 1.0, 0.0)
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.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.head = (0.0, 10.0, 1.7)
ctrl_aim.tail = (0.0, 11.0, 1.7)
ctrl_aim.show_wire = True
rig.data.collections['Controls'].assign(ctrl_aim)
ctrl = bones.new("Camera")
ctrl.head = (0.0, 0.0, 1.7)
ctrl.tail = (0.0, 1.0, 1.7)
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.tail = (0.0, 1.0, 1.7)
ctrl_offset.show_wire = True
rig.data.collections['Controls'].assign(ctrl_offset)
# Setup hierarchy
ctrl.parent = root
@ -67,7 +76,7 @@ def create_dolly_bones(rig):
bpy.ops.object.mode_set(mode='OBJECT')
pose_bones = rig.pose.bones
# 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):
@ -78,42 +87,51 @@ def create_crane_bones(rig):
root = bones.new("Root")
root.tail = (0.0, 1.0, 0.0)
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.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.head = (0.0, 10.0, 1.7)
ctrl_aim.tail = (0.0, 11.0, 1.7)
ctrl_aim.show_wire = True
rig.data.collections['Controls'].assign(ctrl_aim)
ctrl = bones.new("Camera")
ctrl.head = (0.0, 1.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.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.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.tail = (0.0, 0.0, 1.7)
rig.data.collections['Controls'].assign(height)
# Setup hierarchy
ctrl.parent = arm
ctrl_offset.parent = ctrl
ctrl.use_inherit_rotation = False
ctrl.use_inherit_scale = False
ctrl.inherit_scale = "NONE"
ctrl.show_wire = True
arm.parent = height
arm.use_inherit_scale = False
arm.inherit_scale = "NONE"
height.parent = root
ctrl_aim.parent = root
@ -124,12 +142,12 @@ def create_crane_bones(rig):
pose_bones = rig.pose.bones
# Lock the relevant loc, rot and scale
pose_bones["Crane_arm"].lock_rotation = (False, True, False)
pose_bones["Crane_arm"].lock_scale = (True, False, True)
pose_bones["Crane_height"].lock_location = (True,) * 3
pose_bones["Crane_height"].lock_rotation = (True,) * 3
pose_bones["Crane_height"].lock_scale = (True, False, True)
pose_bones["Camera_offset"].lock_scale = (True,) * 3
pose_bones["Crane_Arm"].lock_rotation = (False, True, False)
pose_bones["Crane_Arm"].lock_scale = (True, False, True)
pose_bones["Crane_Height"].lock_location = (True,) * 3
pose_bones["Crane_Height"].lock_rotation = (True,) * 3
pose_bones["Crane_Height"].lock_scale = (True, False, True)
pose_bones["Camera_Offset"].lock_scale = (True,) * 3
def setup_3d_rig(rig, cam):
@ -149,20 +167,20 @@ def setup_3d_rig(rig, cam):
# Build the widgets
root_widget = create_root_widget("Camera_Root")
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")
# Add the custom bone shapes
pose_bones["Root"].custom_shape = root_widget
pose_bones["Aim"].custom_shape = aim_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
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
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.subtarget = "Camera"
@ -189,29 +207,38 @@ def create_2d_bones(context, rig, cam):
root = bones.new("Root")
root.tail = Vector((0.0, 0.0, 1.0))
root.show_wire = True
rig.data.collections.new(name="Controls")
rig.data.collections['Controls'].assign(root)
ctrl = bones.new('Camera')
ctrl.tail = Vector((0.0, 0.0, 1.0))
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.tail = left_corner.head + Vector((0.0, 0.0, 1.0))
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.tail = right_corner.head + Vector((0.0, 0.0, 1.0))
right_corner.show_wire = True
rig.data.collections['Controls'].assign(right_corner)
corner_distance_x = (left_corner.head - right_corner.head).length
corner_distance_y = -left_corner.head.z
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.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
# Setup hierarchy
@ -227,7 +254,7 @@ def create_2d_bones(context, rig, cam):
bone.rotation_mode = 'XYZ'
# Bone drivers
center_drivers = pose_bones["Center-MCH"].driver_add("location")
center_drivers = pose_bones["MCH-Center"].driver_add("location")
# Center X driver
driver = center_drivers[0].driver
@ -238,7 +265,7 @@ def create_2d_bones(context, rig, cam):
var.name = corner
var.type = 'TRANSFORMS'
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_space = 'TRANSFORM_SPACE'
@ -255,7 +282,7 @@ def create_2d_bones(context, rig, cam):
var.name = '%s_%s' % (corner, direction)
var.type = 'TRANSFORMS'
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_space = 'TRANSFORM_SPACE'
@ -275,31 +302,31 @@ def create_2d_bones(context, rig, cam):
var.name = corner
var.type = 'TRANSFORMS'
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_space = 'TRANSFORM_SPACE'
# Bone constraints
con = pose_bones["Camera"].constraints.new('DAMPED_TRACK')
con.target = rig
con.subtarget = "Center-MCH"
con.subtarget = "MCH-Center"
con.track_axis = 'TRACK_NEGATIVE_Z'
# Build the widgets
left_widget = create_corner_widget("Left_corner", reverse=True)
right_widget = create_corner_widget("Right_corner")
left_widget = create_corner_widget("Left_Corner", reverse=True)
right_widget = create_corner_widget("Right_Corner")
parent_widget = create_circle_widget("Root", radius=0.5)
camera_widget = create_circle_widget("Camera_2D", radius=0.3)
# Add the custom bone shapes
pose_bones["Left_corner"].custom_shape = left_widget
pose_bones["Right_corner"].custom_shape = right_widget
pose_bones["Left_Corner"].custom_shape = left_widget
pose_bones["Right_Corner"].custom_shape = right_widget
pose_bones["Root"].custom_shape = parent_widget
pose_bones["Camera"].custom_shape = camera_widget
# Lock the relevant loc, rot and scale
pose_bones["Left_corner"].lock_rotation = (True,) * 3
pose_bones["Right_corner"].lock_rotation = (True,) * 3
pose_bones["Left_Corner"].lock_rotation = (True,) * 3
pose_bones["Right_Corner"].lock_rotation = (True,) * 3
pose_bones["Camera"].lock_rotation = (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.type = 'LOC_DIFF'
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[1].id = rig
var.targets[1].bone_target = "Right_corner"
var.targets[1].bone_target = "Right_Corner"
var.targets[1].transform_space = 'WORLD_SPACE'
for corner in ('left', 'right'):
@ -342,7 +369,7 @@ def create_2d_bones(context, rig, cam):
var.name = corner + '_z'
var.type = 'TRANSFORMS'
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_space = 'TRANSFORM_SPACE'
@ -363,7 +390,7 @@ def create_2d_bones(context, rig, cam):
var.name = corner + '_x'
var.type = 'TRANSFORMS'
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_space = 'TRANSFORM_SPACE'
@ -385,7 +412,7 @@ def create_2d_bones(context, rig, cam):
var.name = '%s_%s' % (corner, direction)
var.type = 'TRANSFORMS'
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_space = 'TRANSFORM_SPACE'
@ -422,7 +449,7 @@ def create_2d_bones(context, rig, cam):
var.name = '%s_%s' % (corner, direction)
var.type = 'TRANSFORMS'
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_space = 'TRANSFORM_SPACE'
@ -484,7 +511,7 @@ def build_camera_rig(context, mode):
if mode == "2D":
cam.parent_bone = "Camera"
else:
cam.parent_bone = "Camera_offset"
cam.parent_bone = "Camera_Offset"
# Change display to BBone: it just looks nicer
rig.data.display_type = 'BBONE'

View File

@ -63,7 +63,7 @@ class ExportUVLayout(bpy.types.Operator):
items=(
('SVG', "Scalable Vector Graphic (.svg)",
"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"),
('PNG', "PNG Image (.png)",
"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)
background_color_chunk.add_variable("color", _3ds_float_color(bg_color))
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_flag = _3ds_chunk(USE_BITMAP)
background_image.add_variable("image", _3ds_string(sane_name(bg_image.name)))
@ -1938,9 +1938,8 @@ def save(operator, context, filepath="", scale_factor=1.0, use_scene_unit=False,
obj_hierarchy_chunk = _3ds_chunk(OBJECT_HIERARCHY)
obj_parent_chunk = _3ds_chunk(OBJECT_PARENT)
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):
obj_parent_chunk.add_variable("parent", _3ds_ushort(ROOT_OBJECT))
else: # Get the parent ID from the object_id dict
if ob.parent is not None and (ob.parent.name in object_id):
obj_parent_chunk = _3ds_chunk(OBJECT_PARENT)
obj_parent_chunk.add_variable("parent", _3ds_ushort(object_id[ob.parent.name]))
obj_hierarchy_chunk.add_subchunk(obj_parent_chunk)
object_chunk.add_subchunk(obj_hierarchy_chunk)
@ -1976,9 +1975,8 @@ def save(operator, context, filepath="", scale_factor=1.0, use_scene_unit=False,
obj_hierarchy_chunk = _3ds_chunk(OBJECT_HIERARCHY)
obj_parent_chunk = _3ds_chunk(OBJECT_PARENT)
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):
obj_parent_chunk.add_variable("parent", _3ds_ushort(ROOT_OBJECT))
else: # Get the parent ID from the object_id dict
if ob.parent is not None and (ob.parent.name in object_id):
obj_parent_chunk = _3ds_chunk(OBJECT_PARENT)
obj_parent_chunk.add_variable("parent", _3ds_ushort(object_id[ob.parent.name]))
obj_hierarchy_chunk.add_subchunk(obj_parent_chunk)
object_chunk.add_subchunk(obj_hierarchy_chunk)

View File

@ -5,7 +5,7 @@
bl_info = {
'name': 'glTF 2.0 format',
'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),
'location': 'File > Import-Export',
'description': 'Import-Export as glTF 2.0',
@ -326,14 +326,6 @@ class ExportGLTF2_Base(ConvertGLTF2_Base):
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(
name='Vertex Colors',
description='Export vertex colors with meshes',
@ -680,6 +672,15 @@ class ExportGLTF2_Base(ConvertGLTF2_Base):
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(
name='Remember Export Settings',
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_cameras'] = self.export_cameras
export_settings['gltf_original_specular'] = self.export_original_specular
export_settings['gltf_visible'] = self.use_visible
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.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):
bl_space_type = 'FILE_BROWSER'
bl_region_type = 'TOOL_PROPS'
@ -1864,7 +1840,6 @@ classes = (
GLTF_PT_export_data_scene,
GLTF_PT_export_data_mesh,
GLTF_PT_export_data_material,
GLTF_PT_export_data_original_pbr,
GLTF_PT_export_data_shapekeys,
GLTF_PT_export_data_sk_optimize,
GLTF_PT_export_data_armature,

View File

@ -18,9 +18,5 @@ def create_settings_group(name):
thicknessFactor.default_value = 0.0
gltf_node_group.nodes.new('NodeGroupOutput')
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
return gltf_node_group

View File

@ -119,7 +119,7 @@ def __fix_json(obj):
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:
return False

View File

@ -71,10 +71,6 @@ def get_socket(blender_material: bpy.types.Material, name: str, volume=False):
elif name == "Background":
type = bpy.types.ShaderNodeBackground
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:
if volume is False:
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):
sheen_extension = {}
sheenColor_socket = gltf2_blender_get.get_socket(blender_material, "sheenColor")
sheenRoughness_socket = gltf2_blender_get.get_socket(blender_material, "sheenRoughness")
sheenTint_socket = gltf2_blender_get.get_socket(blender_material, "Sheen Tint")
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, {}
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
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):
sheen_extension['sheenColorFactor'] = color
else:
# 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:
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]:
sheen_extension['sheenColorFactor'] = fac
# 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(
sheenColor_socket,
(sheenColor_socket,),
sheenTint_socket,
(sheenTint_socket,),
(),
export_settings,
)

View File

@ -4,41 +4,43 @@
import bpy
from .....io.com.gltf2_io_extensions import Extension
from .....io.com.gltf2_io_constants import GLTF_IOR
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 ...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 = {}
original_specular_socket = gltf2_blender_get.get_socket_old(blender_material, 'Specular')
original_specularcolor_socket = gltf2_blender_get.get_socket_old(blender_material, 'Specular Color')
specular_socket = gltf2_blender_get.get_socket(blender_material, 'Specular IOR Level')
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, {}
uvmap_infos = {}
specular_non_linked = isinstance(original_specular_socket, bpy.types.NodeSocket) and not original_specular_socket.is_linked
specularcolor_non_linked = isinstance(original_specularcolor_socket, bpy.types.NodeSocket) and not original_specularcolor_socket.is_linked
specular_non_linked = isinstance(specular_socket, bpy.types.NodeSocket) and not specular_socket.is_linked
specularcolor_non_linked = isinstance(speculartint_socket, bpy.types.NodeSocket) and not speculartint_socket.is_linked
if specular_non_linked is True:
fac = original_specular_socket.default_value
fac = specular_socket.default_value
if fac != 1.0:
specular_extension['specularFactor'] = fac
if fac == 0.0:
return None, {}
else:
# 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:
specular_extension['specularFactor'] = fac
if fac == 0.0:
return None, {}
# 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_socket,
(original_specular_socket,),
specular_socket,
(specular_socket,),
(),
export_settings,
)
@ -46,20 +48,20 @@ def export_original_specular(blender_material, export_settings):
uvmap_infos.update({'specularTexture': uvmap_info})
if specularcolor_non_linked is True:
color = original_specularcolor_socket.default_value[:3]
if color != [1.0, 1.0, 1.0]:
color = speculartint_socket.default_value[:3]
if color != (1.0, 1.0, 1.0):
specular_extension['specularColorFactor'] = color
else:
# Factor
fac = gltf2_blender_get.get_factor_from_socket(original_specularcolor_socket, kind='RGB')
if fac is not None and fac != [1.0, 1.0, 1.0]:
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):
specular_extension['specularColorFactor'] = fac
# 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_socket,
(original_specularcolor_socket,),
speculartint_socket,
(speculartint_socket,),
(),
export_settings,
)
@ -67,103 +69,3 @@ def export_original_specular(blender_material, export_settings):
uvmap_infos.update({'specularColorTexture': uvmap_info})
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
if self.numpy_calc is None:
print(">2")
return self.__encode_unhappy(export_settings), None
else:
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,9 +194,7 @@ def __get_image_data(sockets, default_sockets, export_settings) -> ExportImage:
results = [get_tex_from_socket(socket) for socket in sockets]
# 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]):
return __get_image_data_specular(sockets, results, export_settings)
else:
# There is currently no complex calculation for any textures
return __get_image_data_mapping(sockets, default_sockets, results, export_settings)
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)
if socket.name == 'Metallic':
dst_chan = Channel.B
elif socket.name == 'Roughness' and socket.node.type == "BSDF_PRINCIPLED":
elif socket.name == 'Roughness':
dst_chan = Channel.G
elif socket.name == 'Occlusion':
dst_chan = Channel.R
@ -252,9 +250,9 @@ def __get_image_data_mapping(sockets, default_sockets, results, export_settings)
dst_chan = Channel.G
elif socket.name == 'Thickness': # For KHR_materials_volume
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
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
if dst_chan is not None:
@ -288,54 +286,6 @@ def __get_image_data_mapping(sockets, default_sockets, results, export_settings)
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:
if image.source != 'FILE':
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[1], diffuse_node.outputs[0])
emission_socket, alpha_socket, _, _ = make_output_nodes(
emission_socket, alpha_socket, _ = make_output_nodes(
mh,
location=(370, 250),
additional_location=None, #No additional location needed for SpecGloss
shader_socket=add_node.outputs[0],
make_emission_socket=mh.needs_emissive(),
make_alpha_socket=not mh.is_opaque(),
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
make_volume_socket=None # No possible to have KHR_materials_volume with specular/glossiness
)
if emission_socket:

View File

@ -6,13 +6,14 @@ from ...io.com.gltf2_io import TextureInfo
from .gltf2_blender_texture import texture
def sheen( mh,
location_sheenColor,
location_sheenTint,
location_sheenRoughness,
sheenColor_socket,
sheen_socket,
sheenTint_socket,
sheenRoughness_socket
):
x_sheenColor, y_sheenColor = location_sheenColor
x_sheenTint, y_sheenTint = location_sheenTint
x_sheenRoughness, y_sheenRoughness = location_sheenRoughness
try:
@ -20,7 +21,8 @@ def sheen( mh,
except Exception:
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')
if tex_info_color is not None:
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)
if tex_info_color is None:
sheenColorFactor.extend([1.0])
sheenColor_socket.default_value = sheenColorFactor
sheenTintFactor.extend([1.0])
sheenTint_socket.default_value = sheenTintFactor
else:
# Mix sheenColor factor
sheenColorFactor = sheenColorFactor + [1.0]
if sheenColorFactor != [1.0, 1.0, 1.0, 1.0]:
# Mix sheenTint factor
sheenTintFactor = sheenTintFactor + [1.0]
if sheenTintFactor != [1.0, 1.0, 1.0, 1.0]:
node = mh.node_tree.nodes.new('ShaderNodeMix')
node.label = 'sheenColor Factor'
node.label = 'sheenTint Factor'
node.data_type = 'RGBA'
node.location = x_sheenColor - 140, y_sheenColor
node.location = x_sheenTint - 140, y_sheenTint
node.blend_type = 'MULTIPLY'
# Outputs
mh.node_tree.links.new(sheenColor_socket, node.outputs[2])
mh.node_tree.links.new(sheenTint_socket, node.outputs[2])
# Inputs
node.inputs['Factor'].default_value = 1.0
sheenColor_socket = node.inputs[6]
node.inputs[7].default_value = sheenColorFactor
x_sheenColor -= 200
sheenTint_socket = node.inputs[6]
node.inputs[7].default_value = sheenTintFactor
x_sheenTint -= 200
texture(
mh,
tex_info=tex_info_color,
label='SHEEN COLOR',
location=(x_sheenColor, y_sheenColor),
color_socket=sheenColor_socket
location=(x_sheenTint, y_sheenTint),
color_socket=sheenTint_socket
)
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,
location_specular_tint,
specular_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
specular_tint_socket):
if specular_socket is None:
return
@ -30,316 +24,22 @@ def specular(mh, location_specular,
except Exception:
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
specular_factor = ext.get('specularFactor', 1.0)
tex_specular_info = ext.get('specularTexture')
if tex_specular_info is not None:
tex_specular_info = TextureInfo.from_dict(tex_specular_info)
specular_color_factor = np.array(ext.get('specularColorFactor', [1.0, 1.0, 1.0])[:3])
tex_specular_color_info = ext.get('specularColorTexture')
if tex_specular_color_info is not None:
tex_specular_color_info = TextureInfo.from_dict(tex_specular_color_info)
specular_tint_factor = ext.get('specularColorFactor', [1.0, 1.0, 1.0])[:3]
tex_specular_tint_info = ext.get('specularColorTexture')
if tex_specular_tint_info is not None:
tex_specular_tint_info = TextureInfo.from_dict(tex_specular_tint_info)
base_color_not_linked = base_color_image_name is None
base_color = np.array(mh.pymat.pbr_metallic_roughness.base_color_factor or [1, 1, 1])
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
x_specular, y_specular = location_specular
x_specularcolor, y_specularcolor = location_specular_tint
if tex_specular_info is None:
original_specular_socket.default_value = specular_factor
specular_socket.default_value = specular_factor
else:
# Mix specular factor
if specular_factor != 1.0:
@ -348,9 +48,9 @@ def original_specular( mh,
node.location = x_specular - 140, y_specular
node.operation = 'MULTIPLY'
# Outputs
mh.node_tree.links.new(original_specular_socket, node.outputs[0])
mh.node_tree.links.new(specular_socket, node.outputs[0])
# Inputs
original_specular_socket = node.inputs[0]
specular_socket = node.inputs[0]
node.inputs[1].default_value = specular_factor
x_specular -= 200
@ -361,16 +61,16 @@ def original_specular( mh,
location=(x_specular, y_specular),
is_data=True,
color_socket=None,
alpha_socket=original_specular_socket
alpha_socket=specular_socket
)
if tex_specular_color_info is None:
specular_color_factor = list(specular_color_factor)
specular_color_factor.extend([1.0])
original_specularcolor_socket.default_value = specular_color_factor
if tex_specular_tint_info is None:
specular_tint_factor = list(specular_tint_factor)
specular_tint_factor.extend([1.0])
specular_tint_socket.default_value = specular_tint_factor
else:
specular_color_factor = list(specular_color_factor) + [1.0]
if specular_color_factor != [1.0, 1.0, 1.0, 1.0]:
specular_tint_factor = list(specular_tint_factor) + [1.0]
if specular_tint_factor != [1.0, 1.0, 1.0, 1.0]:
# Mix specularColorFactor
node = mh.node_tree.nodes.new('ShaderNodeMix')
node.label = 'SpecularColor Factor'
@ -378,17 +78,17 @@ def original_specular( mh,
node.location = x_specularcolor - 140, y_specularcolor
node.blend_type = 'MULTIPLY'
# Outputs
mh.node_tree.links.new(original_specularcolor_socket, node.outputs[2])
mh.node_tree.links.new(specular_tint_socket, node.outputs[2])
# Inputs
node.inputs['Factor'].default_value = 1.0
original_specularcolor_socket = node.inputs[6]
node.inputs[7].default_value = specular_color_factor
specular_tint_socket = node.inputs[6]
node.inputs[7].default_value = specular_tint_factor
x_specularcolor -= 200
texture(
mh,
tex_info=tex_specular_color_info,
tex_info=tex_specular_tint_info,
label='SPECULAR COLOR',
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[2], emission_node.outputs[0])
_emission_socket, alpha_socket, _, _ = make_output_nodes(
_emission_socket, alpha_socket, _ = make_output_nodes(
mh,
location=(420, 280) if mh.is_opaque() else (150, 130),
additional_location=None, #No additional location needed for Unlit
shader_socket=mix_node.outputs[0],
make_emission_socket=False,
make_alpha_socket=not mh.is_opaque(),
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
make_volume_socket=None # Not possible to have KHR_materials_volume with unlit
)
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
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:
mh.settings_node = make_settings_node(mh)
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.location = additional_location
mh.settings_node.width = 180
volume_location = additional_location
additional_location = additional_location[0], additional_location[1] - 150
need_sheen_node = False
if mh.pymat.extensions and 'KHR_materials_sheen' in mh.pymat.extensions:
need_sheen_node = True
_, _, volume_socket, sheen_node = make_output_nodes(
_, _, volume_socket = make_output_nodes(
mh,
location=(250, 260),
additional_location=additional_location,
shader_socket=pbr_node.outputs[0],
make_emission_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_sheen_socket=need_sheen_node
make_volume_socket=need_volume_node
)
if mh.pymat.extensions and 'KHR_materials_sheen':
pass #TOTOEXT
locs = calc_locations(mh)
emission(
@ -164,20 +158,16 @@ def pbr_metallic_roughness(mh: MaterialHelper):
location_specular=locs['specularTexture'],
location_specular_tint=locs['specularColorTexture'],
specular_socket=pbr_node.inputs['Specular IOR Level'],
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']
specular_tint_socket=pbr_node.inputs['Specular Tint']
)
if need_sheen_node:
sheen(
mh,
location_sheenColor=locs['sheenColorTexture'],
location_sheenTint=locs['sheenColorTexture'],
location_sheenRoughness=locs['sheenRoughnessTexture'],
sheenColor_socket=sheen_node.inputs[0],
sheenRoughness_socket=sheen_node.inputs[1]
sheen_socket=pbr_node.inputs['Sheen Weight'],
sheenTint_socket=pbr_node.inputs['Sheen Tint'],
sheenRoughness_socket=pbr_node.inputs['Sheen Roughness']
)
ior(
@ -219,12 +209,6 @@ def calc_locations(mh):
except:
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)
if mh.pymat.pbr_metallic_roughness.base_color_texture is not None or mh.vertex_color:
y -= height
@ -243,6 +227,12 @@ def calc_locations(mh):
locs['specularColorTexture'] = (x, y)
if 'specularColorTexture' in specular_ext:
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)
if 'clearcoatTexture' in clearcoat_ext:
y -= height
@ -261,19 +251,6 @@ def calc_locations(mh):
locs['volume_thickness'] = (x, y)
if 'thicknessTexture' in volume_ext:
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
total_height = -y
@ -306,6 +283,11 @@ def emission(mh: MaterialHelper, location, color_socket, strength_socket):
return
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]
strength_socket.default_value = strength
return
@ -624,12 +606,11 @@ def make_output_nodes(
shader_socket,
make_emission_socket,
make_alpha_socket,
make_volume_socket,
make_sheen_socket,
make_volume_socket
):
"""
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.
:return: a pair containing the sockets you should put emission and alpha
@ -637,7 +618,6 @@ def make_output_nodes(
"""
x, y = location
emission_socket = None
sheen_node = None
alpha_socket = None
# Create an Emission node and add it to the shader.
@ -666,31 +646,6 @@ def make_output_nodes(
x += 380
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.
if make_alpha_socket:
# Transparent BSDF
@ -730,7 +685,7 @@ def make_output_nodes(
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):

View File

@ -539,7 +539,7 @@ class NWPreviewNode(Operator, NWBase):
if not 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
return viewer_socket

View File

@ -164,7 +164,7 @@ def create_path(scene):
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_space_type = 'PROPERTIES'
bl_label = "Freestyle Line Style SVG Export"

View File

@ -46,7 +46,7 @@ PROMOTED_FEATURE_SETS = [
"name": "Cessen's Rigify Extensions",
"author": "Nathan Vegdahl",
"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",
}
]

View File

@ -163,7 +163,7 @@ def i18n_updatetranslation_blender_repo_callback(lng, settings):
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_label = "Update I18n Blender Repo"