glTF exporter: support KHR_materials_unlit
This commit is contained in:
parent
df0fa2341e
commit
1f043682f9
|
@ -15,7 +15,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": (1, 4, 30),
|
||||
"version": (1, 4, 31),
|
||||
'blender': (2, 90, 0),
|
||||
'location': 'File > Import-Export',
|
||||
'description': 'Import-Export as glTF 2.0',
|
||||
|
|
|
@ -21,6 +21,7 @@ from io_scene_gltf2.blender.exp import gltf2_blender_gather_texture_info, gltf2_
|
|||
from io_scene_gltf2.blender.exp import gltf2_blender_search_node_tree
|
||||
|
||||
from io_scene_gltf2.blender.exp import gltf2_blender_gather_materials_pbr_metallic_roughness
|
||||
from io_scene_gltf2.blender.exp import gltf2_blender_gather_materials_unlit
|
||||
from ..com.gltf2_blender_extras import generate_extras
|
||||
from io_scene_gltf2.blender.exp import gltf2_blender_get
|
||||
from io_scene_gltf2.io.exp.gltf2_io_user_extensions import export_user_extensions
|
||||
|
@ -28,7 +29,7 @@ from io_scene_gltf2.io.com.gltf2_io_debug import print_console
|
|||
|
||||
|
||||
@cached
|
||||
def gather_material(blender_material, mesh_double_sided, export_settings):
|
||||
def gather_material(blender_material, export_settings):
|
||||
"""
|
||||
Gather the material used by the blender primitive.
|
||||
|
||||
|
@ -39,12 +40,16 @@ def gather_material(blender_material, mesh_double_sided, export_settings):
|
|||
if not __filter_material(blender_material, export_settings):
|
||||
return None
|
||||
|
||||
mat_unlit = __gather_material_unlit(blender_material, export_settings)
|
||||
if mat_unlit is not None:
|
||||
return mat_unlit
|
||||
|
||||
orm_texture = __gather_orm_texture(blender_material, export_settings)
|
||||
|
||||
material = gltf2_io.Material(
|
||||
alpha_cutoff=__gather_alpha_cutoff(blender_material, export_settings),
|
||||
alpha_mode=__gather_alpha_mode(blender_material, export_settings),
|
||||
double_sided=__gather_double_sided(blender_material, mesh_double_sided, export_settings),
|
||||
double_sided=__gather_double_sided(blender_material, export_settings),
|
||||
emissive_factor=__gather_emissive_factor(blender_material, export_settings),
|
||||
emissive_texture=__gather_emissive_texture(blender_material, export_settings),
|
||||
extensions=__gather_extensions(blender_material, export_settings),
|
||||
|
@ -92,8 +97,8 @@ def __gather_alpha_mode(blender_material, export_settings):
|
|||
return None
|
||||
|
||||
|
||||
def __gather_double_sided(blender_material, mesh_double_sided, export_settings):
|
||||
if mesh_double_sided:
|
||||
def __gather_double_sided(blender_material, export_settings):
|
||||
if not blender_material.use_backface_culling:
|
||||
return True
|
||||
|
||||
old_double_sided_socket = gltf2_blender_get.get_socket_old(blender_material, "DoubleSided")
|
||||
|
@ -152,11 +157,6 @@ def __gather_emissive_texture(blender_material, export_settings):
|
|||
def __gather_extensions(blender_material, export_settings):
|
||||
extensions = {}
|
||||
|
||||
# KHR_materials_unlit
|
||||
|
||||
if gltf2_blender_get.get_socket(blender_material, "Background") is not None:
|
||||
extensions["KHR_materials_unlit"] = Extension("KHR_materials_unlit", {}, False)
|
||||
|
||||
# KHR_materials_clearcoat
|
||||
|
||||
clearcoat_extension = __gather_clearcoat_extension(blender_material, export_settings)
|
||||
|
@ -351,3 +351,38 @@ def __gather_transmission_extension(blender_material, export_settings):
|
|||
transmission_extension['transmissionTexture'] = combined_texture
|
||||
|
||||
return Extension('KHR_materials_transmission', transmission_extension, False)
|
||||
|
||||
|
||||
def __gather_material_unlit(blender_material, export_settings):
|
||||
gltf2_unlit = gltf2_blender_gather_materials_unlit
|
||||
|
||||
info = gltf2_unlit.detect_shadeless_material(blender_material, export_settings)
|
||||
if info is None:
|
||||
return None
|
||||
|
||||
material = gltf2_io.Material(
|
||||
alpha_cutoff=__gather_alpha_cutoff(blender_material, export_settings),
|
||||
alpha_mode=__gather_alpha_mode(blender_material, export_settings),
|
||||
double_sided=__gather_double_sided(blender_material, export_settings),
|
||||
extensions={"KHR_materials_unlit": Extension("KHR_materials_unlit", {}, required=False)},
|
||||
extras=__gather_extras(blender_material, export_settings),
|
||||
name=__gather_name(blender_material, export_settings),
|
||||
emissive_factor=None,
|
||||
emissive_texture=None,
|
||||
normal_texture=None,
|
||||
occlusion_texture=None,
|
||||
|
||||
pbr_metallic_roughness=gltf2_io.MaterialPBRMetallicRoughness(
|
||||
base_color_factor=gltf2_unlit.gather_base_color_factor(info, export_settings),
|
||||
base_color_texture=gltf2_unlit.gather_base_color_texture(info, export_settings),
|
||||
metallic_factor=0.0,
|
||||
roughness_factor=0.9,
|
||||
metallic_roughness_texture=None,
|
||||
extensions=None,
|
||||
extras=None,
|
||||
)
|
||||
)
|
||||
|
||||
export_user_extensions('gather_material_unlit_hook', export_settings, material, blender_material)
|
||||
|
||||
return material
|
||||
|
|
|
@ -61,8 +61,6 @@ def __gather_base_color_factor(blender_material, export_settings):
|
|||
base_color_socket = gltf2_blender_get.get_socket(blender_material, "BaseColor")
|
||||
if base_color_socket is None:
|
||||
base_color_socket = gltf2_blender_get.get_socket_old(blender_material, "BaseColorFactor")
|
||||
if base_color_socket is None:
|
||||
base_color_socket = gltf2_blender_get.get_socket(blender_material, "Background")
|
||||
if isinstance(base_color_socket, bpy.types.NodeSocket):
|
||||
rgb = gltf2_blender_get.get_factor_from_socket(base_color_socket, kind='RGB')
|
||||
|
||||
|
@ -81,8 +79,6 @@ def __gather_base_color_texture(blender_material, export_settings):
|
|||
base_color_socket = gltf2_blender_get.get_socket(blender_material, "BaseColor")
|
||||
if base_color_socket is None:
|
||||
base_color_socket = gltf2_blender_get.get_socket_old(blender_material, "BaseColor")
|
||||
if base_color_socket is None:
|
||||
base_color_socket = gltf2_blender_get.get_socket(blender_material, "Background")
|
||||
|
||||
alpha_socket = gltf2_blender_get.get_socket(blender_material, "Alpha")
|
||||
if alpha_socket is not None and alpha_socket.is_linked:
|
||||
|
|
|
@ -0,0 +1,147 @@
|
|||
# Copyright 2018-2019 The glTF-Blender-IO authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from io_scene_gltf2.io.com import gltf2_io
|
||||
from io_scene_gltf2.blender.exp import gltf2_blender_gather_texture_info
|
||||
from io_scene_gltf2.blender.exp import gltf2_blender_get
|
||||
|
||||
|
||||
def detect_shadeless_material(blender_material, export_settings):
|
||||
"""Detect if this material is "shadeless" ie. should be exported
|
||||
with KHR_materials_unlit. Returns None if not. Otherwise, returns
|
||||
a dict with info from parsing the node tree.
|
||||
"""
|
||||
if not blender_material.use_nodes: return None
|
||||
|
||||
# Old Background node detection (unlikely to happen)
|
||||
bg_socket = gltf2_blender_get.get_socket(blender_material, "Background")
|
||||
if bg_socket is not None:
|
||||
return {'rgb_socket': bg_socket}
|
||||
|
||||
# Look for
|
||||
# * any color socket, connected to...
|
||||
# * optionally, the lightpath trick, connected to...
|
||||
# * optionally, a mix-with-transparent (for alpha), connected to...
|
||||
# * the output node
|
||||
|
||||
info = {}
|
||||
|
||||
for node in blender_material.node_tree.nodes:
|
||||
if node.type == 'OUTPUT_MATERIAL':
|
||||
socket = node.inputs[0]
|
||||
break
|
||||
else:
|
||||
return None
|
||||
|
||||
# Be careful not to misidentify a lightpath trick as mix-alpha.
|
||||
result = __detect_lightpath_trick(socket)
|
||||
if result is not None:
|
||||
socket = result['next_socket']
|
||||
else:
|
||||
result = __detect_mix_alpha(socket)
|
||||
if result is not None:
|
||||
socket = result['next_socket']
|
||||
info['alpha_socket'] = result['alpha_socket']
|
||||
|
||||
result = __detect_lightpath_trick(socket)
|
||||
if result is not None:
|
||||
socket = result['next_socket']
|
||||
|
||||
# Check if a color socket, or connected to a color socket
|
||||
if socket.type != 'RGBA':
|
||||
from_socket = gltf2_blender_get.previous_socket(socket)
|
||||
if from_socket is None: return None
|
||||
if from_socket.type != 'RGBA': return None
|
||||
|
||||
info['rgb_socket'] = socket
|
||||
return info
|
||||
|
||||
|
||||
def __detect_mix_alpha(socket):
|
||||
# Detects this (used for an alpha hookup)
|
||||
#
|
||||
# [ Mix ]
|
||||
# alpha_socket => [Factor ] => socket
|
||||
# [Transparent] => [Shader ]
|
||||
# next_socket => [Shader ]
|
||||
#
|
||||
# Returns None if not detected. Otherwise, a dict containing alpha_socket
|
||||
# and next_socket.
|
||||
prev = gltf2_blender_get.previous_node(socket)
|
||||
if prev is None or prev.type != 'MIX_SHADER': return None
|
||||
in1 = gltf2_blender_get.previous_node(prev.inputs[1])
|
||||
if in1 is None or in1.type != 'BSDF_TRANSPARENT': return None
|
||||
return {
|
||||
'alpha_socket': prev.inputs[0],
|
||||
'next_socket': prev.inputs[2],
|
||||
}
|
||||
|
||||
|
||||
def __detect_lightpath_trick(socket):
|
||||
# Detects this (used to prevent casting light on other objects) See ex.
|
||||
# https://blender.stackexchange.com/a/21535/88681
|
||||
#
|
||||
# [ Lightpath ] [ Mix ]
|
||||
# [ Is Camera Ray] => [Factor ] => socket
|
||||
# (don't care) => [Shader ]
|
||||
# next_socket => [ Emission ] => [Shader ]
|
||||
#
|
||||
# The Emission node can be omitted.
|
||||
# Returns None if not detected. Otherwise, a dict containing
|
||||
# next_socket.
|
||||
prev = gltf2_blender_get.previous_node(socket)
|
||||
if prev is None or prev.type != 'MIX_SHADER': return None
|
||||
in0 = gltf2_blender_get.previous_socket(prev.inputs[0])
|
||||
if in0 is None or in0.node.type != 'LIGHT_PATH': return None
|
||||
if in0.name != 'Is Camera Ray': return None
|
||||
next_socket = prev.inputs[2]
|
||||
|
||||
# Detect emission
|
||||
prev = gltf2_blender_get.previous_node(next_socket)
|
||||
if prev is not None and prev.type == 'EMISSION':
|
||||
next_socket = prev.inputs[0]
|
||||
|
||||
return {'next_socket': next_socket}
|
||||
|
||||
|
||||
def gather_base_color_factor(info, export_settings):
|
||||
rgb, alpha = None, None
|
||||
|
||||
if 'rgb_socket' in info:
|
||||
rgb = gltf2_blender_get.get_factor_from_socket(info['rgb_socket'], kind='RGB')
|
||||
if 'alpha_socket' in info:
|
||||
alpha = gltf2_blender_get.get_factor_from_socket(info['alpha_socket'], kind='VALUE')
|
||||
|
||||
if rgb is None: rgb = [1.0, 1.0, 1.0]
|
||||
if alpha is None: alpha = 1.0
|
||||
|
||||
rgba = [*rgb, alpha]
|
||||
if rgba == [1, 1, 1, 1]: return None
|
||||
return rgba
|
||||
|
||||
|
||||
def gather_base_color_texture(info, export_settings):
|
||||
sockets = (info.get('rgb_socket'), info.get('alpha_socket'))
|
||||
sockets = tuple(s for s in sockets if s is not None)
|
||||
if sockets:
|
||||
# NOTE: separate RGB and Alpha textures will not get combined
|
||||
# because gather_image determines how to pack images based on the
|
||||
# names of sockets, and the names are hard-coded to a Principled
|
||||
# style graph.
|
||||
return gltf2_blender_gather_texture_info.gather_texture_info(
|
||||
sockets[0],
|
||||
sockets,
|
||||
export_settings,
|
||||
)
|
||||
return None
|
|
@ -52,15 +52,12 @@ def gather_primitives(
|
|||
|
||||
for internal_primitive in blender_primitives:
|
||||
material_idx = internal_primitive['material']
|
||||
double_sided = False
|
||||
material = None
|
||||
|
||||
if export_settings['gltf_materials'] == "EXPORT":
|
||||
try:
|
||||
blender_material = bpy.data.materials[material_names[material_idx]]
|
||||
double_sided = not blender_material.use_backface_culling
|
||||
material = gltf2_blender_gather_materials.gather_material(blender_material,
|
||||
double_sided,
|
||||
export_settings)
|
||||
except IndexError:
|
||||
# no material at that index
|
||||
|
|
|
@ -231,7 +231,7 @@ def get_factor_from_socket(socket, kind):
|
|||
if fac is not None:
|
||||
return fac
|
||||
|
||||
node = __previous_node(socket)
|
||||
node = previous_node(socket)
|
||||
if node is not None:
|
||||
x1, x2 = None, None
|
||||
if kind == 'RGB':
|
||||
|
@ -259,7 +259,7 @@ def get_const_from_socket(socket, kind):
|
|||
return socket.default_value
|
||||
|
||||
# Handle connection to a constant RGB/Value node
|
||||
prev_node = __previous_node(socket)
|
||||
prev_node = previous_node(socket)
|
||||
if prev_node is not None:
|
||||
if kind == 'RGB' and prev_node.type == 'RGB':
|
||||
return list(prev_node.outputs[0].default_value)[:3]
|
||||
|
@ -269,16 +269,23 @@ def get_const_from_socket(socket, kind):
|
|||
return None
|
||||
|
||||
|
||||
def __previous_node(socket):
|
||||
def previous_socket(socket):
|
||||
while True:
|
||||
if not socket.is_linked:
|
||||
return None
|
||||
|
||||
node = socket.links[0].from_node
|
||||
from_socket = socket.links[0].from_socket
|
||||
|
||||
# Skip over reroute nodes
|
||||
if node.type == 'REROUTE':
|
||||
socket = node.inputs[0]
|
||||
if from_socket.node.type == 'REROUTE':
|
||||
socket = from_socket.node.inputs[0]
|
||||
continue
|
||||
|
||||
return node
|
||||
return from_socket
|
||||
|
||||
|
||||
def previous_node(socket):
|
||||
prev_socket = previous_socket(socket)
|
||||
if prev_socket is not None:
|
||||
return prev_socket.node
|
||||
return None
|
||||
|
|
Loading…
Reference in New Issue